Crackle-free audio playback.
[SDL.s60v3.git] / src / audio / symbian / SDL_epocaudio.cpp
blobf7346ce544681ff9ea19e9fa2c57fcce96addbb5
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();
69 private:
70 void RunL();
73 CSimpleWait::CSimpleWait()
74 : CTimer(CActive::EPriorityStandard)
76 CActiveScheduler::Add(this);
77 ConstructL();
80 void CSimpleWait::RunL()
82 CActiveScheduler::Stop();
85 class CEpocAudio : public CBase, public MStreamObs, public MStreamProvider
87 public:
88 CEpocAudio(int aBufferSize, int aFill, int aRate, int aChannels, TUint32 aType);
89 ~CEpocAudio();
91 inline static CEpocAudio& Current(SDL_AudioDevice* thisdevice);
93 static void Free(SDL_AudioDevice* thisdevice);
95 void Wait();
96 void ThreadInitL();
97 TUint8* Buffer();
99 private:
100 void Complete(int aState, int aError);
101 TPtrC8 Data();
103 CStreamPlayer* iPlayer;
104 int iRate;
105 int iChannels;
106 TUint32 iType;
107 int iBufferSize;
108 TUint8* iBuffer;
109 CSimpleWait* iWait;
112 CEpocAudio::CEpocAudio(int aBufferSize, int aFill, int aRate, int aChannels, TUint32 aType)
113 : iRate(aRate)
114 , iChannels(aChannels)
115 , iType(aType)
116 , iBufferSize(aBufferSize)
118 iBuffer = new TUint8[iBufferSize];
119 memset(iBuffer, aFill, iBufferSize);
122 CEpocAudio::~CEpocAudio()
124 if(iWait != NULL)
126 iWait->Cancel();
127 delete iWait;
129 if(iPlayer != NULL)
131 iPlayer->Close();
132 delete iPlayer;
134 delete iBuffer;
137 inline CEpocAudio& CEpocAudio::Current(SDL_AudioDevice* thisdevice)
139 return *(CEpocAudio*)thisdevice->hidden;
142 void CEpocAudio::Free(SDL_AudioDevice* thisdevice)
144 CEpocAudio* ea = (CEpocAudio*)thisdevice->hidden;
145 if(ea)
147 delete ea;
148 thisdevice->hidden = NULL;
150 CActiveScheduler* as = CActiveScheduler::Current();
151 ASSERT(as->StackDepth() == 0);
152 delete as;
153 CActiveScheduler::Install(NULL);
155 ASSERT(thisdevice->hidden == NULL);
158 void CEpocAudio::ThreadInitL()
160 iWait = new CSimpleWait;
162 iPlayer = new CStreamPlayer(*this, *this);
163 iPlayer->OpenStream(iRate, iChannels, iType);
165 /// \todo Implement proper start/pause conditions
166 iPlayer->Start();
169 TUint8* CEpocAudio::Buffer()
171 return iBuffer;
174 void CEpocAudio::Complete(int aState, int aError)
176 if(iPlayer->Closed())
177 return;
179 switch(aError)
181 case KErrUnderflow:
182 case KErrInUse:
183 iPlayer->Start();
184 break;
185 case KErrAbort:
186 iPlayer->Open();
190 TPtrC8 CEpocAudio::Data()
192 TPtrC8 data(iBuffer, iBufferSize);
194 if(iWait->IsActive())
196 iWait->Cancel();
197 CActiveScheduler::Stop();
200 return data;
203 void CEpocAudio::Wait()
205 // This wait will be terminated by call to Data() from audio buffer callback.
206 iWait->After(10000000);
207 CActiveScheduler::Start();
210 /* Audio driver bootstrap functions */
212 AudioBootStrap EPOCAudio_bootstrap = {
213 "epoc\0\0\0",
214 "EPOC streaming audio\0\0\0",
215 Audio_Available,
216 Audio_CreateDevice
220 static SDL_AudioDevice *Audio_CreateDevice(int /*devindex*/)
222 SDL_AudioDevice *thisdevice;
224 /* Initialize all variables that we clean on shutdown */
225 thisdevice = new SDL_AudioDevice;
226 if ( thisdevice )
228 memset(thisdevice, 0, (sizeof *thisdevice));
229 thisdevice->hidden = NULL;
231 else
233 SDL_OutOfMemory();
234 return(0);
237 /* Set the function pointers */
238 thisdevice->OpenAudio = EPOC_OpenAudio;
239 thisdevice->WaitAudio = EPOC_WaitAudio;
240 thisdevice->PlayAudio = EPOC_PlayAudio;
241 thisdevice->GetAudioBuf = EPOC_GetAudioBuf;
242 thisdevice->CloseAudio = EPOC_CloseAudio;
243 thisdevice->ThreadInit = EPOC_ThreadInit;
244 thisdevice->free = Audio_DeleteDevice;
246 return thisdevice;
249 static void Audio_DeleteDevice(SDL_AudioDevice *device)
251 delete device;
254 static int Audio_Available(void)
256 return(1); // Audio stream modules should be always there!
259 static int EPOC_OpenAudio(SDL_AudioDevice *thisdevice, SDL_AudioSpec *spec)
261 TUint32 type = KMMFFourCCCodePCM16;
263 switch(spec->format)
265 case AUDIO_U16LSB:
266 type = KMMFFourCCCodePCMU16;
267 break;
269 case AUDIO_S16LSB:
270 type = KMMFFourCCCodePCM16;
271 break;
273 case AUDIO_U16MSB:
274 type = KMMFFourCCCodePCMU16B;
275 break;
277 case AUDIO_S16MSB:
278 type = KMMFFourCCCodePCM16B;
279 break;
281 case AUDIO_U8:
282 type = KMMFFourCCCodePCMU8;
283 break;
285 case AUDIO_S8:
286 type = KMMFFourCCCodePCM8;
287 break;
289 default:
290 spec->format = AUDIO_S16LSB;
293 if(spec->channels > 2)
294 spec->channels = 2;
296 spec->freq = CStreamPlayer::ClosestSupportedRate(spec->freq);
298 /* Allocate mixing buffer */
299 const int buflen = spec->size;
301 thisdevice->hidden = (SDL_PrivateAudioData*)new CEpocAudio(buflen, spec->silence, spec->freq, spec->channels, type);
302 thisdevice->enabled = 0; // enable only after audio engine has been initialized!
304 return 0;
307 static void EPOC_CloseAudio(SDL_AudioDevice* thisdevice)
309 CEpocAudio::Free(thisdevice);
312 static void EPOC_ThreadInit(SDL_AudioDevice *thisdevice)
314 CActiveScheduler* as = new CActiveScheduler();
315 CActiveScheduler::Install(as);
317 EpocSdlEnv::AppendCleanupItem(TSdlCleanupItem((TSdlCleanupOperation)EPOC_CloseAudio, thisdevice));
319 CEpocAudio::Current(thisdevice).ThreadInitL();
320 RThread().SetPriority(EPriorityMore);
321 thisdevice->enabled = 1;
324 /* This function waits until it is possible to write a full sound buffer */
325 static void EPOC_WaitAudio(SDL_AudioDevice* thisdevice)
327 CEpocAudio::Current(thisdevice).Wait();
330 static void EPOC_PlayAudio(SDL_AudioDevice* thisdevice)
334 static Uint8 *EPOC_GetAudioBuf(SDL_AudioDevice* thisdevice)
336 return CEpocAudio::Current(thisdevice).Buffer();