Cleanup.
[SDL.s60v3.git] / src / audio / symbian / SDL_epocaudio.cpp
blobd20434ad2b730f1fb2f54804cbadf931e3b84761
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 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 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 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(void* 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);
124 int iBufferSize;
125 CStreamPlayer* iPlayer;
126 int iBufferRate;
127 int iRate;
128 int iChannels;
129 TUint32 iType;
130 int iPosition;
131 TThreadId iTid;
132 TUint8* iAudioPtr;
133 TUint8* iBuffer;
134 TTime iStart;
135 int iTune;
136 CSimpleWait* iWait;
139 inline CEpocAudio& CEpocAudio::Current(SDL_AudioDevice* thisdevice)
141 return *static_cast<CEpocAudio*>((void*)thisdevice->hidden);
144 void CEpocAudio::Free(SDL_AudioDevice* thisdevice)
146 CEpocAudio* ea = static_cast<CEpocAudio*>((void*)thisdevice->hidden);
147 if(ea)
149 ASSERT(ea->iTid == RThread().Id());
150 delete ea;
151 thisdevice->hidden = NULL;
153 CActiveScheduler* as = CActiveScheduler::Current();
154 ASSERT(as->StackDepth() == 0);
155 delete as;
156 CActiveScheduler::Install(NULL);
158 ASSERT(thisdevice->hidden == NULL);
161 CEpocAudio::CEpocAudio(int aBufferSize)
162 : iBufferSize(aBufferSize), iPosition(-1)
166 void* CEpocAudio::NewL(int aBufferSize, int aFill)
168 CEpocAudio* eAudioLib = new CEpocAudio(aBufferSize);
169 CleanupStack::PushL(eAudioLib);
170 eAudioLib->ConstructL(aFill);
171 CleanupStack::Pop();
172 return eAudioLib;
175 void CEpocAudio::ConstructL(int aFill)
177 iBuffer = new TUint8[KAudioBuffers * iBufferSize];
178 memset(iBuffer, aFill, KAudioBuffers * iBufferSize);
179 iAudioPtr = iBuffer;
182 TBool CEpocAudio::SetPause(TBool aPause)
184 if(aPause && iPosition >= 0)
186 iPosition = -1;
187 if(iPlayer != NULL)
188 iPlayer->Stop();
190 if(!aPause && iPosition < 0)
192 iPosition = 0;
193 if(iPlayer != NULL)
194 iPlayer->Start();
196 return iPosition < 0;
199 void CEpocAudio::ThreadInitL(void* aDevice)
201 iTid = RThread().Id();
202 CActiveScheduler* as = new CActiveScheduler();
203 CActiveScheduler::Install(as);
205 EpocSdlEnv::AppendCleanupItem(TSdlCleanupItem((TSdlCleanupOperation)EPOC_CloseAudio, aDevice));
207 iWait = CSimpleWait::NewL();
209 iPlayer = new CStreamPlayer(*this, *this);
210 iPlayer->ConstructL();
211 iPlayer->OpenStream(iRate, iChannels, iType);
214 TUint8* CEpocAudio::Buffer()
216 iStart.UniversalTime();
217 return iAudioPtr;
220 CEpocAudio::~CEpocAudio()
222 if(iWait != NULL)
224 iWait->Cancel();
225 delete iWait;
227 if(iPlayer != NULL)
229 iPlayer->Close();
230 delete iPlayer;
232 delete iBuffer;
235 void CEpocAudio::Complete(int aState, int aError)
237 if(iPlayer->Closed())
238 return;
240 switch(aError)
242 case KErrUnderflow:
243 case KErrInUse:
244 iPlayer->Start();
245 break;
246 case KErrAbort:
247 iPlayer->Open();
251 const int KClip = 256;
253 TPtrC8 CEpocAudio::Data()
255 if(iPosition < 0)
256 return KNullDesC8();
258 TPtrC8 data(iAudioPtr + iPosition, KClip);
260 iPosition += KClip;
261 if(iPosition >= iBufferSize)
263 iAudioPtr += iBufferSize;
265 if((iAudioPtr - iBuffer) >= KAudioBuffers * iBufferSize)
266 iAudioPtr = iBuffer;
268 iPosition = -1;
269 if(iWait->IsActive())
271 iWait->Cancel();
272 CActiveScheduler::Stop();
275 return data;
278 void CEpocAudio::Play()
280 iPosition = 0;
283 void CEpocAudio::Wait()
285 if(iPosition >= 0)
287 const TInt64 bufMs = TInt64(iBufferSize - KClip) * TInt64(1000000);
288 const TInt64 specTime = bufMs / TInt64(iRate * iChannels * 2);
289 iWait->After(specTime);
291 CActiveScheduler::Start();
292 TTime end;
293 end.UniversalTime();
294 const TTimeIntervalMicroSeconds delta = end.MicroSecondsFrom(iStart);
296 const int diff = specTime - delta.Int64();
298 if(diff > 0 && diff < 200000)
300 User::After(diff);
303 else
305 User::After(10000);
309 void CEpocAudio::Open(int aRate, int aChannels, TUint32 aType, int aBytes)
311 iRate = aRate;
312 iChannels = aChannels;
313 iType = aType;
314 iBufferRate = iRate * iChannels * aBytes; //1/x
317 /* Audio driver bootstrap functions */
319 AudioBootStrap EPOCAudio_bootstrap = {
320 "epoc\0\0\0",
321 "EPOC streaming audio\0\0\0",
322 Audio_Available,
323 Audio_CreateDevice
327 static SDL_AudioDevice *Audio_CreateDevice(int /*devindex*/)
329 SDL_AudioDevice *thisdevice;
331 /* Initialize all variables that we clean on shutdown */
332 thisdevice = new SDL_AudioDevice;
333 if ( thisdevice )
335 memset(thisdevice, 0, (sizeof *thisdevice));
336 thisdevice->hidden = NULL;
338 else
340 SDL_OutOfMemory();
341 return(0);
344 /* Set the function pointers */
345 thisdevice->OpenAudio = EPOC_OpenAudio;
346 thisdevice->WaitAudio = EPOC_WaitAudio;
347 thisdevice->PlayAudio = EPOC_PlayAudio;
348 thisdevice->GetAudioBuf = EPOC_GetAudioBuf;
349 thisdevice->CloseAudio = EPOC_CloseAudio;
350 thisdevice->ThreadInit = EPOC_ThreadInit;
351 thisdevice->free = Audio_DeleteDevice;
353 return thisdevice;
356 static void Audio_DeleteDevice(SDL_AudioDevice *device)
358 delete device;
361 static int Audio_Available(void)
363 return(1); // Audio stream modules should be always there!
366 static int EPOC_OpenAudio(SDL_AudioDevice *thisdevice, SDL_AudioSpec *spec)
368 TUint32 type = KMMFFourCCCodePCM16;
369 int bytes = 2;
371 switch(spec->format)
373 case AUDIO_U16LSB:
374 type = KMMFFourCCCodePCMU16;
375 break;
377 case AUDIO_S16LSB:
378 type = KMMFFourCCCodePCM16;
379 break;
381 case AUDIO_U16MSB:
382 type = KMMFFourCCCodePCMU16B;
383 break;
385 case AUDIO_S16MSB:
386 type = KMMFFourCCCodePCM16B;
387 break;
389 case AUDIO_U8:
390 type = KMMFFourCCCodePCMU8;
391 break;
393 case AUDIO_S8:
394 type = KMMFFourCCCodePCM8;
395 break;
397 default:
398 spec->format = AUDIO_S16LSB;
401 if(spec->channels > 2)
402 spec->channels = 2;
404 spec->freq = CStreamPlayer::ClosestSupportedRate(spec->freq);
406 /* Allocate mixing buffer */
407 const int buflen = spec->size;
409 TRAPD(err, thisdevice->hidden = static_cast<SDL_PrivateAudioData*>(CEpocAudio::NewL(buflen, spec->silence)));
410 if(err != KErrNone)
411 return -1;
413 CEpocAudio::Current(thisdevice).Open(spec->freq, spec->channels, type, bytes);
415 CEpocAudio::Current(thisdevice).SetPause(ETrue);
417 thisdevice->enabled = 0; // enable only after audio engine has been initialized!
419 return 0;
422 static void EPOC_CloseAudio(SDL_AudioDevice* thisdevice)
424 CEpocAudio::Free(thisdevice);
427 static void EPOC_ThreadInit(SDL_AudioDevice *thisdevice)
429 CEpocAudio::Current(thisdevice).ThreadInitL(thisdevice);
430 RThread().SetPriority(EPriorityMore);
431 thisdevice->enabled = 1;
434 /* This function waits until it is possible to write a full sound buffer */
435 static void EPOC_WaitAudio(SDL_AudioDevice* thisdevice)
437 CEpocAudio::Current(thisdevice).Wait();
440 static void EPOC_PlayAudio(SDL_AudioDevice* thisdevice)
442 if(CEpocAudio::Current(thisdevice).SetPause(SDL_GetAudioStatus() == SDL_AUDIO_PAUSED))
443 SDL_Delay(500); //hold on the busy loop
444 else
445 CEpocAudio::Current(thisdevice).Play();
448 static Uint8 *EPOC_GetAudioBuf(SDL_AudioDevice* thisdevice)
450 return CEpocAudio::Current(thisdevice).Buffer();