// Filename: fmodAudioSound.cxx
// Created by:  cort (January 22, 2003)
// Extended by: ben  (October 22, 2003)
// Prior system by: cary
// Rewrite [for new Version of FMOD-EX] by: Stan Rosenbaum "Staque" - Spring 2006
//
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
//
// All use of this software is subject to the terms of the Panda 3d
// Software license.  You should have received a copy of this license
// along with this source code; you will also find a current copy of
// the license at http://etc.cmu.edu/panda3d/docs/license/ .
//
// To contact the maintainers of this program write to
// panda3d-general@lists.sourceforge.net .
//
////////////////////////////////////////////////////////////////////

#include "pandabase.h"
#include "dcast.h"

#ifdef HAVE_FMODEX //[

//Panda Headers
#include "config_audio.h"
#include "fmodAudioSound.h"
#include "fmodAudioDSP.h"
#include "string_utils.h"

TypeHandle FmodAudioSound::_type_handle;

////////////////////////////////////////////////////////////////////
//     Function: FmodAudioSound::FmodAudioSound
//       Access: public
//  Description: Constructor
//               All sound will DEFAULT load as a 2D sound unless
//               otherwise specified.
////////////////////////////////////////////////////////////////////

FmodAudioSound::
FmodAudioSound(AudioManager *manager, Filename file_name, bool positional) { 
  audio_debug("FmodAudioSound::FmodAudioSound() Creating new sound, filename: " << file_name  );

  _active = manager->get_active();

  //Local Variables that are needed.
  FMOD_RESULT result;

  //Inits 3D Attributes
  _location.x = 0;
  _location.y = 0;
  _location.z = 0;

  _velocity.x = 0;
  _velocity.y = 0;
  _velocity.z = 0;

  //Play Rate Variable
  _playrate = 1;

  // These set the Speaker Levels to a default if you are using a MultiChannel Setup.
  for (int i=0; i<AudioManager::SPK_COUNT; i++) {
    _mix[i] = 1.0;
  }

  //Assign the values we need
  FmodAudioManager *fmanager;
  DCAST_INTO_V(fmanager, manager);
  _manager = fmanager;

  _channel = 0;
  _file_name = file_name;

  FMOD_CREATESOUNDEXINFO *sound_info = NULL;

  //Get the Speaker Mode [Important for later on.]
  result = _manager->_system->getSpeakerMode( &_speakermode );
  fmod_audio_errcheck("_system->getSpeakerMode()", result);

  // Calculate the approximate uncompressed size of the sound.
  int size =  file_name.get_file_size();
  string ext = downcase(file_name.get_extension());
  if (ext != "wav") size *= 10;
  
  int flag = positional ? FMOD_3D : FMOD_2D;
  int streamflag = (size > 250000) ? FMOD_CREATESTREAM : FMOD_CREATESAMPLE;
  if (ext == "mid") {
    streamflag = FMOD_CREATESTREAM;
    sound_info = &_manager->_midi_info;
    
    if (sound_info->dlsname != NULL) {
      audio_debug("Using DLS file " << sound_info->dlsname);
    }
  }

  result = _manager->_system->createSound( file_name.c_str(), FMOD_SOFTWARE | streamflag | flag , 
                                           sound_info, &_sound);
  if (result != FMOD_OK) {
    audio_error("createSound(" << file_name << "): " << FMOD_ErrorString(result));

    // We couldn't load the sound file.  Create a blank sound record
    // instead.
    char blank_data[100];
    FMOD_CREATESOUNDEXINFO exinfo;
    memset(&exinfo, 0, sizeof(exinfo));
    memset(blank_data, 0, sizeof(blank_data));
    exinfo.cbsize = sizeof(exinfo);
    exinfo.length = sizeof(blank_data);
    exinfo.numchannels = 1;
    exinfo.defaultfrequency = 8000;
    exinfo.format = FMOD_SOUND_FORMAT_PCM16;
    result = _manager->_system->createSound( blank_data, FMOD_SOFTWARE | flag | FMOD_OPENMEMORY | FMOD_OPENRAW, &exinfo, &_sound);
    fmod_audio_errcheck("createSound (blank)", result);
  }
  
  //This is just to collect the defaults of the sound, so we don't
  //Have to query FMOD everytime for the info.
  //It is also important we get the '_sampleFrequency' variable here, for the
  //'set_play_rate()' and 'get_play_rate()' methods later;
  
  result = _sound->getDefaults( &_sampleFrequency, &_volume , &_balance, &_priority);
  fmod_audio_errcheck("_sound->getDefaults()", result);
}


////////////////////////////////////////////////////////////////////
//     Function: FmodAudioSound::~FmodAudioSound
//       Access: public
//  Description: DESTRUCTOR!!!
////////////////////////////////////////////////////////////////////
FmodAudioSound::
~FmodAudioSound() {
  FMOD_RESULT result;

  //Remove me from table of all sounds.
  _manager->_all_sounds.erase(this);

  //Release DSPs First
  _sound_dsp.clear();

  //The Release Sound
  result = _sound->release();
  fmod_audio_errcheck("_sound->release()", result);
}


////////////////////////////////////////////////////////////////////
//     Function: FmodAudioSound:: play
//       Access: public
//  Description: Plays a sound.
////////////////////////////////////////////////////////////////////
void FmodAudioSound::
play() {
  set_time(0.0);
}

////////////////////////////////////////////////////////////////////
//     Function: sound_end_callback
//       Access: Static
//  Description: When fmod finishes playing a sound, decrements the
//               reference count of the associated FmodAudioSound.
////////////////////////////////////////////////////////////////////
FMOD_RESULT F_CALLBACK sound_end_callback(FMOD_CHANNEL *  channel, 
					  FMOD_CHANNEL_CALLBACKTYPE  type, 
					  int  command, 
					  unsigned int  commanddata1, 
					  unsigned int  commanddata2) {
  FmodAudioSound *fsound = (FmodAudioSound*)command;
  fsound->_self_ref = fsound;
  return FMOD_OK;
}

////////////////////////////////////////////////////////////////////
//     Function: FmodAudioSound::stop
//       Access: public
//  Description: Stop a sound
////////////////////////////////////////////////////////////////////
void FmodAudioSound::
stop() {
  FMOD_RESULT result;

  if (_channel != 0) {
    result =_channel->stop();
    if (result == FMOD_OK) {
      _self_ref.clear();
    }
    fmod_audio_errcheck("_channel->stop()", result);
  }
}


////////////////////////////////////////////////////////////////////
//     Function: FmodAudioSound::set_loop
//       Access: public
//  Description: Turns looping on and off
////////////////////////////////////////////////////////////////////
void FmodAudioSound::
set_loop(bool loop) {
  FMOD_RESULT result;
  if ( loop ) {
    result = _sound->setMode(FMOD_LOOP_NORMAL);
  } else {
    result = _sound->setMode(FMOD_LOOP_OFF);  
  }
  fmod_audio_errcheck("_sound->setMode()", result);
}

////////////////////////////////////////////////////////////////////
//     Function: FmodAudioSound::get_loop
//       Access: public
//  Description: Returns whether looping is on or off
////////////////////////////////////////////////////////////////////
bool FmodAudioSound::
get_loop() const {
  // 0 means loop forever,
  // >1 means loop that many times
  // So _loop_count != 1 means we're looping

  FMOD_RESULT result;
  FMOD_MODE loopMode;
  bool loopState;

  result = _sound->getMode( &loopMode );
  fmod_audio_errcheck("_sound->getMode()", result);

  if ( (loopMode & FMOD_LOOP_NORMAL) != 0 ) {
    loopState = true;
  } else {
    loopState = false;
  }

  return (loopState);
}

////////////////////////////////////////////////////////////////////
//     Function: FmodAudioSound::set_loop_count
//       Access: public
//  Description: 
//        Panda uses 0 to mean loop forever.
//        Fmod uses negative numbers to mean loop forever.
//        (0 means don't loop, 1 means play twice, etc.
//        We must convert!
////////////////////////////////////////////////////////////////////
void FmodAudioSound::
set_loop_count(unsigned long loop_count) {

  audio_debug("FmodAudioSound::set_loop_count()   Setting the sound's loop count to: " << loop_count);

  //LOCALS
  FMOD_RESULT result;
  int numberOfLoops;

  numberOfLoops = (int) loop_count;
  numberOfLoops = numberOfLoops - 1;

  if (numberOfLoops == 0) {
    result = _sound->setLoopCount( -1 );
  } else {
    result = _sound->setLoopCount( numberOfLoops );
  }
  fmod_audio_errcheck("_sound->setLoopCount()", result);

  audio_debug("FmodAudioSound::set_loop_count()   Sound's loop count should be set to: " << loop_count);
}

////////////////////////////////////////////////////////////////////
//     Function: FmodAudioSound::get_loop_count
//       Access: public
//  Description: Return how many times a sound will loop.
////////////////////////////////////////////////////////////////////
unsigned long FmodAudioSound::
get_loop_count() const {

  audio_debug("FmodAudioSound::get_loop_count() Getting the sound's loop count. ");

  FMOD_RESULT result;
  int loop_count;
  unsigned long returnedNumber;

  result = _sound->getLoopCount( &loop_count );
  fmod_audio_errcheck("_sound->getLoopCount()", result);

  audio_debug("FmodAudioSound::get_loop_count() returning "<< loop_count);

  returnedNumber = (unsigned long) loop_count;

  return loop_count;
}

////////////////////////////////////////////////////////////////////
//     Function: FmodAudioSound::set_time
//       Access: public
//  Description: Sets the play position within the sound
////////////////////////////////////////////////////////////////////
void FmodAudioSound::
set_time(float start_time) {
  FMOD_RESULT result;

  int startTime = (int)(start_time * 1000);

  if (_channel != 0) {
    // try backing up current sound.
    result = _channel->setPosition( startTime , FMOD_TIMEUNIT_MS );
    if (result == FMOD_ERR_INVALID_HANDLE ||
        result == FMOD_ERR_CHANNEL_STOLEN) {
      _channel = 0;

    } else {
      fmod_audio_errcheck("_channel->setPosition()", result);

      bool playing;
      result = _channel->isPlaying(&playing);
      fmod_audio_errcheck("_channel->isPlaying()", result);
      if (result != FMOD_OK || !playing) {
        _channel = 0;
      }
    }
  }
  
  if (_channel == 0) {

    result = _manager->_system->playSound(FMOD_CHANNEL_FREE, _sound, true, &_channel);
    fmod_audio_errcheck("_system->playSound()", result);
    result = _channel->setCallback(FMOD_CHANNEL_CALLBACKTYPE_END, sound_end_callback, (int)this);
    fmod_audio_errcheck("_channel->setCallback()", result);
    result = _channel->setPosition( startTime , FMOD_TIMEUNIT_MS );
    fmod_audio_errcheck("_channel->setPosition()", result);

    set_volume_on_channel();
    set_play_rate_on_channel();
    set_speaker_mix_or_balance_on_channel();
    add_dsp_on_channel();
    set_3d_attributes_on_channel();

    if (_active) {
      result = _channel->setPaused(false);
      fmod_audio_errcheck("_channel->setPaused()", result);
    }
    
    _self_ref = this;
  }
}

////////////////////////////////////////////////////////////////////
//     Function: FmodAudioSound::get_time
//       Access: public
//  Description: Gets the play position within the sound
////////////////////////////////////////////////////////////////////
float FmodAudioSound::
get_time() const {
  FMOD_RESULT result;
  unsigned int current_time;

  if (_channel == 0) {
    return 0.0f;
  }

  result = _channel->getPosition( &current_time , FMOD_TIMEUNIT_MS );
  if (result == FMOD_ERR_INVALID_HANDLE) {
    return 0.0f;
  }
  fmod_audio_errcheck("_channel->getPosition()", result);

  current_time = current_time / 1000;

  return current_time;
}

////////////////////////////////////////////////////////////////////
//     Function: FmodAudioSound::set_volume(float vol)
//       Access: public
//  Description: 0.0 to 1.0 scale of volume converted to Fmod's
//               internal 0.0 to 255.0 scale.
////////////////////////////////////////////////////////////////////
void FmodAudioSound::
set_volume(float vol) {
  _volume = vol;
  set_volume_on_channel();
}

////////////////////////////////////////////////////////////////////
//     Function: FmodAudioSound::get_volume
//       Access: public
//  Description: Gets the current volume of a sound.  1 is Max. O is Min.
////////////////////////////////////////////////////////////////////
float FmodAudioSound::
get_volume() const {
  return _volume;
}

////////////////////////////////////////////////////////////////////
//     Function: FmodAudioSound::set_volume_on_channel()
//       Access: Private
//  Description: Set the volume on a prepared Sound channel.
////////////////////////////////////////////////////////////////////
void FmodAudioSound::
set_volume_on_channel() {
  FMOD_RESULT result;

  if (_channel != 0) {
    result = _channel->setVolume( _volume );
    if (result == FMOD_ERR_INVALID_HANDLE) {
      _channel = 0;
    } else {
      fmod_audio_errcheck("_channel->setVolume()", result);
    }
  }
}

////////////////////////////////////////////////////////////////////
//     Function: FmodAudioSound::set_balance(float bal)
//       Access: public
//  Description: -1.0 to 1.0 scale
////////////////////////////////////////////////////////////////////
void FmodAudioSound::
set_balance(float bal) {
  _balance = bal;
  set_speaker_mix_or_balance_on_channel();
}

////////////////////////////////////////////////////////////////////
//     Function: FmodAudioSound::get_balance
//       Access: public
//  Description: -1.0 to 1.0 scale 
//        -1 should be all the way left.
//        1 is all the way to the right.
////////////////////////////////////////////////////////////////////
float FmodAudioSound::
get_balance() const {
  return _balance;
}

////////////////////////////////////////////////////////////////////
//     Function: FmodAudioSound::set_play_rate(float rate)
//       Access: public
//  Description: Sets the speed at which a sound plays back.
//        The rate is a multiple of the sound, normal playback speed.
//        IE 2 would play back 2 times fast, 3 would play 3 times, and so on.
//        This can also be set to a negative number so a sound plays backwards.
//        But rememeber if the sound is not playing, you must set the 
//        sound's time to its end to hear a song play backwards.
////////////////////////////////////////////////////////////////////
void FmodAudioSound::
set_play_rate(float rate) {
  _playrate = rate;
  set_play_rate_on_channel();
}

////////////////////////////////////////////////////////////////////
//     Function: FmodAudioSound::get_play_rate
//       Access: public
//  Description: 
////////////////////////////////////////////////////////////////////
float FmodAudioSound::
get_play_rate() const {
  return _playrate;
}

////////////////////////////////////////////////////////////////////
//     Function: FmodAudioSound::set_play_rate_on_channel()
//       Access: public
//  Description: Set the play rate on a prepared Sound channel.
////////////////////////////////////////////////////////////////////
void FmodAudioSound::
set_play_rate_on_channel() {
  FMOD_RESULT result;
  float frequency = _sampleFrequency * _playrate;
  
  if (_channel != 0) {
    result = _channel->setFrequency( frequency );
    if (result == FMOD_ERR_INVALID_HANDLE) {
      _channel = 0;
    } else {
      fmod_audio_errcheck("_channel->setFrequency()", result);
    }
  }
}

////////////////////////////////////////////////////////////////////
//     Function: FmodAudioSound::get_name
//       Access: public
//  Description: Get name of sound file
////////////////////////////////////////////////////////////////////
const string& FmodAudioSound::
get_name() const {
  return _file_name;
}

////////////////////////////////////////////////////////////////////
//     Function: FmodAudioSound::length
//       Access: public
//  Description: Get length
//        FMOD returns the time in MS  so we have to convert to seconds.
////////////////////////////////////////////////////////////////////
float FmodAudioSound::
length() const {
  FMOD_RESULT result;
  unsigned int length;

  result = _sound->getLength( &length, FMOD_TIMEUNIT_MS );
  fmod_audio_errcheck("_sound->getLength()", result);

  length = length / 1000;

  return length;
}

////////////////////////////////////////////////////////////////////
//     Function: FmodAudioSound::set_3d_attributes
//       Access: public
//  Description: Set position and velocity of this sound
//        NOW LISTEN UP!!! THIS IS IMPORTANT!
//        Both Panda3D and FMOD use a left handed coordinate system.
//        But there is a major difference!
//        In Panda3D the Y-Axis is going into the Screen and the Z-Axis is going up.
//        In FMOD the Y-Axis is going up and the Z-Axis is going into the screen.
//        The solution is simple, we just flip the Y and Z axis, as we move coordinates
//        from Panda to FMOD and back.
//        What does did mean to average Panda user?  Nothing, they shouldn't notice anyway.
//        But if you decide to do any 3D audio work in here you have to keep it in mind.
//        I told you, so you can't say I didn't.
////////////////////////////////////////////////////////////////////
void FmodAudioSound::
set_3d_attributes(float px, float py, float pz, float vx, float vy, float vz) {
  _location.x = px;
  _location.y = pz;
  _location.z = py;
  
  _velocity.x = vx;
  _velocity.y = vz;
  _velocity.z = vy;

  set_3d_attributes_on_channel();
}

////////////////////////////////////////////////////////////////////
//     Function: FmodAudioSound::set_3d_attributes_on_channel
//       Access: public
//  Description: 
////////////////////////////////////////////////////////////////////
void FmodAudioSound::
set_3d_attributes_on_channel() {
  FMOD_RESULT result;
  FMOD_MODE soundMode;

  result = _sound->getMode(&soundMode);
  fmod_audio_errcheck("_sound->getMode()", result);
  
  if ((_channel != 0) && (soundMode & FMOD_3D)) {
    result = _channel->set3DAttributes( &_location, &_velocity );
    if (result == FMOD_ERR_INVALID_HANDLE) {
      _channel = 0;
    } else {
      fmod_audio_errcheck("_channel->set3DAttributes()", result);
    }
  }
}

////////////////////////////////////////////////////////////////////
//     Function: FmodAudioSound::get_3d_attributes
//       Access: public
//  Description: Get position and velocity of this sound
//         Currently unimplemented. Get the attributes of the attached object.
////////////////////////////////////////////////////////////////////
void FmodAudioSound::
get_3d_attributes(float *px, float *py, float *pz, float *vx, float *vy, float *vz) {
  audio_error("get3dAttributes: Currently unimplemented. Get the attributes of the attached object.");
}

////////////////////////////////////////////////////////////////////
//     Function: FmodAudioSound::set_3d_min_distance
//       Access: public
//  Description: Set the distance that this sound begins to fall off. Also
//               affects the rate it falls off.
////////////////////////////////////////////////////////////////////
void FmodAudioSound::
set_3d_min_distance(float dist) {
  FMOD_RESULT result;

  _min_dist = dist;

  result = _sound->set3DMinMaxDistance( dist, _max_dist );
  fmod_audio_errcheck("_sound->set3DMinMaxDistance()", result);
}

////////////////////////////////////////////////////////////////////
//     Function: FmodAudioSound::get_3d_min_distance
//       Access: public
//  Description: Get the distance that this sound begins to fall off
////////////////////////////////////////////////////////////////////
float FmodAudioSound::
get_3d_min_distance() const {
  return _min_dist;
}

////////////////////////////////////////////////////////////////////
//     Function: FmodAudioSound::set_3d_max_distance
//       Access: public
//  Description: Set the distance that this sound stops falling off
////////////////////////////////////////////////////////////////////
void FmodAudioSound::
set_3d_max_distance(float dist) {
  FMOD_RESULT result;

  _max_dist = dist;

  result = _sound->set3DMinMaxDistance( _min_dist, dist );
  fmod_audio_errcheck("_sound->set3DMinMaxDistance()", result);
}

////////////////////////////////////////////////////////////////////
//     Function: FmodAudioSound::get_3d_max_distance
//       Access: public
//  Description: Get the distance that this sound stops falling off
////////////////////////////////////////////////////////////////////
float FmodAudioSound::
get_3d_max_distance() const {
  return _max_dist;
}




////////////////////////////////////////////////////////////////////
//     Function: FmodAudioSound::add_dsp
//       Access: Published
//  Description: This adds a DSP effect to a Sound's personal DSP Chain.
//        DSPs set here will only affect it respective sound.
//        
////////////////////////////////////////////////////////////////////
bool FmodAudioSound::
add_dsp( PT(AudioDSP) x) {
  FMOD_RESULT result;
  bool playingState;

  FmodAudioDSP *fdsp;
  DCAST_INTO_R(fdsp, x, false);

  if ( fdsp->get_in_chain() ) {

    audio_debug("This DSP has already been assigned to the system or a sound.");

    return false;

  } else {
    
    _sound_dsp.insert(fdsp);
    
    if ( _channel != 0 ) {
      result = _channel->isPlaying( &playingState );
      fmod_audio_errcheck("_channel->isPlaying()", result);
      if ( playingState ) {
        result = _channel->addDSP( fdsp->_dsp );
        fmod_audio_errcheck("_channel->addDSP()", result);
      }
    }
    
    fdsp->set_in_chain(true);
    
    return true;
    
  }
}

////////////////////////////////////////////////////////////////////
//     Function: FmodAudioSound::add_dsp_on_channel()
//       Access: Published
//  Description: Sets the DSPs on a prepared Sound channel.
////////////////////////////////////////////////////////////////////
void FmodAudioSound::
add_dsp_on_channel() {
  FMOD_RESULT result;

  for (DSPSet::iterator i = _sound_dsp.begin(); i != _sound_dsp.end(); ++i) {
    if (_channel != 0) {
      result = _channel->addDSP( (*i)->_dsp );
      if (result == FMOD_ERR_INVALID_HANDLE) {
        _channel = 0;
      } else {
        fmod_audio_errcheck("_channel->addDSP()", result);
      }
    }
  }
}

////////////////////////////////////////////////////////////////////
//     Function: FmodAudioSound::remove_dsp
//       Access: Published
//  Description: This will remove a DSP from a Sound's DSP Chain, but
//        It will not destroy it!
//        So in theory you could reuse the DSP for something else.
//        In the Global Chain or another sound.
////////////////////////////////////////////////////////////////////
bool FmodAudioSound::
remove_dsp(PT(AudioDSP) x) {
  FMOD_RESULT result;

  FmodAudioDSP *fdsp;
  DCAST_INTO_R(fdsp, x, false);

  if ( fdsp->get_in_chain() ) {

    result = fdsp->_dsp->remove();
    fmod_audio_errcheck("_dsp->remove()", result);

    _sound_dsp.erase(fdsp);

    fdsp->set_in_chain(false);

    return true;

  } else {

    audio_debug("FmodAudioManager()::remove_dsp()");
    audio_debug("This DSP doesn't exist in this chain.");
    
    return false;
    
  }
}


////////////////////////////////////////////////////////////////////
//     Function: FmodAudioSound::get_speaker_mix
//       Access: Published
//  Description: In Multichannel Speaker systems [like Surround].
//
//        Speakers which don't exist in some systems will simply be ignored.
//        But I haven't been able to test this yet, so I am jsut letting you know.
//
//        BTW This will also work in Stereo speaker systems, but since
//        PANDA/FMOD has a balance [pan] function what is the point?
////////////////////////////////////////////////////////////////////
float FmodAudioSound::
get_speaker_mix(AudioManager::SpeakerId speaker) {
  if (_channel == 0) {
    return 0.0;
  }

  FMOD_RESULT result;
  float frontleft;
  float frontright;
  float center;
  float sub;
  float backleft;
  float backright; 
  float sideleft;
  float sideright;

  result = _channel->getSpeakerMix( &frontleft, &frontright, &center, &sub, &backleft, &backright, &sideleft, &sideright );
  fmod_audio_errcheck("_channel->getSpeakerMix()", result);

  switch(speaker) {
  case AudioManager::SPK_frontleft:  return frontleft;
  case AudioManager::SPK_frontright: return frontright;
  case AudioManager::SPK_center:     return center;
  case AudioManager::SPK_sub:        return sub;
  case AudioManager::SPK_backleft:   return backleft;
  case AudioManager::SPK_backright:  return backright;
  case AudioManager::SPK_sideleft:   return sideleft;
  case AudioManager::SPK_sideright:  return sideright;
  default: return 0.0;
  }
}

////////////////////////////////////////////////////////////////////
//     Function: FmodAudioSound::set_speaker_mix
//       Access: Published
//  Description: This sets the speaker mix for Surround Sound sytems.
//               It required 8 parameters which match up to the following:
//
//               * 1 = Front Left
//               * 2 = Front Right
//               * 3 = Center
//               * 4 = Subwoofer
//               * 5 = Back Left
//               * 6 = Back Right
//               * 7 = Side Left
//               * 8 = Side Right
//
////////////////////////////////////////////////////////////////////
void FmodAudioSound::
set_speaker_mix(float frontleft, float frontright, float center, float sub, float backleft, float backright, float sideleft, float  sideright) {
  _mix[AudioManager::SPK_frontleft]  = frontleft;
  _mix[AudioManager::SPK_frontright] = frontright;
  _mix[AudioManager::SPK_center]     = center;
  _mix[AudioManager::SPK_sub]        = sub;
  _mix[AudioManager::SPK_backleft]   = backleft;
  _mix[AudioManager::SPK_backright]  = backright;
  _mix[AudioManager::SPK_sideleft]   = sideleft;
  _mix[AudioManager::SPK_sideright]  = sideright;

  set_speaker_mix_or_balance_on_channel();
}

////////////////////////////////////////////////////////////////////
//     Function: FmodAudioSound::set_speaker_mix_or_balance_on_channel
//       Access: Private
//  Description: This is simply a safety catch.
//        If you are using a Stero speaker setup Panda will only pay attention
//        to 'set_balance()' command when setting speaker balances.
//        Other wise it will use 'set_speaker_mix'.
//        I put this in, because other wise you end up with a sitation,
//        where 'set_speaker_mix()' or 'set_balace()' will override any
//        previous speaker balance setups.  It all depends on which was called last.
////////////////////////////////////////////////////////////////////
void FmodAudioSound::
set_speaker_mix_or_balance_on_channel() {
  FMOD_RESULT result;
  FMOD_MODE soundMode;

  result = _sound->getMode(&soundMode);
  fmod_audio_errcheck("_sound->getMode()", result);

  if ((_channel != 0) && (( soundMode & FMOD_3D ) == 0)) {
    if ( _speakermode == FMOD_SPEAKERMODE_STEREO ) {
      result = _channel->setPan( _balance );
    } else {
      result = _channel->setSpeakerMix( _mix[AudioManager::SPK_frontleft],
                                        _mix[AudioManager::SPK_frontright],
                                        _mix[AudioManager::SPK_center],
                                        _mix[AudioManager::SPK_sub],
                                        _mix[AudioManager::SPK_backleft],
                                        _mix[AudioManager::SPK_backright],
                                        _mix[AudioManager::SPK_sideleft],
                                        _mix[AudioManager::SPK_sideright] 
                                        );
    }
    if (result == FMOD_ERR_INVALID_HANDLE) {
      _channel = 0;
    } else {
      fmod_audio_errcheck("_channel->setSpeakerMix()/setPan()", result);
    }
  }
}

////////////////////////////////////////////////////////////////////
//     Function: FmodAudioSound::get_priority
//       Access: Published
//  Description: Sets the priority of a sound.
//        This is what FMOD uses to determine is a sound will
//        play if all the other real channels have been used up.
////////////////////////////////////////////////////////////////////
int FmodAudioSound::
get_priority() {
  audio_debug("FmodAudioSound::get_priority()");
  return _priority;
}

////////////////////////////////////////////////////////////////////
//     Function: FmodAudioSound::set_priority(int priority)
//       Access: Published
//  Description: Sets the Sound Priority [Whether is will be played
//        over other sound when real audio channels become short.
////////////////////////////////////////////////////////////////////
void FmodAudioSound::
set_priority(int priority) {
  // intentionally blank

  audio_debug("FmodAudioSound::set_priority()");

  FMOD_RESULT result;

  _priority = priority;

  result = _sound->setDefaults( _sampleFrequency, _volume , _balance, _priority);
  fmod_audio_errcheck("_sound->setDefaults()", result);
}

////////////////////////////////////////////////////////////////////
//     Function: FmodAudioSound::status
//       Access: public
//  Description: Get status of the sound.
////////////////////////////////////////////////////////////////////
AudioSound::SoundStatus FmodAudioSound::
status() const {
  FMOD_RESULT result;
  bool playingState;

  if ( _channel == 0 ) {
    return READY;
  }

  result = _channel->isPlaying( &playingState );
  if ((result == FMOD_OK) && (playingState == true)) {
    return PLAYING;
  } else {
    return READY;
  }
}

////////////////////////////////////////////////////////////////////
//     Function: FmodAudioSound::set_active
//       Access: public
//  Description: Sets whether the sound is marked "active".  By
//               default, the active flag true for all sounds.  If the
//               active flag is set to false for any particular sound,
//               the sound will not be heard.
////////////////////////////////////////////////////////////////////
void FmodAudioSound::
set_active(bool active) {
  _active = active;

  if (status() == PLAYING) {
    // If the sound is (or should be) playing, then pause or unpause
    // it in the system.
    FMOD_RESULT result = _channel->setPaused(!_active);
    fmod_audio_errcheck("_channel->setPaused()", result);
  }
}


////////////////////////////////////////////////////////////////////
//     Function: FmodAudioSound::get_active 
//       Access: public
//  Description: Returns whether the sound has been marked "active".
////////////////////////////////////////////////////////////////////
bool FmodAudioSound::
get_active() const {
  return _active;
}

////////////////////////////////////////////////////////////////////
//     Function: FmodAudioSound::finished
//       Access: public
//  Description: NOT USED ANYMORE!!!
//        Called by finishedCallback function when a sound
//              terminates (but doesn't loop).
////////////////////////////////////////////////////////////////////
void FmodAudioSound::
finished() {
  audio_debug("FmodAudioSound::finished()");
  audio_debug("NOT USED ANYMORE in FMOD-EX version of PANDA.");
}

////////////////////////////////////////////////////////////////////
//     Function: FmodAudioSound::set_finished_event
//       Access: public
//  Description: NOT USED ANYMORE!!!
//        Assign a string for the finished event to be referenced 
//              by in python by an accept method
//
////////////////////////////////////////////////////////////////////
void FmodAudioSound::
set_finished_event(const string& event) {
  audio_debug("FmodAudioSound::set_finished_event(event="<<event<<")");
  audio_debug("NOT USED ANYMORE in FMOD-EX version of PANDA.");
}

////////////////////////////////////////////////////////////////////
//     Function: FmodAudioSound::get_finished_event
//       Access: public
//  Description:NOT USED ANYMORE!!!
//        Return the string the finished event is referenced by
//
//        
////////////////////////////////////////////////////////////////////
const string& FmodAudioSound::
get_finished_event() const {
  audio_debug("FmodAudioSound::get_finished_event() returning " << _finished_event );
  audio_debug("NOT USED ANYMORE in FMOD-EX version of PANDA.");
  return _finished_event;
}

#endif //]
