SDL-1.2.14
[sdl_omap.git] / src / audio / symbian / SDL_epocaudio.cpp
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