2 SDL - Simple DirectMedia Layer
3 Copyright (C) 1997, 1998, 1999, 2000, 2001 Sam Lantinga
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.
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.
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
20 slouken@devolution.com
25 Epoc based SDL audio driver implementation
38 #include <sys/ioctl.h>
47 #include "SDL_audio.h"
48 #include "SDL_error.h"
49 #include "SDL_audiomem.h"
50 #include "SDL_audio_c.h"
51 #include "SDL_timer.h"
52 #include "SDL_audiodev_c.h"
55 #include "SDL_epocaudio.h"
57 #include "streamplayer.h"
59 /* Audio driver functions */
61 static int EPOC_OpenAudio(SDL_AudioDevice
*thisdevice
, SDL_AudioSpec
*spec
);
62 static void EPOC_WaitAudio(SDL_AudioDevice
*thisdevice
);
63 static void EPOC_PlayAudio(SDL_AudioDevice
*thisdevice
);
64 static Uint8
*EPOC_GetAudioBuf(SDL_AudioDevice
*thisdevice
);
65 static void EPOC_CloseAudio(SDL_AudioDevice
*thisdevice
);
66 static void EPOC_ThreadInit(SDL_AudioDevice
*thisdevice
);
68 static int Audio_Available(void);
69 static SDL_AudioDevice
*Audio_CreateDevice(int devindex
);
70 static void Audio_DeleteDevice(SDL_AudioDevice
*device
);
72 NONSHARABLE_CLASS(CSimpleWait
) : public CTimer
75 void Wait(TTimeIntervalMicroSeconds32 aWait
);
76 static CSimpleWait
* NewL();
82 CSimpleWait
* CSimpleWait::NewL()
84 CSimpleWait
* wait
= new (ELeave
) CSimpleWait();
85 CleanupStack::PushL(wait
);
91 void CSimpleWait::Wait(TTimeIntervalMicroSeconds32 aWait
)
94 CActiveScheduler::Start();
97 CSimpleWait::CSimpleWait() : CTimer(CActive::EPriorityStandard
)
99 CActiveScheduler::Add(this);
102 void CSimpleWait::RunL()
104 CActiveScheduler::Stop();
107 const TInt
KAudioBuffers(2);
110 NONSHARABLE_CLASS(CEpocAudio
) : public CBase
, public MStreamObs
, public MStreamProvider
113 static void* NewL(TInt BufferSize
, TInt aFill
);
114 inline static CEpocAudio
& Current(SDL_AudioDevice
* thisdevice
);
116 static void Free(SDL_AudioDevice
* thisdevice
);
120 void ThreadInitL(TAny
* aDevice
);
121 void Open(TInt iRate
, TInt iChannels
, TUint32 aType
, TInt aBytes
);
124 TBool
SetPause(TBool aPause
);
127 CEpocAudio(TInt aBufferSize
);
128 void Complete(TInt aState
, TInt aError
);
130 void ConstructL(TInt aFill
);
132 CStreamPlayer
* iPlayer
;
146 inline CEpocAudio
& CEpocAudio::Current(SDL_AudioDevice
* thisdevice
)
148 return *static_cast<CEpocAudio
*>((void*)thisdevice
->hidden
);
151 void CEpocAudio::Free(SDL_AudioDevice
* thisdevice
)
153 CEpocAudio
* ea
= static_cast<CEpocAudio
*>((void*)thisdevice
->hidden
);
156 ASSERT(ea
->iTid
== RThread().Id());
158 thisdevice
->hidden
= NULL
;
160 CActiveScheduler
* as
= CActiveScheduler::Current();
161 ASSERT(as
->StackDepth() == 0);
163 CActiveScheduler::Install(NULL
);
165 ASSERT(thisdevice
->hidden
== NULL
);
168 CEpocAudio::CEpocAudio(TInt aBufferSize
) : iBufferSize(aBufferSize
), iPosition(-1)
172 void* CEpocAudio::NewL(TInt aBufferSize
, TInt aFill
)
174 CEpocAudio
* eAudioLib
= new (ELeave
) CEpocAudio(aBufferSize
);
175 CleanupStack::PushL(eAudioLib
);
176 eAudioLib
->ConstructL(aFill
);
181 void CEpocAudio::ConstructL(TInt aFill
)
183 iBuffer
= (TUint8
*) User::AllocL(KAudioBuffers
* iBufferSize
);
184 memset(iBuffer
, aFill
, KAudioBuffers
* iBufferSize
);
189 TBool
CEpocAudio::SetPause(TBool aPause
)
191 if(aPause
&& iPosition
>= 0)
197 if(!aPause
&& iPosition
< 0)
203 return iPosition
< 0;
206 void CEpocAudio::ThreadInitL(TAny
* aDevice
)
208 iTid
= RThread().Id();
209 CActiveScheduler
* as
= new (ELeave
) CActiveScheduler();
210 CActiveScheduler::Install(as
);
212 EpocSdlEnv::AppendCleanupItem(TSdlCleanupItem((TSdlCleanupOperation
)EPOC_CloseAudio
, aDevice
));
214 iWait
= CSimpleWait::NewL();
216 iPlayer
= new (ELeave
) CStreamPlayer(*this, *this);
217 iPlayer
->ConstructL();
218 iPlayer
->OpenStream(iRate
, iChannels
, iType
);
221 TUint8
* CEpocAudio::Buffer()
223 iStart
.UniversalTime();
227 CEpocAudio::~CEpocAudio()
238 void CEpocAudio::Complete(TInt aState
, TInt aError
)
240 if(aState
== MStreamObs::EClose
)
243 if(iPlayer
->Closed())
257 const TInt
KClip(256);
259 TPtrC8
CEpocAudio::Data()
264 TPtrC8
data(iAudioPtr
+ iPosition
, KClip
);
267 if(iPosition
>= iBufferSize
)
269 iAudioPtr
+= iBufferSize
;
271 if((iAudioPtr
- iBuffer
) >= KAudioBuffers
* iBufferSize
)
275 if(iWait
->IsActive())
278 CActiveScheduler::Stop();
284 void CEpocAudio::Play()
289 void CEpocAudio::Wait()
293 const TInt64 bufMs
= TInt64(iBufferSize
- KClip
) * TInt64(1000000);
294 const TInt64 specTime
= bufMs
/ TInt64(iRate
* iChannels
* 2);
295 iWait
->After(specTime
);
297 CActiveScheduler::Start();
300 const TTimeIntervalMicroSeconds delta
= end
.MicroSecondsFrom(iStart
);
302 const TInt diff
= specTime
- delta
.Int64();
304 if(diff
> 0 && diff
< 200000)
316 void CEpocAudio::Open(TInt aRate
, TInt aChannels
, TUint32 aType
, TInt aBytes
)
319 iChannels
= aChannels
;
321 iBufferRate
= iRate
* iChannels
* aBytes
; //1/x
324 /* Audio driver bootstrap functions */
326 AudioBootStrap EPOCAudio_bootstrap
= {
328 "EPOC streaming audio\0\0\0",
334 static SDL_AudioDevice
*Audio_CreateDevice(int /*devindex*/)
336 SDL_AudioDevice
*thisdevice
;
338 /* Initialize all variables that we clean on shutdown */
339 thisdevice
= (SDL_AudioDevice
*)malloc(sizeof(SDL_AudioDevice
));
341 memset(thisdevice
, 0, (sizeof *thisdevice
));
342 thisdevice
->hidden
= NULL
;
344 if ( (thisdevice
== NULL
) ) {
352 /* Set the function pointers */
353 thisdevice
->OpenAudio
= EPOC_OpenAudio
;
354 thisdevice
->WaitAudio
= EPOC_WaitAudio
;
355 thisdevice
->PlayAudio
= EPOC_PlayAudio
;
356 thisdevice
->GetAudioBuf
= EPOC_GetAudioBuf
;
357 thisdevice
->CloseAudio
= EPOC_CloseAudio
;
358 thisdevice
->ThreadInit
= EPOC_ThreadInit
;
359 thisdevice
->free
= Audio_DeleteDevice
;
365 static void Audio_DeleteDevice(SDL_AudioDevice
*device
)
370 static int Audio_Available(void)
372 return(1); // Audio stream modules should be always there!
376 static int EPOC_OpenAudio(SDL_AudioDevice
*thisdevice
, SDL_AudioSpec
*spec
)
378 TUint32 type
= KMMFFourCCCodePCM16
;
384 type
= KMMFFourCCCodePCMU16
;
387 type
= KMMFFourCCCodePCM16
;
390 type
= KMMFFourCCCodePCMU16B
;
393 type
= KMMFFourCCCodePCM16B
;
395 //8 bit not supported!
399 spec
->format
= AUDIO_S16LSB
;
402 if(spec
->channels
> 2)
405 spec
->freq
= CStreamPlayer::ClosestSupportedRate(spec
->freq
);
407 /* Allocate mixing buffer */
408 const TInt buflen
= spec
->size
;// * bytes * spec->channels;
410 TRAPD(err
, thisdevice
->hidden
= static_cast<SDL_PrivateAudioData
*>(CEpocAudio::NewL(buflen
, spec
->silence
)));
414 CEpocAudio::Current(thisdevice
).Open(spec
->freq
, spec
->channels
, type
, bytes
);
416 CEpocAudio::Current(thisdevice
).SetPause(ETrue
);
418 thisdevice
->enabled
= 0; /* enable only after audio engine has been initialized!*/
420 /* We're ready to rock and roll. :-) */
424 static void EPOC_CloseAudio(SDL_AudioDevice
* thisdevice
)
426 CEpocAudio::Free(thisdevice
);
429 static void EPOC_ThreadInit(SDL_AudioDevice
*thisdevice
)
431 CEpocAudio::Current(thisdevice
).ThreadInitL(thisdevice
);
432 RThread().SetPriority(EPriorityMore
);
433 thisdevice
->enabled
= 1;
436 /* This function waits until it is possible to write a full sound buffer */
437 static void EPOC_WaitAudio(SDL_AudioDevice
* thisdevice
)
439 CEpocAudio::Current(thisdevice
).Wait();
442 static void EPOC_PlayAudio(SDL_AudioDevice
* thisdevice
)
444 if(CEpocAudio::Current(thisdevice
).SetPause(SDL_GetAudioStatus() == SDL_AUDIO_PAUSED
))
445 SDL_Delay(500); //hold on the busy loop
447 CEpocAudio::Current(thisdevice
).Play();
450 static Uint8
*EPOC_GetAudioBuf(SDL_AudioDevice
* thisdevice
)
452 return CEpocAudio::Current(thisdevice
).Buffer();