1/*
2    SDL - Simple DirectMedia Layer
3    Copyright (C) 1997-2012 Sam Lantinga
4
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.
9
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.
14
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
18
19    Sam Lantinga
20    slouken@devolution.com
21*/
22
23/*
24    SDL_epocaudio.cpp
25    Epoc based SDL audio driver implementation
26
27    Markus Mertama
28*/
29
30#ifdef SAVE_RCSID
31static char rcsid =
32 "@(#) $Id: SDL_epocaudio.c,v 0.0.0.0 2001/06/19 17:19:56 hercules Exp $";
33#endif
34
35
36#include <stdlib.h>
37#include <stdio.h>
38#include <string.h>
39#include <errno.h>
40#include <unistd.h>
41#include <fcntl.h>
42#include <signal.h>
43#include <sys/time.h>
44#include <sys/ioctl.h>
45#include <sys/stat.h>
46
47#include "epoc_sdl.h"
48
49#include <e32hal.h>
50
51
52extern "C" {
53#include "SDL_audio.h"
54#include "SDL_error.h"
55#include "SDL_audiomem.h"
56#include "SDL_audio_c.h"
57#include "SDL_timer.h"
58#include "SDL_audiodev_c.h"
59}
60
61#include "SDL_epocaudio.h"
62
63#include "streamplayer.h"
64
65
66//#define DEBUG_AUDIO
67
68
69/* Audio driver functions */
70
71static int EPOC_OpenAudio(SDL_AudioDevice *thisdevice, SDL_AudioSpec *spec);
72static void EPOC_WaitAudio(SDL_AudioDevice *thisdevice);
73static void EPOC_PlayAudio(SDL_AudioDevice *thisdevice);
74static Uint8 *EPOC_GetAudioBuf(SDL_AudioDevice *thisdevice);
75static void EPOC_CloseAudio(SDL_AudioDevice *thisdevice);
76static void EPOC_ThreadInit(SDL_AudioDevice *thisdevice);
77
78static int Audio_Available(void);
79static SDL_AudioDevice *Audio_CreateDevice(int devindex);
80static void Audio_DeleteDevice(SDL_AudioDevice *device);
81
82
83//void sos_adump(SDL_AudioDevice* thisdevice, void* data, int len);
84
85#ifdef __WINS__
86#define DODUMP
87#endif
88
89#ifdef DODUMP
90NONSHARABLE_CLASS(TDump)
91	{
92	public:
93	TInt Open();
94	void Close();
95	void Dump(const TDesC8& aDes);
96	private:
97		RFile iFile;
98    	RFs iFs;
99	};
100
101TInt TDump::Open()
102	{
103	TInt err = iFs.Connect();
104	if(err == KErrNone)
105		{
106#ifdef __WINS__
107_LIT(target, "C:\\sdlau.raw");
108#else
109_LIT(target, "E:\\sdlau.raw");
110#endif
111		err = iFile.Replace(iFs, target, EFileWrite);
112		}
113	return err;
114	}
115void TDump::Close()
116	{
117	iFile.Close();
118	iFs.Close();
119	}
120void TDump::Dump(const TDesC8& aDes)
121	{
122	iFile.Write(aDes);
123	}
124#endif
125
126
127NONSHARABLE_CLASS(CSimpleWait) : public CTimer
128	{
129	public:
130		void Wait(TTimeIntervalMicroSeconds32 aWait);
131		static CSimpleWait* NewL();
132	private:
133		CSimpleWait();
134		void RunL();
135	};
136
137
138CSimpleWait* CSimpleWait::NewL()
139	{
140	CSimpleWait* wait = new (ELeave) CSimpleWait();
141	CleanupStack::PushL(wait);
142	wait->ConstructL();
143	CleanupStack::Pop();
144	return wait;
145	}
146
147void CSimpleWait::Wait(TTimeIntervalMicroSeconds32 aWait)
148	{
149	After(aWait);
150	CActiveScheduler::Start();
151	}
152
153CSimpleWait::CSimpleWait() : CTimer(CActive::EPriorityStandard)
154	{
155	CActiveScheduler::Add(this);
156	}
157
158void CSimpleWait::RunL()
159	{
160	CActiveScheduler::Stop();
161	}
162
163const TInt KAudioBuffers(2);
164
165
166NONSHARABLE_CLASS(CEpocAudio) : public CBase, public MStreamObs, public MStreamProvider
167    {
168    public:
169    	static void* NewL(TInt BufferSize, TInt aFill);
170    	inline static CEpocAudio& Current(SDL_AudioDevice* thisdevice);
171
172    	static void Free(SDL_AudioDevice* thisdevice);
173
174    	void Wait();
175    	void Play();
176    //	void SetBuffer(const TDesC8& aBuffer);
177    	void ThreadInitL(TAny* aDevice);
178    	void Open(TInt iRate, TInt iChannels, TUint32 aType, TInt aBytes);
179    	~CEpocAudio();
180    	TUint8* Buffer();
181    	TBool SetPause(TBool aPause);
182    #ifdef DODUMP
183    	void Dump(const TDesC8& aBuf) {iDump.Dump(aBuf);}
184    #endif
185    private:
186    	CEpocAudio(TInt aBufferSize);
187    	void Complete(TInt aState, TInt aError);
188    	TPtrC8 Data();
189    	void ConstructL(TInt aFill);
190    private:
191    	TInt iBufferSize;
192    	CStreamPlayer* iPlayer;
193    	TInt iBufferRate;
194    	TInt iRate;
195    	TInt iChannels;
196    	TUint32 iType;
197    	TInt iPosition;
198    	TThreadId iTid;
199    	TUint8* iAudioPtr;
200    	TUint8* iBuffer;
201    //	TTimeIntervalMicroSeconds iStart;
202    	TTime iStart;
203    	TInt iTune;
204    	CSimpleWait* iWait;
205    #ifdef DODUMP
206    	TDump iDump;
207    #endif
208    };
209
210inline CEpocAudio& CEpocAudio::Current(SDL_AudioDevice* thisdevice)
211	{
212	return *static_cast<CEpocAudio*>((void*)thisdevice->hidden);
213	}
214
215/*
216
217TBool EndSc(TAny*)
218	{
219	CActiveScheduler::Stop();
220	}
221
222LOCAL_C void CleanScL()
223	{
224	CIdle* d = CIdle::NewLC(CActive:::EPriorityIdle);
225	d->Start(TCallBack(EndSc));
226	CActiveScheduler::Start();
227
228	}
229*/
230
231void CEpocAudio::Free(SDL_AudioDevice* thisdevice)
232	{
233    CEpocAudio* ea = static_cast<CEpocAudio*>((void*)thisdevice->hidden);
234    if(ea)
235    	{
236		ASSERT(ea->iTid == RThread().Id());
237    	delete ea;
238    	thisdevice->hidden = NULL;
239
240    	CActiveScheduler* as =  CActiveScheduler::Current();
241    	ASSERT(as->StackDepth() == 0);
242    	delete as;
243    	CActiveScheduler::Install(NULL);
244    	}
245    ASSERT(thisdevice->hidden == NULL);
246	}
247
248CEpocAudio::CEpocAudio(TInt aBufferSize) : iBufferSize(aBufferSize), iPosition(-1)
249	{
250	}
251
252void* CEpocAudio::NewL(TInt aBufferSize, TInt aFill)
253	{
254	CEpocAudio* eAudioLib = new (ELeave) CEpocAudio(aBufferSize);
255	CleanupStack::PushL(eAudioLib);
256	eAudioLib->ConstructL(aFill);
257	CleanupStack::Pop();
258	return eAudioLib;
259	}
260
261void CEpocAudio::ConstructL(TInt aFill)
262	{
263	iBuffer = (TUint8*) User::AllocL(KAudioBuffers * iBufferSize);
264	memset(iBuffer, aFill, KAudioBuffers * iBufferSize);
265	iAudioPtr = iBuffer;
266	}
267
268
269TBool CEpocAudio::SetPause(TBool aPause)
270	{
271	if(aPause && iPosition >= 0)
272		{
273		iPosition = -1;
274		if(iPlayer != NULL)
275			iPlayer->Stop();
276		}
277	if(!aPause && iPosition < 0)
278		{
279		iPosition = 0;
280		if(iPlayer != NULL)
281			iPlayer->Start();
282		}
283	return iPosition < 0;
284	}
285
286void CEpocAudio::ThreadInitL(TAny* aDevice)
287	{
288	iTid = RThread().Id();
289	CActiveScheduler* as =  new (ELeave) CActiveScheduler();
290    CActiveScheduler::Install(as);
291
292    EpocSdlEnv::AppendCleanupItem(TSdlCleanupItem((TSdlCleanupOperation)EPOC_CloseAudio, aDevice));
293
294    iWait = CSimpleWait::NewL();
295
296    iPlayer = new (ELeave) CStreamPlayer(*this, *this);
297    iPlayer->ConstructL();
298    iPlayer->OpenStream(iRate, iChannels, iType);
299
300    #ifdef DODUMP
301    User::LeaveIfError(iDump.Open());
302    #endif
303	}
304
305
306
307TUint8* CEpocAudio::Buffer()
308	{
309	iStart.UniversalTime();
310//	iStart = iPlayer->Position();
311	return iAudioPtr;
312
313	}
314
315CEpocAudio::~CEpocAudio()
316	{
317	if(iWait != NULL)
318		iWait->Cancel();
319	delete iWait;
320	if(iPlayer != NULL)
321		iPlayer->Close();
322	delete iPlayer;
323	delete iBuffer;
324	}
325
326void CEpocAudio::Complete(TInt aState, TInt aError)
327	{
328	if(aState == MStreamObs::EClose)
329		{
330		}
331	if(iPlayer->Closed())
332		return;
333	switch(aError)
334		{
335		case KErrUnderflow:
336		case KErrInUse:
337			iPlayer->Start();
338			break;
339		case KErrAbort:
340			iPlayer->Open();
341		}
342	}
343
344
345void sos_adump(SDL_AudioDevice* thisdevice, void* data, int len)
346	{
347#ifdef DODUMP
348	const TPtrC8 buf((TUint8*)data, len);
349	CEpocAudio::Current(thisdevice).Dump(buf);
350#endif
351	}
352
353const TInt KClip(256);
354
355TPtrC8 CEpocAudio::Data()
356	{
357	if(iPosition < 0)
358		return KNullDesC8();
359
360	TPtrC8 data(iAudioPtr + iPosition, KClip);
361
362#ifdef DODUMP
363	iDump.Dump(data);
364#endif
365
366	iPosition += KClip;
367	if(iPosition >= iBufferSize)
368		{
369
370/*		if(iAudioPtr == iBuffer)
371			iAudioPtr = iBuffer + iBufferSize;
372		else
373			iAudioPtr = iBuffer;
374*/
375		iAudioPtr += iBufferSize;
376
377		if((iAudioPtr - iBuffer) >= KAudioBuffers * iBufferSize)
378			iAudioPtr = iBuffer;
379
380		iPosition = -1;
381		if(iWait->IsActive())
382			{
383			iWait->Cancel();
384			CActiveScheduler::Stop();
385			}
386		}
387	return data;
388	}
389
390
391
392
393void CEpocAudio::Play()
394	{
395	iPosition = 0;
396	}
397
398void CEpocAudio::Wait()
399	{
400	if(iPosition >= 0 /*&& iPlayer->Playing()*/)
401		{
402		const TInt64 bufMs = TInt64(iBufferSize - KClip) * TInt64(1000000);
403		const TInt64 specTime =  bufMs / TInt64(iRate * iChannels * 2);
404		iWait->After(specTime);
405
406		CActiveScheduler::Start();
407		TTime end;
408		end.UniversalTime();
409		const TTimeIntervalMicroSeconds delta = end.MicroSecondsFrom(iStart);
410
411
412//		const TTimeIntervalMicroSeconds end = iPlayer->Position();
413
414
415
416
417		const TInt diff = specTime - delta.Int64();
418
419		if(diff > 0 && diff < 200000)
420			{
421			User::After(diff);
422			}
423
424		}
425	else
426		{
427	User::After(10000);
428//	iWait->Wait(10000); //just give some time...
429		}
430	}
431
432void CEpocAudio::Open(TInt aRate, TInt aChannels, TUint32 aType, TInt aBytes)
433	{
434	iRate = aRate;
435	iChannels = aChannels;
436	iType = aType;
437    iBufferRate = iRate * iChannels * aBytes; //1/x
438	}
439
440
441/* Audio driver bootstrap functions */
442
443AudioBootStrap EPOCAudio_bootstrap = {
444	"epoc\0\0\0",
445	"EPOC streaming audio\0\0\0",
446	Audio_Available,
447	Audio_CreateDevice
448};
449
450
451static SDL_AudioDevice *Audio_CreateDevice(int /*devindex*/)
452{
453	SDL_AudioDevice *thisdevice;
454
455	/* Initialize all variables that we clean on shutdown */
456	thisdevice = (SDL_AudioDevice *)malloc(sizeof(SDL_AudioDevice));
457	if ( thisdevice ) {
458		memset(thisdevice, 0, (sizeof *thisdevice));
459		thisdevice->hidden = NULL; /*(struct SDL_PrivateAudioData *)
460			 malloc((sizeof thisdevice->hidden)); */
461	}
462	if ( (thisdevice == NULL) /*|| (thisdevice->hidden == NULL) */) {
463		SDL_OutOfMemory();
464		if ( thisdevice ) {
465			free(thisdevice);
466		}
467		return(0);
468	}
469//	memset(thisdevice->hidden, 0, (sizeof *thisdevice->hidden));
470
471	/* Set the function pointers */
472	thisdevice->OpenAudio = EPOC_OpenAudio;
473	thisdevice->WaitAudio = EPOC_WaitAudio;
474	thisdevice->PlayAudio = EPOC_PlayAudio;
475	thisdevice->GetAudioBuf = EPOC_GetAudioBuf;
476	thisdevice->CloseAudio = EPOC_CloseAudio;
477    thisdevice->ThreadInit = EPOC_ThreadInit;
478	thisdevice->free = Audio_DeleteDevice;
479
480	return thisdevice;
481}
482
483
484static void Audio_DeleteDevice(SDL_AudioDevice *device)
485    {
486	//free(device->hidden);
487	free(device);
488    }
489
490static int Audio_Available(void)
491{
492	return(1); // Audio stream modules should be always there!
493}
494
495
496static int EPOC_OpenAudio(SDL_AudioDevice *thisdevice, SDL_AudioSpec *spec)
497{
498	SDL_TRACE("SDL:EPOC_OpenAudio");
499
500
501	TUint32 type = KMMFFourCCCodePCM16;
502	TInt bytes = 2;
503
504	switch(spec->format)
505		{
506		case AUDIO_U16LSB:
507			type = KMMFFourCCCodePCMU16;
508			break;
509		case AUDIO_S16LSB:
510			type = KMMFFourCCCodePCM16;
511			break;
512		case AUDIO_U16MSB:
513			type = KMMFFourCCCodePCMU16B;
514			break;
515		case AUDIO_S16MSB:
516			type = KMMFFourCCCodePCM16B;
517			break;
518			//8 bit not supported!
519		case AUDIO_U8:
520		case AUDIO_S8:
521		default:
522			spec->format = AUDIO_S16LSB;
523		};
524
525
526
527	if(spec->channels > 2)
528		spec->channels = 2;
529
530	spec->freq = CStreamPlayer::ClosestSupportedRate(spec->freq);
531
532
533	/* Allocate mixing buffer */
534	const TInt buflen = spec->size;// * bytes * spec->channels;
535//	audiobuf = NULL;
536
537    TRAPD(err, thisdevice->hidden = static_cast<SDL_PrivateAudioData*>(CEpocAudio::NewL(buflen, spec->silence)));
538    if(err != KErrNone)
539        return -1;
540
541	CEpocAudio::Current(thisdevice).Open(spec->freq, spec->channels, type, bytes);
542
543	CEpocAudio::Current(thisdevice).SetPause(ETrue);
544
545   // isSDLAudioPaused = 1;
546
547    thisdevice->enabled = 0; /* enable only after audio engine has been initialized!*/
548
549	/* We're ready to rock and roll. :-) */
550	return(0);
551}
552
553
554static void EPOC_CloseAudio(SDL_AudioDevice* thisdevice)
555    {
556#ifdef DEBUG_AUDIO
557    SDL_TRACE("Close audio\n");
558#endif
559
560	CEpocAudio::Free(thisdevice);
561	}
562
563
564static void EPOC_ThreadInit(SDL_AudioDevice *thisdevice)
565    {
566	SDL_TRACE("SDL:EPOC_ThreadInit");
567    CEpocAudio::Current(thisdevice).ThreadInitL(thisdevice);
568    RThread().SetPriority(EPriorityMore);
569    thisdevice->enabled = 1;
570    }
571
572/* This function waits until it is possible to write a full sound buffer */
573static void EPOC_WaitAudio(SDL_AudioDevice* thisdevice)
574{
575#ifdef DEBUG_AUDIO
576    SDL_TRACE1("wait %d audio\n", CEpocAudio::AudioLib().StreamPlayer(KSfxChannel).SyncTime());
577    TInt tics = User::TickCount();
578#endif
579
580	CEpocAudio::Current(thisdevice).Wait();
581
582#ifdef DEBUG_AUDIO
583    TInt ntics =  User::TickCount() - tics;
584    SDL_TRACE1("audio waited %d\n", ntics);
585    SDL_TRACE1("audio at %d\n", tics);
586#endif
587}
588
589
590
591static void EPOC_PlayAudio(SDL_AudioDevice* thisdevice)
592	{
593 	if(CEpocAudio::Current(thisdevice).SetPause(SDL_GetAudioStatus() == SDL_AUDIO_PAUSED))
594 		SDL_Delay(500); //hold on the busy loop
595 	else
596 		CEpocAudio::Current(thisdevice).Play();
597
598#ifdef DEBUG_AUDIO
599    SDL_TRACE("buffer has audio data\n");
600#endif
601
602
603#ifdef DEBUG_AUDIO
604	SDL_TRACE1("Wrote %d bytes of audio data\n", buflen);
605#endif
606}
607
608static Uint8 *EPOC_GetAudioBuf(SDL_AudioDevice* thisdevice)
609	{
610	return CEpocAudio::Current(thisdevice).Buffer();
611	}
612
613
614
615