1 /*******************************************************************
\r
3 * File: Audio_motorola.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 // if only I had Motorola to test this on..
\r
18 #include "audio_motorola.h"
\r
20 #ifdef __DEBUG_PRINT_SND
\r
21 #include <e32svr.h> // RDebug
\r
22 #define DEBUGPRINT(x...) RDebug::Print(x)
\r
24 #define DEBUGPRINT(x...)
\r
28 GLDEF_C TInt E32Dll(TDllReason)
\r
34 /*******************************************
\r
38 *******************************************/
\r
40 CGameAudioMot::CGameAudioMot(TInt aRate, TBool aStereo, TInt aPcmFrames, TInt aBufferedFrames)
\r
41 : iRate(aRate), iStereo(aStereo), iBufferedFrames(aBufferedFrames), iPcmFrames(aPcmFrames)
\r
43 DEBUGPRINT(_L("CGameAudioMot::CGameAudioMot"));
\r
47 CGameAudioMot* CGameAudioMot::NewL(TInt aRate, TBool aStereo, TInt aPcmFrames, TInt aBufferedFrames)
\r
49 DEBUGPRINT(_L("CGameAudioMot::NewL(%i, %i, %i, %i)"),aRate, aStereo, aPcmFrames, aBufferedFrames);
\r
50 CGameAudioMot* self = new(ELeave) CGameAudioMot(aRate, aStereo, aPcmFrames, aBufferedFrames);
\r
51 CleanupStack::PushL(self);
\r
53 CleanupStack::Pop(); // self
\r
58 CGameAudioMot::~CGameAudioMot()
\r
60 DEBUGPRINT(_L("CGameAudioMot::~CGameAudioMot()"));
\r
61 if(iAudioOutputStream) {
\r
62 iScheduler->Schedule(); // let it finish it's stuff
\r
63 //iAudioOutputStream->Stop();
\r
64 delete iAudioOutputStream;
\r
67 if(iAudioControl) delete iAudioControl;
\r
69 for (TInt i=0 ; i < KSoundBuffers+1; i++) {
\r
70 delete iSoundBufferPtrs[i];
\r
71 delete iSoundBuffers[i];
\r
75 if(iScheduler) delete iScheduler;
\r
79 void CGameAudioMot::ConstructL()
\r
81 iScheduler = CPolledActiveScheduler::NewL();
\r
83 iSettings.iPCMSettings.iSamplingFreq = (TMSampleRate) iRate;
\r
84 iSettings.iPCMSettings.iStereo = iStereo;
\r
86 TInt bytesPerFrame = iStereo ? iPcmFrames << 2 : iPcmFrames << 1;
\r
87 for (TInt i=0 ; i<KSoundBuffers ; i++)
\r
89 iSoundBuffers[i] = HBufC8::NewL(bytesPerFrame * iBufferedFrames);
\r
90 iSoundBuffers[i]->Des().FillZ (bytesPerFrame * iBufferedFrames);
\r
91 iSoundBufferPtrs[i] = new TPtr8( iSoundBuffers[i]->Des() );
\r
93 // because feeding 2 buffers after an underflow is a little too much, but feeding 1 may be not enough,
\r
94 // prepare this ~50ms empty buffer to additionaly feed after every underflow.
\r
95 iSoundBuffers[KSoundBuffers] = HBufC8::NewL(bytesPerFrame * (iBufferedFrames / 4));
\r
96 iSoundBuffers[KSoundBuffers]->Des().FillZ (bytesPerFrame * (iBufferedFrames / 4));
\r
97 iSoundBufferPtrs[KSoundBuffers] = new TPtr8( iSoundBuffers[KSoundBuffers]->Des() );
\r
100 iListener.iFatalError = iListener.iIsOpen = iListener.iIsCtrlOpen = EFalse;
\r
102 // here we actually test if we can create and open CMdaAudioOutputStream at all, but really create and use it later.
\r
103 iAudioOutputStream = CMAudioFB::NewL(EMAudioFBRequestTypeDecode, EMAudioFBFormatPCM, iSettings, iListener);
\r
104 if(iAudioOutputStream) {
\r
105 delete iAudioOutputStream;
\r
106 iAudioOutputStream = 0;
\r
109 // ceate audio control object
\r
110 iAudioControl = CMAudioAC::NewL(iListener);
\r
114 // returns a pointer to buffer for next frame,
\r
115 // to be used when iSoundBuffers are used directly
\r
116 TInt16 *CGameAudioMot::NextFrameL()
\r
118 iCurrentPosition += iPcmFrames << (iStereo?1:0);
\r
120 if (++iFrameCount == iBufferedFrames)
\r
125 iScheduler->Schedule();
\r
127 if(iListener.iFatalError || iListener.iUnderflowed > KMaxUnderflows) {
\r
128 if(iAudioOutputStream) delete iAudioOutputStream;
\r
129 iAudioOutputStream = 0;
\r
132 else if(iListener.iUnderflowed) UnderflowedL();
\r
134 return iCurrentPosition;
\r
137 TInt16 *CGameAudioMot::DupeFrameL(TInt &aUnderflowed)
\r
139 TInt shorts = iStereo ? (iPcmFrames << 1) : iPcmFrames;
\r
141 Mem::Copy(iCurrentPosition, iCurrentPosition-shorts, shorts<<1);
\r
143 TInt lastBuffer = iCurrentBuffer;
\r
144 if(--lastBuffer < 0) lastBuffer = KSoundBuffers - 1;
\r
145 Mem::Copy(iCurrentPosition, ((TInt16*) (iSoundBuffers[lastBuffer]->Ptr()))+shorts*(iBufferedFrames-1), shorts<<1);
\r
147 iCurrentPosition += shorts;
\r
149 if (++iFrameCount == iBufferedFrames)
\r
154 iScheduler->Schedule();
\r
156 if(iListener.iFatalError || iListener.iUnderflowed > KMaxUnderflows) {
\r
157 if(iAudioOutputStream) delete iAudioOutputStream;
\r
158 iAudioOutputStream = 0;
\r
161 else if((aUnderflowed = iListener.iUnderflowed)) UnderflowedL(); // not again!
\r
163 return iCurrentPosition;
\r
166 void CGameAudioMot::WriteBlockL()
\r
168 iScheduler->Schedule();
\r
170 // do not write until stream is open
\r
171 if(!iListener.iIsOpen) WaitForOpenToCompleteL();
\r
172 //if(!iListener.iHasCopied) WaitForCopyToCompleteL(); // almost never happens anyway and sometimes even deadlocks?
\r
173 //iListener.iHasCopied = EFalse;
\r
176 if(!iListener.iUnderflowed) {
\r
177 iAudioOutputStream->QueueBufferL(iSoundBufferPtrs[iCurrentBuffer]);
\r
178 // it is certain we already Queued at least 2 buffers (one just after underflow, another above)
\r
180 iAudioOutputStream->DecodeL();
\r
186 if (++iCurrentBuffer == KSoundBuffers)
\r
187 iCurrentBuffer = 0;
\r
188 iCurrentPosition = (TInt16*) iSoundBuffers[iCurrentBuffer]->Ptr();
\r
191 void CGameAudioMot::Pause()
\r
193 if(!iAudioOutputStream) return;
\r
195 iScheduler->Schedule();
\r
196 // iAudioOutputStream->Stop(); // may be this breaks everything in A925?
\r
197 delete iAudioOutputStream;
\r
198 iAudioOutputStream = 0;
\r
201 // call this before doing any playback!
\r
202 TInt16 *CGameAudioMot::ResumeL()
\r
204 DEBUGPRINT(_L("CGameAudioMot::Resume()"));
\r
205 iScheduler->Schedule();
\r
207 // we act a bit strange here: simulate buffer underflow, which actually starts audio
\r
208 iListener.iIsOpen = ETrue;
\r
209 iListener.iUnderflowed = 1;
\r
210 iListener.iFatalError = EFalse;
\r
212 iCurrentPosition = (TInt16*) iSoundBuffers[iCurrentBuffer]->Ptr();
\r
213 return iCurrentPosition;
\r
216 // handles underflow condition
\r
217 void CGameAudioMot::UnderflowedL()
\r
219 // recreate the stream
\r
220 if(iAudioOutputStream) delete iAudioOutputStream;
\r
221 if(iListener.iUnderflowed > 4) {
\r
222 // HACK: A925 user said sound works for the first time, but fails after pause/resume, etc.
\r
223 // at the very beginning we create and delete CMAudioFB object, maybe we should do this every time?
\r
224 iAudioOutputStream = CMAudioFB::NewL(EMAudioFBRequestTypeDecode, EMAudioFBFormatPCM, iSettings, iListener);
\r
225 if(iAudioOutputStream) delete iAudioOutputStream;
\r
228 iAudioOutputStream = CMAudioFB::NewL(EMAudioFBRequestTypeDecode, EMAudioFBFormatPCM, iSettings, iListener);
\r
229 iListener.iIsOpen = EFalse; // wait for it to open
\r
230 iDecoding = EFalse;
\r
231 //iListener.iHasCopied = ETrue; // but don't wait for last copy to complete
\r
232 // let it open and feed some stuff to make it happy
\r
234 //TInt lastBuffer = iCurrentBuffer;
\r
235 //if(--lastBuffer < 0) lastBuffer = KSoundBuffers - 1;
\r
236 iScheduler->Schedule();
\r
237 if(!iListener.iIsOpen) WaitForOpenToCompleteL();
\r
238 if(iListener.iUnderflowed) {
\r
239 // something went wrong again. May be it needs time? Trying to fix something without ability to test is hell.
\r
240 if(iAudioOutputStream) delete iAudioOutputStream;
\r
241 iAudioOutputStream = 0;
\r
242 User::After(50*000);
\r
243 iScheduler->Schedule();
\r
247 iAudioOutputStream->QueueBufferL(iSoundBufferPtrs[KSoundBuffers]); // try a short buffer with hope to reduce lag
\r
251 void CGameAudioMot::ChangeVolume(TInt aUp)
\r
253 if(iAudioControl && iListener.iIsCtrlOpen)
\r
255 TInt vol = iAudioControl->GetMasterVolume();
\r
256 TInt max = iAudioControl->GetMaxMasterVolume();
\r
258 if(aUp) vol++; // adjust volume
\r
261 if(vol >= 0 && vol <= max)
\r
263 iAudioControl->SetMasterVolume(vol);
\r
269 void CGameAudioMot::WaitForOpenToCompleteL()
\r
271 DEBUGPRINT(_L("CGameAudioMot::WaitForOpenToCompleteL"));
\r
272 TInt count = 20; // 2 seconds
\r
273 TInt waitPeriod = 100 * 1000;
\r
275 if(!iListener.iIsOpen) {
\r
276 // it is often enough to do this
\r
278 iScheduler->Schedule();
\r
280 while (!iListener.iIsOpen && --count)
\r
282 User::After(waitPeriod);
\r
283 iScheduler->Schedule();
\r
285 if (!iListener.iIsOpen)
\r
286 User::LeaveIfError(KErrNotSupported);
\r
291 void TGameAudioEventListener::OnEvent(TMAudioFBCallbackState aState, TInt aError)
\r
295 case EMAudioFBCallbackStateReady:
\r
300 case EMAudioFBCallbackStateDecodeCompleteStopped:
\r
303 //case EMAudioFBCallbackStateDecodeFileSystemError:
\r
304 case EMAudioFBCallbackStateDecodeError:
\r
307 case EMAudioFBCallbackErrorBufferFull:
\r
308 case EMAudioFBCallbackErrorForcedStop:
\r
309 case EMAudioFBCallbackErrorForcedClose:
\r
310 //case EMAudioFBCallbackErrorForcedPause:
\r
311 case EMAudioFBCallbackErrorPriorityRejection:
\r
312 case EMAudioFBCallbackErrorAlertModeRejection:
\r
313 case EMAudioFBCallbackErrorResourceRejection:
\r
314 case EMAudioFBCallbackErrorUnknown:
\r
318 // these look like really bad errors
\r
319 case EMAudioFBCallbackErrorInvalidParameter:
\r
320 case EMAudioFBCallbackErrorWrongState:
\r
321 case EMAudioFBCallbackErrorFormatNotSupported:
\r
322 case EMAudioFBCallbackErrorFunctionNotSupported:
\r
323 case EMAudioFBCallbackErrorNoBuffer:
\r
324 case EMAudioFBCallbackErrorSampleOrBitRateNotSupported:
\r
325 //case EMAudioFBCallbackErrorPriorityOrPreferenceNotSupported:
\r
326 //case EMAudioFBCallbackErrorFileSystemFull:
\r
327 //iFatalError = ETrue;
\r
328 // who cares, just keep retrying
\r
336 // in error condition we also set to open, so that the
\r
337 // framework would not leave, catch the error and retry
\r
346 void TGameAudioEventListener::OnEvent(TMAudioFBCallbackState aState, TInt aError, TDes8* aBuffer)
\r
350 case EMAudioFBCallbackStateDecodeBufferDecoded:
\r
354 OnEvent( aState, aError );
\r
359 void TGameAudioEventListener::OnEvent(TMAudioACCallbackState aState, TInt aError)
\r
361 if(aState == EMAudioACCallbackStateReady) iIsCtrlOpen = ETrue;
\r