initial import
[picodrive.git] / platform / uiq2 / audio / motorola / audio_motorola.cpp
1 /*******************************************************************\r
2  *\r
3  *      File:           Audio_motorola.cpp\r
4  *\r
5  *      Author:         Peter van Sebille (peter@yipton.net)\r
6  *\r
7  *  Modified/adapted for picodriveN by notaz, 2006\r
8  *\r
9  *  (c) Copyright 2006, notaz\r
10  *      (c) Copyright 2001, Peter van Sebille\r
11  *      All Rights Reserved\r
12  *\r
13  *******************************************************************/\r
14 \r
15 // if only I had Motorola to test this on..\r
16 \r
17 \r
18 #include "audio_motorola.h"\r
19 \r
20 #ifdef __DEBUG_PRINT_SND\r
21         #include <e32svr.h> // RDebug\r
22         #define DEBUGPRINT(x...) RDebug::Print(x)\r
23 #else\r
24         #define DEBUGPRINT(x...)\r
25 #endif\r
26 \r
27 \r
28 GLDEF_C TInt E32Dll(TDllReason)\r
29 {\r
30         return KErrNone;\r
31 }\r
32 \r
33 \r
34 /*******************************************\r
35  *\r
36  * CGameAudioMot\r
37  *\r
38  *******************************************/\r
39 \r
40 CGameAudioMot::CGameAudioMot(TInt aRate, TBool aStereo, TInt aPcmFrames,  TInt aBufferedFrames)\r
41 : iRate(aRate), iStereo(aStereo), iBufferedFrames(aBufferedFrames), iPcmFrames(aPcmFrames)\r
42 {\r
43         DEBUGPRINT(_L("CGameAudioMot::CGameAudioMot"));\r
44 }\r
45 \r
46 \r
47 CGameAudioMot* CGameAudioMot::NewL(TInt aRate, TBool aStereo, TInt aPcmFrames, TInt aBufferedFrames)\r
48 {\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
52         self->ConstructL();\r
53         CleanupStack::Pop();            // self\r
54         return self;\r
55 }\r
56 \r
57 \r
58 CGameAudioMot::~CGameAudioMot()\r
59 {\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
65         }\r
66 \r
67         if(iAudioControl) delete iAudioControl;\r
68 \r
69         for (TInt i=0 ; i < KSoundBuffers+1; i++) {\r
70                 delete iSoundBufferPtrs[i];\r
71                 delete iSoundBuffers[i];\r
72         }\r
73 \r
74         // Polled AS\r
75         if(iScheduler) delete iScheduler;\r
76 }\r
77 \r
78 \r
79 void CGameAudioMot::ConstructL()\r
80 {\r
81         iScheduler = CPolledActiveScheduler::NewL();\r
82 \r
83         iSettings.iPCMSettings.iSamplingFreq = (TMSampleRate) iRate;\r
84         iSettings.iPCMSettings.iStereo       = iStereo;\r
85 \r
86         TInt    bytesPerFrame = iStereo ? iPcmFrames << 2 : iPcmFrames << 1;\r
87         for (TInt i=0 ; i<KSoundBuffers ; i++)\r
88         {\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
92         }\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
98 \r
99         iCurrentBuffer = 0;\r
100         iListener.iFatalError = iListener.iIsOpen = iListener.iIsCtrlOpen = EFalse;\r
101 \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
107         }\r
108 \r
109         // ceate audio control object\r
110         iAudioControl = CMAudioAC::NewL(iListener);\r
111 }\r
112 \r
113 \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
117 {\r
118         iCurrentPosition += iPcmFrames << (iStereo?1:0);\r
119 \r
120         if (++iFrameCount == iBufferedFrames)\r
121         {\r
122                 WriteBlockL();\r
123         }\r
124 \r
125         iScheduler->Schedule();\r
126 \r
127         if(iListener.iFatalError || iListener.iUnderflowed > KMaxUnderflows) {\r
128                 if(iAudioOutputStream) delete iAudioOutputStream;\r
129                 iAudioOutputStream = 0;\r
130                 return 0;\r
131         }\r
132         else if(iListener.iUnderflowed) UnderflowedL();\r
133 \r
134         return iCurrentPosition;\r
135 }\r
136 \r
137 TInt16 *CGameAudioMot::DupeFrameL(TInt &aUnderflowed)\r
138 {\r
139         TInt shorts = iStereo ? (iPcmFrames << 1) : iPcmFrames;\r
140         if(iFrameCount)\r
141                 Mem::Copy(iCurrentPosition, iCurrentPosition-shorts, shorts<<1);\r
142         else {\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
146         }                               \r
147         iCurrentPosition += shorts;\r
148 \r
149         if (++iFrameCount == iBufferedFrames)\r
150         {\r
151                 WriteBlockL();\r
152         }\r
153 \r
154         iScheduler->Schedule();\r
155 \r
156         if(iListener.iFatalError || iListener.iUnderflowed > KMaxUnderflows) {\r
157                 if(iAudioOutputStream) delete iAudioOutputStream;\r
158                 iAudioOutputStream = 0;\r
159                 return 0;\r
160         }\r
161         else if((aUnderflowed = iListener.iUnderflowed)) UnderflowedL(); // not again!\r
162 \r
163         return iCurrentPosition;\r
164 }\r
165 \r
166 void CGameAudioMot::WriteBlockL()\r
167 {\r
168         iScheduler->Schedule();\r
169 \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
174         \r
175 \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
179                 if(!iDecoding) {\r
180                         iAudioOutputStream->DecodeL();\r
181                         iDecoding = ETrue;\r
182                 }\r
183         }\r
184 \r
185         iFrameCount = 0;\r
186         if (++iCurrentBuffer == KSoundBuffers)\r
187                 iCurrentBuffer = 0;\r
188         iCurrentPosition = (TInt16*) iSoundBuffers[iCurrentBuffer]->Ptr();\r
189 }\r
190 \r
191 void CGameAudioMot::Pause()\r
192 {\r
193         if(!iAudioOutputStream) return;\r
194 \r
195         iScheduler->Schedule();\r
196         // iAudioOutputStream->Stop(); // may be this breaks everything in A925?\r
197         delete iAudioOutputStream;\r
198         iAudioOutputStream = 0;\r
199 }\r
200 \r
201 // call this before doing any playback!\r
202 TInt16 *CGameAudioMot::ResumeL()\r
203 {\r
204         DEBUGPRINT(_L("CGameAudioMot::Resume()"));\r
205         iScheduler->Schedule();\r
206 \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
211         iFrameCount = 0;\r
212         iCurrentPosition = (TInt16*) iSoundBuffers[iCurrentBuffer]->Ptr();\r
213         return iCurrentPosition;\r
214 }\r
215 \r
216 // handles underflow condition\r
217 void CGameAudioMot::UnderflowedL()\r
218 {\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
226         }\r
227 \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
233         User::After(0);\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
244                 return;\r
245         }\r
246 \r
247         iAudioOutputStream->QueueBufferL(iSoundBufferPtrs[KSoundBuffers]); // try a short buffer with hope to reduce lag\r
248 }\r
249 \r
250 \r
251 void CGameAudioMot::ChangeVolume(TInt aUp)\r
252 {\r
253         if(iAudioControl && iListener.iIsCtrlOpen)\r
254         {\r
255                 TInt vol = iAudioControl->GetMasterVolume();\r
256                 TInt max = iAudioControl->GetMaxMasterVolume();\r
257 \r
258                 if(aUp) vol++; // adjust volume\r
259                 else    vol--;\r
260 \r
261                 if(vol >= 0 && vol <= max)\r
262                 {\r
263                         iAudioControl->SetMasterVolume(vol);\r
264                 }\r
265         }\r
266 }\r
267 \r
268 \r
269 void CGameAudioMot::WaitForOpenToCompleteL()\r
270 {\r
271         DEBUGPRINT(_L("CGameAudioMot::WaitForOpenToCompleteL"));\r
272         TInt    count = 20;             // 2 seconds\r
273         TInt    waitPeriod = 100 * 1000;\r
274 \r
275         if(!iListener.iIsOpen) {\r
276                 // it is often enough to do this\r
277                 User::After(0);\r
278                 iScheduler->Schedule();\r
279         }\r
280         while (!iListener.iIsOpen && --count)\r
281         {\r
282                 User::After(waitPeriod);\r
283                 iScheduler->Schedule();\r
284         }\r
285         if (!iListener.iIsOpen)\r
286                 User::LeaveIfError(KErrNotSupported);\r
287 }\r
288 \r
289 \r
290 \r
291 void TGameAudioEventListener::OnEvent(TMAudioFBCallbackState aState, TInt aError)\r
292 {\r
293         switch ( aState )\r
294         {\r
295         case EMAudioFBCallbackStateReady:\r
296                 iIsOpen = ETrue;\r
297                 iUnderflowed = 0;\r
298                 break;\r
299 \r
300         case EMAudioFBCallbackStateDecodeCompleteStopped:\r
301                 break;\r
302 \r
303         //case EMAudioFBCallbackStateDecodeFileSystemError:\r
304         case EMAudioFBCallbackStateDecodeError:\r
305                 switch( aError )\r
306                 {\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
315                         iUnderflowed++;\r
316                         break;\r
317 \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
329                         iUnderflowed++;\r
330                         break;\r
331 \r
332                 default:\r
333                         iUnderflowed++;\r
334                         break;\r
335                 }\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
338                 iIsOpen = ETrue;\r
339                 break;\r
340 \r
341         default:\r
342                 break;\r
343         }\r
344 }\r
345 \r
346 void TGameAudioEventListener::OnEvent(TMAudioFBCallbackState aState, TInt aError, TDes8* aBuffer)\r
347 {\r
348    switch( aState )\r
349    {\r
350    case EMAudioFBCallbackStateDecodeBufferDecoded:\r
351           break;\r
352 \r
353    default:\r
354       OnEvent( aState, aError );\r
355       break;\r
356    }\r
357 }\r
358 \r
359 void TGameAudioEventListener::OnEvent(TMAudioACCallbackState aState, TInt aError)\r
360 {\r
361         if(aState == EMAudioACCallbackStateReady) iIsCtrlOpen = ETrue;\r
362 }\r
363 \r