/*
* Copyright 2007 Sun Microsystems, Inc.
*
* This file is part of jVoiceBridge.
*
* jVoiceBridge is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation and distributed hereunder
* to you.
*
* jVoiceBridge is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* Sun designates this particular file as subject to the "Classpath"
* exception as provided by Sun in the License file that accompanied this
* code.
*/
package com.sun.voip;
import java.io.IOException;
/**
* Upsampler
*/
public class Upsampler extends Resampler {
private long totalTime;
private int resampleCount;
private int[] lastSample; // for upsampling
/*
* XXX We only support big endian 16 bit samples!
*/
public Upsampler(String id, int inSampleRate, int inChannels,
int outSampleRate, int outChannels) throws IOException {
super(id, inSampleRate, inChannels, outSampleRate, outChannels);
if (inSampleRate > outSampleRate) {
throw new IOException("Upsampler inSampleRate "
+ inSampleRate + " > outSampleRate " + outSampleRate);
}
if (Logger.logLevel >= Logger.LOG_MOREINFO) {
Logger.println("New Upsampler: from "
+ inSampleRate + "/" + inChannels + " to "
+ outSampleRate + "/" + outChannels);
}
reset();
}
public void reset() {
lastSample = new int[outChannels];
}
public byte[] resample(byte[] inSamples, int offset, int length)
throws IOException {
length = length & ~1; // round down
int[] ints = new int[length / 2];
AudioConversion.bytesToInts(inSamples, offset, length, ints);
ints = resample(ints);
byte[] bytes = new byte[ints.length * 2];
AudioConversion.intsToBytes(ints, bytes, offset);
return bytes;
}
public int[] resample(int[] inSamples) throws IOException {
if (inSampleRate == outSampleRate && inChannels == outChannels) {
return inSamples;
}
resampleCount++;
long start = CurrentTime.getTime();
/*
* Convert mono to multi-channel or vice versa as needed.
*
* Upsampling is done by interpolating between data points
* and producing the right number of output samples.
*/
int[] outSamples = reChannel(inSamples);
if (inSampleRate == outSampleRate) {
return outSamples; // no need to resample
}
outSamples = upsample(outSamples);
outSamples = lowPassFilter.lpf(outSamples);
totalTime += (CurrentTime.getTime() - start);
return outSamples;
}
private int[] upsample(int[] inSamples) {
/*
* Calculate the number of inSamples needed to produce an outSample.
* Round to the nearest integer.
* XXX The number of input samples must be divide into the
* input sample rate or else the outLength will be too small!
*/
int nSamples = inSamples.length / outChannels;
double sampleTime = (nSamples * 1000.0D) / inSampleRate;
int outLength = (int)(Math.round(
(sampleTime * outSampleRate * outChannels / 1000)));
//Logger.println("outLength " + outLength);
if ((outLength & 1) != 0) {
outLength++;
}
int[] outSamples = new int[outLength];
double frameIncr = (double)inSampleRate / (double)(outSampleRate);
int outIx = 0;
int[] last = new int[outChannels];
last[0] = inSamples[inSamples.length - outChannels];
if (outChannels == 2) {
last[1] = inSamples[inSamples.length - outChannels + 1];
}
int ix = 0;
double i = 0;
/*
* Linear interpolation between each two samples
*/
while (true) {
int intI = (int)i;
ix = intI * outChannels;
if (ix >= inSamples.length || outIx + outChannels > outLength ) {
break;
}
int s1;
if (ix == 0) {
s1 = lastSample[0];
} else {
s1 = inSamples[ix - outChannels];
}
int s2 = inSamples[ix];
int newSample = (int)(s1 + ((s2 - s1) * (i - intI)));
outSamples[outIx] = (int) newSample;
outIx++;
if (outChannels == 2) {
if (ix == 0) {
s1 = lastSample[1];
} else {
s1 = inSamples[ix - outChannels + 1];
}
s2 = inSamples[ix + 1];
newSample = (int)(s1 + ((s2 - s1) * (i - intI)));
outSamples[outIx] = (int) newSample;
outIx++;
}
if (outIx >= outLength) {
break;
}
i += frameIncr;
}
lastSample = last;
return outSamples;
}
public void printStatistics() {
if (resampleCount == 0) {
return;
}
double avg = (double)totalTime / resampleCount;
long timeUnitsPerSecond = CurrentTime.getTimeUnitsPerSecond();
avg = (avg / timeUnitsPerSecond) * 1000;
String s = "";
if (id != null) {
s += "Call " + id + ": ";
}
Logger.writeFile(s
+ avg + "ms avg upsample time from "
+ inSampleRate + "/" + inChannels + " to " + outSampleRate + "/"
+ outChannels);
lowPassFilter.printStatistics();
}
}