/* * 11/19/04 1.0 moved to LGPL. * 01/12/99 Initial version. mdm@techie.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 audio.javazoom.jl.decoder; /** * The <code>Decoder</code> class encapsulates the details of * decoding an MPEG audio frame. * * @author MDM * @version 0.0.7 12/12/99 * @since 0.0.5 */ public class Decoder implements DecoderErrors { static private final Params DEFAULT_PARAMS = new Params(); /** * The Bistream from which the MPEG audio frames are read. */ //private Bitstream stream; /** * The Obuffer instance that will receive the decoded * PCM samples. */ private Obuffer output; /** * Synthesis filter for the left channel. */ private SynthesisFilter filter1; /** * Sythesis filter for the right channel. */ private SynthesisFilter filter2; /** * The decoder used to decode layer III frames. */ private LayerIIIDecoder l3decoder; private LayerIIDecoder l2decoder; private LayerIDecoder l1decoder; private int outputFrequency; private int outputChannels; private Equalizer equalizer = new Equalizer(); private Params params; private boolean initialized; /** * Creates a new <code>Decoder</code> instance with default * parameters. */ public Decoder() { this(null); } /** * Creates a new <code>Decoder</code> instance with default * parameters. * * @param params The <code>Params</code> instance that describes * the customizable aspects of the decoder. */ public Decoder(Params params0) { if (params0==null) params0 = DEFAULT_PARAMS; params = params0; Equalizer eq = params.getInitialEqualizerSettings(); if (eq!=null) { equalizer.setFrom(eq); } } static public Params getDefaultParams() { return (Params)DEFAULT_PARAMS.clone(); } public void setEqualizer(Equalizer eq) { if (eq==null) eq = Equalizer.PASS_THRU_EQ; equalizer.setFrom(eq); float[] factors = equalizer.getBandFactors(); if (filter1!=null) filter1.setEQ(factors); if (filter2!=null) filter2.setEQ(factors); } /** * Decodes one frame from an MPEG audio bitstream. * * @param header The header describing the frame to decode. * @param bitstream The bistream that provides the bits for te body of the frame. * * @return A SampleBuffer containing the decoded samples. */ public Obuffer decodeFrame(Header header, Bitstream stream) throws DecoderException { if (!initialized) { initialize(header); } int layer = header.layer(); output.clear_buffer(); FrameDecoder decoder = retrieveDecoder(header, stream, layer); decoder.decodeFrame(); output.write_buffer(1); return output; } /** * Changes the output buffer. This will take effect the next time * decodeFrame() is called. */ public void setOutputBuffer(Obuffer out) { output = out; } /** * Retrieves the sample frequency of the PCM samples output * by this decoder. This typically corresponds to the sample * rate encoded in the MPEG audio stream. * * @param the sample rate (in Hz) of the samples written to the * output buffer when decoding. */ public int getOutputFrequency() { return outputFrequency; } /** * Retrieves the number of channels of PCM samples output by * this decoder. This usually corresponds to the number of * channels in the MPEG audio stream, although it may differ. * * @return The number of output channels in the decoded samples: 1 * for mono, or 2 for stereo. * */ public int getOutputChannels() { return outputChannels; } /** * Retrieves the maximum number of samples that will be written to * the output buffer when one frame is decoded. This can be used to * help calculate the size of other buffers whose size is based upon * the number of samples written to the output buffer. NB: this is * an upper bound and fewer samples may actually be written, depending * upon the sample rate and number of channels. * * @return The maximum number of samples that are written to the * output buffer when decoding a single frame of MPEG audio. */ public int getOutputBlockSize() { return Obuffer.OBUFFERSIZE; } protected DecoderException newDecoderException(int errorcode) { return new DecoderException(errorcode, null); } protected DecoderException newDecoderException(int errorcode, Throwable throwable) { return new DecoderException(errorcode, throwable); } protected FrameDecoder retrieveDecoder(Header header, Bitstream stream, int layer) throws DecoderException { FrameDecoder decoder = null; // REVIEW: allow channel output selection type // (LEFT, RIGHT, BOTH, DOWNMIX) switch (layer) { case 3: if (l3decoder==null) { l3decoder = new LayerIIIDecoder(stream, header, filter1, filter2, output, OutputChannels.BOTH_CHANNELS); } decoder = l3decoder; break; case 2: if (l2decoder==null) { l2decoder = new LayerIIDecoder(); l2decoder.create(stream, header, filter1, filter2, output, OutputChannels.BOTH_CHANNELS); } decoder = l2decoder; break; case 1: if (l1decoder==null) { l1decoder = new LayerIDecoder(); l1decoder.create(stream, header, filter1, filter2, output, OutputChannels.BOTH_CHANNELS); } decoder = l1decoder; break; } if (decoder==null) { throw newDecoderException(UNSUPPORTED_LAYER, null); } return decoder; } private void initialize(Header header) throws DecoderException { // REVIEW: allow customizable scale factor float scalefactor = 32700.0f; int mode = header.mode(); int layer = header.layer(); int channels = mode==Header.SINGLE_CHANNEL ? 1 : 2; // set up output buffer if not set up by client. if (output==null) output = new SampleBuffer(header.frequency(), channels); float[] factors = equalizer.getBandFactors(); filter1 = new SynthesisFilter(0, scalefactor, factors); // REVIEW: allow mono output for stereo if (channels==2) filter2 = new SynthesisFilter(1, scalefactor, factors); outputChannels = channels; outputFrequency = header.frequency(); initialized = true; } /** * The <code>Params</code> class presents the customizable * aspects of the decoder. * <p> * Instances of this class are not thread safe. */ public static class Params implements Cloneable { private OutputChannels outputChannels = OutputChannels.BOTH; private Equalizer equalizer = new Equalizer(); public Params() { } public Object clone() { try { return super.clone(); } catch (CloneNotSupportedException ex) { throw new InternalError(this+": "+ex); } } public void setOutputChannels(OutputChannels out) { if (out==null) throw new NullPointerException("out"); outputChannels = out; } public OutputChannels getOutputChannels() { return outputChannels; } /** * Retrieves the equalizer settings that the decoder's equalizer * will be initialized from. * <p> * The <code>Equalizer</code> instance returned * cannot be changed in real time to affect the * decoder output as it is used only to initialize the decoders * EQ settings. To affect the decoder's output in realtime, * use the Equalizer returned from the getEqualizer() method on * the decoder. * * @return The <code>Equalizer</code> used to initialize the * EQ settings of the decoder. */ public Equalizer getInitialEqualizerSettings() { return equalizer; } }; }