/* * DecodedMpegAudioInputStream. * * JavaZOOM : mp3spi@javazoom.net * http://www.javazoom.net * *----------------------------------------------------------------------------- * 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 javazoom.spi.mpeg.sampled.convert; import java.io.IOException; import java.io.InputStream; import java.util.HashMap; import java.util.Map; import javax.sound.sampled.AudioFormat; import javax.sound.sampled.AudioInputStream; import javazoom.jl.decoder.Bitstream; import javazoom.jl.decoder.BitstreamException; import javazoom.jl.decoder.Decoder; import javazoom.jl.decoder.DecoderException; import javazoom.jl.decoder.Equalizer; import javazoom.jl.decoder.Header; import javazoom.jl.decoder.Obuffer; import javazoom.spi.PropertiesContainer; import javazoom.spi.mpeg.sampled.file.IcyListener; import javazoom.spi.mpeg.sampled.file.tag.TagParseEvent; import javazoom.spi.mpeg.sampled.file.tag.TagParseListener; import org.tritonus.share.TDebug; import org.tritonus.share.sampled.convert.TAsynchronousFilteredAudioInputStream; /** * Main decoder. */ public class DecodedMpegAudioInputStream extends TAsynchronousFilteredAudioInputStream implements PropertiesContainer, TagParseListener { private InputStream m_encodedStream; private Bitstream m_bitstream; private Decoder m_decoder; private Equalizer m_equalizer; private float[] m_equalizer_values; private Header m_header; private DMAISObuffer m_oBuffer; // Bytes info. private long byteslength = -1; private long currentByte = 0; // Frame info. private int frameslength = -1; private long currentFrame = 0; private int currentFramesize = 0; private int currentBitrate = -1; // Time info. private long currentMicrosecond = 0; // Shoutcast stream info private IcyListener shoutlst = null; private HashMap properties = null; public DecodedMpegAudioInputStream(AudioFormat outputFormat, AudioInputStream inputStream) { super(outputFormat, -1); if (TDebug.TraceAudioConverter) { TDebug.out(">DecodedMpegAudioInputStream(AudioFormat outputFormat, AudioInputStream inputStream)"); } try { // Try to find out inputstream length to allow skip. byteslength = inputStream.available(); } catch (IOException e) { TDebug.out("DecodedMpegAudioInputStream : Cannot run inputStream.available() : "+e.getMessage()); byteslength = -1; } m_encodedStream = inputStream; shoutlst = IcyListener.getInstance(); shoutlst.reset(); m_bitstream = new Bitstream(inputStream); m_decoder = new Decoder(null); m_equalizer = new Equalizer(); m_equalizer_values = new float[32]; for (int b=0;b 0)) frameslength = m_header.max_number_of_frames((int)byteslength); } catch (BitstreamException e) { TDebug.out("DecodedMpegAudioInputStream : Cannot read first frame : "+e.getMessage()); byteslength = -1; } properties = new HashMap(); } /** * Return dynamic properties. * * */ public Map properties() { properties.put("mp3.frame",new Long(currentFrame)); properties.put("mp3.frame.bitrate",new Integer(currentBitrate)); properties.put("mp3.frame.size.bytes",new Integer(currentFramesize)); properties.put("mp3.position.byte",new Long(currentByte)); properties.put("mp3.position.microseconds",new Long(currentMicrosecond)); properties.put("mp3.equalizer",m_equalizer_values); // Optionnal shoutcast stream meta-data. if (shoutlst != null) { String surl = shoutlst.getStreamUrl(); String stitle = shoutlst.getStreamTitle(); if ((stitle != null) && (stitle.trim().length()>0)) properties.put("mp3.shoutcast.metadata.StreamTitle",stitle); if ((surl != null) && (surl.trim().length()>0)) properties.put("mp3.shoutcast.metadata.StreamUrl",surl); } return properties; } public void execute() { if (TDebug.TraceAudioConverter) TDebug.out("execute() : begin"); try { // Following line hangs when FrameSize is available in AudioFormat. Header header = null; if (m_header == null) header = m_bitstream.readFrame(); else header = m_header; if (TDebug.TraceAudioConverter) TDebug.out("execute() : header = "+header); if (header == null) { if (TDebug.TraceAudioConverter) { TDebug.out("header is null (end of mpeg stream)"); } getCircularBuffer().close(); return; } currentFrame++; currentBitrate = header.bitrate_instant(); currentFramesize = header.calculate_framesize(); currentByte = currentByte + currentFramesize; currentMicrosecond = (long) (currentFrame* header.ms_per_frame()*1000.0f); for (int b=0;b 0) && (frameslength > 0)) { float ratio = bytes*1.0f/byteslength*1.0f; long bytesread = skipFrames((long) (ratio*frameslength)); currentByte = currentByte + bytesread; return bytesread; } else return -1; } /** * Skip frames. * You don't need to call it severals times, it will exactly skip given frames number. * @param frames * @return bytes length skipped matching to frames skipped. */ public long skipFrames(long frames) { if (TDebug.TraceAudioConverter) TDebug.out("skip(long frames) : begin"); int framesRead = 0; int bytesReads = 0; try { for (int i=0;i>> 8) & 0xFF); bSecondByte = (byte) (sValue & 0xFF); } else // little endian { bFirstByte = (byte) (sValue & 0xFF); bSecondByte = (byte) ((sValue >>> 8) & 0xFF); } m_abBuffer[m_anBufferPointers[nChannel]] = bFirstByte; m_abBuffer[m_anBufferPointers[nChannel] + 1] = bSecondByte; m_anBufferPointers[nChannel] += m_nChannels * 2; } public void set_stop_flag() { } public void close() { } public void write_buffer(int nValue) { } public void clear_buffer() { } public byte[] getBuffer() { return m_abBuffer; } public int getCurrentBufferSize() { return m_anBufferPointers[0]; } public void reset() { for (int i = 0; i < m_nChannels; i++) { /* Points to byte location, * implicitely assuming 16 bit * samples. */ m_anBufferPointers[i] = i * 2; } } } public void tagParsed(TagParseEvent tpe) { System.out.println("TAG:"+tpe.getTag()); } }