e14743d1 |
1 | /* |
2 | SDL - Simple DirectMedia Layer |
3 | Copyright (C) 1997-2009 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 | |