/* * 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; /** * Sample rate converter, convert from one sample rate to another. */ public abstract class Resampler { protected int inSampleRate; protected int inChannels; protected int outSampleRate; protected int outChannels; protected String id; protected LowPassFilter lowPassFilter; protected Resampler(String id, int inSampleRate, int inChannels, int outSampleRate, int outChannels) throws IOException { this.id = id; if (inChannels != 1 && inChannels != 2) { Logger.println("invalid in channels " + inChannels); throw new IOException("invalid in channels " + inChannels); } if (outChannels != 1 && outChannels != 2) { Logger.println("SampleRateConverter: invalid out channels " + outChannels); throw new IOException("SampleRateConverter: invalid in channels " + inChannels); } if (inSampleRate <= 0) { Logger.println("SampleRateConverter: invalid input sample rate " + inSampleRate); throw new IOException("SampleRateConverter: " + " invalid input sample rate " + inSampleRate); } if (outSampleRate <= 0) { Logger.println("SampleRateConverter: invalid output sample rate " + outSampleRate); throw new IOException("SampleRateConverter: " + " invalid output sample rate " + outSampleRate); } this.inSampleRate = inSampleRate; this.inChannels = inChannels; this.outSampleRate = outSampleRate; this.outChannels = outChannels; if (inSampleRate > outSampleRate) { lowPassFilter = new LowPassFilter(id, inSampleRate, inChannels); } else { lowPassFilter = new LowPassFilter(id, outSampleRate, outChannels); } if (Logger.logLevel >= Logger.LOG_MOREINFO) { Logger.println("New Sample Converter: from " + inSampleRate + "/" + inChannels + " to " + outSampleRate + "/" + outChannels); } } protected 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; } protected abstract int[] resample(int[] inSamples) throws IOException; protected int[] reChannel(int[] inSamples) throws IOException { if ((inSamples.length % inChannels) != 0) { Logger.println("length " + inSamples.length + " is not a multiple of the frame size " + inChannels); throw new IOException("length " + inSamples.length + " is not a multiple of the frame size " + inChannels); } /* * XXX Possible optimization here. Always reduce channels before * resampling and wait to increase channels until after resampling. * The assumption is that resampling takes longer than resampling. */ int[] outSamples; if (inChannels > outChannels) { outSamples = reduceChannels(inSamples); } else if (inChannels < outChannels) { outSamples = increaseChannels(inSamples); } else { outSamples = inSamples; } return outSamples; } private int[] reduceChannels(int[] inSamples) { /* * inChannels is 2 and outChannels is 1 */ int outIx = 0; int[] outSamples = new int[inSamples.length / 2]; for (int inIx = 0; inIx < inSamples.length; inIx += 2) { int s1 = inSamples[inIx]; int s2 = inSamples[inIx + 1]; //outSamples[outIx] = (int) ((s1 + s2) / 2); outSamples[outIx] = (int) s1; outIx ++; } return outSamples; } private int[] increaseChannels(int[] inSamples) { /* * inChannels is 1 and outChannels is 2 */ int[] outSamples = new int[inSamples.length * 2]; int outIx = 0; for (int inIx = 0; inIx < inSamples.length; inIx++) { outSamples[outIx] = inSamples[inIx]; outIx++; outSamples[outIx] = inSamples[inIx]; outIx++; } return outSamples; } public abstract void reset(); public abstract void printStatistics(); }