1 /*******************************************************************
\r
3 * File: Audio_mediaserver.cpp
\r
5 * Author: Peter van Sebille (peter@yipton.net)
\r
7 * Modified/adapted for picodriveN by notaz, 2006
\r
9 * (c) Copyright 2006, notaz
\r
10 * (c) Copyright 2001, Peter van Sebille
\r
11 * All Rights Reserved
\r
13 *******************************************************************/
\r
15 #include "audio_mediaserver.h"
\r
17 //#define __DEBUG_PRINT_SND
\r
19 #ifdef __DEBUG_PRINT_SND
\r
20 #include <e32svr.h> // RDebug
\r
21 #define DEBUGPRINT(x...) RDebug::Print(x)
\r
23 #define DEBUGPRINT(x...)
\r
27 GLDEF_C TInt E32Dll(TDllReason)
\r
33 /*******************************************
\r
37 *******************************************/
\r
39 CGameAudioMS::CGameAudioMS(TInt aRate, TBool aStereo, TInt aPcmFrames, TInt aBufferedFrames)
\r
40 : iRate(aRate), iStereo(aStereo), iBufferedFrames(aBufferedFrames), iPcmFrames(aPcmFrames)
\r
45 CGameAudioMS* CGameAudioMS::NewL(TInt aRate, TBool aStereo, TInt aPcmFrames, TInt aBufferedFrames)
\r
47 DEBUGPRINT(_L("CGameAudioMS::NewL(%i, %i, %i, %i)"),aRate, aStereo, aPcmFrames, aBufferedFrames);
\r
48 CGameAudioMS* self = new(ELeave) CGameAudioMS(aRate, aStereo, aPcmFrames, aBufferedFrames);
\r
49 CleanupStack::PushL(self);
\r
51 CleanupStack::Pop(); // self
\r
56 CGameAudioMS::~CGameAudioMS()
\r
58 DEBUGPRINT(_L("CGameAudioMS::~CGameAudioMS()"));
\r
59 if(iMdaAudioOutputStream) {
\r
60 iScheduler->Schedule(); // let it finish it's stuff
\r
61 iMdaAudioOutputStream->Stop();
\r
62 delete iMdaAudioOutputStream;
\r
64 if(iServer) delete iServer;
\r
66 for (TInt i=0 ; i<KSoundBuffers+1 ; i++)
\r
67 delete iSoundBuffers[i];
\r
70 if(iScheduler) delete iScheduler;
\r
74 void CGameAudioMS::ConstructL()
\r
76 iServer = CMdaServer::NewL();
\r
78 iScheduler = CPolledActiveScheduler::NewL();
\r
81 case 11025: iMdaAudioDataSettings.iSampleRate = TMdaAudioDataSettings::ESampleRate11025Hz; break;
\r
82 case 16000: iMdaAudioDataSettings.iSampleRate = TMdaAudioDataSettings::ESampleRate16000Hz; break;
\r
83 case 22050: iMdaAudioDataSettings.iSampleRate = TMdaAudioDataSettings::ESampleRate22050Hz; break;
\r
84 default: iMdaAudioDataSettings.iSampleRate = TMdaAudioDataSettings::ESampleRate8000Hz; break;
\r
87 iMdaAudioDataSettings.iChannels = (iStereo) ? TMdaAudioDataSettings::EChannelsStereo : TMdaAudioDataSettings::EChannelsMono;
\r
88 iMdaAudioDataSettings.iCaps = TMdaAudioDataSettings::ESampleRateFixed | iMdaAudioDataSettings.iSampleRate;
\r
89 iMdaAudioDataSettings.iFlags = TMdaAudioDataSettings::ENoNetworkRouting;
\r
91 TInt bytesPerFrame = iStereo ? iPcmFrames << 2 : iPcmFrames << 1;
\r
92 for (TInt i=0 ; i<KSoundBuffers ; i++)
\r
94 iSoundBuffers[i] = HBufC8::NewL(bytesPerFrame * iBufferedFrames);
\r
95 iSoundBuffers[i]->Des().FillZ (bytesPerFrame * iBufferedFrames);
\r
97 // because feeding 2 buffers after an underflow is a little too much, but feeding 1 may be not enough,
\r
98 // prepare this ~50ms empty buffer to additionaly feed after every underflow.
\r
99 // Another strange thing here: if we try to make and odd-length sound buffer here,
\r
100 // system then outputs horrible noise! (this happened on 22050 mono and when there
\r
101 // were no parenthesis around iBufferedFrames / 4.
\r
102 iSoundBuffers[KSoundBuffers] = HBufC8::NewL(bytesPerFrame * (iBufferedFrames / 4));
\r
103 iSoundBuffers[KSoundBuffers]->Des().FillZ (bytesPerFrame * (iBufferedFrames / 4));
\r
105 iCurrentBuffer = 0;
\r
107 // here we actually test if we can create and open CMdaAudioOutputStream at all, but really create and use it later.
\r
108 iMdaAudioOutputStream = CMdaAudioOutputStream::NewL(iListener, iServer);
\r
109 if(iMdaAudioOutputStream) {
\r
110 delete iMdaAudioOutputStream;
\r
111 iMdaAudioOutputStream = 0;
\r
115 /* currently unused
\r
116 TInt CGameAudioMS::Write(TInt16* aBuffer, TInt aSize)
\r
118 TInt byteSize = iStereo ? aSize << 2 : aSize << 1;
\r
119 Mem::Copy(iCurrentPosition, aBuffer, byteSize);
\r
120 iCurrentPosition += aSize;
\r
122 if (++iFrameCount == iBufferedFrames)
\r
127 CPolledActiveScheduler::Instance()->Schedule();
\r
128 if(iListener.iUnderflowed) Underflowed(); // oh no, CMdaAudioOutputStream underflowed!
\r
134 // returns a pointer to buffer for next frame,
\r
135 // to be used when iSoundBuffers are used directly
\r
136 TInt16 *CGameAudioMS::NextFrameL()
\r
138 iCurrentPosition += iPcmFrames << (iStereo?1:0);
\r
140 if (++iFrameCount == iBufferedFrames)
\r
145 iScheduler->Schedule();
\r
147 if(iListener.iUnderflowed) {
\r
148 if(iListener.iUnderflowed > KMaxUnderflows) {
\r
149 delete iMdaAudioOutputStream;
\r
150 iMdaAudioOutputStream = 0;
\r
153 UnderflowedL(); // not again!
\r
156 return iCurrentPosition;
\r
159 TInt16 *CGameAudioMS::DupeFrameL(TInt &aUnderflowed)
\r
161 TInt shorts = iStereo ? (iPcmFrames << 1) : iPcmFrames;
\r
163 Mem::Copy(iCurrentPosition, iCurrentPosition-shorts, shorts<<1);
\r
165 TInt lastBuffer = iCurrentBuffer;
\r
166 if(--lastBuffer < 0) lastBuffer = KSoundBuffers - 1;
\r
167 Mem::Copy(iCurrentPosition, ((TInt16*) (iSoundBuffers[lastBuffer]->Ptr()))+shorts*(iBufferedFrames-1), shorts<<1);
\r
169 iCurrentPosition += shorts;
\r
171 if (++iFrameCount == iBufferedFrames)
\r
176 iScheduler->Schedule();
\r
178 if((aUnderflowed = iListener.iUnderflowed)) { // not again!
\r
179 if(iListener.iUnderflowed > KMaxUnderflows) {
\r
180 delete iMdaAudioOutputStream;
\r
181 iMdaAudioOutputStream = 0;
\r
184 UnderflowedL(); // not again!
\r
187 return iCurrentPosition;
\r
190 void CGameAudioMS::WriteBlockL()
\r
192 iScheduler->Schedule();
\r
193 // do not write until stream is open
\r
194 if(!iListener.iIsOpen) WaitForOpenToCompleteL();
\r
195 //if(!iListener.iHasCopied) WaitForCopyToCompleteL(); // almost never happens anyway and sometimes even deadlocks?
\r
196 //iListener.iHasCopied = EFalse;
\r
199 if(!iListener.iUnderflowed) {
\r
200 // don't write if sound is lagging too much
\r
201 if(iTime - iMdaAudioOutputStream->Position().Int64() <= TInt64(0, KMaxLag)) {
\r
202 //RDebug::Print(_L("delta: %i"), iTime.Low() - iMdaAudioOutputStream->Position().Int64().Low());
\r
203 iMdaAudioOutputStream->WriteL(*iSoundBuffers[iCurrentBuffer]);
\r
204 iTime += KBlockTime;
\r
209 if (++iCurrentBuffer == KSoundBuffers)
\r
210 iCurrentBuffer = 0;
\r
211 iCurrentPosition = (TInt16*) iSoundBuffers[iCurrentBuffer]->Ptr();
\r
214 void CGameAudioMS::Pause()
\r
216 if(!iMdaAudioOutputStream) return;
\r
218 iScheduler->Schedule(); // let it finish it's stuff
\r
219 iMdaAudioOutputStream->Stop();
\r
220 delete iMdaAudioOutputStream;
\r
221 iMdaAudioOutputStream = 0;
\r
224 // call this before doing any playback!
\r
225 TInt16 *CGameAudioMS::ResumeL()
\r
227 DEBUGPRINT(_L("CGameAudioMS::Resume()"));
\r
228 iScheduler->Schedule();
\r
230 // we act a bit strange here: simulate buffer underflow, which actually starts audio
\r
231 iListener.iIsOpen = ETrue;
\r
232 iListener.iUnderflowed = 1;
\r
234 iCurrentPosition = (TInt16*) iSoundBuffers[iCurrentBuffer]->Ptr();
\r
235 return iCurrentPosition;
\r
238 // handles underflow condition
\r
239 void CGameAudioMS::UnderflowedL()
\r
241 // recreate the stream
\r
242 //iMdaAudioOutputStream->Stop();
\r
243 if(iMdaAudioOutputStream) delete iMdaAudioOutputStream;
\r
244 iMdaAudioOutputStream = CMdaAudioOutputStream::NewL(iListener, iServer);
\r
245 iMdaAudioOutputStream->Open(&iMdaAudioDataSettings);
\r
246 iListener.iIsOpen = EFalse; // wait for it to open
\r
247 //iListener.iHasCopied = ETrue; // but don't wait for last copy to complete
\r
248 // let it open and feed some stuff to make it happy
\r
250 TInt lastBuffer = iCurrentBuffer;
\r
251 if(--lastBuffer < 0) lastBuffer = KSoundBuffers - 1;
\r
252 iScheduler->Schedule();
\r
253 if(!iListener.iIsOpen) WaitForOpenToCompleteL();
\r
254 iMdaAudioOutputStream->WriteL(*iSoundBuffers[KSoundBuffers]); // special empty fill-up
\r
255 iMdaAudioOutputStream->WriteL(*iSoundBuffers[lastBuffer]);
\r
256 iTime = TInt64(0, KBlockTime/4 + KBlockTime);
\r
260 void CGameAudioMS::WaitForCopyToCompleteL()
\r
262 DEBUGPRINT(_L("CGameAudioMS::WaitForCopyToCompleteL"));
\r
263 while (!iListener.iHasCopied) {
\r
265 iScheduler->Schedule();
\r
270 void CGameAudioMS::WaitForOpenToCompleteL()
\r
272 DEBUGPRINT(_L("CGameAudioMS::WaitForOpenToCompleteL"));
\r
273 TInt count = 20; // 2 seconds
\r
274 TInt waitPeriod = 100 * 1000;
\r
276 if(!iListener.iIsOpen) {
\r
277 // it is often enough to do this
\r
279 iScheduler->Schedule();
\r
281 while (!iListener.iIsOpen && --count)
\r
283 User::After(waitPeriod);
\r
284 iScheduler->Schedule();
\r
286 if (!iListener.iIsOpen)
\r
287 User::LeaveIfError(KErrNotSupported);
\r
290 void CGameAudioMS::ChangeVolume(TInt aUp)
\r
293 DEBUGPRINT(_L("CGameAudioMS::ChangeVolume(%i)"), aUp);
\r
296 void TGameAudioEventListener::MaoscOpenComplete(TInt aError)
\r
298 DEBUGPRINT(_L("CGameAudioMS::MaoscOpenComplete, error=%d"), aError);
\r
301 if(aError) iUnderflowed++;
\r
302 else iUnderflowed = 0;
\r
305 void TGameAudioEventListener::MaoscBufferCopied(TInt aError, const TDesC8& aBuffer)
\r
307 DEBUGPRINT(_L("CGameAudioMS::MaoscBufferCopied, error=%d"), aError);
\r
309 // iHasCopied = ETrue;
\r
311 if(aError) // shit!
\r
315 void TGameAudioEventListener::MaoscPlayComplete(TInt aError)
\r
317 DEBUGPRINT(_L("CGameAudioMS::MaoscPlayComplete: %i"), aError);
\r
319 iUnderflowed++; // never happened to me while testing, but just in case
\r