UIQ3 update, some makefile unification, rm old configs, stuff
[picodrive.git] / platform / uiq3 / engine / audio_mediaserver.cpp
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
18 //#define DEBUG_UNDERFLOWS\r
19 //#undef DEBUGPRINT\r
20 //#define DEBUGPRINT(x...)\r
21 \r
22 \r
23 const TInt KUpdatesPerSec = 10;\r
24 const TInt KBlockTime = 1000000 / KUpdatesPerSec;\r
25 const TInt KMaxLag = 200000; // max sound lag, lower values increase chance of underflow\r
26 const 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
35 CGameAudioMS::CGameAudioMS(TInt aRate, TBool aStereo, TInt aWritesPerSec, TInt aVolume)\r
36 : iRate(aRate), iStereo(aStereo), iWritesPerSec(aWritesPerSec), iVolume(aVolume)\r
37 {\r
38 }\r
39 \r
40 \r
41 CGameAudioMS* CGameAudioMS::NewL(TInt aRate, TBool aStereo, TInt aWritesPerSec, TInt aVolume)\r
42 {\r
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
45         CleanupStack::PushL(self);\r
46         self->ConstructL();\r
47         CleanupStack::Pop();            // self\r
48         return self;\r
49 }\r
50 \r
51 \r
52 CGameAudioMS::~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
70 void 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
89         iMaxWriteSamples = iRate / iWritesPerSec;\r
90         if (iRate % iWritesPerSec)\r
91                 iMaxWriteSamples++;\r
92         int bufferedFrames = iWritesPerSec / KUpdatesPerSec;\r
93 \r
94         iBufferSize = iMaxWriteSamples * (iStereo ? 4 : 2);\r
95         iBufferSize *= bufferedFrames;\r
96         for (TInt i=0 ; i<KSoundBuffers ; i++)\r
97         {\r
98                 iSoundBuffers[i] = HBufC8::NewL(iBufferSize);\r
99                 iSoundBuffers[i]->Des().FillZ  (iBufferSize);\r
100         }\r
101 \r
102         iCurrentBuffer = 0;\r
103         iCurrentBufferSize = 0;\r
104 \r
105         DEBUGPRINT(_L("sound: iMaxWriteSamples: %i, iBufferSize: %i"), iMaxWriteSamples, iBufferSize);\r
106 \r
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
109         if (iMdaAudioOutputStream) {\r
110                 if (iVolume < 0 || iVolume > iMdaAudioOutputStream->MaxVolume())\r
111                         iVolume = iMdaAudioOutputStream->MaxVolume();\r
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
119 TInt16 *CGameAudioMS::NextFrameL(TInt aPcmFrames)\r
120 {\r
121         TInt mul = iStereo ? 4 : 2;\r
122         TInt bytes = aPcmFrames * mul;\r
123         iCurrentPosition   += bytes / 2;\r
124         iCurrentBufferSize += bytes;\r
125 \r
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
131         {\r
132                 //DEBUGPRINT(_L("write on iCurrentBufferSize %i"), iCurrentBufferSize);\r
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
150 void 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
177         if (++iCurrentBuffer == KSoundBuffers)\r
178                 iCurrentBuffer = 0;\r
179         iSoundBuffers[iCurrentBuffer]->Des().SetMax();\r
180         iCurrentPosition = (TInt16*) iSoundBuffers[iCurrentBuffer]->Ptr();\r
181         iCurrentBufferSize = 0;\r
182 }\r
183 \r
184 void 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
195 TInt16 *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
204         iCurrentBufferSize = 0;\r
205         iCurrentPosition = (TInt16*) iSoundBuffers[iCurrentBuffer]->Ptr();\r
206         return iCurrentPosition;\r
207 }\r
208 \r
209 // handles underflow condition\r
210 void CGameAudioMS::UnderflowedL()\r
211 {\r
212 #ifdef DEBUG_UNDERFLOWS\r
213         DEBUGPRINT(_L("UnderflowedL()"));\r
214 #endif\r
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
239 void 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
259 TInt CGameAudioMS::ChangeVolume(TInt aUp)\r
260 {\r
261         //DEBUGPRINT(_L("CGameAudioMS::ChangeVolume(%i)"), aUp);\r
262 \r
263         if (iMdaAudioOutputStream) {\r
264                 if (aUp) {\r
265                         iVolume += 5;\r
266                         if (iVolume > iMdaAudioOutputStream->MaxVolume())\r
267                                 iVolume = iMdaAudioOutputStream->MaxVolume();\r
268                 } else {\r
269                         iVolume -= 5;\r
270                         if (iVolume < 0) iVolume = 0;\r
271                 }\r
272                 iMdaAudioOutputStream->SetVolume(iVolume);\r
273         }\r
274 \r
275         return iVolume;\r
276 }\r
277 \r
278 void TGameAudioEventListener::MaoscOpenComplete(TInt aError)\r
279 {\r
280 #ifdef DEBUG_UNDERFLOWS\r
281         DEBUGPRINT(_L("CGameAudioMS::MaoscOpenComplete, error=%d"), aError);\r
282 #endif\r
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
292 void 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
305 void TGameAudioEventListener::MaoscPlayComplete(TInt aError)\r
306 {\r
307 #ifdef DEBUG_UNDERFLOWS\r
308         DEBUGPRINT(_L("CGameAudioMS::MaoscPlayComplete: %i"), aError);\r
309 #endif\r
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