Home | History | Annotate | Download | only in symbian
      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 (at) 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
     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