/* * PCM2PCMConversionProvider.java * * This file is part of Tritonus: http://www.tritonus.org/ */ /* * Copyright (c) 2000,2006,2007 by Florian Bomers <http://www.bomers.de> * * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as published * by the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program 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 Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * */ /* |<--- this code is formatted to fit into 80 columns --->| */ package org.tritonus.sampled.convert; import java.util.Arrays; import java.util.Iterator; import javax.sound.sampled.AudioSystem; import javax.sound.sampled.AudioFormat; import javax.sound.sampled.AudioInputStream; import org.tritonus.share.TDebug; import org.tritonus.share.sampled.AudioFormats; import org.tritonus.share.sampled.AudioUtils; import org.tritonus.share.sampled.TConversionTool; import org.tritonus.share.sampled.FloatSampleBuffer; import org.tritonus.share.sampled.convert.TSimpleFormatConversionProvider; import org.tritonus.share.sampled.convert.TSynchronousFilteredAudioInputStream; import org.tritonus.share.ArraySet; /** * This provider supports these PCM conversions (<--> meaning both directions): * <ul> * <li>8 Signed <-> 8 unsigned * <li>16/24/32 Signed little endian <-> 16/24/32 Signed big endian * <li>arbitrary conversion between 8/16/24/32 bit sample width<BR> * (up-conversion is done by adding low-byte zero(s)). * <li>1 channel <-> x channels * </ul> * The class uses 2 different approaches for conversion: * <ol> * <li>Simple, often used conversions with performance-optimized methods.<br> * These are the following conversions:<br> * <ul> * <li>8 Signed <-> 8 unsigned * <li>16 signed little endian <--> 16 signed big endian * <li>24 signed little endian <--> 24 signed big endian * <li>32 signed little endian <--> 32 signed big endian * <li>16 signed little endian <--> 8 signed * <li>16 signed big endian <--> 8 signed * <li>16 signed little endian <--> 8 unsigned * <li>16 signed big endian <--> 8 unsigned * </ul> * <br> * Downsampling from 16bit to 8bit is currently done using the float conversion * (see next point), in order to profit of dithering. * <li>All other conversions are done using the FloatSampleBuffer.<br> * Mixdown of channels (x channels -> 1 channel) is done by plainly adding all * channels together. Thus, up mixing and down mixing will not result in the * same audio, as downmixing does NOT lower the volume and clippings are very * probable. To avoid that, the volume of the channels should be lowered before * using this converter for down mixing. * <li>All conversions support upmixing of channels: 1 channel -> x channels. * This is done by copying the channel to the other channels <b>after</b> * conversion of the format (if necessary). * </ol> * <p> * SampleRate CANNOT be converted. * * @author Florian Bomers * @see org.tritonus.share.sampled.FloatSampleBuffer * @see org.tritonus.share.sampled.TConversionTool */ public class PCM2PCMConversionProvider extends TSimpleFormatConversionProvider { // if true, always use FloatSampleBuffer private static final boolean ONLY_FLOAT_CONVERSION = false; // only used as abbreviation public final static AudioFormat.Encoding PCM_SIGNED = AudioFormat.Encoding.PCM_SIGNED; public final static AudioFormat.Encoding PCM_UNSIGNED = AudioFormat.Encoding.PCM_UNSIGNED; private static final int ALL = AudioSystem.NOT_SPECIFIED; private static final AudioFormat[] OUTPUT_FORMATS = { // Encoding, SampleRate, sampleSizeInBits, channels, frameSize, // frameRate, bigEndian new AudioFormat(PCM_SIGNED, ALL, 8, ALL, ALL, ALL, false), new AudioFormat(PCM_SIGNED, ALL, 8, ALL, ALL, ALL, true), new AudioFormat(PCM_UNSIGNED, ALL, 8, ALL, ALL, ALL, false), new AudioFormat(PCM_UNSIGNED, ALL, 8, ALL, ALL, ALL, true), new AudioFormat(PCM_SIGNED, ALL, 16, ALL, ALL, ALL, false), new AudioFormat(PCM_SIGNED, ALL, 16, ALL, ALL, ALL, true), new AudioFormat(PCM_SIGNED, ALL, 24, ALL, ALL, ALL, false), new AudioFormat(PCM_SIGNED, ALL, 24, ALL, ALL, ALL, true), new AudioFormat(PCM_SIGNED, ALL, 32, ALL, ALL, ALL, false), new AudioFormat(PCM_SIGNED, ALL, 32, ALL, ALL, ALL, true), }; /** * Constructor. */ public PCM2PCMConversionProvider() { super(Arrays.asList(OUTPUT_FORMATS), Arrays.asList(OUTPUT_FORMATS)); } // formatType constants private static final int UNSIGNED8 = 1; private static final int SIGNED8 = 2; private static final int BIG_ENDIAN16 = 3; private static final int LITTLE_ENDIAN16 = 4; private static final int BIG_ENDIAN24 = 5; private static final int LITTLE_ENDIAN24 = 6; private static final int BIG_ENDIAN32 = 7; private static final int LITTLE_ENDIAN32 = 8; // conversionType private static final int CONVERT_NOT_POSSIBLE = 0; private static final int CONVERT_SIGN = 1; private static final int CONVERT_BYTE_ORDER16 = 2; private static final int CONVERT_BYTE_ORDER24 = 3; private static final int CONVERT_BYTE_ORDER32 = 4; private static final int CONVERT_16LTO8S = 5; private static final int CONVERT_16LTO8U = 6; private static final int CONVERT_16BTO8S = 7; private static final int CONVERT_16BTO8U = 8; private static final int CONVERT_8STO16L = 9; private static final int CONVERT_8STO16B = 10; private static final int CONVERT_8UTO16L = 11; private static final int CONVERT_8UTO16B = 12; private static final int CONVERT_ONLY_EXPAND_CHANNELS = 13; private static final int CONVERT_FLOAT = 100; // all other conversions private static final int CONVERT_NONE = 101; // no conversion necessary public AudioInputStream getAudioInputStream(AudioFormat targetFormat, AudioInputStream sourceStream) { AudioFormat sourceFormat = sourceStream.getFormat(); // the non-conversion case if (AudioFormats.matches(sourceFormat, targetFormat)) { return sourceStream; } if (doMatch(targetFormat.getFrameRate(), sourceFormat.getFrameRate()) && doMatch(targetFormat.getSampleRate(), sourceFormat.getSampleRate())) { targetFormat = replaceNotSpecified(sourceFormat, targetFormat); int sourceType = getFormatType(sourceFormat); int targetType = getFormatType(targetFormat); int conversionType = getConversionType(sourceType, sourceFormat.getChannels(), targetType, targetFormat.getChannels()); if (TDebug.TraceAudioConverter) { TDebug.out("PCM2PCM: sourceType=" + formatType2Str(sourceType) + ", " + sourceFormat.getChannels() + "ch" + " targetType=" + formatType2Str(targetType) + ", " + targetFormat.getChannels() + "ch" + " conversionType=" + conversionType2Str(conversionType)); } if (conversionType == CONVERT_NOT_POSSIBLE) { throw new IllegalArgumentException( "format conversion not supported"); } return new PCM2PCMStream(sourceStream, targetFormat, conversionType); } throw new IllegalArgumentException("format conversion not supported"); } public AudioFormat[] getTargetFormats(AudioFormat.Encoding targetEncoding, AudioFormat sourceFormat) { if (TDebug.TraceAudioConverter) { TDebug.out(">PCM2PCMFormatConversionProvider.getTargetFormats(AudioFormat.Encoding, AudioFormat):"); TDebug.out("checking out possible target formats"); TDebug.out("from: " + sourceFormat); TDebug.out("to : " + targetEncoding); } if (isConversionSupported(targetEncoding, sourceFormat)) { // TODO: check that no duplicates may occur... ArraySet<AudioFormat> result = new ArraySet<AudioFormat>(); Iterator<AudioFormat> iterator = getCollectionTargetFormats().iterator(); while (iterator.hasNext()) { AudioFormat targetFormat = iterator.next(); targetFormat = replaceNotSpecified(sourceFormat, targetFormat); if (isConversionSupported(targetFormat, sourceFormat)) { result.add(targetFormat); } } if (TDebug.TraceAudioConverter) { TDebug.out("<found " + result.size() + " matching formats."); } return result.toArray(EMPTY_FORMAT_ARRAY); } else { if (TDebug.TraceAudioConverter) { TDebug.out("<returning empty array."); } return EMPTY_FORMAT_ARRAY; } } /** * method overidden due to the difficult situation with the channel count * and the possible conversions possible. */ public boolean isConversionSupported(AudioFormat targetFormat, AudioFormat sourceFormat) { targetFormat = replaceNotSpecified(sourceFormat, targetFormat); boolean res = AudioFormats.matches(sourceFormat, targetFormat) || (doMatch(targetFormat.getFrameRate(), sourceFormat.getFrameRate()) && doMatch(targetFormat.getSampleRate(), sourceFormat.getSampleRate()) && getConversionType( getFormatType(sourceFormat), sourceFormat.getChannels(), getFormatType(targetFormat), targetFormat.getChannels()) != CONVERT_NOT_POSSIBLE); if (TDebug.TraceAudioConverter) { TDebug.out(">PCM2PCM: isConversionSupported(AudioFormat, AudioFormat):"); TDebug.out("checking if conversion possible"); TDebug.out("from: " + sourceFormat); TDebug.out("to : " + targetFormat); TDebug.out("< result : " + res); } return res; } private int getFormatType(AudioFormat af) { int result = 0; AudioFormat.Encoding encoding = af.getEncoding(); boolean bigEndian = af.isBigEndian(); int ssib = af.getSampleSizeInBits(); // now set up the convert type if (encoding.equals(PCM_SIGNED)) { if (ssib == 32) { if (bigEndian) { result = BIG_ENDIAN32; } else { result = LITTLE_ENDIAN32; } } else if (ssib == 24) { if (bigEndian) { result = BIG_ENDIAN24; } else { result = LITTLE_ENDIAN24; } } else if (ssib == 16) { if (bigEndian) { result = BIG_ENDIAN16; } else { result = LITTLE_ENDIAN16; } } else if (ssib == 8) { result = SIGNED8; } } else if (encoding.equals(PCM_UNSIGNED)) { if (ssib == 8) { result = UNSIGNED8; } } return result; } private int getConversionType(int sourceType, int sourceChannels, int targetType, int targetChannels) { if (sourceType == 0 || targetType == 0 || (sourceChannels != 1 && targetChannels != 1 && targetChannels != sourceChannels)) { return CONVERT_NOT_POSSIBLE; } if (sourceType == targetType) { if (sourceChannels == targetChannels) { return CONVERT_NONE; } else if (sourceChannels == 1 && targetChannels > 1) { return CONVERT_ONLY_EXPAND_CHANNELS; } } if (!ONLY_FLOAT_CONVERSION && (sourceChannels == 1 && targetChannels >= 1 || sourceChannels == targetChannels)) { // when channels only have to be duplicated, direct conversions can // be done if ((sourceType == UNSIGNED8 && targetType == SIGNED8) || (sourceType == SIGNED8 && targetType == UNSIGNED8)) { return CONVERT_SIGN; } else if ((sourceType == BIG_ENDIAN16 && targetType == LITTLE_ENDIAN16) || (sourceType == LITTLE_ENDIAN16 && targetType == BIG_ENDIAN16)) { return CONVERT_BYTE_ORDER16; } else if ((sourceType == BIG_ENDIAN24 && targetType == LITTLE_ENDIAN24) || (sourceType == LITTLE_ENDIAN24 && targetType == BIG_ENDIAN24)) { return CONVERT_BYTE_ORDER24; } else if ((sourceType == BIG_ENDIAN32 && targetType == LITTLE_ENDIAN32) || (sourceType == LITTLE_ENDIAN32 && targetType == BIG_ENDIAN32)) { return CONVERT_BYTE_ORDER32; /* * downsampling is better handled with Float conversion -> * dithering } else if (sourceType==LITTLE_ENDIAN16 && * targetType==SIGNED8) { return CONVERT_16LTO8S; } else if * (sourceType==LITTLE_ENDIAN16 && targetType==UNSIGNED8) { * return CONVERT_16LTO8U; } else if (sourceType==BIG_ENDIAN16 && * targetType==SIGNED8) { return CONVERT_16BTO8S; } else if * (sourceType==BIG_ENDIAN16 && targetType==UNSIGNED8) { return * CONVERT_16BTO8U; */ } else if (sourceType == SIGNED8 && targetType == LITTLE_ENDIAN16) { return CONVERT_8STO16L; } else if (sourceType == SIGNED8 && targetType == BIG_ENDIAN16) { return CONVERT_8STO16B; } else if (sourceType == UNSIGNED8 && targetType == LITTLE_ENDIAN16) { return CONVERT_8UTO16L; } else if (sourceType == UNSIGNED8 && targetType == BIG_ENDIAN16) { return CONVERT_8UTO16B; } } return CONVERT_FLOAT; } /** * Debugging function */ private static String formatType2Str(int formatType) { switch (formatType) { case 0: return "unsupported"; case UNSIGNED8: return "UNSIGNED8"; case SIGNED8: return "SIGNED8"; case BIG_ENDIAN16: return "BIG_ENDIAN16"; case LITTLE_ENDIAN16: return "LITTLE_ENDIAN16"; case BIG_ENDIAN24: return "BIG_ENDIAN24"; case LITTLE_ENDIAN24: return "LITTLE_ENDIAN24"; case BIG_ENDIAN32: return "BIG_ENDIAN32"; case LITTLE_ENDIAN32: return "LITTLE_ENDIAN32"; } return "unknown"; } /** * Debugging function */ protected static String conversionType2Str(int conversionType) { switch (conversionType) { case CONVERT_NOT_POSSIBLE: return "CONVERT_NOT_POSSIBLE"; case CONVERT_SIGN: return "CONVERT_SIGN"; case CONVERT_BYTE_ORDER16: return "CONVERT_BYTE_ORDER16"; case CONVERT_BYTE_ORDER24: return "CONVERT_BYTE_ORDER24"; case CONVERT_BYTE_ORDER32: return "CONVERT_BYTE_ORDER32"; case CONVERT_16LTO8S: return "CONVERT_16LTO8S"; case CONVERT_16LTO8U: return "CONVERT_16LTO8U"; case CONVERT_16BTO8S: return "CONVERT_16BTO8S"; case CONVERT_16BTO8U: return "CONVERT_16BTO8U"; case CONVERT_8STO16L: return "CONVERT_8STO16L"; case CONVERT_8STO16B: return "CONVERT_8STO16B"; case CONVERT_8UTO16L: return "CONVERT_8UTO16L"; case CONVERT_8UTO16B: return "CONVERT_8UTO16B"; case CONVERT_ONLY_EXPAND_CHANNELS: return "CONVERT_ONLY_EXPAND_CHANNELS"; case CONVERT_FLOAT: return "CONVERT_FLOAT"; case CONVERT_NONE: return "CONVERT_NONE"; } return "unknown"; } /** * PCM2PCMStream Provides direct conversion of some selected formats and * rxpanding of channels. */ class PCM2PCMStream extends TSynchronousFilteredAudioInputStream { private int conversionType; private boolean needExpandChannels; private boolean needMixDown; private AudioFormat intermediateFloatBufferFormat; private FloatSampleBuffer floatBuffer = null; public PCM2PCMStream(AudioInputStream sourceStream, AudioFormat targetFormat, int conversionType) { // transform the targetFormat so that // FrameRate, and SampleRate match the sourceFormat super(sourceStream, new AudioFormat(targetFormat.getEncoding(), sourceStream.getFormat().getSampleRate(), targetFormat.getSampleSizeInBits(), targetFormat.getChannels(), AudioUtils.getFrameSize( targetFormat.getChannels(), targetFormat.getSampleSizeInBits()), sourceStream.getFormat().getFrameRate(), targetFormat.isBigEndian(), targetFormat.properties())); if (TDebug.TraceAudioConverter) { TDebug.out("PCM2PCMStream: constructor. ConversionType=" + conversionType2Str(conversionType)); } this.conversionType = conversionType; needExpandChannels = sourceStream.getFormat().getChannels() < targetFormat.getChannels(); needMixDown = sourceStream.getFormat().getChannels() > targetFormat.getChannels(); // some sanity tests. These can be dropped when this converter has // been tested enough... if (needMixDown && conversionType != CONVERT_FLOAT) { throw new IllegalArgumentException( "PCM2PCMStream: MixDown only possible with CONVERT_FLOAT"); } if (needMixDown && targetFormat.getChannels() != 1) { throw new IllegalArgumentException( "PCM2PCMStream: MixDown only possible with target channel count=1"); } if (needExpandChannels && sourceStream.getFormat().getChannels() != 1) { throw new IllegalArgumentException( "PCM2PCMStream: Expanding channels only possible with source channel count=1"); } // end sanity if (conversionType == CONVERT_FLOAT) { int floatChannels = needExpandChannels ? 1 : targetFormat.getChannels(); intermediateFloatBufferFormat = new AudioFormat( targetFormat.getEncoding(), sourceStream.getFormat().getSampleRate(), targetFormat.getSampleSizeInBits(), floatChannels, AudioUtils.getFrameSize(floatChannels, targetFormat.getSampleSizeInBits()), sourceStream.getFormat().getFrameRate(), targetFormat.isBigEndian(), targetFormat.properties()); // with floatBuffer we need to copy anyway, so enable in-place // conversion enableConvertInPlace(); } if (!needExpandChannels && (conversionType == CONVERT_SIGN || conversionType == CONVERT_BYTE_ORDER16 || conversionType == CONVERT_BYTE_ORDER24 || conversionType == CONVERT_BYTE_ORDER32)) { enableConvertInPlace(); } // can always convert in float layer enableFloatConversion(); } // these functions only treat the highbyte of 16bit samples // obsolete: is handled with FloatBuffer because of dithering private final void do16BTO8S(byte[] inBuffer, int inCounter, byte[] outBuffer, int outByteOffset, int sampleCount) { for (; sampleCount > 0; sampleCount--, inCounter++) { outBuffer[outByteOffset++] = inBuffer[inCounter++]; } } // obsolete: is handled with FloatBuffer because of dithering private final void do16BTO8U(byte[] inBuffer, int inCounter, byte[] outBuffer, int outByteOffset, int sampleCount) { for (; sampleCount > 0; sampleCount--, inCounter++) { outBuffer[outByteOffset++] = (byte) (inBuffer[inCounter++] + 128); } } private final void do8STO16L(byte[] inBuffer, byte[] outBuffer, int outByteOffset, int sampleCount) { for (int inCounter = 0; sampleCount > 0; sampleCount--) { outBuffer[outByteOffset++] = 0; outBuffer[outByteOffset++] = inBuffer[inCounter++]; } } private final void do8UTO16L(byte[] inBuffer, byte[] outBuffer, int outByteOffset, int sampleCount) { for (int inCounter = 0; sampleCount > 0; sampleCount--) { outBuffer[outByteOffset++] = 0; outBuffer[outByteOffset++] = (byte) (inBuffer[inCounter++] + 128); } } private final void do8STO16B(byte[] inBuffer, byte[] outBuffer, int outByteOffset, int sampleCount) { for (int inCounter = 0; sampleCount > 0; sampleCount--) { outBuffer[outByteOffset++] = inBuffer[inCounter++]; outBuffer[outByteOffset++] = 0; } } private final void do8UTO16B(byte[] inBuffer, byte[] outBuffer, int outByteOffset, int sampleCount) { for (int inCounter = 0; sampleCount > 0; sampleCount--) { outBuffer[outByteOffset++] = (byte) (inBuffer[inCounter++] + 128); outBuffer[outByteOffset++] = 0; } } /** copy the channels: in the buffer there is only one channel */ private final void expandChannels(byte[] buffer, int offset, int frameCount, int bytesPerFrame, int channels) { int inOffset = offset + bytesPerFrame * frameCount; int outOffset = offset + bytesPerFrame * channels * frameCount; switch (bytesPerFrame) { case 1: if (channels == 2) { for (; frameCount > 0; frameCount--) { buffer[--outOffset] = buffer[--inOffset]; buffer[--outOffset] = buffer[inOffset]; } } else { for (; frameCount > 0; frameCount--) { inOffset--; for (int channel = 0; channel < channels; channel++) { buffer[--outOffset] = buffer[inOffset]; } } } break; case 2: if (channels == 2) { for (; frameCount > 0; frameCount--) { buffer[--outOffset] = buffer[--inOffset]; buffer[--outOffset] = buffer[inOffset - 1]; buffer[--outOffset] = buffer[inOffset]; buffer[--outOffset] = buffer[--inOffset]; } } else { for (; frameCount > 0; frameCount--) { inOffset--; for (int channel = 0; channel < channels; channel++) { buffer[--outOffset] = buffer[inOffset]; buffer[--outOffset] = buffer[inOffset - 1]; } inOffset--; } } break; default: for (; frameCount > 0; frameCount--) { for (int channel = 0; channel < channels; channel++) { for (int by = 1; by <= bytesPerFrame; by++) { buffer[--outOffset] = buffer[inOffset - by]; } } inOffset -= bytesPerFrame; } break; } } private final void doFloatConversion(FloatSampleBuffer buffer) { doFloatConversion(buffer, needExpandChannels); } private final void doFloatConversion(FloatSampleBuffer buffer, boolean expandChannels) { if (needMixDown) { buffer.mixDownChannels(); } if (expandChannels) { buffer.expandChannel(getFormat().getChannels()); } } private final void doFloatConversion(byte[] inBuffer, int inByteOffset, byte[] outBuffer, int outByteOffset, int sampleCount) { int byteCount = sampleCount * ((getOriginalStream().getFormat().getSampleSizeInBits() + 7) / 8); if (floatBuffer == null) { floatBuffer = new FloatSampleBuffer(); } floatBuffer.initFromByteArray(inBuffer, inByteOffset, byteCount, getOriginalStream().getFormat()); // expansion is done on byte array doFloatConversion(floatBuffer, false); floatBuffer.convertToByteArray(outBuffer, outByteOffset, intermediateFloatBufferFormat); } @Override protected int convert(byte[] inBuffer, byte[] outBuffer, int outByteOffset, int inFrameCount) { int sampleCount = inFrameCount * getOriginalStream().getFormat().getChannels(); switch (conversionType) { case CONVERT_SIGN: TConversionTool.convertSign8(inBuffer, 0, outBuffer, outByteOffset, sampleCount); break; case CONVERT_BYTE_ORDER16: TConversionTool.swapOrder16(inBuffer, 0, outBuffer, outByteOffset, sampleCount); break; case CONVERT_BYTE_ORDER24: TConversionTool.swapOrder24(inBuffer, 0, outBuffer, outByteOffset, sampleCount); break; case CONVERT_BYTE_ORDER32: TConversionTool.swapOrder32(inBuffer, 0, outBuffer, outByteOffset, sampleCount); break; case CONVERT_16LTO8S: do16BTO8S(inBuffer, 1, outBuffer, outByteOffset, sampleCount); break; case CONVERT_16LTO8U: do16BTO8U(inBuffer, 1, outBuffer, outByteOffset, sampleCount); break; case CONVERT_16BTO8S: do16BTO8S(inBuffer, 0, outBuffer, outByteOffset, sampleCount); break; case CONVERT_16BTO8U: do16BTO8U(inBuffer, 0, outBuffer, outByteOffset, sampleCount); break; case CONVERT_8STO16L: do8STO16L(inBuffer, outBuffer, outByteOffset, sampleCount); break; case CONVERT_8STO16B: do8STO16B(inBuffer, outBuffer, outByteOffset, sampleCount); break; case CONVERT_8UTO16L: do8UTO16L(inBuffer, outBuffer, outByteOffset, sampleCount); break; case CONVERT_8UTO16B: do8UTO16B(inBuffer, outBuffer, outByteOffset, sampleCount); break; case CONVERT_ONLY_EXPAND_CHANNELS: // implicit: channelCount in inBuffer=1 System.arraycopy( inBuffer, 0, outBuffer, outByteOffset, inFrameCount * getOriginalStream().getFormat().getFrameSize()); break; case CONVERT_FLOAT: doFloatConversion(inBuffer, 0, outBuffer, outByteOffset, sampleCount); break; default: throw new RuntimeException( "PCM2PCMStream: Call to convert with unknown conversionType."); } if (needExpandChannels) { expandChannels(outBuffer, outByteOffset, inFrameCount, (getFormat().getSampleSizeInBits() + 7) / 8, getFormat().getChannels()); } return inFrameCount; } @Override protected void convertInPlace(byte[] buffer, int byteOffset, int frameCount) { int sampleCount = frameCount * getOriginalStream().getFormat().getChannels(); switch (conversionType) { case CONVERT_SIGN: TConversionTool.convertSign8(buffer, byteOffset, sampleCount); break; case CONVERT_BYTE_ORDER16: TConversionTool.swapOrder16(buffer, byteOffset, sampleCount); break; case CONVERT_BYTE_ORDER24: TConversionTool.swapOrder24(buffer, byteOffset, sampleCount); break; case CONVERT_BYTE_ORDER32: TConversionTool.swapOrder32(buffer, byteOffset, sampleCount); break; case CONVERT_FLOAT: doFloatConversion(buffer, byteOffset, buffer, byteOffset, sampleCount); if (needExpandChannels) { expandChannels(buffer, byteOffset, frameCount, (getFormat().getSampleSizeInBits() + 7) / 8, getFormat().getChannels()); } break; default: throw new RuntimeException( "PCM2PCMStream: Call to convertInPlace, but it cannot convert in place."); } } /** * Convert this buffer. Since float buffers do not need to be PCM * converted, offset and count are ignored. */ @Override protected void convert(FloatSampleBuffer buffer, int offset, int count) { doFloatConversion(buffer); } } } /** * PCM2PCMFormatConversionProvider.java ** */