- // INCLUDES
- #include "CMixerThread.h"
- #include <e32svr.h>
- #include "TSample.h"
- #include "TAudioShared.h"
- // CONSTANTS
- const TInt KSampleRate = 16000; // sample rate used
- const TInt KBufferSize = KSampleRate / 20; // 20 buffers per second
- TInt CMixerThread::ThreadFunction( TAny* aData )
- {
- TAudioShared& shared = *((TAudioShared*)aData);
- // tell client we're alive
- // signaled off when destroying this thread
- shared.iAliveMutex.Wait();
- CMixerThread* mixerThread = CMixerThread::Create( aData );
- if( mixerThread == NULL )
- {
- return KErrGeneral;
- }
- // if we're still here, activescheduler has been constructed
- // start wait loop which runs until it's time to end the thread
- CActiveScheduler::Start();
- delete mixerThread;
- // tell owning thread it's safe to exit
- shared.iAliveMutex.Signal();
- return KErrNone;
- }
- CMixerThread* CMixerThread::Create( TAny* aData )
- {
- CMixerThread* self = new CMixerThread( aData );
- if( self == NULL )
- return self;
- if( self->Construct() != KErrNone )
- {
- delete self;
- return NULL;
- }
- return self;
- }
- TInt CMixerThread::Construct()
- {
- // create cleanup stack
- iCleanupStack = CTrapCleanup::New();
- if( iCleanupStack == NULL )
- {
- return KErrNoMemory;
- }
- TInt err = KErrNone;
- TRAP( err, ConstructL() );
- return err;
- }
- void CMixerThread::ConstructL()
- {
- // create active scheduler
- iActiveScheduler = new( ELeave )CActiveScheduler;
- CActiveScheduler::Install( iActiveScheduler );
- // sound inits
- iSet.iChannels = TMdaAudioDataSettings::EChannelsMono;
- iSet.iSampleRate = TMdaAudioDataSettings::ESampleRate16000Hz;
- iSet.iVolume = 1;
- iMixBuffer = new( ELeave )TInt[ KBufferSize ];
- iBuffer = new( ELeave )TInt16[ KBufferSize ];
- iBufferPtr.Set( TPtrC8( (TUint8*)iBuffer, KBufferSize*2 ) );
- // store pointer of this class to thread local store
- // for use in ExcHandler ( static function )
- Dll::SetTls( this );
- RThread().SetExceptionHandler( ExcHandler, 0xffffffff );
- }
- CMixerThread::CMixerThread( TAny* aData )
- : iShared( *((TAudioShared*)aData) )
- {}
- CMixerThread::~CMixerThread()
- {
- delete iStream;
- delete iBuffer;
- delete iMixBuffer;
- delete iActiveScheduler;
- delete iCleanupStack;
- }
- void CMixerThread::ExcHandler( TExcType aExc )
- {
- // exception handler entry function
- CMixerThread* mixerPointer = (CMixerThread*)Dll::Tls();
- mixerPointer->HandleException( aExc );
- }
- void CMixerThread::HandleException( TExcType aExc )
- {
- // handle exceptions
- // exception type EExcUserInterrupt is reserved for inter-thread communication
- switch( aExc )
- {
- case EExcUserInterrupt: // Command from client
- {
- switch( iShared.iCmd )
- {
- case ECmdStartMixer:
- {
- StartMixer();
- }break;
- case ECmdStopMixer:
- {
- StopMixer();
- }break;
- case ECmdDestroyMixer:
- {
- CActiveScheduler::Stop(); // Exit
- }break;
- }
- }break;
- default:
- {
- // if unknown exception, just exit this thread
- CActiveScheduler::Stop(); // Exit
- }break;
- }
- }
- void CMixerThread::StartMixer()
- {
- iStream = CMdaAudioOutputStream::NewL( *this );
- iStream->Open( &iSet );
- }
- void CMixerThread::StopMixer()
- {
- iStream->Stop();
- delete iStream;
- iStream = NULL;
- }
- void CMixerThread::FillBuffer()
- {
- // wait for access to shared data
- iShared.iMutex.Wait();
- TInt volume = iShared.iMainVolume;
- //
- // Gather new sample information
- //
- TInt i;
- for( i=0; i<KMaxChannels; i++ )
- {
- if( iShared.iPlayStarted[ i ] )
- {
- iShared.iPlayStarted[ i ] = EFalse;
- TSample& s = iShared.iSample[ i ];
- iAudioData[ i ] = s.iData;
- iAudioPos[ i ] = 0;
- iAudioEnd[ i ] = s.iLength << KAudioShift;
- iRepStart[ i ] = s.iRepStart << KAudioShift;
- iRepEnd[ i ] = s.iRepEnd << KAudioShift;
- }
- }
- // give access to shared data
- iShared.iMutex.Signal();
- //
- // Clear buffer
- // has to be done because channels are mixed by adding their values
- // to each other
- //
- Mem::FillZ( iMixBuffer, KBufferSize*4 );
- //
- // Mix active channels
- //
- for( i=0; i<KMaxChannels; i++ )
- {
- if( iAudioData[ i ] != NULL )
- {
- TInt* buf = iMixBuffer;
- TInt* bufEnd = buf + KBufferSize;
- TInt16* src = iAudioData[ i ];
- TInt pos = iAudioPos[ i ];
- TInt posEnd = iAudioEnd[ i ];
- TInt repStart = iRepStart[ i ];
- TInt repEnd = iRepEnd[ i ];
- TInt posAdd = ( iShared.iFrequency[ i ] << KAudioShift ) / KSampleRate;
- TInt volume = iShared.iVolume[ i ];
- while( buf < bufEnd )
- {
- TInt sample = ( src[ pos >> KAudioShift ] * volume );
- pos += posAdd;
- if( pos >= posEnd )
- {
- if( repEnd == 0 )
- {
- iAudioData[ i ] = NULL;
- break;
- }
- else
- {
- pos = repStart;
- posEnd = repEnd;
- }
- }
- *buf++ += sample;
- }
- iAudioPos[ i ] = pos;
- iAudioEnd[ i ] = posEnd;
- }
- }
- // convert 32-bit mixing buffer to 16-bit output buffer
- TInt* buf = iMixBuffer;
- TInt* bufEnd = buf + KBufferSize;
- TInt16* buf2 = iBuffer;
- while( buf < bufEnd )
- {
- // 32-bit mixer buffer contents are multiplied by main volume
- // shifts are in two phases to prevent overflow and maintain quality
- TInt value = ( ( *buf++ >> 8 ) * volume ) >> 8;
- // Prevent sound from trashing on overboost volume:
- if( value < -0x7fff ) value = -0x7fff;
- if( value > 0x7fff ) value = 0x7fff;
- // and write to buffer
- *buf2++ = (TInt16)value;
- }
- // write 16-bit buffer to CMdaAudioOutputStream
- iStream->WriteL( iBufferPtr );
- }
- void CMixerThread::MaoscBufferCopied( TInt aError, const TDesC8& /*aBuffer*/ )
- {
- if( aError )
- {
- iError = aError;
- }
- else
- {
- FillBuffer();
- }
- }
- void CMixerThread::MaoscOpenComplete( TInt aError )
- {
- if( aError )
- {
- iError = aError;
- }
- else
- {
- iStream->SetVolume( iStream->MaxVolume() );
- FillBuffer();
- }
- }
- void CMixerThread::MaoscPlayComplete( TInt aError )
- {
- if( aError )
- {
- iError = aError;
- }
- else
- {
- iStream->SetVolume( iStream->MaxVolume() );
- FillBuffer();
- }
- }