initial import
[picodrive.git] / platform / uiq3 / engine / audio_mediaserver.cpp
diff --git a/platform/uiq3/engine/audio_mediaserver.cpp b/platform/uiq3/engine/audio_mediaserver.cpp
new file mode 100644 (file)
index 0000000..aff12d1
--- /dev/null
@@ -0,0 +1,293 @@
+/*******************************************************************\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