Enable 8 bit audio playback.
[SDL.s60v3.git] / src / audio / symbian / SDL_epocaudio.cpp
blob5deb84489e1dafa55cec7947ed89d75767d9ff34
1 /*
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
19 Sam Lantinga
20 slouken@devolution.com
23 #include <stdlib.h>
24 #include <stdio.h>
25 #include <string.h>
26 #include <errno.h>
27 #include <unistd.h>
28 #include <fcntl.h>
29 #include <signal.h>
30 #include <sys/time.h>
31 #include <sys/ioctl.h>
32 #include <sys/stat.h>
34 #include "epoc_sdl.h"
36 #include <e32hal.h>
39 extern "C" {
40 #include "SDL_audio.h"
41 #include "SDL_error.h"
42 #include "SDL_audiomem.h"
43 #include "SDL_audio_c.h"
44 #include "SDL_timer.h"
45 #include "SDL_audiodev_c.h"
48 #include "SDL_epocaudio.h"
50 #include "streamplayer.h"
52 /* Audio driver functions */
54 static int EPOC_OpenAudio(SDL_AudioDevice *thisdevice, SDL_AudioSpec *spec);
55 static void EPOC_WaitAudio(SDL_AudioDevice *thisdevice);
56 static void EPOC_PlayAudio(SDL_AudioDevice *thisdevice);
57 static Uint8 *EPOC_GetAudioBuf(SDL_AudioDevice *thisdevice);
58 static void EPOC_CloseAudio(SDL_AudioDevice *thisdevice);
59 static void EPOC_ThreadInit(SDL_AudioDevice *thisdevice);
61 static int Audio_Available(void);
62 static SDL_AudioDevice *Audio_CreateDevice(int devindex);
63 static void Audio_DeleteDevice(SDL_AudioDevice *device);
65 const int KAudioBuffers = 2;
67 NONSHARABLE_CLASS(CSimpleWait) : public CTimer
69 public:
70 void Wait(TTimeIntervalMicroSeconds32 aWait);
71 static CSimpleWait* NewL();
72 private:
73 CSimpleWait();
74 void RunL();
77 CSimpleWait* CSimpleWait::NewL()
79 CSimpleWait* wait = new (ELeave) CSimpleWait();
80 CleanupStack::PushL(wait);
81 wait->ConstructL();
82 CleanupStack::Pop();
83 return wait;
86 void CSimpleWait::Wait(TTimeIntervalMicroSeconds32 aWait)
88 After(aWait);
89 CActiveScheduler::Start();
92 CSimpleWait::CSimpleWait() : CTimer(CActive::EPriorityStandard)
94 CActiveScheduler::Add(this);
97 void CSimpleWait::RunL()
99 CActiveScheduler::Stop();
102 NONSHARABLE_CLASS(CEpocAudio) : public CBase, public MStreamObs, public MStreamProvider
104 public:
105 static void* NewL(int BufferSize, int aFill);
106 inline static CEpocAudio& Current(SDL_AudioDevice* thisdevice);
108 static void Free(SDL_AudioDevice* thisdevice);
110 void Wait();
111 void Play();
112 void ThreadInitL(TAny* aDevice);
113 void Open(int iRate, int iChannels, TUint32 aType, int aBytes);
114 ~CEpocAudio();
115 TUint8* Buffer();
116 TBool SetPause(TBool aPause);
118 private:
119 CEpocAudio(int aBufferSize);
120 void Complete(int aState, int aError);
121 TPtrC8 Data();
122 void ConstructL(int aFill);
123 int iBufferSize;
124 CStreamPlayer* iPlayer;
125 int iBufferRate;
126 int iRate;
127 int iChannels;
128 TUint32 iType;
129 int iPosition;
130 TThreadId iTid;
131 TUint8* iAudioPtr;
132 TUint8* iBuffer;
133 TTime iStart;
134 int iTune;
135 CSimpleWait* iWait;
138 inline CEpocAudio& CEpocAudio::Current(SDL_AudioDevice* thisdevice)
140 return *static_cast<CEpocAudio*>((void*)thisdevice->hidden);
143 void CEpocAudio::Free(SDL_AudioDevice* thisdevice)
145 CEpocAudio* ea = static_cast<CEpocAudio*>((void*)thisdevice->hidden);
146 if(ea)
148 ASSERT(ea->iTid == RThread().Id());
149 delete ea;
150 thisdevice->hidden = NULL;
152 CActiveScheduler* as = CActiveScheduler::Current();
153 ASSERT(as->StackDepth() == 0);
154 delete as;
155 CActiveScheduler::Install(NULL);
157 ASSERT(thisdevice->hidden == NULL);
160 CEpocAudio::CEpocAudio(int aBufferSize)
161 : iBufferSize(aBufferSize), iPosition(-1)
165 void* CEpocAudio::NewL(int aBufferSize, int aFill)
167 CEpocAudio* eAudioLib = new (ELeave) CEpocAudio(aBufferSize);
168 CleanupStack::PushL(eAudioLib);
169 eAudioLib->ConstructL(aFill);
170 CleanupStack::Pop();
171 return eAudioLib;
174 void CEpocAudio::ConstructL(int aFill)
176 iBuffer = new TUint8[KAudioBuffers * iBufferSize];
177 memset(iBuffer, aFill, KAudioBuffers * iBufferSize);
178 iAudioPtr = iBuffer;
181 TBool CEpocAudio::SetPause(TBool aPause)
183 if(aPause && iPosition >= 0)
185 iPosition = -1;
186 if(iPlayer != NULL)
187 iPlayer->Stop();
189 if(!aPause && iPosition < 0)
191 iPosition = 0;
192 if(iPlayer != NULL)
193 iPlayer->Start();
195 return iPosition < 0;
198 void CEpocAudio::ThreadInitL(TAny* aDevice)
200 iTid = RThread().Id();
201 CActiveScheduler* as = new (ELeave) CActiveScheduler();
202 CActiveScheduler::Install(as);
204 EpocSdlEnv::AppendCleanupItem(TSdlCleanupItem((TSdlCleanupOperation)EPOC_CloseAudio, aDevice));
206 iWait = CSimpleWait::NewL();
208 iPlayer = new (ELeave) CStreamPlayer(*this, *this);
209 iPlayer->ConstructL();
210 iPlayer->OpenStream(iRate, iChannels, iType);
213 TUint8* CEpocAudio::Buffer()
215 iStart.UniversalTime();
216 return iAudioPtr;
219 CEpocAudio::~CEpocAudio()
221 if(iWait != NULL)
223 iWait->Cancel();
224 delete iWait;
226 if(iPlayer != NULL)
228 iPlayer->Close();
229 delete iPlayer;
231 delete iBuffer;
234 void CEpocAudio::Complete(int aState, int aError)
236 if(iPlayer->Closed())
237 return;
239 switch(aError)
241 case KErrUnderflow:
242 case KErrInUse:
243 iPlayer->Start();
244 break;
245 case KErrAbort:
246 iPlayer->Open();
250 const int KClip = 256;
252 TPtrC8 CEpocAudio::Data()
254 if(iPosition < 0)
255 return KNullDesC8();
257 TPtrC8 data(iAudioPtr + iPosition, KClip);
259 iPosition += KClip;
260 if(iPosition >= iBufferSize)
262 iAudioPtr += iBufferSize;
264 if((iAudioPtr - iBuffer) >= KAudioBuffers * iBufferSize)
265 iAudioPtr = iBuffer;
267 iPosition = -1;
268 if(iWait->IsActive())
270 iWait->Cancel();
271 CActiveScheduler::Stop();
274 return data;
277 void CEpocAudio::Play()
279 iPosition = 0;
282 void CEpocAudio::Wait()
284 if(iPosition >= 0)
286 const TInt64 bufMs = TInt64(iBufferSize - KClip) * TInt64(1000000);
287 const TInt64 specTime = bufMs / TInt64(iRate * iChannels * 2);
288 iWait->After(specTime);
290 CActiveScheduler::Start();
291 TTime end;
292 end.UniversalTime();
293 const TTimeIntervalMicroSeconds delta = end.MicroSecondsFrom(iStart);
295 const int diff = specTime - delta.Int64();
297 if(diff > 0 && diff < 200000)
299 User::After(diff);
302 else
304 User::After(10000);
308 void CEpocAudio::Open(int aRate, int aChannels, TUint32 aType, int aBytes)
310 iRate = aRate;
311 iChannels = aChannels;
312 iType = aType;
313 iBufferRate = iRate * iChannels * aBytes; //1/x
316 /* Audio driver bootstrap functions */
318 AudioBootStrap EPOCAudio_bootstrap = {
319 "epoc\0\0\0",
320 "EPOC streaming audio\0\0\0",
321 Audio_Available,
322 Audio_CreateDevice
326 static SDL_AudioDevice *Audio_CreateDevice(int /*devindex*/)
328 SDL_AudioDevice *thisdevice;
330 /* Initialize all variables that we clean on shutdown */
331 thisdevice = new SDL_AudioDevice;
332 if ( thisdevice )
334 memset(thisdevice, 0, (sizeof *thisdevice));
335 thisdevice->hidden = NULL;
337 else
339 SDL_OutOfMemory();
340 return(0);
343 /* Set the function pointers */
344 thisdevice->OpenAudio = EPOC_OpenAudio;
345 thisdevice->WaitAudio = EPOC_WaitAudio;
346 thisdevice->PlayAudio = EPOC_PlayAudio;
347 thisdevice->GetAudioBuf = EPOC_GetAudioBuf;
348 thisdevice->CloseAudio = EPOC_CloseAudio;
349 thisdevice->ThreadInit = EPOC_ThreadInit;
350 thisdevice->free = Audio_DeleteDevice;
352 return thisdevice;
355 static void Audio_DeleteDevice(SDL_AudioDevice *device)
357 delete device;
360 static int Audio_Available(void)
362 return(1); // Audio stream modules should be always there!
365 static int EPOC_OpenAudio(SDL_AudioDevice *thisdevice, SDL_AudioSpec *spec)
367 TUint32 type = KMMFFourCCCodePCM16;
368 int bytes = 2;
370 switch(spec->format)
372 case AUDIO_U16LSB:
373 type = KMMFFourCCCodePCMU16;
374 break;
376 case AUDIO_S16LSB:
377 type = KMMFFourCCCodePCM16;
378 break;
380 case AUDIO_U16MSB:
381 type = KMMFFourCCCodePCMU16B;
382 break;
384 case AUDIO_S16MSB:
385 type = KMMFFourCCCodePCM16B;
386 break;
388 case AUDIO_U8:
389 type = KMMFFourCCCodePCMU8;
390 break;
392 case AUDIO_S8:
393 type = KMMFFourCCCodePCM8;
394 break;
396 default:
397 spec->format = AUDIO_S16LSB;
400 if(spec->channels > 2)
401 spec->channels = 2;
403 spec->freq = CStreamPlayer::ClosestSupportedRate(spec->freq);
405 /* Allocate mixing buffer */
406 const int buflen = spec->size;
408 TRAPD(err, thisdevice->hidden = static_cast<SDL_PrivateAudioData*>(CEpocAudio::NewL(buflen, spec->silence)));
409 if(err != KErrNone)
410 return -1;
412 CEpocAudio::Current(thisdevice).Open(spec->freq, spec->channels, type, bytes);
414 CEpocAudio::Current(thisdevice).SetPause(ETrue);
416 thisdevice->enabled = 0; // enable only after audio engine has been initialized!
418 return(0);
421 static void EPOC_CloseAudio(SDL_AudioDevice* thisdevice)
423 CEpocAudio::Free(thisdevice);
426 static void EPOC_ThreadInit(SDL_AudioDevice *thisdevice)
428 CEpocAudio::Current(thisdevice).ThreadInitL(thisdevice);
429 RThread().SetPriority(EPriorityMore);
430 thisdevice->enabled = 1;
433 /* This function waits until it is possible to write a full sound buffer */
434 static void EPOC_WaitAudio(SDL_AudioDevice* thisdevice)
436 CEpocAudio::Current(thisdevice).Wait();
439 static void EPOC_PlayAudio(SDL_AudioDevice* thisdevice)
441 if(CEpocAudio::Current(thisdevice).SetPause(SDL_GetAudioStatus() == SDL_AUDIO_PAUSED))
442 SDL_Delay(500); //hold on the busy loop
443 else
444 CEpocAudio::Current(thisdevice).Play();
447 static Uint8 *EPOC_GetAudioBuf(SDL_AudioDevice* thisdevice)
449 return CEpocAudio::Current(thisdevice).Buffer();