Remove two-stage construction.
[SDL.s60v3.git] / src / audio / symbian / SDL_epocaudio.cpp
blob7c491fdea27938273288082c8e17fa6ac609e620
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"
46 #include "SDL_sysaudio.h"
49 #include "streamplayer.h"
51 /* Audio driver functions */
53 static int EPOC_OpenAudio(SDL_AudioDevice *thisdevice, SDL_AudioSpec *spec);
54 static void EPOC_WaitAudio(SDL_AudioDevice *thisdevice);
55 static void EPOC_PlayAudio(SDL_AudioDevice *thisdevice);
56 static Uint8 *EPOC_GetAudioBuf(SDL_AudioDevice *thisdevice);
57 static void EPOC_CloseAudio(SDL_AudioDevice *thisdevice);
58 static void EPOC_ThreadInit(SDL_AudioDevice *thisdevice);
60 static int Audio_Available(void);
61 static SDL_AudioDevice *Audio_CreateDevice(int devindex);
62 static void Audio_DeleteDevice(SDL_AudioDevice *device);
64 class CSimpleWait : public CTimer
66 public:
67 CSimpleWait();
68 void Wait(TTimeIntervalMicroSeconds32 aWait);
70 private:
71 void RunL();
74 CSimpleWait::CSimpleWait()
75 : CTimer(CActive::EPriorityStandard)
77 CActiveScheduler::Add(this);
78 ConstructL();
81 void CSimpleWait::Wait(TTimeIntervalMicroSeconds32 aWait)
83 After(aWait);
84 CActiveScheduler::Start();
87 void CSimpleWait::RunL()
89 CActiveScheduler::Stop();
92 class CEpocAudio : public CBase, public MStreamObs, public MStreamProvider
94 public:
95 CEpocAudio(int aBufferSize, int aFill);
96 ~CEpocAudio();
98 inline static CEpocAudio& Current(SDL_AudioDevice* thisdevice);
100 static void Free(SDL_AudioDevice* thisdevice);
102 void Wait();
103 void Play();
104 void ThreadInitL(void* aDevice);
105 void Open(int iRate, int iChannels, TUint32 aType, int aBytes);
106 TUint8* Buffer();
107 bool SetPause(bool aPause);
109 private:
110 void Complete(int aState, int aError);
111 TPtrC8 Data();
113 CStreamPlayer* iPlayer;
114 int iRate;
115 int iChannels;
116 TUint32 iType;
117 TThreadId iTid;
118 int iBufferSize;
119 TUint8* iBuffer;
120 TTime iStart;
121 int iTune;
122 CSimpleWait* iWait;
123 bool iPause;
126 CEpocAudio::CEpocAudio(int aBufferSize, int aFill)
127 : iBufferSize(aBufferSize)
128 , iPause(true)
130 iBuffer = new TUint8[iBufferSize];
131 memset(iBuffer, aFill, iBufferSize);
134 CEpocAudio::~CEpocAudio()
136 if(iWait != NULL)
138 iWait->Cancel();
139 delete iWait;
141 if(iPlayer != NULL)
143 iPlayer->Close();
144 delete iPlayer;
146 delete iBuffer;
149 inline CEpocAudio& CEpocAudio::Current(SDL_AudioDevice* thisdevice)
151 return *(CEpocAudio*)thisdevice->hidden;
154 void CEpocAudio::Free(SDL_AudioDevice* thisdevice)
156 CEpocAudio* ea = (CEpocAudio*)thisdevice->hidden;
157 if(ea)
159 ASSERT(ea->iTid == RThread().Id());
160 delete ea;
161 thisdevice->hidden = NULL;
163 CActiveScheduler* as = CActiveScheduler::Current();
164 ASSERT(as->StackDepth() == 0);
165 delete as;
166 CActiveScheduler::Install(NULL);
168 ASSERT(thisdevice->hidden == NULL);
171 bool CEpocAudio::SetPause(bool aPause)
173 if(iPlayer != NULL && aPause != iPause)
175 if(aPause)
176 iPlayer->Stop();
177 else
178 iPlayer->Start();
181 iPause = aPause;
183 return iPause;
186 void CEpocAudio::ThreadInitL(void* aDevice)
188 iTid = RThread().Id();
189 CActiveScheduler* as = new CActiveScheduler();
190 CActiveScheduler::Install(as);
192 EpocSdlEnv::AppendCleanupItem(TSdlCleanupItem((TSdlCleanupOperation)EPOC_CloseAudio, aDevice));
194 iWait = new CSimpleWait;
196 iPlayer = new CStreamPlayer(*this, *this);
197 iPlayer->OpenStream(iRate, iChannels, iType);
200 TUint8* CEpocAudio::Buffer()
202 iStart.UniversalTime();
203 return iBuffer;
206 void CEpocAudio::Complete(int aState, int aError)
208 if(iPlayer->Closed())
209 return;
211 switch(aError)
213 case KErrUnderflow:
214 case KErrInUse:
215 iPlayer->Start();
216 break;
217 case KErrAbort:
218 iPlayer->Open();
222 TPtrC8 CEpocAudio::Data()
224 if(iPause)
225 return KNullDesC8();
227 TPtrC8 data(iBuffer, iBufferSize);
229 iPause = true;
230 if(iWait->IsActive())
232 iWait->Cancel();
233 CActiveScheduler::Stop();
236 return data;
239 void CEpocAudio::Play()
241 iPause = false;
244 void CEpocAudio::Wait()
246 if(!iPause)
248 const TInt64 bufMs = TInt64(iBufferSize) * TInt64(1000000);
249 const TInt64 specTime = bufMs / TInt64(iRate * iChannels * 2);
250 iWait->After(specTime);
252 CActiveScheduler::Start();
253 TTime end;
254 end.UniversalTime();
255 const TTimeIntervalMicroSeconds delta = end.MicroSecondsFrom(iStart);
257 const int diff = specTime - delta.Int64();
259 if(diff > 0)
261 usleep(diff);
264 else
266 usleep(10000);
270 void CEpocAudio::Open(int aRate, int aChannels, TUint32 aType, int aBytes)
272 iRate = aRate;
273 iChannels = aChannels;
274 iType = aType;
277 /* Audio driver bootstrap functions */
279 AudioBootStrap EPOCAudio_bootstrap = {
280 "epoc\0\0\0",
281 "EPOC streaming audio\0\0\0",
282 Audio_Available,
283 Audio_CreateDevice
287 static SDL_AudioDevice *Audio_CreateDevice(int /*devindex*/)
289 SDL_AudioDevice *thisdevice;
291 /* Initialize all variables that we clean on shutdown */
292 thisdevice = new SDL_AudioDevice;
293 if ( thisdevice )
295 memset(thisdevice, 0, (sizeof *thisdevice));
296 thisdevice->hidden = NULL;
298 else
300 SDL_OutOfMemory();
301 return(0);
304 /* Set the function pointers */
305 thisdevice->OpenAudio = EPOC_OpenAudio;
306 thisdevice->WaitAudio = EPOC_WaitAudio;
307 thisdevice->PlayAudio = EPOC_PlayAudio;
308 thisdevice->GetAudioBuf = EPOC_GetAudioBuf;
309 thisdevice->CloseAudio = EPOC_CloseAudio;
310 thisdevice->ThreadInit = EPOC_ThreadInit;
311 thisdevice->free = Audio_DeleteDevice;
313 return thisdevice;
316 static void Audio_DeleteDevice(SDL_AudioDevice *device)
318 delete device;
321 static int Audio_Available(void)
323 return(1); // Audio stream modules should be always there!
326 static int EPOC_OpenAudio(SDL_AudioDevice *thisdevice, SDL_AudioSpec *spec)
328 TUint32 type = KMMFFourCCCodePCM16;
329 int bytes = 2;
331 switch(spec->format)
333 case AUDIO_U16LSB:
334 type = KMMFFourCCCodePCMU16;
335 break;
337 case AUDIO_S16LSB:
338 type = KMMFFourCCCodePCM16;
339 break;
341 case AUDIO_U16MSB:
342 type = KMMFFourCCCodePCMU16B;
343 break;
345 case AUDIO_S16MSB:
346 type = KMMFFourCCCodePCM16B;
347 break;
349 case AUDIO_U8:
350 type = KMMFFourCCCodePCMU8;
351 break;
353 case AUDIO_S8:
354 type = KMMFFourCCCodePCM8;
355 break;
357 default:
358 spec->format = AUDIO_S16LSB;
361 if(spec->channels > 2)
362 spec->channels = 2;
364 spec->freq = CStreamPlayer::ClosestSupportedRate(spec->freq);
366 /* Allocate mixing buffer */
367 const int buflen = spec->size;
369 thisdevice->hidden = (SDL_PrivateAudioData*)new CEpocAudio(buflen, spec->silence);
371 CEpocAudio::Current(thisdevice).Open(spec->freq, spec->channels, type, bytes);
373 CEpocAudio::Current(thisdevice).SetPause(true);
375 thisdevice->enabled = 0; // enable only after audio engine has been initialized!
377 return 0;
380 static void EPOC_CloseAudio(SDL_AudioDevice* thisdevice)
382 CEpocAudio::Free(thisdevice);
385 static void EPOC_ThreadInit(SDL_AudioDevice *thisdevice)
387 CEpocAudio::Current(thisdevice).ThreadInitL(thisdevice);
388 RThread().SetPriority(EPriorityMore);
389 thisdevice->enabled = 1;
392 /* This function waits until it is possible to write a full sound buffer */
393 static void EPOC_WaitAudio(SDL_AudioDevice* thisdevice)
395 CEpocAudio::Current(thisdevice).Wait();
398 static void EPOC_PlayAudio(SDL_AudioDevice* thisdevice)
400 if(CEpocAudio::Current(thisdevice).SetPause(SDL_GetAudioStatus() == SDL_AUDIO_PAUSED))
401 SDL_Delay(500); //hold on the busy loop
402 else
403 CEpocAudio::Current(thisdevice).Play();
406 static Uint8 *EPOC_GetAudioBuf(SDL_AudioDevice* thisdevice)
408 return CEpocAudio::Current(thisdevice).Buffer();