/*
 *	MpegAudioFileReader.java
 *
 *	This file is part of Tritonus: http://www.tritonus.org/
 */

/*
 *  Copyright (c) 1999 -2001 by Matthias Pfisterer
 *
 *   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.file.mpeg;

import java.io.InputStream;
import java.io.IOException;
import java.io.EOFException;

import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioFileFormat;
import javax.sound.sampled.UnsupportedAudioFileException;

import org.tritonus.share.TDebug;
import org.tritonus.share.sampled.file.TAudioFileFormat;
import org.tritonus.share.sampled.file.TAudioFileReader;



/*
 * @author Matthias Pfisterer
 */
public class MpegAudioFileReader
extends	TAudioFileReader
{
	private static final int	SYNC = 0xFFE00000;
	private static final AudioFormat.Encoding[][]	sm_aEncodings =
	{ 
		{new AudioFormat.Encoding("MPEG2DOT5L3"), 
		 new AudioFormat.Encoding("MPEG2DOT5L2"), 
		 new AudioFormat.Encoding("MPEG2DOT5L1")},
		{null,
		 null,
		 null},	/* reserved */
		{new AudioFormat.Encoding("MPEG2L3"),
		 new AudioFormat.Encoding("MPEG2L2"),
		 new AudioFormat.Encoding("MPEG2L1")},
		{new AudioFormat.Encoding("MPEG1L3"),
		 new AudioFormat.Encoding("MPEG1L2"),
		 new AudioFormat.Encoding("MPEG1L1")},
	};
	private static final float[][]	sm_afSamplingRates =
	{
		{11025.0F, 12000.0F, 8000.0F},
		{0.0F, 0.0F, 0.0F},	/* reserved */
		{22050.0F, 24000.0F, 16000.0F},
		{44100.0F, 48000.0F, 32000.0F},
	};

	private static final int	MARK_LIMIT = 4;



	public MpegAudioFileReader()
	{
		super(MARK_LIMIT, true);
	}



	protected AudioFileFormat getAudioFileFormat(InputStream inputStream, long lFileSizeInBytes)
		throws UnsupportedAudioFileException, IOException
	{
		if (TDebug.TraceAudioFileReader) { TDebug.out("MpegAudioFileReader.getAudioFileFormat(): begin"); }
		int	b0 = inputStream.read();
		int	b1 = inputStream.read();
		int	b2 = inputStream.read();
		int	b3 = inputStream.read();
		if ((b0 | b1 | b2 | b3) < 0)
		{
			throw new EOFException();
		}
		int	nHeader = (b0 << 24) + (b1 << 16) + (b2 << 8) + (b3 << 0);
		/*
		 *	We check for the sync bits. If they are present, we
		 *	assume that we have an MPEG bitstream.
		 */
		if ((nHeader & SYNC) != SYNC)
		{
			throw new UnsupportedAudioFileException("not a MPEG stream: no sync bits");
		}
		int	nVersion = (nHeader >> 19) & 0x3;
		if (nVersion == 1)
		{
			throw new UnsupportedAudioFileException("not a MPEG stream: wrong version");
		}
		int nLayer = (nHeader >> 17) & 0x3;
		if (nLayer == 0)
		{
			throw new UnsupportedAudioFileException("not a MPEG stream: wrong layer");
		}
		AudioFormat.Encoding	encoding = sm_aEncodings[nVersion][nLayer - 1];
		// TODO: bit rate, protection
		int	nSFIndex = (nHeader >> 10) & 0x3;
		if (nSFIndex == 3)
		{
			throw new UnsupportedAudioFileException("not a MPEG stream: wrong sampling rate");
		}
		float	fSamplingRate = sm_afSamplingRates[nVersion][nSFIndex];
		int	nMode = (nHeader >> 6) & 0x3;
		int	nChannels = nMode == 3 ? 1 : 2;

		AudioFormat	format = new AudioFormat(
			encoding,
			fSamplingRate,
			AudioSystem.NOT_SPECIFIED /*???*/,
			nChannels,
			AudioSystem.NOT_SPECIFIED /*????*/,
			AudioSystem.NOT_SPECIFIED /*????*/,
			true);
		//$$fb 2000-08-15: workaround for the fixed extension problem in AudioFileFormat.Type
		// see org.tritonus.share.sampled.AudioFileTypes.java
		AudioFileFormat.Type type = new AudioFileFormat.Type("MPEG", "mpeg");
		if (encoding.equals(new AudioFormat.Encoding("MPEG1L3")))
		{
			type = new AudioFileFormat.Type("MP3", "mp3");
		}

		/*
		  If the file size is known, we derive the number of frames
		  ('frame size') from it.
		  If the values don't fit into integers, we leave them at
		  NOT_SPECIFIED. 'Unknown' is considered less incorrect than
		  a wrong value.
		*/
		// [fb] not specifying it causes Sun's Wave file writer to write rubbish
		int	nByteSize = AudioSystem.NOT_SPECIFIED;
		int	nFrameSize = AudioSystem.NOT_SPECIFIED;
		if (lFileSizeInBytes != AudioSystem.NOT_SPECIFIED
		    && lFileSizeInBytes <= Integer.MAX_VALUE)
		{
			nByteSize = (int) lFileSizeInBytes;
			// TODO: check if we can calculate a useful value here
			// nFrameSize = (int) (lFileSizeInBytes / 33);
		}

		AudioFileFormat	audioFileFormat =
			new TAudioFileFormat(
				type,
				format,
				nFrameSize,
				nByteSize);
		if (TDebug.TraceAudioFileReader) { TDebug.out("MpegAudioFileReader.getAudioFileFormat(): end"); }
		return audioFileFormat;
	}
}



/*** MpegAudioFileReader.java ***/

