switch Cyclone to submodule on it's own git repo
[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
6a13ef3f 98 // it seems .SetLength(max) throws USER:23 panic,\r
99 // so make them a bit larger\r
100 iSoundBuffers[i] = HBufC8::NewL(iBufferSize+4);\r
101 iSoundBuffers[i]->Des().FillZ (iBufferSize+4);\r
cc68a136 102 }\r
103\r
104 iCurrentBuffer = 0;\r
105 iCurrentBufferSize = 0;\r
106\r
ca482e5d 107 DEBUGPRINT(_L("sound: iMaxWriteSamples: %i, iBufferSize: %i"), iMaxWriteSamples, iBufferSize);\r
108\r
cc68a136 109 // here we actually test if we can create and open CMdaAudioOutputStream at all, but really create and use it later.\r
110 iMdaAudioOutputStream = CMdaAudioOutputStream::NewL(iListener, iServer);\r
ca482e5d 111 if (iMdaAudioOutputStream) {\r
112 if (iVolume < 0 || iVolume > iMdaAudioOutputStream->MaxVolume())\r
113 iVolume = iMdaAudioOutputStream->MaxVolume();\r
cc68a136 114 delete iMdaAudioOutputStream;\r
115 iMdaAudioOutputStream = 0;\r
116 }\r
117}\r
118\r
119// returns a pointer to buffer for next frame,\r
120// to be used when iSoundBuffers are used directly\r
121TInt16 *CGameAudioMS::NextFrameL(TInt aPcmFrames)\r
122{\r
ca482e5d 123 TInt mul = iStereo ? 4 : 2;\r
124 TInt bytes = aPcmFrames * mul;\r
125 iCurrentPosition += bytes / 2;\r
126 iCurrentBufferSize += bytes;\r
cc68a136 127\r
ca482e5d 128 if (aPcmFrames > iMaxWriteSamples) {\r
129 DEBUGPRINT(_L("too many samples: %i > %i"), aPcmFrames, iMaxWriteSamples);\r
130 }\r
131\r
132 if (iCurrentBufferSize + iMaxWriteSamples * mul > iBufferSize)\r
cc68a136 133 {\r
ca482e5d 134 //DEBUGPRINT(_L("write on iCurrentBufferSize %i"), iCurrentBufferSize);\r
cc68a136 135 WriteBlockL();\r
136 }\r
137\r
138 iScheduler->Schedule();\r
139\r
140 if(iListener.iUnderflowed) {\r
141 if(iListener.iUnderflowed > KMaxUnderflows) {\r
142 delete iMdaAudioOutputStream;\r
143 iMdaAudioOutputStream = 0;\r
144 return 0;\r
145 }\r
146 UnderflowedL(); // not again!\r
147 }\r
148\r
149 return iCurrentPosition;\r
150}\r
151\r
152void CGameAudioMS::WriteBlockL()\r
153{\r
154 iScheduler->Schedule();\r
155 // do not write until stream is open\r
156 if(!iListener.iIsOpen) WaitForOpenToCompleteL();\r
157 //if(!iListener.iHasCopied) WaitForCopyToCompleteL(); // almost never happens anyway and sometimes even deadlocks?\r
158 //iListener.iHasCopied = EFalse;\r
159 \r
160\r
161 if(!iListener.iUnderflowed) {\r
162 TInt64 delta;\r
163 // don't write if sound is lagging too much\r
164 delta = iTime - iMdaAudioOutputStream->Position().Int64();\r
165 if (delta > MAKE_TINT64(0, KMaxLag))\r
166 // another query sometimes returns very different result\r
167 delta = iTime - iMdaAudioOutputStream->Position().Int64();\r
168\r
169 if(delta <= MAKE_TINT64(0, KMaxLag)) {\r
170 //RDebug::Print(_L("delta: %i"), iTime.Low() - iMdaAudioOutputStream->Position().Int64().Low());\r
171 iSoundBuffers[iCurrentBuffer]->Des().SetLength(iCurrentBufferSize);\r
172 iMdaAudioOutputStream->WriteL(*iSoundBuffers[iCurrentBuffer]);\r
173 iTime += KBlockTime;\r
174 } else {\r
175 DEBUGPRINT(_L("lag: %i"), I64LOW(delta));\r
176 }\r
177 }\r
178\r
cc68a136 179 if (++iCurrentBuffer == KSoundBuffers)\r
180 iCurrentBuffer = 0;\r
ca482e5d 181 iSoundBuffers[iCurrentBuffer]->Des().SetMax();\r
cc68a136 182 iCurrentPosition = (TInt16*) iSoundBuffers[iCurrentBuffer]->Ptr();\r
183 iCurrentBufferSize = 0;\r
184}\r
185\r
186void CGameAudioMS::Pause()\r
187{\r
188 if(!iMdaAudioOutputStream) return;\r
189\r
190 iScheduler->Schedule(); // let it finish it's stuff\r
191 iMdaAudioOutputStream->Stop();\r
192 delete iMdaAudioOutputStream;\r
193 iMdaAudioOutputStream = 0;\r
194}\r
195\r
196// call this before doing any playback!\r
197TInt16 *CGameAudioMS::ResumeL()\r
198{\r
199 DEBUGPRINT(_L("CGameAudioMS::Resume()"));\r
200 iScheduler->Schedule();\r
201\r
202 // we act a bit strange here: simulate buffer underflow, which actually starts audio\r
203 iListener.iIsOpen = ETrue;\r
204 iListener.iUnderflowed = 1;\r
205 iListener.iLastError = 0;\r
cc68a136 206 iCurrentBufferSize = 0;\r
207 iCurrentPosition = (TInt16*) iSoundBuffers[iCurrentBuffer]->Ptr();\r
208 return iCurrentPosition;\r
209}\r
210\r
211// handles underflow condition\r
212void CGameAudioMS::UnderflowedL()\r
213{\r
ca482e5d 214#ifdef DEBUG_UNDERFLOWS\r
cc68a136 215 DEBUGPRINT(_L("UnderflowedL()"));\r
ca482e5d 216#endif\r
cc68a136 217\r
218 if (iListener.iLastError != KErrUnderflow)\r
219 {\r
220 // recreate the stream\r
221 //iMdaAudioOutputStream->Stop();\r
222 if(iMdaAudioOutputStream) delete iMdaAudioOutputStream;\r
223 iMdaAudioOutputStream = CMdaAudioOutputStream::NewL(iListener, iServer);\r
224 iMdaAudioOutputStream->Open(&iMdaAudioDataSettings);\r
225 iMdaAudioOutputStream->SetAudioPropertiesL(iMdaAudioDataSettings.iSampleRate, iMdaAudioDataSettings.iChannels);\r
226 iMdaAudioOutputStream->SetVolume(iVolume); // new in UIQ3\r
227\r
228 iListener.iIsOpen = EFalse; // wait for it to open\r
229 //iListener.iHasCopied = ETrue; // but don't wait for last copy to complete\r
230 // let it open and feed some stuff to make it happy\r
231 User::After(0);\r
232 iScheduler->Schedule();\r
233 iListener.iLastError = 0;\r
234 if(!iListener.iIsOpen) WaitForOpenToCompleteL();\r
235 } else {\r
236 iListener.iLastError = iListener.iUnderflowed = 0;\r
237 }\r
238 iTime = iMdaAudioOutputStream->Position().Int64();\r
239}\r
240\r
241void CGameAudioMS::WaitForOpenToCompleteL()\r
242{\r
243 DEBUGPRINT(_L("CGameAudioMS::WaitForOpenToCompleteL"));\r
244 TInt count = 20; // 2 seconds\r
245 TInt waitPeriod = 100 * 1000;\r
246\r
247 if(!iListener.iIsOpen) {\r
248 // it is often enough to do this\r
249 User::After(0);\r
250 iScheduler->Schedule();\r
251 }\r
252 while (!iListener.iIsOpen && --count)\r
253 {\r
254 User::After(waitPeriod);\r
255 iScheduler->Schedule();\r
256 }\r
257 if (!iListener.iIsOpen)\r
258 User::LeaveIfError(KErrNotSupported);\r
259}\r
260\r
ca482e5d 261TInt CGameAudioMS::ChangeVolume(TInt aUp)\r
cc68a136 262{\r
263 //DEBUGPRINT(_L("CGameAudioMS::ChangeVolume(%i)"), aUp);\r
264\r
265 if (iMdaAudioOutputStream) {\r
266 if (aUp) {\r
ca482e5d 267 iVolume += 5;\r
268 if (iVolume > iMdaAudioOutputStream->MaxVolume())\r
269 iVolume = iMdaAudioOutputStream->MaxVolume();\r
cc68a136 270 } else {\r
ca482e5d 271 iVolume -= 5;\r
272 if (iVolume < 0) iVolume = 0;\r
cc68a136 273 }\r
274 iMdaAudioOutputStream->SetVolume(iVolume);\r
275 }\r
ca482e5d 276\r
277 return iVolume;\r
cc68a136 278}\r
279\r
280void TGameAudioEventListener::MaoscOpenComplete(TInt aError)\r
281{\r
ca482e5d 282#ifdef DEBUG_UNDERFLOWS\r
cc68a136 283 DEBUGPRINT(_L("CGameAudioMS::MaoscOpenComplete, error=%d"), aError);\r
ca482e5d 284#endif\r
cc68a136 285\r
286 iIsOpen = ETrue;\r
287 if(aError) {\r
288 iLastError = aError;\r
289 iUnderflowed++;\r
290 }\r
291 else iUnderflowed = 0;\r
292}\r
293\r
294void TGameAudioEventListener::MaoscBufferCopied(TInt aError, const TDesC8& aBuffer)\r
295{\r
296 if (aError)\r
297 DEBUGPRINT(_L("CGameAudioMS::MaoscBufferCopied, error=%d"), aError);\r
298\r
299// iHasCopied = ETrue;\r
300\r
301 if(aError) { // shit!\r
302 iLastError = aError;\r
303 iUnderflowed++;\r
304 }\r
305}\r
306\r
307void TGameAudioEventListener::MaoscPlayComplete(TInt aError)\r
308{\r
ca482e5d 309#ifdef DEBUG_UNDERFLOWS\r
cc68a136 310 DEBUGPRINT(_L("CGameAudioMS::MaoscPlayComplete: %i"), aError);\r
ca482e5d 311#endif\r
cc68a136 312 if(aError) {\r
313 iLastError = aError;\r
314 iUnderflowed++; // never happened to me while testing, but just in case\r
315 }\r
316}\r
317\r