1 /*
2 SDL - Simple DirectMedia Layer
3 Copyright (C) 1997-2012 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
NONSHARABLE_CLASS(TDump)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
615