import java.io.File;
import java.io.IOException;

import javax.sound.sampled.AudioFileFormat;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.SourceDataLine;
import javax.sound.sampled.AudioFormat.Encoding;
import javax.sound.sampled.Line.Info;

import kr.util.Util;

public class MP3ToWav {
	public static void main(String []argv) throws Exception
	{
		//playMp3Clip(argv[0]);
		mp3ToWav(argv[0], argv[1]);
	}
	
	private static void playMp3Clip(String mp3) throws Exception
	{
		JS_MP3FileReader reader = new JS_MP3FileReader();
		
		AudioInputStream s = reader.getAudioInputStream(new File(mp3));
		
		JS_MP3ConversionProvider p = new JS_MP3ConversionProvider();
		Encoding [] encodings = p.getTargetEncodings(s.getFormat());
		
		System.out.println("Encodings: "+Util.arrayToString(encodings));
		
		AudioFormat [] formats = p.getTargetFormats(encodings[0], s.getFormat());

		System.out.println("Formats: "+Util.arrayToString(formats));
		
		AudioInputStream converted = p.getAudioInputStream(formats[0], s);
		
		rawplay(converted.getFormat(), converted);
		
	}
	

	private static void rawplay(AudioFormat targetFormat,
	                     AudioInputStream din)
	   throws IOException, LineUnavailableException
	{
	  byte[] data = new byte[4096];
	  SourceDataLine line = getLine(targetFormat);
	  if (line != null)
	  {
	    // Start
	    line.start();
	    
	    int nBytesRead = 0, nBytesWritten = 0;
	    while (nBytesRead != -1)
	    {
	        nBytesRead = din.read(data, 0, data.length);
	        if (nBytesRead != -1)
	            nBytesWritten = line.write(data, 0, nBytesRead);
	    }
	    // Stop
	    line.drain();
	    line.stop();
	    line.close();
	    din.close();
	  }
	}

	private static SourceDataLine getLine(AudioFormat audioFormat)
	    throws LineUnavailableException
	{
	  SourceDataLine res = null;
	  DataLine.Info info =
	    new DataLine.Info(SourceDataLine.class, audioFormat);
	  res = (SourceDataLine) AudioSystem.getLine(info);
	  res.open(audioFormat);
	  return res;
	}


	
	private static void mp3ToWav(String mp3, String wav) throws Exception
	{
		JS_MP3FileReader reader = new JS_MP3FileReader();
		
		AudioInputStream s = reader.getAudioInputStream(new File(mp3));
		
		JS_MP3ConversionProvider p = new JS_MP3ConversionProvider();
		Encoding [] encodings = p.getTargetEncodings(s.getFormat());
		
		System.out.println("Encodings: "+Util.arrayToString(encodings));
		
		AudioFormat [] formats = p.getTargetFormats(encodings[0], s.getFormat());

		System.out.println("Formats: "+Util.arrayToString(formats));
		
		AudioInputStream converted = p.getAudioInputStream(formats[0], s);
		
		//
		//now it's converted to wav, convert it to special ms wav format
		//
		AudioFormat inFormat = formats[0];
		
		//first, make a corresponding JMF AudioFormat for the java sound AudioFormat
		javax.media.format.AudioFormat jmfAudioFormat = convertSoundAudioFormatToJMFAudioFormat(inFormat); 
		
		
		JavaEncoder_ms msEncoder = new JavaEncoder_ms();
		msEncoder.setInputFormat(jmfAudioFormat);
		
		AudioInputStreamJMFCodecAdaptor adaptor = new AudioInputStreamJMFCodecAdaptor();

		adaptor.setSource(converted);
		adaptor.addCodec(msEncoder);
		
		WaveFileWriter wavFileWriter = new WaveFileWriter();
		wavFileWriter.write(adaptor, AudioFileFormat.Type.WAVE, new File(wav));
		
	}
	
	private static javax.media.format.AudioFormat convertSoundAudioFormatToJMFAudioFormat(AudioFormat inFormat)
	{
		//NOTE: May contain bugs, not all formats checked or even understood completely
		
		String e = inFormat.getEncoding().toString();
		boolean signed = true;
		
		String outE;
		
		if(e.equals(Encoding.PCM_UNSIGNED.toString()) || e.equals(Encoding.PCM_SIGNED.toString()))
			outE = javax.media.format.AudioFormat.LINEAR;
		if(e.equals(Encoding.ULAW.toString()))
			outE = javax.media.format.AudioFormat.ULAW;
		if(e.equals(Encoding.ALAW.toString()))
			outE = javax.media.format.AudioFormat.ALAW;			
		
		if(e.equals(Encoding.PCM_UNSIGNED.toString()))
			signed = false;
		
		return new javax.media.format.AudioFormat(inFormat.getEncoding().toString(),
				(double)inFormat.getSampleRate(), inFormat.getSampleSizeInBits(),
				inFormat.getChannels(),
				inFormat.isBigEndian() ? javax.media.format.AudioFormat.BIG_ENDIAN
						: javax.media.format.AudioFormat.LITTLE_ENDIAN,
						signed ? javax.media.format.AudioFormat.SIGNED :
							javax.media.format.AudioFormat.UNSIGNED);
	}
	
}
