UIQ3 update, some makefile unification, rm old configs, stuff
[picodrive.git] / platform / uiq3 / engine / audio_mediaserver.cpp
CommitLineData
cc68a136 1/*******************************************************************\r
2 *\r
3 * File: Audio_mediaserver.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#include "audio_mediaserver.h"\r
16#include "debug.h"\r
17\r
ca482e5d 18//#define DEBUG_UNDERFLOWS\r
cc68a136 19//#undef DEBUGPRINT\r
20//#define DEBUGPRINT(x...)\r
21\r
22\r
23const TInt KUpdatesPerSec = 10;\r
24const TInt KBlockTime = 1000000 / KUpdatesPerSec;\r
25const TInt KMaxLag = 200000; // max sound lag, lower values increase chance of underflow\r
26const TInt KMaxUnderflows = 50; // max underflows/API errors we are going allow in a row (to prevent lockups)\r
27\r
28\r
29/*******************************************\r
30 *\r
31 * CGameAudioMS\r
32 *\r
33 *******************************************/\r
34\r
ca482e5d 35CGameAudioMS::CGameAudioMS(TInt aRate, TBool aStereo, TInt aWritesPerSec, TInt aVolume)\r
36: iRate(aRate), iStereo(aStereo), iWritesPerSec(aWritesPerSec), iVolume(aVolume)\r
cc68a136 37{\r
38}\r
39\r
40\r
ca482e5d 41CGameAudioMS* CGameAudioMS::NewL(TInt aRate, TBool aStereo, TInt aWritesPerSec, TInt aVolume)\r
cc68a136 42{\r
ca482e5d 43 DEBUGPRINT(_L("CGameAudioMS::NewL(%i, %i, %i, %i)"), aRate, aStereo, aWritesPerSec, aVolume);\r
44 CGameAudioMS* self = new(ELeave) CGameAudioMS(aRate, aStereo, aWritesPerSec, aVolume);\r
cc68a136 45 CleanupStack::PushL(self);\r
46 self->ConstructL();\r
47 CleanupStack::Pop(); // self\r
48 return self;\r
49}\r
50\r
51\r
52CGameAudioMS::~CGameAudioMS()\r
53{\r
54 DEBUGPRINT(_L("CGameAudioMS::~CGameAudioMS()"));\r
55 if(iMdaAudioOutputStream) {\r
56 iScheduler->Schedule(); // let it finish it's stuff\r
57 iMdaAudioOutputStream->Stop();\r
58 delete iMdaAudioOutputStream;\r
59 }\r
60 if(iServer) delete iServer;\r
61\r
62 for (TInt i=0; i<KSoundBuffers; i++)\r
63 delete iSoundBuffers[i];\r
64\r
65 // Polled AS\r
66 //if(iScheduler) delete iScheduler;\r
67}\r
68\r
69\r
70void CGameAudioMS::ConstructL()\r
71{\r
72 iServer = CMdaServer::NewL();\r
73\r
74 // iScheduler = CPolledActiveScheduler::NewL();\r
75 iScheduler = CPolledActiveScheduler::Instance();\r
76\r
77 switch(iRate) {\r
78 case 11025: iMdaAudioDataSettings.iSampleRate = TMdaAudioDataSettings::ESampleRate11025Hz; break;\r
79 case 16000: iMdaAudioDataSettings.iSampleRate = TMdaAudioDataSettings::ESampleRate16000Hz; break;\r
80 case 22050: iMdaAudioDataSettings.iSampleRate = TMdaAudioDataSettings::ESampleRate22050Hz; break;\r
81 case 44100: iMdaAudioDataSettings.iSampleRate = TMdaAudioDataSettings::ESampleRate44100Hz; break;\r
82 default: iMdaAudioDataSettings.iSampleRate = TMdaAudioDataSettings::ESampleRate8000Hz; break;\r
83 }\r
84\r
85 iMdaAudioDataSettings.iChannels = (iStereo) ? TMdaAudioDataSettings::EChannelsStereo : TMdaAudioDataSettings::EChannelsMono;\r
86 iMdaAudioDataSettings.iCaps = TMdaAudioDataSettings::ESampleRateFixed | iMdaAudioDataSettings.iSampleRate;\r
87 iMdaAudioDataSettings.iFlags = TMdaAudioDataSettings::ENoNetworkRouting;\r
88\r
ca482e5d 89 iMaxWriteSamples = iRate / iWritesPerSec;\r
90 if (iRate % iWritesPerSec)\r
91 iMaxWriteSamples++;\r
92 int bufferedFrames = iWritesPerSec / KUpdatesPerSec;\r
cc68a136 93\r
ca482e5d 94 iBufferSize = iMaxWriteSamples * (iStereo ? 4 : 2);\r
95 iBufferSize *= bufferedFrames;\r
cc68a136 96 for (TInt i=0 ; i<KSoundBuffers ; i++)\r
97 {\r
ca482e5d 98 iSoundBuffers[i] = HBufC8::NewL(iBufferSize);\r
99 iSoundBuffers[i]->Des().FillZ (iBufferSize);\r
cc68a136 100 }\r
101\r
102 iCurrentBuffer = 0;\r
103 iCurrentBufferSize = 0;\r
104\r
ca482e5d 105 DEBUGPRINT(_L("sound: iMaxWriteSamples: %i, iBufferSize: %i"), iMaxWriteSamples, iBufferSize);\r
106\r
cc68a136 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
ca482e5d 109 if (iMdaAudioOutputStream) {\r
110 if (iVolume < 0 || iVolume > iMdaAudioOutputStream->MaxVolume())\r
111 iVolume = iMdaAudioOutputStream->MaxVolume();\r
cc68a136 112 delete iMdaAudioOutputStream;\r
113 iMdaAudioOutputStream = 0;\r
114 }\r
115}\r
116\r
117// returns a pointer to buffer for next frame,\r
118// to be used when iSoundBuffers are used directly\r
119TInt16 *CGameAudioMS::NextFrameL(TInt aPcmFrames)\r
120{\r
ca482e5d 121 TInt mul = iStereo ? 4 : 2;\r
122 TInt bytes = aPcmFrames * mul;\r
123 iCurrentPosition += bytes / 2;\r
124 iCurrentBufferSize += bytes;\r
cc68a136 125\r
ca482e5d 126 if (aPcmFrames > iMaxWriteSamples) {\r
127 DEBUGPRINT(_L("too many samples: %i > %i"), aPcmFrames, iMaxWriteSamples);\r
128 }\r
129\r
130 if (iCurrentBufferSize + iMaxWriteSamples * mul > iBufferSize)\r
cc68a136 131 {\r
ca482e5d 132 //DEBUGPRINT(_L("write on iCurrentBufferSize %i"), iCurrentBufferSize);\r
cc68a136 133 WriteBlockL();\r
134 }\r
135\r
136 iScheduler->Schedule();\r
137\r
138 if(iListener.iUnderflowed) {\r
139 if(iListener.iUnderflowed > KMaxUnderflows) {\r
140 delete iMdaAudioOutputStream;\r
141 iMdaAudioOutputStream = 0;\r
142 return 0;\r
143 }\r
144 UnderflowedL(); // not again!\r
145 }\r
146\r
147 return iCurrentPosition;\r
148}\r
149\r
150void CGameAudioMS::WriteBlockL()\r
151{\r
152 iScheduler->Schedule();\r
153 // do not write until stream is open\r
154 if(!iListener.iIsOpen) WaitForOpenToCompleteL();\r
155 //if(!iListener.iHasCopied) WaitForCopyToCompleteL(); // almost never happens anyway and sometimes even deadlocks?\r
156 //iListener.iHasCopied = EFalse;\r
157 \r
158\r
159 if(!iListener.iUnderflowed) {\r
160 TInt64 delta;\r
161 // don't write if sound is lagging too much\r
162 delta = iTime - iMdaAudioOutputStream->Position().Int64();\r
163 if (delta > MAKE_TINT64(0, KMaxLag))\r
164 // another query sometimes returns very different result\r
165 delta = iTime - iMdaAudioOutputStream->Position().Int64();\r
166\r
167 if(delta <= MAKE_TINT64(0, KMaxLag)) {\r
168 //RDebug::Print(_L("delta: %i"), iTime.Low() - iMdaAudioOutputStream->Position().Int64().Low());\r
169 iSoundBuffers[iCurrentBuffer]->Des().SetLength(iCurrentBufferSize);\r
170 iMdaAudioOutputStream->WriteL(*iSoundBuffers[iCurrentBuffer]);\r
171 iTime += KBlockTime;\r
172 } else {\r
173 DEBUGPRINT(_L("lag: %i"), I64LOW(delta));\r
174 }\r
175 }\r
176\r
cc68a136 177 if (++iCurrentBuffer == KSoundBuffers)\r
178 iCurrentBuffer = 0;\r
ca482e5d 179 iSoundBuffers[iCurrentBuffer]->Des().SetMax();\r
cc68a136 180 iCurrentPosition = (TInt16*) iSoundBuffers[iCurrentBuffer]->Ptr();\r
181 iCurrentBufferSize = 0;\r
182}\r
183\r
184void CGameAudioMS::Pause()\r
185{\r
186 if(!iMdaAudioOutputStream) return;\r
187\r
188 iScheduler->Schedule(); // let it finish it's stuff\r
189 iMdaAudioOutputStream->Stop();\r
190 delete iMdaAudioOutputStream;\r
191 iMdaAudioOutputStream = 0;\r
192}\r
193\r
194// call this before doing any playback!\r
195TInt16 *CGameAudioMS::ResumeL()\r
196{\r
197 DEBUGPRINT(_L("CGameAudioMS::Resume()"));\r
198 iScheduler->Schedule();\r
199\r
200 // we act a bit strange here: simulate buffer underflow, which actually starts audio\r
201 iListener.iIsOpen = ETrue;\r
202 iListener.iUnderflowed = 1;\r
203 iListener.iLastError = 0;\r
cc68a136 204 iCurrentBufferSize = 0;\r
205 iCurrentPosition = (TInt16*) iSoundBuffers[iCurrentBuffer]->Ptr();\r
206 return iCurrentPosition;\r
207}\r
208\r
209// handles underflow condition\r
210void CGameAudioMS::UnderflowedL()\r
211{\r
ca482e5d 212#ifdef DEBUG_UNDERFLOWS\r
cc68a136 213 DEBUGPRINT(_L("UnderflowedL()"));\r
ca482e5d 214#endif\r
cc68a136 215\r
216 if (iListener.iLastError != KErrUnderflow)\r
217 {\r
218 // recreate the stream\r
219 //iMdaAudioOutputStream->Stop();\r
220 if(iMdaAudioOutputStream) delete iMdaAudioOutputStream;\r
221 iMdaAudioOutputStream = CMdaAudioOutputStream::NewL(iListener, iServer);\r
222 iMdaAudioOutputStream->Open(&iMdaAudioDataSettings);\r
223 iMdaAudioOutputStream->SetAudioPropertiesL(iMdaAudioDataSettings.iSampleRate, iMdaAudioDataSettings.iChannels);\r
224 iMdaAudioOutputStream->SetVolume(iVolume); // new in UIQ3\r
225\r
226 iListener.iIsOpen = EFalse; // wait for it to open\r
227 //iListener.iHasCopied = ETrue; // but don't wait for last copy to complete\r
228 // let it open and feed some stuff to make it happy\r
229 User::After(0);\r
230 iScheduler->Schedule();\r
231 iListener.iLastError = 0;\r
232 if(!iListener.iIsOpen) WaitForOpenToCompleteL();\r
233 } else {\r
234 iListener.iLastError = iListener.iUnderflowed = 0;\r
235 }\r
236 iTime = iMdaAudioOutputStream->Position().Int64();\r
237}\r
238\r
239void CGameAudioMS::WaitForOpenToCompleteL()\r
240{\r
241 DEBUGPRINT(_L("CGameAudioMS::WaitForOpenToCompleteL"));\r
242 TInt count = 20; // 2 seconds\r
243 TInt waitPeriod = 100 * 1000;\r
244\r
245 if(!iListener.iIsOpen) {\r
246 // it is often enough to do this\r
247 User::After(0);\r
248 iScheduler->Schedule();\r
249 }\r
250 while (!iListener.iIsOpen && --count)\r
251 {\r
252 User::After(waitPeriod);\r
253 iScheduler->Schedule();\r
254 }\r
255 if (!iListener.iIsOpen)\r
256 User::LeaveIfError(KErrNotSupported);\r
257}\r
258\r
ca482e5d 259TInt CGameAudioMS::ChangeVolume(TInt aUp)\r
cc68a136 260{\r
261 //DEBUGPRINT(_L("CGameAudioMS::ChangeVolume(%i)"), aUp);\r
262\r
263 if (iMdaAudioOutputStream) {\r
264 if (aUp) {\r
ca482e5d 265 iVolume += 5;\r
266 if (iVolume > iMdaAudioOutputStream->MaxVolume())\r
267 iVolume = iMdaAudioOutputStream->MaxVolume();\r
cc68a136 268 } else {\r
ca482e5d 269 iVolume -= 5;\r
270 if (iVolume < 0) iVolume = 0;\r
cc68a136 271 }\r
272 iMdaAudioOutputStream->SetVolume(iVolume);\r
273 }\r
ca482e5d 274\r
275 return iVolume;\r
cc68a136 276}\r
277\r
278void TGameAudioEventListener::MaoscOpenComplete(TInt aError)\r
279{\r
ca482e5d 280#ifdef DEBUG_UNDERFLOWS\r
cc68a136 281 DEBUGPRINT(_L("CGameAudioMS::MaoscOpenComplete, error=%d"), aError);\r
ca482e5d 282#endif\r
cc68a136 283\r
284 iIsOpen = ETrue;\r
285 if(aError) {\r
286 iLastError = aError;\r
287 iUnderflowed++;\r
288 }\r
289 else iUnderflowed = 0;\r
290}\r
291\r
292void TGameAudioEventListener::MaoscBufferCopied(TInt aError, const TDesC8& aBuffer)\r
293{\r
294 if (aError)\r
295 DEBUGPRINT(_L("CGameAudioMS::MaoscBufferCopied, error=%d"), aError);\r
296\r
297// iHasCopied = ETrue;\r
298\r
299 if(aError) { // shit!\r
300 iLastError = aError;\r
301 iUnderflowed++;\r
302 }\r
303}\r
304\r
305void TGameAudioEventListener::MaoscPlayComplete(TInt aError)\r
306{\r
ca482e5d 307#ifdef DEBUG_UNDERFLOWS\r
cc68a136 308 DEBUGPRINT(_L("CGameAudioMS::MaoscPlayComplete: %i"), aError);\r
ca482e5d 309#endif\r
cc68a136 310 if(aError) {\r
311 iLastError = aError;\r
312 iUnderflowed++; // never happened to me while testing, but just in case\r
313 }\r
314}\r
315\r