/* * 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.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.File; import java.io.IOException; /** * Down sample */ public class Downsampler extends Resampler { private long totalTime; private int resampleCount; /* * XXX We only support big endian 16 bit samples! */ public Downsampler(String id, int inSampleRate, int inChannels, int outSampleRate, int outChannels) throws IOException { super(id, inSampleRate, inChannels, outSampleRate, outChannels); if (inSampleRate < outSampleRate) { throw new IOException("Downsampler inSampleRate " + inSampleRate + " < outSampleRate " + outSampleRate); } if (Logger.logLevel >= Logger.LOG_MOREINFO) { Logger.println("New DownSampler: from " + inSampleRate + "/" + inChannels + " to " + outSampleRate + "/" + outChannels); } } public void reset() { } 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(); int[] outSamples = reChannel(inSamples); if (inSampleRate == outSampleRate) { return outSamples; // no need to resample } outSamples = lowPassFilter.lpf(outSamples); outSamples = downsample(outSamples); totalTime += (CurrentTime.getTime() - start); return outSamples; } public int[] downsample(int[] inSamples) { int nFrames = inSamples.length / outChannels; int sampleTime = nFrames * 1000 / inSampleRate; if (sampleTime == 0) { sampleTime = 1; } int outLength = (sampleTime * outSampleRate * outChannels / 1000); if ((outLength & 1) != 0) { outLength++; } if (outLength == 0 || Logger.logLevel == -9) { Logger.println("downsample: inLength " + inSamples.length + " nFrames " + nFrames + " sampleTime " + sampleTime + " outLength " + outLength); } int[] outSamples = new int[outLength]; double frameIncr = (double)inSampleRate / (double)outSampleRate; int ix; double i = 0; int outIx = 0; if (Logger.logLevel == -9) { Logger.println("downsample frameIncr " + frameIncr + " nFrames " + nFrames + " inLength " + inSamples.length + " outLength " + outLength); Logger.println("inSamples"); } /* * Linear interpolation between the two closest samples. */ while (true) { ix = (int)i * outChannels; if (ix >= inSamples.length - outChannels) { // Don't we need to continue until outIx >= outLength? if (Logger.logLevel == -9) { Logger.println("Out of here! ix " + ix + " outIx " + outIx); } break; } int s1 = inSamples[ix]; int s2 = inSamples[ix + outChannels]; if (Logger.logLevel == -9) { Logger.println("s1 " + s1 + " s2 " + s2 + " int i " + (int)i + " ix " + ix + " outIx " + outIx); } outSamples[outIx] = (int) ((s1 + ((s2 - s1) * (i - (int)i)))); outIx++; if (outChannels == 2) { ix++; s1 = inSamples[ix]; s2 = inSamples[ix + outChannels]; outSamples[outIx] = (int) ((s1 + ((s2 - s1) * (i - (int)i)))); if (Logger.logLevel == -9) { Logger.println("+s1 " + s1 + " s2 " + s2 + " int i " + (int)i + " ix " + ix + " outIx " + outIx); } outIx++; } if (outIx >= outLength) { if (Logger.logLevel == -9) { Logger.println("Out of here! outIX " + outIx + " ix " + ix); } break; } i += frameIncr; } if (Logger.logLevel == -9) { Logger.logLevel = 3; Logger.println("downsample in len " + inSamples.length); Logger.println("downsample out len " + outSamples.length); } 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 downsample time from " + inSampleRate + "/" + inChannels + " to " + outSampleRate + "/" + outChannels); lowPassFilter.printStatistics(); } }