--- /dev/null
+/*******************************************************************\r
+ *\r
+ * File: Audio_mediaserver.cpp\r
+ *\r
+ * Author: Peter van Sebille (peter@yipton.net)\r
+ *\r
+ * Modified/adapted for picodriveN by notaz, 2006\r
+ *\r
+ * (c) Copyright 2006, notaz\r
+ * (c) Copyright 2001, Peter van Sebille\r
+ * All Rights Reserved\r
+ *\r
+ *******************************************************************/\r
+\r
+#include "audio_mediaserver.h"\r
+#include "debug.h"\r
+\r
+//#undef DEBUGPRINT\r
+//#define DEBUGPRINT(x...)\r
+\r
+\r
+const TInt KUpdatesPerSec = 10;\r
+const TInt KBlockTime = 1000000 / KUpdatesPerSec;\r
+const TInt KMaxLag = 200000; // max sound lag, lower values increase chance of underflow\r
+const TInt KMaxUnderflows = 50; // max underflows/API errors we are going allow in a row (to prevent lockups)\r
+\r
+\r
+/*******************************************\r
+ *\r
+ * CGameAudioMS\r
+ *\r
+ *******************************************/\r
+\r
+CGameAudioMS::CGameAudioMS(TInt aRate, TBool aStereo, TInt aWritesPerSec)\r
+: iRate(aRate), iStereo(aStereo), iWritesPerSec(aWritesPerSec)\r
+{\r
+}\r
+\r
+\r
+CGameAudioMS* CGameAudioMS::NewL(TInt aRate, TBool aStereo, TInt aWritesPerSec)\r
+{\r
+ DEBUGPRINT(_L("CGameAudioMS::NewL(%i, %i, %i)"), aRate, aStereo, aWritesPerSec);\r
+ CGameAudioMS* self = new(ELeave) CGameAudioMS(aRate, aStereo, aWritesPerSec);\r
+ CleanupStack::PushL(self);\r
+ self->ConstructL();\r
+ CleanupStack::Pop(); // self\r
+ return self;\r
+}\r
+\r
+\r
+CGameAudioMS::~CGameAudioMS()\r
+{\r
+ DEBUGPRINT(_L("CGameAudioMS::~CGameAudioMS()"));\r
+ if(iMdaAudioOutputStream) {\r
+ iScheduler->Schedule(); // let it finish it's stuff\r
+ iMdaAudioOutputStream->Stop();\r
+ delete iMdaAudioOutputStream;\r
+ }\r
+ if(iServer) delete iServer;\r
+\r
+ for (TInt i=0; i<KSoundBuffers; i++)\r
+ delete iSoundBuffers[i];\r
+\r
+ // Polled AS\r
+ //if(iScheduler) delete iScheduler;\r
+}\r
+\r
+\r
+void CGameAudioMS::ConstructL()\r
+{\r
+ iServer = CMdaServer::NewL();\r
+\r
+ // iScheduler = CPolledActiveScheduler::NewL();\r
+ iScheduler = CPolledActiveScheduler::Instance();\r
+\r
+ switch(iRate) {\r
+ case 11025: iMdaAudioDataSettings.iSampleRate = TMdaAudioDataSettings::ESampleRate11025Hz; break;\r
+ case 16000: iMdaAudioDataSettings.iSampleRate = TMdaAudioDataSettings::ESampleRate16000Hz; break;\r
+ case 22050: iMdaAudioDataSettings.iSampleRate = TMdaAudioDataSettings::ESampleRate22050Hz; break;\r
+ case 44100: iMdaAudioDataSettings.iSampleRate = TMdaAudioDataSettings::ESampleRate44100Hz; break;\r
+ default: iMdaAudioDataSettings.iSampleRate = TMdaAudioDataSettings::ESampleRate8000Hz; break;\r
+ }\r
+\r
+ iMdaAudioDataSettings.iChannels = (iStereo) ? TMdaAudioDataSettings::EChannelsStereo : TMdaAudioDataSettings::EChannelsMono;\r
+ iMdaAudioDataSettings.iCaps = TMdaAudioDataSettings::ESampleRateFixed | iMdaAudioDataSettings.iSampleRate;\r
+ iMdaAudioDataSettings.iFlags = TMdaAudioDataSettings::ENoNetworkRouting;\r
+\r
+ int pcmFrames = iRate / iWritesPerSec;\r
+ pcmFrames += iRate - (iRate / iWritesPerSec) * iWritesPerSec; // add division remainder too for our buffer size\r
+ iBufferedFrames = iWritesPerSec / KUpdatesPerSec;\r
+\r
+ TInt bytesPerFrame = pcmFrames << (iStereo?2:1);\r
+ for (TInt i=0 ; i<KSoundBuffers ; i++)\r
+ {\r
+ iSoundBuffers[i] = HBufC8::NewL(bytesPerFrame * iBufferedFrames);\r
+ iSoundBuffers[i]->Des().FillZ (bytesPerFrame * iBufferedFrames);\r
+ }\r
+\r
+ iCurrentBuffer = 0;\r
+ iCurrentBufferSize = 0;\r
+\r
+ // here we actually test if we can create and open CMdaAudioOutputStream at all, but really create and use it later.\r
+ iMdaAudioOutputStream = CMdaAudioOutputStream::NewL(iListener, iServer);\r
+ if(iMdaAudioOutputStream) {\r
+ iVolume = iMdaAudioOutputStream->MaxVolume();\r
+ DEBUGPRINT(_L("MaxVolume: %i"), iVolume);\r
+ delete iMdaAudioOutputStream;\r
+ iMdaAudioOutputStream = 0;\r
+ }\r
+}\r
+\r
+// returns a pointer to buffer for next frame,\r
+// to be used when iSoundBuffers are used directly\r
+TInt16 *CGameAudioMS::NextFrameL(TInt aPcmFrames)\r
+{\r
+ iCurrentPosition += aPcmFrames << (iStereo?1:0);\r
+ iCurrentBufferSize += aPcmFrames << (iStereo?2:1);\r
+\r
+ if (++iFrameCount == iBufferedFrames)\r
+ {\r
+ WriteBlockL();\r
+ }\r
+\r
+ iScheduler->Schedule();\r
+\r
+ if(iListener.iUnderflowed) {\r
+ if(iListener.iUnderflowed > KMaxUnderflows) {\r
+ delete iMdaAudioOutputStream;\r
+ iMdaAudioOutputStream = 0;\r
+ return 0;\r
+ }\r
+ UnderflowedL(); // not again!\r
+ }\r
+\r
+ return iCurrentPosition;\r
+}\r
+\r
+void CGameAudioMS::WriteBlockL()\r
+{\r
+ iScheduler->Schedule();\r
+ // do not write until stream is open\r
+ if(!iListener.iIsOpen) WaitForOpenToCompleteL();\r
+ //if(!iListener.iHasCopied) WaitForCopyToCompleteL(); // almost never happens anyway and sometimes even deadlocks?\r
+ //iListener.iHasCopied = EFalse;\r
+ \r
+\r
+ if(!iListener.iUnderflowed) {\r
+ TInt64 delta;\r
+ // don't write if sound is lagging too much\r
+ delta = iTime - iMdaAudioOutputStream->Position().Int64();\r
+ if (delta > MAKE_TINT64(0, KMaxLag))\r
+ // another query sometimes returns very different result\r
+ delta = iTime - iMdaAudioOutputStream->Position().Int64();\r
+\r
+ if(delta <= MAKE_TINT64(0, KMaxLag)) {\r
+ //RDebug::Print(_L("delta: %i"), iTime.Low() - iMdaAudioOutputStream->Position().Int64().Low());\r
+ iSoundBuffers[iCurrentBuffer]->Des().SetLength(iCurrentBufferSize);\r
+ iMdaAudioOutputStream->WriteL(*iSoundBuffers[iCurrentBuffer]);\r
+ iTime += KBlockTime;\r
+ } else {\r
+ DEBUGPRINT(_L("lag: %i"), I64LOW(delta));\r
+ }\r
+ }\r
+\r
+ iFrameCount = 0;\r
+ if (++iCurrentBuffer == KSoundBuffers)\r
+ iCurrentBuffer = 0;\r
+ iCurrentPosition = (TInt16*) iSoundBuffers[iCurrentBuffer]->Ptr();\r
+ iCurrentBufferSize = 0;\r
+}\r
+\r
+void CGameAudioMS::Pause()\r
+{\r
+ if(!iMdaAudioOutputStream) return;\r
+\r
+ iScheduler->Schedule(); // let it finish it's stuff\r
+ iMdaAudioOutputStream->Stop();\r
+ delete iMdaAudioOutputStream;\r
+ iMdaAudioOutputStream = 0;\r
+}\r
+\r
+// call this before doing any playback!\r
+TInt16 *CGameAudioMS::ResumeL()\r
+{\r
+ DEBUGPRINT(_L("CGameAudioMS::Resume()"));\r
+ iScheduler->Schedule();\r
+\r
+ // we act a bit strange here: simulate buffer underflow, which actually starts audio\r
+ iListener.iIsOpen = ETrue;\r
+ iListener.iUnderflowed = 1;\r
+ iListener.iLastError = 0;\r
+ iFrameCount = 0;\r
+ iCurrentBufferSize = 0;\r
+ iCurrentPosition = (TInt16*) iSoundBuffers[iCurrentBuffer]->Ptr();\r
+ return iCurrentPosition;\r
+}\r
+\r
+// handles underflow condition\r
+void CGameAudioMS::UnderflowedL()\r
+{\r
+ DEBUGPRINT(_L("UnderflowedL()"));\r
+\r
+ if (iListener.iLastError != KErrUnderflow)\r
+ {\r
+ // recreate the stream\r
+ //iMdaAudioOutputStream->Stop();\r
+ if(iMdaAudioOutputStream) delete iMdaAudioOutputStream;\r
+ iMdaAudioOutputStream = CMdaAudioOutputStream::NewL(iListener, iServer);\r
+ iMdaAudioOutputStream->Open(&iMdaAudioDataSettings);\r
+ iMdaAudioOutputStream->SetAudioPropertiesL(iMdaAudioDataSettings.iSampleRate, iMdaAudioDataSettings.iChannels);\r
+ iMdaAudioOutputStream->SetVolume(iVolume); // new in UIQ3\r
+\r
+ iListener.iIsOpen = EFalse; // wait for it to open\r
+ //iListener.iHasCopied = ETrue; // but don't wait for last copy to complete\r
+ // let it open and feed some stuff to make it happy\r
+ User::After(0);\r
+ iScheduler->Schedule();\r
+ iListener.iLastError = 0;\r
+ if(!iListener.iIsOpen) WaitForOpenToCompleteL();\r
+ } else {\r
+ iListener.iLastError = iListener.iUnderflowed = 0;\r
+ }\r
+ iTime = iMdaAudioOutputStream->Position().Int64();\r
+}\r
+\r
+void CGameAudioMS::WaitForOpenToCompleteL()\r
+{\r
+ DEBUGPRINT(_L("CGameAudioMS::WaitForOpenToCompleteL"));\r
+ TInt count = 20; // 2 seconds\r
+ TInt waitPeriod = 100 * 1000;\r
+\r
+ if(!iListener.iIsOpen) {\r
+ // it is often enough to do this\r
+ User::After(0);\r
+ iScheduler->Schedule();\r
+ }\r
+ while (!iListener.iIsOpen && --count)\r
+ {\r
+ User::After(waitPeriod);\r
+ iScheduler->Schedule();\r
+ }\r
+ if (!iListener.iIsOpen)\r
+ User::LeaveIfError(KErrNotSupported);\r
+}\r
+\r
+void CGameAudioMS::ChangeVolume(TInt aUp)\r
+{\r
+ //DEBUGPRINT(_L("CGameAudioMS::ChangeVolume(%i)"), aUp);\r
+\r
+ if (iMdaAudioOutputStream) {\r
+ if (aUp) {\r
+ if (iVolume < iMdaAudioOutputStream->MaxVolume()) iVolume+=5;\r
+ } else {\r
+ if (iVolume > 0) iVolume-=5;\r
+ }\r
+ iMdaAudioOutputStream->SetVolume(iVolume);\r
+ }\r
+}\r
+\r
+void TGameAudioEventListener::MaoscOpenComplete(TInt aError)\r
+{\r
+ DEBUGPRINT(_L("CGameAudioMS::MaoscOpenComplete, error=%d"), aError);\r
+\r
+ iIsOpen = ETrue;\r
+ if(aError) {\r
+ iLastError = aError;\r
+ iUnderflowed++;\r
+ }\r
+ else iUnderflowed = 0;\r
+}\r
+\r
+void TGameAudioEventListener::MaoscBufferCopied(TInt aError, const TDesC8& aBuffer)\r
+{\r
+ if (aError)\r
+ DEBUGPRINT(_L("CGameAudioMS::MaoscBufferCopied, error=%d"), aError);\r
+\r
+// iHasCopied = ETrue;\r
+\r
+ if(aError) { // shit!\r
+ iLastError = aError;\r
+ iUnderflowed++;\r
+ }\r
+}\r
+\r
+void TGameAudioEventListener::MaoscPlayComplete(TInt aError)\r
+{\r
+ DEBUGPRINT(_L("CGameAudioMS::MaoscPlayComplete: %i"), aError);\r
+ if(aError) {\r
+ iLastError = aError;\r
+ iUnderflowed++; // never happened to me while testing, but just in case\r
+ }\r
+}\r
+\r