⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 rateconverter.java

📁 FMJ(freedom media for java)是java视频开发的新选择
💻 JAVA
📖 第 1 页 / 共 2 页
字号:
package net.sf.fmj.media.codec.audio;import java.util.ArrayList;import java.util.List;import java.util.logging.Logger;import javax.media.Buffer;import javax.media.Format;import javax.media.format.AudioFormat;import net.sf.fmj.codegen.MediaCGUtils;import net.sf.fmj.media.AbstractCodec;import net.sf.fmj.utility.LoggerSingleton;import net.sf.fmj.utility.UnsignedUtils;/** *  * Converts between different linear audio formats.  Able to change signed/unsigned, endian-ness, bits per sample, sample rate, channels. * TODO: optimize. * TODO: improve quality of conversions.  See for example http://www.fmjsoft.com/atquality.html (name fmjsoft is coincidental). * TODO: change stereo to mono by averaging, rather than by omission of 2nd channel. * TODO: handle data types besides byte arrays * This converter has so many formats that it causes a big slowdown in filter graph building, both for FMJ and JMF. * @author Ken Larson * */public class RateConverter extends AbstractCodec{	private static final boolean ONLY_CHANGE_1_PARAMETER = false;	// TODO: more efficient if false.  Still needs testing to make sure changing multiple works.	private static final Logger logger = LoggerSingleton.logger;	public String getName()	{		return "Rate Converter";	}		public RateConverter()	{		super();		this.inputFormats = new Format[] {				new AudioFormat(AudioFormat.LINEAR, -1.0, -1, -1, -1, -1, -1, -1.0, Format.byteArray)				};			}		// TODO: move to base class?	protected Format[] outputFormats = new Format[] {new AudioFormat(AudioFormat.LINEAR, -1.0, -1, -1, -1, -1, -1, -1.0, Format.byteArray)};		// supported parameter values for conversions:	// common audio sample rates.  Others could be added with no impact.	private static final double[] sampleRateValues = new double[] {8000.0, 11025.0, 22050.0, 44100.0, 48000.0};	private static final int[] sampleSizesInBits = new int[] {8, 16, 24, 32};	// TODO: test 24 and 32.	private static final int[] endianValues = new int[] {AudioFormat.BIG_ENDIAN, AudioFormat.LITTLE_ENDIAN};	private static final int[] signedValues = new int[] {AudioFormat.SIGNED, AudioFormat.UNSIGNED};	private static final int[] channelsValues = new int[] {1, 2};	                         	public Format[] getSupportedOutputFormats(Format input)	{		if (input == null)			return outputFormats;		else		{				if (!(input instanceof AudioFormat))			{	logger.warning(this.getClass().getSimpleName() + ".getSupportedOutputFormats: input format does not match, returning format array of {null} for " + input); // this can cause an NPE in JMF if it ever happens.				return new Format[] {null};			}			final AudioFormat inputCast = (AudioFormat) input;			if (!inputCast.getEncoding().equals(AudioFormat.LINEAR) ||				(inputCast.getDataType() != null && inputCast.getDataType() != Format.byteArray)				)			{					logger.warning(this.getClass().getSimpleName() + ".getSupportedOutputFormats: input format does not match, returning format array of {null} for " + input); // this can cause an NPE in JMF if it ever happens.				return new Format[] {null};			}						final List resultList = new ArrayList();						// copy this.sampleRateValues, add input sample rate to it, in case it is not in the supported list.			final double[] sampleRateValues = new double[RateConverter.sampleRateValues.length + 1];			sampleRateValues[0] = inputCast.getSampleRate();			for (int i = 0; i < RateConverter.sampleRateValues.length; ++i)				sampleRateValues[i+1] = RateConverter.sampleRateValues[i];						for (int i = 0; i < sampleRateValues.length; ++i)			{				final double sampleRate = sampleRateValues[i];				final boolean sampleRateChanged = sampleRate != inputCast.getSampleRate();				for (int j = 0; j < sampleSizesInBits.length; ++j)				{					final int sampleSizeInBits = sampleSizesInBits[j];					final boolean sampleSizeInBitsChanged = sampleSizeInBits != inputCast.getSampleSizeInBits();					for (int k = 0; k < endianValues.length; ++k)					{						// endian is only meaningful if there are more than 8 bits						final int endian = sampleSizeInBits <= 8 ? inputCast.getEndian() : endianValues[k];						final boolean endianChanged = (sampleSizeInBits > 8 && inputCast.getSampleSizeInBits() > 8) && // only consider endian changed if neither of the formsts is 8 bits														endian != inputCast.getEndian();						for (int m = 0; m < signedValues.length; ++m)						{							final int signed = signedValues[m];							final boolean signedChanged = signed != inputCast.getSigned();											for (int n = 0; n < channelsValues.length; ++n)							{								final int channels = channelsValues[n];								final boolean channelsChanged = channels != inputCast.getChannels();													        int numChanged = 0;						        if (sampleRateChanged) ++numChanged;									        if (sampleSizeInBitsChanged) ++numChanged;	 						        if (endianChanged) ++numChanged;						        if (signedChanged) ++numChanged;						        if (channelsChanged) ++numChanged;							        if (numChanged < 1)						        	continue;						        						        if (ONLY_CHANGE_1_PARAMETER && numChanged != 1)						        	continue;																{										AudioFormat f = new AudioFormat(AudioFormat.LINEAR, sampleRate, sampleSizeInBits,											channels, endian, signed, channels * sampleSizeInBits, 											sampleRate, Format.byteArray);									// Note: for linear, frame size in bits is always the same as for sample size in bits * the number of channels.																		if (!resultList.contains(f))										resultList.add(f);								}							}						}					}				}			}						final Format[] result = new Format[resultList.size()];			for (int i = 0; i < resultList.size(); ++i)				result[i] = (Format) resultList.get(i);			return result;		}	}	public void open()	{		for (Averager a : averagers)			a.reset();	}		public void close()	{	}			private static final boolean TRACE = false;		private final Averager averagers[] = new Averager[] {new Averager(), new Averager()};		private static class Averager	{	    public double accumulatedSample = 0.0;		// used only for averaging, if output sample rate is less than input	    public double numAccumulatedSamples = 0.0;	// ditto	    	    public void reset()	    {	    	accumulatedSample = 0.0;					numAccumulatedSamples = 0.0;		    }	}        	public int process(Buffer inputBuffer, Buffer outputBuffer)	{		//if (TRACE) dump("input ", inputBuffer);		        if (!checkInputBuffer(inputBuffer))        {            return BUFFER_PROCESSED_FAILED;        }        if (isEOM(inputBuffer))        {            propagateEOM(outputBuffer);	// TODO: what about data? can there be any?            return BUFFER_PROCESSED_OK;        }                // TODO: check in checkInputBuffer?        if (!inputBuffer.getFormat().equals(inputFormat))        {	        	throw new RuntimeException("Expected inputBuffer.getFormat().equals(inputFormat): [" + inputBuffer.getFormat() + "] [" + inputFormat + "]");        }        final AudioFormat inputAudioFormat = ((AudioFormat) inputBuffer.getFormat());        final AudioFormat outputAudioFormat = (AudioFormat) outputFormat;        final boolean sampleRateChanged = inputAudioFormat.getSampleRate() != outputAudioFormat.getSampleRate();        final boolean sampleSizeInBitsChanged = inputAudioFormat.getSampleSizeInBits() != outputAudioFormat.getSampleSizeInBits();        final boolean endianChanged = (outputAudioFormat.getSampleSizeInBits() > 8 && inputAudioFormat.getSampleSizeInBits() > 8) && // only consider endian changed if neither of the formats is 8 bits										inputAudioFormat.getEndian() != outputAudioFormat.getEndian();        final boolean signedChanged = inputAudioFormat.getSigned() != outputAudioFormat.getSigned();        final boolean channelsChanged = inputAudioFormat.getChannels() != outputAudioFormat.getChannels();        //System.out.println("RateConvert \n\t\t from: " + inputFormat + "\n\t\t to:" + outputFormat);                int numChanged = 0;        if (sampleRateChanged) ++numChanged;			        if (sampleSizeInBitsChanged) ++numChanged;	         if (endianChanged) ++numChanged;        if (signedChanged) ++numChanged;        if (channelsChanged) ++numChanged;                if (ONLY_CHANGE_1_PARAMETER && numChanged > 1)        {	            // in this case, we only support doing one thing in a particular instance of this codec.            // this keeps it simple.  Converting multiple format attributes is achieved then by chaining multiple instances            // of this codec, with different input and output formats.        	logger.warning("Input format:  " + inputAudioFormat);        	logger.warning("Output format: " + outputAudioFormat);        	        	throw new RuntimeException("Expected RateConverter to change no more than one attribute: " + numChanged + "; sampleRateChanged=" + sampleRateChanged + " sampleSizeInBitsChanged=" + sampleSizeInBitsChanged + " endianChanged=" + endianChanged + " signedChanged=" + signedChanged);        }                // TODO: check these in setInputFormat and setOutputFormat    	if (outputAudioFormat.getSampleSizeInBits() % 8 != 0)    		throw new RuntimeException("RateConverter only supports output sample sizes that are a multiple of 8: " + outputAudioFormat.getSampleSizeInBits());    	if (inputAudioFormat.getSampleSizeInBits() % 8 != 0)    		throw new RuntimeException("RateConverter only supports input sample sizes that are a multiple of 8: " + inputAudioFormat.getSampleSizeInBits());    	if (inputAudioFormat.getSampleSizeInBits() > 8 && inputAudioFormat.getEndian() == -1)    		throw new RuntimeException("RateConverter (input format) requires endian to be specified if sample size in bits is greater than 8");    	if (outputAudioFormat.getSampleSizeInBits() > 8 && outputAudioFormat.getEndian() == -1)    		throw new RuntimeException("RateConverter (output format) requires endian to be specified if sample size in bits is greater than 8");    	//    	if (inputAudioFormat.getFrameSizeInBits() != inputAudioFormat.getChannels() * inputAudioFormat.getSampleSizeInBits())//    		throw new RuntimeException("Expected (input format) frame size in bits to be sample size in bits * number of channels");//    	if (outputAudioFormat.getFrameSizeInBits() != outputAudioFormat.getChannels() * outputAudioFormat.getSampleSizeInBits())//    		throw new RuntimeException("Expected (output format) frame size in bits to be sample size in bits * number of channels");    	    	// output : input    	final double sampleRateRatio = outputAudioFormat.getSampleRate() / inputAudioFormat.getSampleRate();    	final int sampleRateRatioWhole = (int) sampleRateRatio;    	final double sampleRateRatioRemainder = sampleRateRatio - sampleRateRatioWhole;	// TODO: this is most certainly wrong.    	// input : output    	final double sampleRateInverseRatio = inputAudioFormat.getSampleRate() / outputAudioFormat.getSampleRate();    	final int sampleRateInverseRatioWhole = (int) sampleRateInverseRatio;    	final int sampleRateInverseRatioCeil = (int) Math.ceil(sampleRateInverseRatio);    	// sampleRateAveragingErrorIncrement is the amount that our sample rate error increases with each input sample.    	// say input sample rate is 22050 and output is 8000.  Then this is 2.76:1.    	// without compensation, we will output a sample for every 3 input samples.  So we need to output an extra sample    	// every so often to bring the output to 2.76 for every 3.  So 3-2.76 is the error in the "input" side.  To    	// convert this to an error in the "output" side, we have to divide by the ratio, or 2.76 (helps to draw triangles to visualize this).  So the increment is (3-2.76)/2.76.    	final double sampleRateAveragingErrorIncrement = (sampleRateInverseRatioCeil - sampleRateInverseRatio) / sampleRateInverseRatio;        final int inputSampleSizeInBytes = inputAudioFormat.getSampleSizeInBits() / 8;        final int inputSamples = inputBuffer.getLength() / inputSampleSizeInBytes;	// this implies that we will only process whole samples in the input buffer.        final int outputSampleSizeInBytes = outputAudioFormat.getSampleSizeInBits() / 8;        int requiredOutputBufferLength;        if (sampleRateChanged)        {	        	// allocate 1 extra output sample, for rounding errors.  The actual number of bytes put in the buffer will be correct, based on outputSampleIndex * outputSampleSizeInBytes        	requiredOutputBufferLength = (int) ((inputSamples * outputSampleSizeInBytes) * Math.ceil(sampleRateRatio));	// TODO: rounds way up//        	 TODO: what if this truncates some samples?        }        else if (sampleSizeInBitsChanged)        {	// we only support changing of sample sizes         	requiredOutputBufferLength = inputSamples * outputSampleSizeInBytes;        	// TODO: what if this truncates some samples?        }        else        	requiredOutputBufferLength = inputBuffer.getLength();	// same number of bytes output as input        boolean stereoToMono = false;        boolean monoToStereo = false;        int outputChannelRepeatCount = 1;	// equal to 2 only if monoToStereo is true.        if (channelsChanged)        {        	if (inputAudioFormat.getChannels() == 2 && outputAudioFormat.getChannels() == 1)        		stereoToMono = true;        	else if (inputAudioFormat.getChannels() == 1 && outputAudioFormat.getChannels() == 2)        	{	outputChannelRepeatCount = 2;        		monoToStereo = true;

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -