switch Cyclone to submodule on it's own git repo
[picodrive.git] / platform / uiq2 / audio / mediaserver / 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\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
27GLDEF_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
39CGameAudioMS::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
45CGameAudioMS* 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
56CGameAudioMS::~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
74void 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
116TInt 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
136TInt16 *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
159TInt16 *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
190void 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
214void 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
225TInt16 *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
239void 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
260void 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
270void 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
290void CGameAudioMS::ChangeVolume(TInt aUp)\r
291{\r
292 // do nothing\r
293 DEBUGPRINT(_L("CGameAudioMS::ChangeVolume(%i)"), aUp);\r
294}\r
295\r
296void 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
305void 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
315void 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