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
31 static 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 
52 extern "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 
71 static int EPOC_OpenAudio(SDL_AudioDevice *thisdevice, SDL_AudioSpec *spec);
72 static void EPOC_WaitAudio(SDL_AudioDevice *thisdevice);
73 static void EPOC_PlayAudio(SDL_AudioDevice *thisdevice);
74 static Uint8 *EPOC_GetAudioBuf(SDL_AudioDevice *thisdevice);
75 static void EPOC_CloseAudio(SDL_AudioDevice *thisdevice);
76 static void EPOC_ThreadInit(SDL_AudioDevice *thisdevice);
77 
78 static int Audio_Available(void);
79 static SDL_AudioDevice *Audio_CreateDevice(int devindex);
80 static 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
NONSHARABLE_CLASS(TDump)90 NONSHARABLE_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 
101 TInt 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 	}
115 void TDump::Close()
116 	{
117 	iFile.Close();
118 	iFs.Close();
119 	}
120 void TDump::Dump(const TDesC8& aDes)
121 	{
122 	iFile.Write(aDes);
123 	}
124 #endif
125 
126 
127 NONSHARABLE_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 
138 CSimpleWait* CSimpleWait::NewL()
139 	{
140 	CSimpleWait* wait = new (ELeave) CSimpleWait();
141 	CleanupStack::PushL(wait);
142 	wait->ConstructL();
143 	CleanupStack::Pop();
144 	return wait;
145 	}
146 
147 void CSimpleWait::Wait(TTimeIntervalMicroSeconds32 aWait)
148 	{
149 	After(aWait);
150 	CActiveScheduler::Start();
151 	}
152 
153 CSimpleWait::CSimpleWait() : CTimer(CActive::EPriorityStandard)
154 	{
155 	CActiveScheduler::Add(this);
156 	}
157 
158 void CSimpleWait::RunL()
159 	{
160 	CActiveScheduler::Stop();
161 	}
162 
163 const TInt KAudioBuffers(2);
164 
165 
166 NONSHARABLE_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 
210 inline CEpocAudio& CEpocAudio::Current(SDL_AudioDevice* thisdevice)
211 	{
212 	return *static_cast<CEpocAudio*>((void*)thisdevice->hidden);
213 	}
214 
215 /*
216 
217 TBool EndSc(TAny*)
218 	{
219 	CActiveScheduler::Stop();
220 	}
221 
222 LOCAL_C void CleanScL()
223 	{
224 	CIdle* d = CIdle::NewLC(CActive:::EPriorityIdle);
225 	d->Start(TCallBack(EndSc));
226 	CActiveScheduler::Start();
227 
228 	}
229 */
230 
231 void 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 
248 CEpocAudio::CEpocAudio(TInt aBufferSize) : iBufferSize(aBufferSize), iPosition(-1)
249 	{
250 	}
251 
252 void* 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 
261 void CEpocAudio::ConstructL(TInt aFill)
262 	{
263 	iBuffer = (TUint8*) User::AllocL(KAudioBuffers * iBufferSize);
264 	memset(iBuffer, aFill, KAudioBuffers * iBufferSize);
265 	iAudioPtr = iBuffer;
266 	}
267 
268 
269 TBool 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 
286 void 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 
307 TUint8* CEpocAudio::Buffer()
308 	{
309 	iStart.UniversalTime();
310 //	iStart = iPlayer->Position();
311 	return iAudioPtr;
312 
313 	}
314 
315 CEpocAudio::~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 
326 void 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 
345 void 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 
353 const TInt KClip(256);
354 
355 TPtrC8 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 
393 void CEpocAudio::Play()
394 	{
395 	iPosition = 0;
396 	}
397 
398 void 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 
432 void 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 
443 AudioBootStrap EPOCAudio_bootstrap = {
444 	"epoc\0\0\0",
445 	"EPOC streaming audio\0\0\0",
446 	Audio_Available,
447 	Audio_CreateDevice
448 };
449 
450 
451 static 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 
484 static void Audio_DeleteDevice(SDL_AudioDevice *device)
485     {
486 	//free(device->hidden);
487 	free(device);
488     }
489 
490 static int Audio_Available(void)
491 {
492 	return(1); // Audio stream modules should be always there!
493 }
494 
495 
496 static 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 
554 static 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 
564 static 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 */
573 static 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 
591 static 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 
608 static Uint8 *EPOC_GetAudioBuf(SDL_AudioDevice* thisdevice)
609 	{
610 	return CEpocAudio::Current(thisdevice).Buffer();
611 	}
612 
613 
614 
615