#include "ofSoundPlayer.h"		


bool bFmodInitialized = false;
bool bUseSpectrum = false;
float fftValues[512];	// max # bands is 512
float fftSpectrum[512];

// ---------------------  static vars 
static FMOD_CHANNELGROUP * channelgroup;
static FMOD_SYSTEM       * sys;

// these are global functions, that affect every sound / channel:
// ------------------------------------------------------------		
// ------------------------------------------------------------		

//--------------------
void ofSoundStopAll(){
	ofSoundPlayer::initializeFmod();	
	FMOD_ChannelGroup_Stop(channelgroup);
}

//--------------------
void ofSoundSetVolume(float vol){
	ofSoundPlayer::initializeFmod();
	FMOD_ChannelGroup_SetVolume(channelgroup, vol);
}

//--------------------
float * ofSoundGetSpectrum(int nBands){

	ofSoundPlayer::initializeFmod();

	// ------------------------------------
	// [note] this code snippet comes from:
	// http://qepsilon.org/doc/snd__main_8cpp-source.html
	// via gamedev.net forum
	// ------------------------------------
	
	if (nBands > 512){
		printf("error in ofSoundGetSpectrum, the maximum number of bands is 512 - you asked for %i\nwe will return 512\n", nBands);
		nBands = 512;
	}

	// now, get the spectrum and 
	// place it into fftValues array with the nBands requested
	// for returning:
	
	FMOD_System_GetSpectrum(sys, fftSpectrum, nBands, 0, FMOD_DSP_FFT_WINDOW_HANNING);
	
    int ctr = 0;
    int FreqPerBand =  448 / nBands;
    for (int i = 0; i < nBands; i++){
        fftValues[i] = 0.0f;
        for (int j = 0; j < FreqPerBand; j++){
            fftValues[i] += fftSpectrum[ctr++];
        }
        fftValues[i] = fftValues[i] / 1.5f;
        if (fftValues[i] > 1.0f){
            fftValues[i] = 1.0f;
        }
    }
    
    return fftValues;    
}


//--------------------
//handy error checking function 
/*
void checkErr(FMOD_RESULT result)
{
    if (result != FMOD_OK)
    {
        printf("FMOD error! (%d) %s\n", result, FMOD_ErrorString(result));
    }else{
    	//printf("All good \n");
    }
}
*/


// ------------------------------------------------------------		
// ------------------------------------------------------------		


// now, the individual sound player:
//------------------------------------------------------------		
ofSoundPlayer::ofSoundPlayer(){
	bLoop 			= false;
	bLoadedOk 		= false;
	pan 			= 0.5f;					
	volume 			= 1.0f;
	internalFreq 	= 44100;  	
	speed 			= 1;
	bPaused 		= false;
}


//---------------------------------------
// this should only be called once
void ofSoundPlayer::initializeFmod(){
	if(!bFmodInitialized){
		FMOD_System_Create(&sys);
		FMOD_System_Init(sys, 32, FMOD_INIT_NORMAL, NULL);  //do we want just 32 channels?
		FMOD_System_GetMasterChannelGroup(sys, &channelgroup);
		bFmodInitialized = true;
	}
}


//---------------------------------------
void ofSoundPlayer::closeFmod(){
	if(bFmodInitialized){
		FMOD_System_Close(sys);
		bFmodInitialized = false;
	}
}

//------------------------------------------------------------
void ofSoundPlayer::loadSound(string fileName, bool stream){
	
	fileName = ofToDataPath(fileName);
	
	// fmod uses IO posix internally, might have trouble
	// with unicode paths...
	// says this code:
	// http://66.102.9.104/search?q=cache:LM47mq8hytwJ:www.cleeker.com/doxygen/audioengine__fmod_8cpp-source.html+FSOUND_Sample_Load+cpp&hl=en&ct=clnk&cd=18&client=firefox-a
	// for now we use FMODs way, but we could switch if
	// there are problems:
	
	bMultiPlay = false;
	
	// [1] init fmod, if necessary
	
	initializeFmod();	
	
	// [2] try to unload any previously loaded sounds
	// & prevent user-created memory leaks
	// if they call "loadSound" repeatedly, for example
	
	unloadSound();
	
	// [3] load sound
	
	//choose if we want streaming
	int fmodFlags =  FMOD_SOFTWARE;
	if(stream)fmodFlags =  FMOD_SOFTWARE | FMOD_CREATESTREAM;
	
	result = FMOD_System_CreateSound(sys, fileName.c_str(),  fmodFlags, NULL, &sound);

	if (result != FMOD_OK){
		bLoadedOk = false;
		printf("ofSoundPlayer: Could not load sound file %s \n", fileName.c_str() );
	} else {
		bLoadedOk = true;
		FMOD_Sound_GetLength(sound, &length, FMOD_TIMEUNIT_PCM);
	}
}

//------------------------------------------------------------
void ofSoundPlayer::unloadSound(){
	if (bLoadedOk){
		stop();						// try to stop the sound
		FMOD_Sound_Release(sound);
	}
}

//------------------------------------------------------------
bool ofSoundPlayer::getIsPlaying(){
	
	if (!bLoadedOk) return false;
	
	int playing = 0;
	FMOD_Channel_IsPlaying(channel, &playing);
	return playing;
}

//------------------------------------------------------------
float ofSoundPlayer::getSpeed(){
	return speed;
}

//------------------------------------------------------------
float ofSoundPlayer::getPan(){
	return pan;
}

//------------------------------------------------------------
void ofSoundPlayer::setVolume(float vol){
	if (getIsPlaying() == true){
		FMOD_Channel_SetVolume(channel, vol);
	}
	volume = vol;
}

//------------------------------------------------------------
void ofSoundPlayer::setPosition(float pct){
	if (getIsPlaying() == true){
		int sampleToBeAt = (int)(length * pct);		
		FMOD_Channel_SetPosition(channel, sampleToBeAt, FMOD_TIMEUNIT_PCM);		
	}
}

//------------------------------------------------------------
float ofSoundPlayer::getPosition(){
	if (getIsPlaying() == true){
		unsigned int sampleImAt;

		FMOD_Channel_GetPosition(channel, &sampleImAt, FMOD_TIMEUNIT_PCM);

		float pct = 0.0f;
		if (length > 0){
			pct = sampleImAt / (float)length;
		}
		return pct;
	} else {
		return 0;
	}
}

//------------------------------------------------------------
void ofSoundPlayer::setPan(float p){
	if (getIsPlaying() == true){
		FMOD_Channel_SetPan(channel,p);
	}
	pan = p;
}


//------------------------------------------------------------
void ofSoundPlayer::setPaused(bool bP){
	if (getIsPlaying() == true){
		FMOD_Channel_SetPaused(channel,bP);
		bPaused = bP;
	}
}


//------------------------------------------------------------
void ofSoundPlayer::setSpeed(float spd){
	if (getIsPlaying() == true){
			FMOD_Channel_SetFrequency(channel, internalFreq * spd);
	}
	speed = spd;
}


//------------------------------------------------------------
void ofSoundPlayer::setLoop(bool bLp){
	if (getIsPlaying() == true){
		FMOD_Channel_SetMode(channel,  (bLp == true) ? FMOD_LOOP_NORMAL : FMOD_LOOP_OFF);
	}
	bLoop = bLp;
}

// ---------------------------------------------------------------------------- 
void ofSoundPlayer::setMultiPlay(bool bMp){
	bMultiPlay = bMp;		// be careful with this...
}
		
// ---------------------------------------------------------------------------- 
void ofSoundPlayer::play(){
	
	// if it's a looping sound, we should try to kill it, no?
	// or else people will have orphan channels that are looping
	if (bLoop == true){
		FMOD_Channel_Stop(channel);
	}
	
	// if the sound is not set to multiplay, then stop the current,
	// before we start another
	if (!bMultiPlay){
		FMOD_Channel_Stop(channel);
	}
	
	FMOD_System_PlaySound(sys, FMOD_CHANNEL_FREE, sound, bPaused, &channel);

	FMOD_Channel_GetFrequency(channel, &internalFreq);
	FMOD_Channel_SetVolume(channel,volume);
	FMOD_Channel_SetPan(channel,pan);
	FMOD_Channel_SetFrequency(channel, internalFreq * speed);
	FMOD_Channel_SetMode(channel,  (bLoop == true) ? FMOD_LOOP_NORMAL : FMOD_LOOP_OFF);
		
}

// ---------------------------------------------------------------------------- 
void ofSoundPlayer::stop(){
	FMOD_Channel_Stop(channel);
}
