/*
*
      
_______
                       
_____
   
_____ _____
  

*
     
|__
   
__|
                     
|
  
__ \ / ____|__ \
 

*
        
| | __ _ _ __ ___
  
______| || | (___ | |__) |
*
        
| |/ _` | '__/ __|/ _ \/ __| |
  
| |\___ \|___/
 

*
        
| | (_| | |
  
\__ \ (_) \__ \ |__| |____) | |
     

*
        
|_|\__,_|_|
  
|___/\___/|___/_____/|_____/|_|
     

*
                                                         

* -------------------------------------------------------------
*
* TarsosDSP is developed by Joren Six at IPEM, University Ghent
*
  

* -------------------------------------------------------------
*
*
  
Info:
 
http://0110.be/tag/TarsosDSP
*
  
Github:
 
https://github.com/JorenSix/TarsosDSP
*
  
Releases:
 
http://0110.be/releases/TarsosDSP/
*
  

*
  
TarsosDSP includes modified source code by various authors,
*
  
for credits and info, see README.
*
 

*/


package be.tarsos.dsp.example;

import java.awt.BorderLayout;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;

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.Mixer;
import javax.sound.sampled.TargetDataLine;
import javax.sound.sampled.UnsupportedAudioFileException;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.border.TitledBorder;

import be.tarsos.dsp.AudioDispatcher;
import be.tarsos.dsp.AudioEvent;
import be.tarsos.dsp.AudioProcessor;
import be.tarsos.dsp.io.jvm.AudioDispatcherFactory;
import be.tarsos.dsp.io.jvm.AudioPlayer;
import be.tarsos.dsp.io.jvm.JVMAudioInputStream;
import be.tarsos.dsp.pitch.PitchDetectionHandler;
import be.tarsos.dsp.pitch.PitchDetectionResult;
import be.tarsos.dsp.pitch.PitchProcessor;
import be.tarsos.dsp.pitch.PitchProcessor.PitchEstimationAlgorithm;
import be.tarsos.dsp.util.fft.FFT;

public class Spectrogram extends JFrame implements PitchDetectionHandler {
	

	
/**
	 
*
 

	 
*/
	
private static final long serialVersionUID = 1383896180290138076L;
	
private final SpectrogramPanel panel;
	
private AudioDispatcher dispatcher;
	
private Mixer currentMixer;
	

	
private PitchEstimationAlgorithm algo;
	
private double pitch;
	

	
private float sampleRate = 44100;
	
private int bufferSize = 1024 * 4;
	
private int overlap = 768 * 4 ;
	

	
private String fileName;
	

	

	
private ActionListener algoChangeListener = new ActionListener(){
		
@Override
		
public void actionPerformed(final ActionEvent e) {
			
String name = e.getActionCommand();
			
PitchEstimationAlgorithm newAlgo = PitchEstimationAlgorithm.valueOf(name);
			
algo = newAlgo;
			
try {
				
setNewMixer(currentMixer);
			
} catch (LineUnavailableException e1) {
				
e1.printStackTrace();
			
} catch (UnsupportedAudioFileException e1) {
				
e1.printStackTrace();
			
}
	
}};
		

	
public Spectrogram(String fileName){
		
this.setLayout(new BorderLayout());
		
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		
this.setTitle("Spectrogram");
		
panel = new SpectrogramPanel();
		
algo = PitchEstimationAlgorithm.DYNAMIC_WAVELET;
		
this.fileName = fileName;
		

		
JPanel pitchDetectionPanel = new PitchDetectionPanel(algoChangeListener);
		

		
JPanel inputPanel = new InputPanel();
	

		
inputPanel.addPropertyChangeListener("mixer",
				
new PropertyChangeListener() {
					
@Override
					
public void propertyChange(PropertyChangeEvent arg0) {
						
try {
							
setNewMixer((Mixer) arg0.getNewValue());
						
} catch (LineUnavailableException e) {
							
// TODO Auto-generated catch block
							
e.printStackTrace();
						
} catch (UnsupportedAudioFileException e) {
							
// TODO Auto-generated catch block
							
e.printStackTrace();
						
}
					
}
				
});
		

		
JPanel containerPanel = new JPanel(new GridLayout(1,0));
		
containerPanel.add(inputPanel);
		
containerPanel.add(pitchDetectionPanel);
		
this.add(containerPanel,BorderLayout.NORTH);
		

		
JPanel otherContainer = new JPanel(new BorderLayout());
		
otherContainer.add(panel,BorderLayout.CENTER);
		
otherContainer.setBorder(new TitledBorder("3. Utter a sound (whistling works best)"));
		

		

		
this.add(otherContainer,BorderLayout.CENTER);
	
}
	

	

	

	
private void setNewMixer(Mixer mixer) throws LineUnavailableException, UnsupportedAudioFileException {

		
if(dispatcher!= null){
			
dispatcher.stop();
		
}
		
if(fileName == null){
			
final AudioFormat format = new AudioFormat(sampleRate, 16, 1, true,
					
false);
			
final DataLine.Info dataLineInfo = new DataLine.Info(
					
TargetDataLine.class, format);
			
TargetDataLine line;
			
line = (TargetDataLine) mixer.getLine(dataLineInfo);
			
final int numberOfSamples = bufferSize;
			
line.open(format, numberOfSamples);
			
line.start();
			
final AudioInputStream stream = new AudioInputStream(line);

			
JVMAudioInputStream audioStream = new JVMAudioInputStream(stream);
			
// create a new dispatcher
			
dispatcher = new AudioDispatcher(audioStream, bufferSize,
					
overlap);
		
} else {
			
try {
				
File audioFile = new File(fileName);
				
dispatcher = AudioDispatcherFactory.fromFile(audioFile, bufferSize, overlap);
				
AudioFormat format = AudioSystem.getAudioFileFormat(audioFile).getFormat();
				
dispatcher.addAudioProcessor(new AudioPlayer(format));
			
} catch (IOException e) {
				
// TODO Auto-generated catch block
				
e.printStackTrace();
			
}
		
}
		
currentMixer = mixer;

		
// add a processor, handle pitch event.
		
dispatcher.addAudioProcessor(new PitchProcessor(algo, sampleRate, bufferSize, this));
		
dispatcher.addAudioProcessor(fftProcessor);

		
// run the dispatcher (on a new thread).
		
new Thread(dispatcher,"Audio dispatching").start();
	
}
	

	
AudioProcessor fftProcessor = new AudioProcessor(){
		

		
FFT fft = new FFT(bufferSize);
		
float[] amplitudes = new float[bufferSize/2];

		
@Override
		
public void processingFinished() {
			
// TODO Auto-generated method stub
		
}

		
@Override
		
public boolean process(AudioEvent audioEvent) {
			
float[] audioFloatBuffer = audioEvent.getFloatBuffer();
			
float[] transformbuffer = new float[bufferSize*2];
			
System.arraycopy(audioFloatBuffer, 0, transformbuffer, 0, audioFloatBuffer.length);
			
fft.forwardTransform(transformbuffer);
			
fft.modulus(transformbuffer, amplitudes);
			
panel.drawFFT(pitch, amplitudes,fft);
			
panel.repaint();
			
return true;
		
}
		

	
};
	

	
@Override
	
public void handlePitch(PitchDetectionResult pitchDetectionResult,AudioEvent audioEvent) {
		
if(pitchDetectionResult.isPitched()){
			
pitch = pitchDetectionResult.getPitch();
		
} else {
			
pitch = -1;
		
}
		

	
}
	

	
public static void main(final String... strings) throws InterruptedException,
			
InvocationTargetException {
		
SwingUtilities.invokeAndWait(new Runnable() {
			
@Override
			
public void run() {
				
try {
					
UIManager.setLookAndFeel(UIManager
							
.getSystemLookAndFeelClassName
());
				
} catch (Exception e) {
					
// ignore failure to set default look en feel;
				
}
				
JFrame frame = strings.length == 0 ? new Spectrogram(null) : new Spectrogram(strings[0]) ;
				
frame.pack();
				
frame.setSize(640, 480);
				
frame.setVisible(true);
			
}
		
});
}
	


}