initial import
[picodrive.git] / platform / uiq2 / audio / mediaserver / 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 \r
17 //#define __DEBUG_PRINT_SND\r
18 \r
19 #ifdef __DEBUG_PRINT_SND\r
20         #include <e32svr.h> // RDebug\r
21         #define DEBUGPRINT(x...) RDebug::Print(x)\r
22 #else\r
23         #define DEBUGPRINT(x...)\r
24 #endif\r
25 \r
26 \r
27 GLDEF_C TInt E32Dll(TDllReason)\r
28 {\r
29         return KErrNone;\r
30 }\r
31 \r
32 \r
33 /*******************************************\r
34  *\r
35  * CGameAudioMS\r
36  *\r
37  *******************************************/\r
38 \r
39 CGameAudioMS::CGameAudioMS(TInt aRate, TBool aStereo, TInt aPcmFrames,  TInt aBufferedFrames)\r
40 : iRate(aRate), iStereo(aStereo), iBufferedFrames(aBufferedFrames), iPcmFrames(aPcmFrames)\r
41 {\r
42 }\r
43 \r
44 \r
45 CGameAudioMS* CGameAudioMS::NewL(TInt aRate, TBool aStereo, TInt aPcmFrames, TInt aBufferedFrames)\r
46 {\r
47         DEBUGPRINT(_L("CGameAudioMS::NewL(%i, %i, %i, %i)"),aRate, aStereo, aPcmFrames, aBufferedFrames);\r
48         CGameAudioMS*           self = new(ELeave) CGameAudioMS(aRate, aStereo, aPcmFrames, aBufferedFrames);\r
49         CleanupStack::PushL(self);\r
50         self->ConstructL();\r
51         CleanupStack::Pop();            // self\r
52         return self;\r
53 }\r
54 \r
55 \r
56 CGameAudioMS::~CGameAudioMS()\r
57 {\r
58         DEBUGPRINT(_L("CGameAudioMS::~CGameAudioMS()"));\r
59         if(iMdaAudioOutputStream) {\r
60                 iScheduler->Schedule(); // let it finish it's stuff\r
61                 iMdaAudioOutputStream->Stop();\r
62                 delete iMdaAudioOutputStream;\r
63         }\r
64         if(iServer) delete iServer;\r
65 \r
66         for (TInt i=0 ; i<KSoundBuffers+1 ; i++)\r
67                 delete iSoundBuffers[i];\r
68 \r
69         // Polled AS\r
70         if(iScheduler) delete iScheduler;\r
71 }\r
72 \r
73 \r
74 void CGameAudioMS::ConstructL()\r
75 {\r
76         iServer = CMdaServer::NewL();\r
77 \r
78         iScheduler = CPolledActiveScheduler::NewL();\r
79 \r
80         switch(iRate) {\r
81                 case 11025: iMdaAudioDataSettings.iSampleRate = TMdaAudioDataSettings::ESampleRate11025Hz; break;\r
82                 case 16000: iMdaAudioDataSettings.iSampleRate = TMdaAudioDataSettings::ESampleRate16000Hz; break;\r
83                 case 22050: iMdaAudioDataSettings.iSampleRate = TMdaAudioDataSettings::ESampleRate22050Hz; break;\r
84                 default:    iMdaAudioDataSettings.iSampleRate = TMdaAudioDataSettings::ESampleRate8000Hz;  break;\r
85         }\r
86 \r
87         iMdaAudioDataSettings.iChannels   = (iStereo) ? TMdaAudioDataSettings::EChannelsStereo : TMdaAudioDataSettings::EChannelsMono;\r
88         iMdaAudioDataSettings.iCaps       = TMdaAudioDataSettings::ESampleRateFixed | iMdaAudioDataSettings.iSampleRate;\r
89         iMdaAudioDataSettings.iFlags      = TMdaAudioDataSettings::ENoNetworkRouting;\r
90 \r
91         TInt    bytesPerFrame = iStereo ? iPcmFrames << 2 : iPcmFrames << 1;\r
92         for (TInt i=0 ; i<KSoundBuffers ; i++)\r
93         {\r
94                 iSoundBuffers[i] = HBufC8::NewL(bytesPerFrame * iBufferedFrames);\r
95                 iSoundBuffers[i]->Des().FillZ  (bytesPerFrame * iBufferedFrames);\r
96         }\r
97         // because feeding 2 buffers after an underflow is a little too much, but feeding 1 may be not enough,\r
98         // prepare this ~50ms empty buffer to additionaly feed after every underflow.\r
99         // Another strange thing here: if we try to make and odd-length sound buffer here,\r
100         // system then outputs horrible noise! (this happened on 22050 mono and when there\r
101         // were no parenthesis around iBufferedFrames / 4.\r
102         iSoundBuffers[KSoundBuffers] = HBufC8::NewL(bytesPerFrame * (iBufferedFrames / 4));\r
103         iSoundBuffers[KSoundBuffers]->Des().FillZ  (bytesPerFrame * (iBufferedFrames / 4));\r
104 \r
105         iCurrentBuffer = 0;\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                 delete iMdaAudioOutputStream;\r
111                 iMdaAudioOutputStream = 0;\r
112         }\r
113 }\r
114 \r
115 /* currently unused\r
116 TInt CGameAudioMS::Write(TInt16* aBuffer, TInt aSize)\r
117 {\r
118         TInt    byteSize = iStereo ? aSize << 2 : aSize << 1;\r
119         Mem::Copy(iCurrentPosition, aBuffer, byteSize);\r
120         iCurrentPosition += aSize;\r
121 \r
122         if (++iFrameCount == iBufferedFrames)\r
123         {\r
124                 WriteBlock();\r
125         }\r
126 \r
127         CPolledActiveScheduler::Instance()->Schedule();\r
128         if(iListener.iUnderflowed) Underflowed(); // oh no, CMdaAudioOutputStream underflowed!\r
129 \r
130         return aSize;\r
131 }\r
132 */\r
133 \r
134 // returns a pointer to buffer for next frame,\r
135 // to be used when iSoundBuffers are used directly\r
136 TInt16 *CGameAudioMS::NextFrameL()\r
137 {\r
138         iCurrentPosition += iPcmFrames << (iStereo?1:0);\r
139 \r
140         if (++iFrameCount == iBufferedFrames)\r
141         {\r
142                 WriteBlockL();\r
143         }\r
144 \r
145         iScheduler->Schedule();\r
146 \r
147         if(iListener.iUnderflowed) {\r
148                 if(iListener.iUnderflowed > KMaxUnderflows) {\r
149                         delete iMdaAudioOutputStream;\r
150                         iMdaAudioOutputStream = 0;\r
151                         return 0;\r
152                 }\r
153                 UnderflowedL(); // not again!\r
154         }\r
155 \r
156         return iCurrentPosition;\r
157 }\r
158 \r
159 TInt16 *CGameAudioMS::DupeFrameL(TInt &aUnderflowed)\r
160 {\r
161         TInt shorts = iStereo ? (iPcmFrames << 1) : iPcmFrames;\r
162         if(iFrameCount)\r
163                 Mem::Copy(iCurrentPosition, iCurrentPosition-shorts, shorts<<1);\r
164         else {\r
165                 TInt lastBuffer = iCurrentBuffer;\r
166                 if(--lastBuffer < 0) lastBuffer = KSoundBuffers - 1;\r
167                 Mem::Copy(iCurrentPosition, ((TInt16*) (iSoundBuffers[lastBuffer]->Ptr()))+shorts*(iBufferedFrames-1), shorts<<1);\r
168         }                               \r
169         iCurrentPosition += shorts;\r
170 \r
171         if (++iFrameCount == iBufferedFrames)\r
172         {\r
173                 WriteBlockL();\r
174         }\r
175 \r
176         iScheduler->Schedule();\r
177 \r
178         if((aUnderflowed = iListener.iUnderflowed)) { // not again!\r
179                 if(iListener.iUnderflowed > KMaxUnderflows) {\r
180                         delete iMdaAudioOutputStream;\r
181                         iMdaAudioOutputStream = 0;\r
182                         return 0;\r
183                 }\r
184                 UnderflowedL(); // not again!\r
185         }\r
186 \r
187         return iCurrentPosition;\r
188 }\r
189 \r
190 void CGameAudioMS::WriteBlockL()\r
191 {\r
192         iScheduler->Schedule();\r
193         // do not write until stream is open\r
194         if(!iListener.iIsOpen) WaitForOpenToCompleteL();\r
195         //if(!iListener.iHasCopied) WaitForCopyToCompleteL(); // almost never happens anyway and sometimes even deadlocks?\r
196         //iListener.iHasCopied = EFalse;\r
197         \r
198 \r
199         if(!iListener.iUnderflowed) {\r
200                 // don't write if sound is lagging too much\r
201                 if(iTime - iMdaAudioOutputStream->Position().Int64() <= TInt64(0, KMaxLag)) {\r
202                         //RDebug::Print(_L("delta: %i"), iTime.Low() - iMdaAudioOutputStream->Position().Int64().Low());\r
203                         iMdaAudioOutputStream->WriteL(*iSoundBuffers[iCurrentBuffer]);\r
204                         iTime += KBlockTime;\r
205                 }\r
206         }\r
207 \r
208         iFrameCount = 0;\r
209         if (++iCurrentBuffer == KSoundBuffers)\r
210                 iCurrentBuffer = 0;\r
211         iCurrentPosition = (TInt16*) iSoundBuffers[iCurrentBuffer]->Ptr();\r
212 }\r
213 \r
214 void CGameAudioMS::Pause()\r
215 {\r
216         if(!iMdaAudioOutputStream) return;\r
217 \r
218         iScheduler->Schedule(); // let it finish it's stuff\r
219         iMdaAudioOutputStream->Stop();\r
220         delete iMdaAudioOutputStream;\r
221         iMdaAudioOutputStream = 0;\r
222 }\r
223 \r
224 // call this before doing any playback!\r
225 TInt16 *CGameAudioMS::ResumeL()\r
226 {\r
227         DEBUGPRINT(_L("CGameAudioMS::Resume()"));\r
228         iScheduler->Schedule();\r
229 \r
230         // we act a bit strange here: simulate buffer underflow, which actually starts audio\r
231         iListener.iIsOpen = ETrue;\r
232         iListener.iUnderflowed = 1;\r
233         iFrameCount = 0;\r
234         iCurrentPosition = (TInt16*) iSoundBuffers[iCurrentBuffer]->Ptr();\r
235         return iCurrentPosition;\r
236 }\r
237 \r
238 // handles underflow condition\r
239 void CGameAudioMS::UnderflowedL()\r
240 {\r
241         // recreate the stream\r
242         //iMdaAudioOutputStream->Stop();\r
243         if(iMdaAudioOutputStream) delete iMdaAudioOutputStream;\r
244         iMdaAudioOutputStream = CMdaAudioOutputStream::NewL(iListener, iServer);\r
245         iMdaAudioOutputStream->Open(&iMdaAudioDataSettings);\r
246         iListener.iIsOpen = EFalse;   // wait for it to open\r
247         //iListener.iHasCopied = ETrue; // but don't wait for last copy to complete\r
248         // let it open and feed some stuff to make it happy\r
249         User::After(0);\r
250         TInt lastBuffer = iCurrentBuffer;\r
251         if(--lastBuffer < 0) lastBuffer = KSoundBuffers - 1;\r
252         iScheduler->Schedule();\r
253         if(!iListener.iIsOpen) WaitForOpenToCompleteL();\r
254         iMdaAudioOutputStream->WriteL(*iSoundBuffers[KSoundBuffers]); // special empty fill-up\r
255         iMdaAudioOutputStream->WriteL(*iSoundBuffers[lastBuffer]);\r
256         iTime = TInt64(0, KBlockTime/4 + KBlockTime);\r
257 }\r
258 \r
259 /*\r
260 void CGameAudioMS::WaitForCopyToCompleteL()\r
261 {\r
262         DEBUGPRINT(_L("CGameAudioMS::WaitForCopyToCompleteL"));\r
263         while (!iListener.iHasCopied) {\r
264                 //User::After(0);\r
265                 iScheduler->Schedule();\r
266         }\r
267 }\r
268 */\r
269 \r
270 void CGameAudioMS::WaitForOpenToCompleteL()\r
271 {\r
272         DEBUGPRINT(_L("CGameAudioMS::WaitForOpenToCompleteL"));\r
273         TInt    count = 20;             // 2 seconds\r
274         TInt    waitPeriod = 100 * 1000;\r
275 \r
276         if(!iListener.iIsOpen) {\r
277                 // it is often enough to do this\r
278                 User::After(0);\r
279                 iScheduler->Schedule();\r
280         }\r
281         while (!iListener.iIsOpen && --count)\r
282         {\r
283                 User::After(waitPeriod);\r
284                 iScheduler->Schedule();\r
285         }\r
286         if (!iListener.iIsOpen)\r
287                 User::LeaveIfError(KErrNotSupported);\r
288 }\r
289 \r
290 void CGameAudioMS::ChangeVolume(TInt aUp)\r
291 {\r
292         // do nothing\r
293         DEBUGPRINT(_L("CGameAudioMS::ChangeVolume(%i)"), aUp);\r
294 }\r
295 \r
296 void TGameAudioEventListener::MaoscOpenComplete(TInt aError)\r
297 {\r
298         DEBUGPRINT(_L("CGameAudioMS::MaoscOpenComplete, error=%d"), aError);\r
299 \r
300         iIsOpen = ETrue;\r
301         if(aError) iUnderflowed++;\r
302         else       iUnderflowed = 0;\r
303 }\r
304 \r
305 void TGameAudioEventListener::MaoscBufferCopied(TInt aError, const TDesC8& aBuffer)\r
306 {\r
307         DEBUGPRINT(_L("CGameAudioMS::MaoscBufferCopied, error=%d"), aError);\r
308 \r
309 //      iHasCopied = ETrue;\r
310 \r
311         if(aError) // shit!\r
312                  iUnderflowed++;\r
313 }\r
314 \r
315 void TGameAudioEventListener::MaoscPlayComplete(TInt aError)\r
316 {\r
317         DEBUGPRINT(_L("CGameAudioMS::MaoscPlayComplete: %i"), aError);\r
318         if(aError)\r
319                 iUnderflowed++; // never happened to me while testing, but just in case\r
320 }\r
321 \r