/*****************************************************************************
 * $LastChangedDate: 2011-04-23 21:07:07 -0400 (Sat, 23 Apr 2011) $
 * @file
 * @author  Jim E. Brooks  http://www.palomino3d.org
 * @brief   Sound sample (PLIB).
 *//*
 * LEGAL:   COPYRIGHT (C) 2009 JIM E. BROOKS
 *          THIS SOURCE CODE IS RELEASED UNDER THE TERMS
 *          OF THE GNU GENERAL PUBLIC LICENSE VERSION 2 (GPL 2).
 *****************************************************************************/

#if COMPILE_PLIB

#define SOUND_SOUND_SAMPLE_PLIB_CC 1
#include "base/module.hh"
#include "base/file.hh"
#include "base/stream.hh"
using namespace base;
#include "sound/module.hh"
#include "sound/defs.hh"
#include "sound/sound_sample.hh"
#include "sound/sound_sample_plib.hh"
#include "sound/sound_base.hh"

#include <plib/sl.h>
#include <plib/sm.h>

namespace sound {

////////////////////////////////////////////////////////////////////////////////
////////////////////////////////  SamplePLIB  //////////////////////////////////
////////////////////////////////////////////////////////////////////////////////

/*****************************************************************************
 * ctor/dtor.
 *****************************************************************************/
SamplePLIB::SamplePLIB( const Sample::Name& sampleName )
:   Sample(sampleName),
    mVolume(defs::DEFAULT_VOLUME),
    mPlayLoop(defs::PLAY_ONCE),
    mSLSample(NULL),
    mBuf()
{
    // Read gzipped sound file into C++ string mBuf.
    if ( ReadFileGzipIntoString( SoundBase::ComputePathnameSoundFile(sampleName), mBuf ) )
    {
    ASSERT( mBuf.size() > 0 );

        // Create PLIB sound object.
        // PLIB will copy the buffer (I read the source, Luke).
        mSLSample = new slSample( reinterpret_cast<const uchar*>(mBuf.c_str()), mBuf.size() );
    }
    else
    {
        mSLSample = NULL;
        const string msg = string("ERROR[SamplePLIB]: cannot read sound file ") + sampleName;
        #if LOG_SOUND_ERRORS
        CERROR << msg << std::endl;
        #endif
        throw Exception( msg );
    }

ASSERT( mSLSample != NULL );
ASSERT( mBuf.size() > 0 );
}

SamplePLIB::~SamplePLIB()
{
#if SOUND_FREE_PLIB
    DELETE_NULL( mSLSample );
#endif
}

/*****************************************************************************
 * @return True if sample is playing.
 *****************************************************************************/
bool
SamplePLIB::IfPlaying( void )
{
CHECK_TYPESIG(this,TYPESIG_SOUND_SAMPLE);

    return mSLSample->getPlayCount() > 0;
}

/*****************************************************************************
 * Play sound sample.
 * @param   playLoop
 *          Play continuously or once.
 * @param   volume
 *          Default is DEFAULT_VOLUME.
 * @return False if error.
 *****************************************************************************/
bool
SamplePLIB::Play( const defs::ePlayLoop playLoop, const fp volume )
{
CHECK_TYPESIG(this,TYPESIG_SOUND_SAMPLE);

    // Remember.
    mPlayLoop = playLoop;

    // Adjust volume before playing to avoid PLIB
    // modifying waveform for volume adjustment while playing.
    SetVolume( volume );
    ASSERT( mVolume == volume );

    // Play one-shot or loop.
    if ( playLoop )
        SoundPLIB::GetInstance().mSched->loopSample( mSLSample );
    else
        SoundPLIB::GetInstance().mSched->playSample( mSLSample );

    return true;
}

/*****************************************************************************
 * Stop playing a sound sample.
 * @return False if error.
 *****************************************************************************/
bool
SamplePLIB::Stop( void )
{
CHECK_TYPESIG(this,TYPESIG_SOUND_SAMPLE);

    SoundPLIB::GetInstance().mSched->stopSample( mSLSample );

    return true;
}

/*****************************************************************************
 * Set volume of a sound sample.
 * @return False if error.
 *****************************************************************************/
bool
SamplePLIB::SetVolume( const fp volume )
{
CHECK_TYPESIG(this,TYPESIG_SOUND_SAMPLE);

    // NOP unless volume need changing.
    if ( volume != mVolume )
    {
        mVolume = volume;

        // Restore original sample to avoid degradation (yuck).
        // Adjusting volume modifies the waveform (yuck).
        SoundPLIB::GetInstance().mSched->stopSample( mSLSample );
        SoundPLIB::GetInstance().EnqueueZombieSample( mSLSample );
        mSLSample = new slSample( reinterpret_cast<const uchar*>(mBuf.c_str()), mBuf.size() );
        mSLSample->adjustVolume( volume );

        // Resuming playing?
        if ( IfPlaying() )
        {
            if ( mPlayLoop )
                SoundPLIB::GetInstance().mSched->loopSample( mSLSample );
            else  // one-shot
                SoundPLIB::GetInstance().mSched->playSample( mSLSample );
        }
    }

    return true;
}

/*****************************************************************************
 * @return Volume of a sound sample.
 *****************************************************************************/
fp
SamplePLIB::GetVolume( void )
{
CHECK_TYPESIG(this,TYPESIG_SOUND_SAMPLE);

    return mVolume;
}

} // namespace sound

#endif // COMPILE_PLIB
