switch Cyclone to submodule on it's own git repo
[picodrive.git] / platform / uiq2 / audio / motorola / audio_motorola.cpp
CommitLineData
cc68a136 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
28GLDEF_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
40CGameAudioMot::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
47CGameAudioMot* 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
58CGameAudioMot::~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
79void 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
116TInt16 *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
137TInt16 *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
166void 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
191void 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
202TInt16 *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
217void 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
251void 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
269void 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
291void 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
346void 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
359void TGameAudioEventListener::OnEvent(TMAudioACCallbackState aState, TInt aError)\r
360{\r
361 if(aState == EMAudioACCallbackStateReady) iIsCtrlOpen = ETrue;\r
362}\r
363\r