Mercurial > sdl-ios-xcode
comparison src/audio/symbian/SDL_epocaudio.cpp @ 3975:e85e65aec22f SDL-1.2
Added S60 port.
author | Ryan C. Gordon <icculus@icculus.org> |
---|---|
date | Sun, 24 Jun 2007 18:26:35 +0000 |
parents | |
children | a1b03ba2fcd0 |
comparison
equal
deleted
inserted
replaced
3974:42578e98a295 | 3975:e85e65aec22f |
---|---|
1 /* | |
2 SDL - Simple DirectMedia Layer | |
3 Copyright (C) 1997, 1998, 1999, 2000, 2001 Sam Lantinga | |
4 | |
5 This library is free software; you can redistribute it and/or | |
6 modify it under the terms of the GNU Library General Public | |
7 License as published by the Free Software Foundation; either | |
8 version 2 of the License, or (at your option) any later version. | |
9 | |
10 This library is distributed in the hope that it will be useful, | |
11 but WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
13 Library General Public License for more details. | |
14 | |
15 You should have received a copy of the GNU Library General Public | |
16 License along with this library; if not, write to the Free | |
17 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
18 | |
19 Sam Lantinga | |
20 slouken@devolution.com | |
21 */ | |
22 | |
23 /* | |
24 SDL_epocaudio.cpp | |
25 Epoc based SDL audio driver implementation | |
26 | |
27 Markus Mertama | |
28 */ | |
29 | |
30 #ifdef SAVE_RCSID | |
31 static char rcsid = | |
32 "@(#) $Id: SDL_epocaudio.c,v 0.0.0.0 2001/06/19 17:19:56 hercules Exp $"; | |
33 #endif | |
34 | |
35 | |
36 #include <stdlib.h> | |
37 #include <stdio.h> | |
38 #include <string.h> | |
39 #include <errno.h> | |
40 #include <unistd.h> | |
41 #include <fcntl.h> | |
42 #include <signal.h> | |
43 #include <sys/time.h> | |
44 #include <sys/ioctl.h> | |
45 #include <sys/stat.h> | |
46 | |
47 #include "epoc_sdl.h" | |
48 | |
49 #include <e32hal.h> | |
50 | |
51 | |
52 extern "C" { | |
53 #include "SDL_audio.h" | |
54 #include "SDL_error.h" | |
55 #include "SDL_audiomem.h" | |
56 #include "SDL_audio_c.h" | |
57 #include "SDL_timer.h" | |
58 #include "SDL_audiodev_c.h" | |
59 } | |
60 | |
61 #include "SDL_epocaudio.h" | |
62 | |
63 #include "streamplayer.h" | |
64 | |
65 | |
66 //#define DEBUG_AUDIO | |
67 | |
68 | |
69 /* Audio driver functions */ | |
70 | |
71 static int EPOC_OpenAudio(SDL_AudioDevice *thisdevice, SDL_AudioSpec *spec); | |
72 static void EPOC_WaitAudio(SDL_AudioDevice *thisdevice); | |
73 static void EPOC_PlayAudio(SDL_AudioDevice *thisdevice); | |
74 static Uint8 *EPOC_GetAudioBuf(SDL_AudioDevice *thisdevice); | |
75 static void EPOC_CloseAudio(SDL_AudioDevice *thisdevice); | |
76 static void EPOC_ThreadInit(SDL_AudioDevice *thisdevice); | |
77 | |
78 static int Audio_Available(void); | |
79 static SDL_AudioDevice *Audio_CreateDevice(int devindex); | |
80 static void Audio_DeleteDevice(SDL_AudioDevice *device); | |
81 | |
82 | |
83 //void sos_adump(SDL_AudioDevice* thisdevice, void* data, int len); | |
84 | |
85 #ifdef __WINS__ | |
86 #define DODUMP | |
87 #endif | |
88 | |
89 #ifdef DODUMP | |
90 NONSHARABLE_CLASS(TDump) | |
91 { | |
92 public: | |
93 TInt Open(); | |
94 void Close(); | |
95 void Dump(const TDesC8& aDes); | |
96 private: | |
97 RFile iFile; | |
98 RFs iFs; | |
99 }; | |
100 | |
101 TInt TDump::Open() | |
102 { | |
103 TInt err = iFs.Connect(); | |
104 if(err == KErrNone) | |
105 { | |
106 #ifdef __WINS__ | |
107 _LIT(target, "C:\\sdlau.raw"); | |
108 #else | |
109 _LIT(target, "E:\\sdlau.raw"); | |
110 #endif | |
111 err = iFile.Replace(iFs, target, EFileWrite); | |
112 } | |
113 return err; | |
114 } | |
115 void TDump::Close() | |
116 { | |
117 iFile.Close(); | |
118 iFs.Close(); | |
119 } | |
120 void TDump::Dump(const TDesC8& aDes) | |
121 { | |
122 iFile.Write(aDes); | |
123 } | |
124 #endif | |
125 | |
126 | |
127 NONSHARABLE_CLASS(CSimpleWait) : public CTimer | |
128 { | |
129 public: | |
130 void Wait(TTimeIntervalMicroSeconds32 aWait); | |
131 static CSimpleWait* NewL(); | |
132 private: | |
133 CSimpleWait(); | |
134 void RunL(); | |
135 }; | |
136 | |
137 | |
138 CSimpleWait* CSimpleWait::NewL() | |
139 { | |
140 CSimpleWait* wait = new (ELeave) CSimpleWait(); | |
141 CleanupStack::PushL(wait); | |
142 wait->ConstructL(); | |
143 CleanupStack::Pop(); | |
144 return wait; | |
145 } | |
146 | |
147 void CSimpleWait::Wait(TTimeIntervalMicroSeconds32 aWait) | |
148 { | |
149 After(aWait); | |
150 CActiveScheduler::Start(); | |
151 } | |
152 | |
153 CSimpleWait::CSimpleWait() : CTimer(CActive::EPriorityStandard) | |
154 { | |
155 CActiveScheduler::Add(this); | |
156 } | |
157 | |
158 void CSimpleWait::RunL() | |
159 { | |
160 CActiveScheduler::Stop(); | |
161 } | |
162 | |
163 const TInt KAudioBuffers(2); | |
164 | |
165 | |
166 NONSHARABLE_CLASS(CEpocAudio) : public CBase, public MStreamObs, public MStreamProvider | |
167 { | |
168 public: | |
169 static void* NewL(TInt BufferSize, TInt aFill); | |
170 inline static CEpocAudio& Current(SDL_AudioDevice* thisdevice); | |
171 | |
172 static void Free(SDL_AudioDevice* thisdevice); | |
173 | |
174 void Wait(); | |
175 void Play(); | |
176 // void SetBuffer(const TDesC8& aBuffer); | |
177 void ThreadInitL(TAny* aDevice); | |
178 void Open(TInt iRate, TInt iChannels, TUint32 aType, TInt aBytes); | |
179 ~CEpocAudio(); | |
180 TUint8* Buffer(); | |
181 TBool SetPause(TBool aPause); | |
182 #ifdef DODUMP | |
183 void Dump(const TDesC8& aBuf) {iDump.Dump(aBuf);} | |
184 #endif | |
185 private: | |
186 CEpocAudio(TInt aBufferSize); | |
187 void Complete(TInt aState, TInt aError); | |
188 TPtrC8 Data(); | |
189 void ConstructL(TInt aFill); | |
190 private: | |
191 TInt iBufferSize; | |
192 CStreamPlayer* iPlayer; | |
193 TInt iBufferRate; | |
194 TInt iRate; | |
195 TInt iChannels; | |
196 TUint32 iType; | |
197 TInt iPosition; | |
198 TThreadId iTid; | |
199 TUint8* iAudioPtr; | |
200 TUint8* iBuffer; | |
201 // TTimeIntervalMicroSeconds iStart; | |
202 TTime iStart; | |
203 TInt iTune; | |
204 CSimpleWait* iWait; | |
205 #ifdef DODUMP | |
206 TDump iDump; | |
207 #endif | |
208 }; | |
209 | |
210 inline CEpocAudio& CEpocAudio::Current(SDL_AudioDevice* thisdevice) | |
211 { | |
212 return *static_cast<CEpocAudio*>((void*)thisdevice->hidden); | |
213 } | |
214 | |
215 /* | |
216 | |
217 TBool EndSc(TAny*) | |
218 { | |
219 CActiveScheduler::Stop(); | |
220 } | |
221 | |
222 LOCAL_C void CleanScL() | |
223 { | |
224 CIdle* d = CIdle::NewLC(CActive:::EPriorityIdle); | |
225 d->Start(TCallBack(EndSc)); | |
226 CActiveScheduler::Start(); | |
227 | |
228 } | |
229 */ | |
230 | |
231 void CEpocAudio::Free(SDL_AudioDevice* thisdevice) | |
232 { | |
233 CEpocAudio* ea = static_cast<CEpocAudio*>((void*)thisdevice->hidden); | |
234 if(ea) | |
235 { | |
236 ASSERT(ea->iTid == RThread().Id()); | |
237 delete ea; | |
238 thisdevice->hidden = NULL; | |
239 | |
240 CActiveScheduler* as = CActiveScheduler::Current(); | |
241 ASSERT(as->StackDepth() == 0); | |
242 delete as; | |
243 CActiveScheduler::Install(NULL); | |
244 } | |
245 ASSERT(thisdevice->hidden == NULL); | |
246 } | |
247 | |
248 CEpocAudio::CEpocAudio(TInt aBufferSize) : iBufferSize(aBufferSize), iPosition(-1) | |
249 { | |
250 } | |
251 | |
252 void* CEpocAudio::NewL(TInt aBufferSize, TInt aFill) | |
253 { | |
254 CEpocAudio* eAudioLib = new (ELeave) CEpocAudio(aBufferSize); | |
255 CleanupStack::PushL(eAudioLib); | |
256 eAudioLib->ConstructL(aFill); | |
257 CleanupStack::Pop(); | |
258 return eAudioLib; | |
259 } | |
260 | |
261 void CEpocAudio::ConstructL(TInt aFill) | |
262 { | |
263 iBuffer = (TUint8*) User::AllocL(KAudioBuffers * iBufferSize); | |
264 memset(iBuffer, aFill, KAudioBuffers * iBufferSize); | |
265 iAudioPtr = iBuffer; | |
266 } | |
267 | |
268 | |
269 TBool CEpocAudio::SetPause(TBool aPause) | |
270 { | |
271 if(aPause && iPosition >= 0) | |
272 { | |
273 iPosition = -1; | |
274 if(iPlayer != NULL) | |
275 iPlayer->Stop(); | |
276 } | |
277 if(!aPause && iPosition < 0) | |
278 { | |
279 iPosition = 0; | |
280 if(iPlayer != NULL) | |
281 iPlayer->Start(); | |
282 } | |
283 return iPosition < 0; | |
284 } | |
285 | |
286 void CEpocAudio::ThreadInitL(TAny* aDevice) | |
287 { | |
288 iTid = RThread().Id(); | |
289 CActiveScheduler* as = new (ELeave) CActiveScheduler(); | |
290 CActiveScheduler::Install(as); | |
291 | |
292 EpocSdlEnv::AppendCleanupItem(TSdlCleanupItem((TSdlCleanupOperation)EPOC_CloseAudio, aDevice)); | |
293 | |
294 iWait = CSimpleWait::NewL(); | |
295 | |
296 iPlayer = new (ELeave) CStreamPlayer(*this, *this); | |
297 iPlayer->ConstructL(); | |
298 iPlayer->OpenStream(iRate, iChannels, iType); | |
299 | |
300 #ifdef DODUMP | |
301 User::LeaveIfError(iDump.Open()); | |
302 #endif | |
303 } | |
304 | |
305 | |
306 | |
307 TUint8* CEpocAudio::Buffer() | |
308 { | |
309 iStart.UniversalTime(); | |
310 // iStart = iPlayer->Position(); | |
311 return iAudioPtr; | |
312 | |
313 } | |
314 | |
315 CEpocAudio::~CEpocAudio() | |
316 { | |
317 if(iWait != NULL) | |
318 iWait->Cancel(); | |
319 delete iWait; | |
320 if(iPlayer != NULL) | |
321 iPlayer->Close(); | |
322 delete iPlayer; | |
323 delete iBuffer; | |
324 } | |
325 | |
326 void CEpocAudio::Complete(TInt aState, TInt aError) | |
327 { | |
328 if(aState == MStreamObs::EClose) | |
329 { | |
330 } | |
331 if(iPlayer->Closed()) | |
332 return; | |
333 switch(aError) | |
334 { | |
335 case KErrUnderflow: | |
336 case KErrInUse: | |
337 iPlayer->Start(); | |
338 break; | |
339 case KErrAbort: | |
340 iPlayer->Open(); | |
341 } | |
342 } | |
343 | |
344 | |
345 void sos_adump(SDL_AudioDevice* thisdevice, void* data, int len) | |
346 { | |
347 #ifdef DODUMP | |
348 const TPtrC8 buf((TUint8*)data, len); | |
349 CEpocAudio::Current(thisdevice).Dump(buf); | |
350 #endif | |
351 } | |
352 | |
353 const TInt KClip(256); | |
354 | |
355 TPtrC8 CEpocAudio::Data() | |
356 { | |
357 if(iPosition < 0) | |
358 return KNullDesC8(); | |
359 | |
360 TPtrC8 data(iAudioPtr + iPosition, KClip); | |
361 | |
362 #ifdef DODUMP | |
363 iDump.Dump(data); | |
364 #endif | |
365 | |
366 iPosition += KClip; | |
367 if(iPosition >= iBufferSize) | |
368 { | |
369 | |
370 /* if(iAudioPtr == iBuffer) | |
371 iAudioPtr = iBuffer + iBufferSize; | |
372 else | |
373 iAudioPtr = iBuffer; | |
374 */ | |
375 iAudioPtr += iBufferSize; | |
376 | |
377 if((iAudioPtr - iBuffer) >= KAudioBuffers * iBufferSize) | |
378 iAudioPtr = iBuffer; | |
379 | |
380 iPosition = -1; | |
381 if(iWait->IsActive()) | |
382 { | |
383 iWait->Cancel(); | |
384 CActiveScheduler::Stop(); | |
385 } | |
386 } | |
387 return data; | |
388 } | |
389 | |
390 | |
391 | |
392 | |
393 void CEpocAudio::Play() | |
394 { | |
395 iPosition = 0; | |
396 } | |
397 | |
398 void CEpocAudio::Wait() | |
399 { | |
400 if(iPosition >= 0 /*&& iPlayer->Playing()*/) | |
401 { | |
402 const TInt64 bufMs = TInt64(iBufferSize - KClip) * TInt64(1000000); | |
403 const TInt64 specTime = bufMs / TInt64(iRate * iChannels * 2); | |
404 iWait->After(specTime); | |
405 | |
406 CActiveScheduler::Start(); | |
407 TTime end; | |
408 end.UniversalTime(); | |
409 const TTimeIntervalMicroSeconds delta = end.MicroSecondsFrom(iStart); | |
410 | |
411 | |
412 // const TTimeIntervalMicroSeconds end = iPlayer->Position(); | |
413 | |
414 | |
415 | |
416 | |
417 const TInt diff = specTime - delta.Int64(); | |
418 | |
419 if(diff > 0 && diff < 200000) | |
420 { | |
421 User::After(diff); | |
422 } | |
423 | |
424 } | |
425 else | |
426 { | |
427 User::After(10000); | |
428 // iWait->Wait(10000); //just give some time... | |
429 } | |
430 } | |
431 | |
432 void CEpocAudio::Open(TInt aRate, TInt aChannels, TUint32 aType, TInt aBytes) | |
433 { | |
434 iRate = aRate; | |
435 iChannels = aChannels; | |
436 iType = aType; | |
437 iBufferRate = iRate * iChannels * aBytes; //1/x | |
438 } | |
439 | |
440 | |
441 /* Audio driver bootstrap functions */ | |
442 | |
443 AudioBootStrap EPOCAudio_bootstrap = { | |
444 "epoc\0\0\0", | |
445 "EPOC streaming audio\0\0\0", | |
446 Audio_Available, | |
447 Audio_CreateDevice | |
448 }; | |
449 | |
450 | |
451 static SDL_AudioDevice *Audio_CreateDevice(int /*devindex*/) | |
452 { | |
453 SDL_AudioDevice *thisdevice; | |
454 | |
455 /* Initialize all variables that we clean on shutdown */ | |
456 thisdevice = (SDL_AudioDevice *)malloc(sizeof(SDL_AudioDevice)); | |
457 if ( thisdevice ) { | |
458 memset(thisdevice, 0, (sizeof *thisdevice)); | |
459 thisdevice->hidden = NULL; /*(struct SDL_PrivateAudioData *) | |
460 malloc((sizeof thisdevice->hidden)); */ | |
461 } | |
462 if ( (thisdevice == NULL) /*|| (thisdevice->hidden == NULL) */) { | |
463 SDL_OutOfMemory(); | |
464 if ( thisdevice ) { | |
465 free(thisdevice); | |
466 } | |
467 return(0); | |
468 } | |
469 // memset(thisdevice->hidden, 0, (sizeof *thisdevice->hidden)); | |
470 | |
471 /* Set the function pointers */ | |
472 thisdevice->OpenAudio = EPOC_OpenAudio; | |
473 thisdevice->WaitAudio = EPOC_WaitAudio; | |
474 thisdevice->PlayAudio = EPOC_PlayAudio; | |
475 thisdevice->GetAudioBuf = EPOC_GetAudioBuf; | |
476 thisdevice->CloseAudio = EPOC_CloseAudio; | |
477 thisdevice->ThreadInit = EPOC_ThreadInit; | |
478 thisdevice->free = Audio_DeleteDevice; | |
479 | |
480 return thisdevice; | |
481 } | |
482 | |
483 | |
484 static void Audio_DeleteDevice(SDL_AudioDevice *device) | |
485 { | |
486 //free(device->hidden); | |
487 free(device); | |
488 } | |
489 | |
490 static int Audio_Available(void) | |
491 { | |
492 return(1); // Audio stream modules should be always there! | |
493 } | |
494 | |
495 | |
496 static int EPOC_OpenAudio(SDL_AudioDevice *thisdevice, SDL_AudioSpec *spec) | |
497 { | |
498 SDL_TRACE("SDL:EPOC_OpenAudio"); | |
499 | |
500 | |
501 TUint32 type = KMMFFourCCCodePCM16; | |
502 TInt bytes = 2; | |
503 | |
504 switch(spec->format) | |
505 { | |
506 case AUDIO_U16LSB: | |
507 type = KMMFFourCCCodePCMU16; | |
508 break; | |
509 case AUDIO_S16LSB: | |
510 type = KMMFFourCCCodePCM16; | |
511 break; | |
512 case AUDIO_U16MSB: | |
513 type = KMMFFourCCCodePCMU16B; | |
514 break; | |
515 case AUDIO_S16MSB: | |
516 type = KMMFFourCCCodePCM16B; | |
517 break; | |
518 //8 bit not supported! | |
519 case AUDIO_U8: | |
520 case AUDIO_S8: | |
521 default: | |
522 spec->format = AUDIO_S16LSB; | |
523 }; | |
524 | |
525 | |
526 | |
527 if(spec->channels > 2) | |
528 spec->channels = 2; | |
529 | |
530 spec->freq = CStreamPlayer::ClosestSupportedRate(spec->freq); | |
531 | |
532 | |
533 /* Allocate mixing buffer */ | |
534 const TInt buflen = spec->size;// * bytes * spec->channels; | |
535 // audiobuf = NULL; | |
536 | |
537 TRAPD(err, thisdevice->hidden = static_cast<SDL_PrivateAudioData*>(CEpocAudio::NewL(buflen, spec->silence))); | |
538 if(err != KErrNone) | |
539 return -1; | |
540 | |
541 CEpocAudio::Current(thisdevice).Open(spec->freq, spec->channels, type, bytes); | |
542 | |
543 CEpocAudio::Current(thisdevice).SetPause(ETrue); | |
544 | |
545 // isSDLAudioPaused = 1; | |
546 | |
547 thisdevice->enabled = 0; /* enable only after audio engine has been initialized!*/ | |
548 | |
549 /* We're ready to rock and roll. :-) */ | |
550 return(0); | |
551 } | |
552 | |
553 | |
554 static void EPOC_CloseAudio(SDL_AudioDevice* thisdevice) | |
555 { | |
556 #ifdef DEBUG_AUDIO | |
557 SDL_TRACE("Close audio\n"); | |
558 #endif | |
559 | |
560 CEpocAudio::Free(thisdevice); | |
561 } | |
562 | |
563 | |
564 static void EPOC_ThreadInit(SDL_AudioDevice *thisdevice) | |
565 { | |
566 SDL_TRACE("SDL:EPOC_ThreadInit"); | |
567 CEpocAudio::Current(thisdevice).ThreadInitL(thisdevice); | |
568 RThread().SetPriority(EPriorityMore); | |
569 thisdevice->enabled = 1; | |
570 } | |
571 | |
572 /* This function waits until it is possible to write a full sound buffer */ | |
573 static void EPOC_WaitAudio(SDL_AudioDevice* thisdevice) | |
574 { | |
575 #ifdef DEBUG_AUDIO | |
576 SDL_TRACE1("wait %d audio\n", CEpocAudio::AudioLib().StreamPlayer(KSfxChannel).SyncTime()); | |
577 TInt tics = User::TickCount(); | |
578 #endif | |
579 | |
580 CEpocAudio::Current(thisdevice).Wait(); | |
581 | |
582 #ifdef DEBUG_AUDIO | |
583 TInt ntics = User::TickCount() - tics; | |
584 SDL_TRACE1("audio waited %d\n", ntics); | |
585 SDL_TRACE1("audio at %d\n", tics); | |
586 #endif | |
587 } | |
588 | |
589 | |
590 | |
591 static void EPOC_PlayAudio(SDL_AudioDevice* thisdevice) | |
592 { | |
593 if(CEpocAudio::Current(thisdevice).SetPause(SDL_GetAudioStatus() == SDL_AUDIO_PAUSED)) | |
594 SDL_Delay(500); //hold on the busy loop | |
595 else | |
596 CEpocAudio::Current(thisdevice).Play(); | |
597 | |
598 #ifdef DEBUG_AUDIO | |
599 SDL_TRACE("buffer has audio data\n"); | |
600 #endif | |
601 | |
602 | |
603 #ifdef DEBUG_AUDIO | |
604 SDL_TRACE1("Wrote %d bytes of audio data\n", buflen); | |
605 #endif | |
606 } | |
607 | |
608 static Uint8 *EPOC_GetAudioBuf(SDL_AudioDevice* thisdevice) | |
609 { | |
610 return CEpocAudio::Current(thisdevice).Buffer(); | |
611 } | |
612 | |
613 | |
614 |