using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using UnityEngine.UI;

public class AudioGetter : MonoBehaviour {

	public AudioSource microphoneAudio;
	public Text txtFeedback;
	public float[] samples;
	public float[] spectrum = new float[8192];
	public float pitchValue; // sound pitch - Hz
    public float pitchAvg = 0f;

	int qSamples = 8192;  // array size
	float refValue = 0.1f; // RMS value for 0 dB
	float threshold = 0.0002f;      // minimum amplitude to extract pitch
	float rmsValue;   // sound level - RMS
	float dbValue; // sound level - dB

	public float fSample;
    public bool doVis = false;

    //Because life is imperfect
    public Queue<float> averageTone;

    //for the visualizer
    float[] band;
	GameObject[] g;

    //Let's just make this even more complex..
    public OctaveSensor octaveSensor;
    public string curNote;
    public int iCurNote;


	// Use this for initialization
	void Start () {

        octaveSensor = new OctaveSensor();

        averageTone = new Queue<float>();

		Debug.Log( "numMics:" + Microphone.devices.Length );

        foreach (string device in Microphone.devices)
        {
            Debug.Log("Name: " + device);
        }

        //all of this is the BS to start getting the audio
        microphoneAudio = this.GetComponent<AudioSource>();
        microphoneAudio.clip = Microphone.Start(null, true, 10, 44100);
        //microphoneAudio.clip = Microphone.Start("Headset Microphone (Plantronics Blackwire C210 M)", true, 10, 44100);
        while (!(Microphone.GetPosition(null) > 1)) { }
        microphoneAudio.Play();

		//hmm
		samples = new float[qSamples];
		spectrum = new float[qSamples];
		fSample = AudioSettings.outputSampleRate;

		//all 8192 kHZ
		int n = spectrum.Length;
		int k = 0;

		// loop through and halve the samples every time
		// essentially this counts how many 'powers of two' are in the samples
		for(int j=0;j<spectrum.Length;j++)
		{
			n /= 2;
			if(n < 1) break;
			k++;
		}

		//create bands for every power of two in the samples
		band  = new float[k+1];
		//this will store an array of crappy visualizer eleemtns
		g = new GameObject[k+1];

   
            //create game objects for each band
            for (var i = 0; i < band.Length; i++)
            {
                band[i] = 0;

                if (doVis)
                {
                    g[i] = GameObject.CreatePrimitive(PrimitiveType.Capsule);
                    g[i].transform.position = new Vector3(i - 3.5f, 0, 0);
                }
            }

            
		//15 times a second
		InvokeRepeating("UpdateSound", 0, 1.0f / 15.0f);
        
	}

    void OnDestroy()
    {
        CancelInvoke("UpdateSound");
        microphoneAudio.Stop();
    }


    private void UpdateSound() {

		//fill with samples
		microphoneAudio.GetSpectrumData(spectrum,0,FFTWindow.BlackmanHarris);
		microphoneAudio.GetOutputData(samples, 0); // fill array with samples

		//things to do with our pretty, pretty sound
		Analysis();
        pitchAvg = AveragePitch();

        //get the note AND octave..
        curNote = octaveSensor.getNoteFromTone(pitchAvg);
        iCurNote = octaveSensor.iCurNote;

        if (doVis) Visualize();
    }

	private void Analysis()
	{

		//sum samples
		int i;
		float sum = 0;
		for (i=0; i < qSamples; i++){
			sum += samples[i]*samples[i]; // sum squared samples
		}

		//some things to be used later
		rmsValue = Mathf.Sqrt(sum/qSamples); // rms = square root of average

		//get dB value of the root mean, clamp it
		//gets out total soudn volume
		dbValue = 20*Mathf.Log10(rmsValue/refValue); // calculate dB
		if (dbValue < -160) dbValue = -160; // clamp it to -160dB min

		// get sound spectrums
		float maxV = 0;
		int maxN = 0;
		//loop through the spectrums looking for the highest valued freq
		//Additionally the freq to count also has to have a highenough amplitude to be a valid target
		for (i=0; i < qSamples; i++){ // find max 

			if (spectrum[i] > maxV && spectrum[i] > threshold){
				maxV = spectrum[i];
				maxN = i; // maxN is the index of max
			}
		}

		//get the freg
		//this is done by combining this frequency with its neighbors to get a more accurate value
		float freqN = maxN; // pass the index to a float variable
		if (maxN > 0 && maxN < qSamples-1){ // interpolate index using neighbours
			var dL = spectrum[maxN-1]/spectrum[maxN];
			var dR = spectrum[maxN+1]/spectrum[maxN];
			freqN += 0.5f*(dR*dR - dL*dL);
		}

		pitchValue = freqN*(fSample/2)/qSamples; // convert index to frequency
	}


    float AveragePitch()
    {

        float average = 0f;

        averageTone.Enqueue(this.pitchValue);
        if (averageTone.Count > 5) averageTone.Dequeue();

        foreach (float samp in averageTone)
        {
            average += samp;
        }

        return average / (float)averageTone.Count;
    }

    void Visualize() {
		int k = 0;
		int crossover = 2; 
		for(int i = 0; i < spectrum.Length; i++)
		{  
			float d = spectrum[i];
			float b = band[k];       
			band[k] = (d>b)? d : b;   // find the max as the peak value in that frequency band.
			if (i>crossover-3)
			{
				k++;
				crossover *= 2;   // frequency crossover point for each band.
				Vector3 pos = g[k].transform.position;
				pos.y = band[k]*32;
				g[k].transform.position = pos;
				band[k] = 0;
			}  
		}

		txtFeedback.text = pitchValue.ToString();
	}
    

}
