📄 pcm2pcmconversionprovider.java
字号:
/* * PCM2PCMConversionProvider.java *//* * Copyright (c) 2000 by Florian Bomers <florian@bome.com> * * * 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. * */package org.tritonus.sampled.convert;import java.util.Arrays;import java.util.Iterator;import javax.sound.sampled.AudioFormat;import javax.sound.sampled.AudioInputStream;import javax.sound.sampled.AudioSystem;import org.tritonus.share.ArraySet;import org.tritonus.share.TDebug;import org.tritonus.share.sampled.AudioFormats;import org.tritonus.share.sampled.Encodings;import org.tritonus.share.sampled.FloatSampleBuffer;import org.tritonus.share.sampled.TConversionTool;import org.tritonus.share.sampled.convert.TSimpleFormatConversionProvider;import org.tritonus.share.sampled.convert.TSynchronousFilteredAudioInputStream;/** * 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 { public static AudioFormat.Encoding PCM_SIGNED=Encodings.getEncoding("PCM_SIGNED"); public static AudioFormat.Encoding PCM_UNSIGNED=Encodings.getEncoding("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, sourceType, targetType, 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 result=new ArraySet(); Iterator iterator=getCollectionTargetFormats().iterator(); while (iterator.hasNext()) { AudioFormat targetFormat = (AudioFormat) iterator.next(); targetFormat=replaceNotSpecified(sourceFormat, targetFormat); if (isConversionSupported(targetFormat, sourceFormat)) { result.add(targetFormat); } } if (TDebug.TraceAudioConverter) { TDebug.out("<found "+result.size()+" matching formats."); } return (AudioFormat[]) 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 (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 functions */ 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";
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -