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