blob: 72a4eaf4e174d055fee2ac9564cff144fe01a60b [file] [log] [blame]
/*
SDL - Simple DirectMedia Layer
Copyright (C) 1997-2012 Sam Lantinga
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with this library; if not, write to the Free
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Sam Lantinga
slouken@devolution.com
*/
/*
SDL_epocaudio.cpp
Epoc based SDL audio driver implementation
Markus Mertama
*/
#ifdef SAVE_RCSID
static char rcsid =
"@(#) $Id: SDL_epocaudio.c,v 0.0.0.0 2001/06/19 17:19:56 hercules Exp $";
#endif
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
#include <signal.h>
#include <sys/time.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include "epoc_sdl.h"
#include <e32hal.h>
extern "C" {
#include "SDL_audio.h"
#include "SDL_error.h"
#include "SDL_audiomem.h"
#include "SDL_audio_c.h"
#include "SDL_timer.h"
#include "SDL_audiodev_c.h"
}
#include "SDL_epocaudio.h"
#include "streamplayer.h"
//#define DEBUG_AUDIO
/* Audio driver functions */
static int EPOC_OpenAudio(SDL_AudioDevice *thisdevice, SDL_AudioSpec *spec);
static void EPOC_WaitAudio(SDL_AudioDevice *thisdevice);
static void EPOC_PlayAudio(SDL_AudioDevice *thisdevice);
static Uint8 *EPOC_GetAudioBuf(SDL_AudioDevice *thisdevice);
static void EPOC_CloseAudio(SDL_AudioDevice *thisdevice);
static void EPOC_ThreadInit(SDL_AudioDevice *thisdevice);
static int Audio_Available(void);
static SDL_AudioDevice *Audio_CreateDevice(int devindex);
static void Audio_DeleteDevice(SDL_AudioDevice *device);
//void sos_adump(SDL_AudioDevice* thisdevice, void* data, int len);
#ifdef __WINS__
#define DODUMP
#endif
#ifdef DODUMP
NONSHARABLE_CLASS(TDump)
{
public:
TInt Open();
void Close();
void Dump(const TDesC8& aDes);
private:
RFile iFile;
RFs iFs;
};
TInt TDump::Open()
{
TInt err = iFs.Connect();
if(err == KErrNone)
{
#ifdef __WINS__
_LIT(target, "C:\\sdlau.raw");
#else
_LIT(target, "E:\\sdlau.raw");
#endif
err = iFile.Replace(iFs, target, EFileWrite);
}
return err;
}
void TDump::Close()
{
iFile.Close();
iFs.Close();
}
void TDump::Dump(const TDesC8& aDes)
{
iFile.Write(aDes);
}
#endif
NONSHARABLE_CLASS(CSimpleWait) : public CTimer
{
public:
void Wait(TTimeIntervalMicroSeconds32 aWait);
static CSimpleWait* NewL();
private:
CSimpleWait();
void RunL();
};
CSimpleWait* CSimpleWait::NewL()
{
CSimpleWait* wait = new (ELeave) CSimpleWait();
CleanupStack::PushL(wait);
wait->ConstructL();
CleanupStack::Pop();
return wait;
}
void CSimpleWait::Wait(TTimeIntervalMicroSeconds32 aWait)
{
After(aWait);
CActiveScheduler::Start();
}
CSimpleWait::CSimpleWait() : CTimer(CActive::EPriorityStandard)
{
CActiveScheduler::Add(this);
}
void CSimpleWait::RunL()
{
CActiveScheduler::Stop();
}
const TInt KAudioBuffers(2);
NONSHARABLE_CLASS(CEpocAudio) : public CBase, public MStreamObs, public MStreamProvider
{
public:
static void* NewL(TInt BufferSize, TInt aFill);
inline static CEpocAudio& Current(SDL_AudioDevice* thisdevice);
static void Free(SDL_AudioDevice* thisdevice);
void Wait();
void Play();
// void SetBuffer(const TDesC8& aBuffer);
void ThreadInitL(TAny* aDevice);
void Open(TInt iRate, TInt iChannels, TUint32 aType, TInt aBytes);
~CEpocAudio();
TUint8* Buffer();
TBool SetPause(TBool aPause);
#ifdef DODUMP
void Dump(const TDesC8& aBuf) {iDump.Dump(aBuf);}
#endif
private:
CEpocAudio(TInt aBufferSize);
void Complete(TInt aState, TInt aError);
TPtrC8 Data();
void ConstructL(TInt aFill);
private:
TInt iBufferSize;
CStreamPlayer* iPlayer;
TInt iBufferRate;
TInt iRate;
TInt iChannels;
TUint32 iType;
TInt iPosition;
TThreadId iTid;
TUint8* iAudioPtr;
TUint8* iBuffer;
// TTimeIntervalMicroSeconds iStart;
TTime iStart;
TInt iTune;
CSimpleWait* iWait;
#ifdef DODUMP
TDump iDump;
#endif
};
inline CEpocAudio& CEpocAudio::Current(SDL_AudioDevice* thisdevice)
{
return *static_cast<CEpocAudio*>((void*)thisdevice->hidden);
}
/*
TBool EndSc(TAny*)
{
CActiveScheduler::Stop();
}
LOCAL_C void CleanScL()
{
CIdle* d = CIdle::NewLC(CActive:::EPriorityIdle);
d->Start(TCallBack(EndSc));
CActiveScheduler::Start();
}
*/
void CEpocAudio::Free(SDL_AudioDevice* thisdevice)
{
CEpocAudio* ea = static_cast<CEpocAudio*>((void*)thisdevice->hidden);
if(ea)
{
ASSERT(ea->iTid == RThread().Id());
delete ea;
thisdevice->hidden = NULL;
CActiveScheduler* as = CActiveScheduler::Current();
ASSERT(as->StackDepth() == 0);
delete as;
CActiveScheduler::Install(NULL);
}
ASSERT(thisdevice->hidden == NULL);
}
CEpocAudio::CEpocAudio(TInt aBufferSize) : iBufferSize(aBufferSize), iPosition(-1)
{
}
void* CEpocAudio::NewL(TInt aBufferSize, TInt aFill)
{
CEpocAudio* eAudioLib = new (ELeave) CEpocAudio(aBufferSize);
CleanupStack::PushL(eAudioLib);
eAudioLib->ConstructL(aFill);
CleanupStack::Pop();
return eAudioLib;
}
void CEpocAudio::ConstructL(TInt aFill)
{
iBuffer = (TUint8*) User::AllocL(KAudioBuffers * iBufferSize);
memset(iBuffer, aFill, KAudioBuffers * iBufferSize);
iAudioPtr = iBuffer;
}
TBool CEpocAudio::SetPause(TBool aPause)
{
if(aPause && iPosition >= 0)
{
iPosition = -1;
if(iPlayer != NULL)
iPlayer->Stop();
}
if(!aPause && iPosition < 0)
{
iPosition = 0;
if(iPlayer != NULL)
iPlayer->Start();
}
return iPosition < 0;
}
void CEpocAudio::ThreadInitL(TAny* aDevice)
{
iTid = RThread().Id();
CActiveScheduler* as = new (ELeave) CActiveScheduler();
CActiveScheduler::Install(as);
EpocSdlEnv::AppendCleanupItem(TSdlCleanupItem((TSdlCleanupOperation)EPOC_CloseAudio, aDevice));
iWait = CSimpleWait::NewL();
iPlayer = new (ELeave) CStreamPlayer(*this, *this);
iPlayer->ConstructL();
iPlayer->OpenStream(iRate, iChannels, iType);
#ifdef DODUMP
User::LeaveIfError(iDump.Open());
#endif
}
TUint8* CEpocAudio::Buffer()
{
iStart.UniversalTime();
// iStart = iPlayer->Position();
return iAudioPtr;
}
CEpocAudio::~CEpocAudio()
{
if(iWait != NULL)
iWait->Cancel();
delete iWait;
if(iPlayer != NULL)
iPlayer->Close();
delete iPlayer;
delete iBuffer;
}
void CEpocAudio::Complete(TInt aState, TInt aError)
{
if(aState == MStreamObs::EClose)
{
}
if(iPlayer->Closed())
return;
switch(aError)
{
case KErrUnderflow:
case KErrInUse:
iPlayer->Start();
break;
case KErrAbort:
iPlayer->Open();
}
}
void sos_adump(SDL_AudioDevice* thisdevice, void* data, int len)
{
#ifdef DODUMP
const TPtrC8 buf((TUint8*)data, len);
CEpocAudio::Current(thisdevice).Dump(buf);
#endif
}
const TInt KClip(256);
TPtrC8 CEpocAudio::Data()
{
if(iPosition < 0)
return KNullDesC8();
TPtrC8 data(iAudioPtr + iPosition, KClip);
#ifdef DODUMP
iDump.Dump(data);
#endif
iPosition += KClip;
if(iPosition >= iBufferSize)
{
/* if(iAudioPtr == iBuffer)
iAudioPtr = iBuffer + iBufferSize;
else
iAudioPtr = iBuffer;
*/
iAudioPtr += iBufferSize;
if((iAudioPtr - iBuffer) >= KAudioBuffers * iBufferSize)
iAudioPtr = iBuffer;
iPosition = -1;
if(iWait->IsActive())
{
iWait->Cancel();
CActiveScheduler::Stop();
}
}
return data;
}
void CEpocAudio::Play()
{
iPosition = 0;
}
void CEpocAudio::Wait()
{
if(iPosition >= 0 /*&& iPlayer->Playing()*/)
{
const TInt64 bufMs = TInt64(iBufferSize - KClip) * TInt64(1000000);
const TInt64 specTime = bufMs / TInt64(iRate * iChannels * 2);
iWait->After(specTime);
CActiveScheduler::Start();
TTime end;
end.UniversalTime();
const TTimeIntervalMicroSeconds delta = end.MicroSecondsFrom(iStart);
// const TTimeIntervalMicroSeconds end = iPlayer->Position();
const TInt diff = specTime - delta.Int64();
if(diff > 0 && diff < 200000)
{
User::After(diff);
}
}
else
{
User::After(10000);
// iWait->Wait(10000); //just give some time...
}
}
void CEpocAudio::Open(TInt aRate, TInt aChannels, TUint32 aType, TInt aBytes)
{
iRate = aRate;
iChannels = aChannels;
iType = aType;
iBufferRate = iRate * iChannels * aBytes; //1/x
}
/* Audio driver bootstrap functions */
AudioBootStrap EPOCAudio_bootstrap = {
"epoc\0\0\0",
"EPOC streaming audio\0\0\0",
Audio_Available,
Audio_CreateDevice
};
static SDL_AudioDevice *Audio_CreateDevice(int /*devindex*/)
{
SDL_AudioDevice *thisdevice;
/* Initialize all variables that we clean on shutdown */
thisdevice = (SDL_AudioDevice *)malloc(sizeof(SDL_AudioDevice));
if ( thisdevice ) {
memset(thisdevice, 0, (sizeof *thisdevice));
thisdevice->hidden = NULL; /*(struct SDL_PrivateAudioData *)
malloc((sizeof thisdevice->hidden)); */
}
if ( (thisdevice == NULL) /*|| (thisdevice->hidden == NULL) */) {
SDL_OutOfMemory();
if ( thisdevice ) {
free(thisdevice);
}
return(0);
}
// memset(thisdevice->hidden, 0, (sizeof *thisdevice->hidden));
/* Set the function pointers */
thisdevice->OpenAudio = EPOC_OpenAudio;
thisdevice->WaitAudio = EPOC_WaitAudio;
thisdevice->PlayAudio = EPOC_PlayAudio;
thisdevice->GetAudioBuf = EPOC_GetAudioBuf;
thisdevice->CloseAudio = EPOC_CloseAudio;
thisdevice->ThreadInit = EPOC_ThreadInit;
thisdevice->free = Audio_DeleteDevice;
return thisdevice;
}
static void Audio_DeleteDevice(SDL_AudioDevice *device)
{
//free(device->hidden);
free(device);
}
static int Audio_Available(void)
{
return(1); // Audio stream modules should be always there!
}
static int EPOC_OpenAudio(SDL_AudioDevice *thisdevice, SDL_AudioSpec *spec)
{
SDL_TRACE("SDL:EPOC_OpenAudio");
TUint32 type = KMMFFourCCCodePCM16;
TInt bytes = 2;
switch(spec->format)
{
case AUDIO_U16LSB:
type = KMMFFourCCCodePCMU16;
break;
case AUDIO_S16LSB:
type = KMMFFourCCCodePCM16;
break;
case AUDIO_U16MSB:
type = KMMFFourCCCodePCMU16B;
break;
case AUDIO_S16MSB:
type = KMMFFourCCCodePCM16B;
break;
//8 bit not supported!
case AUDIO_U8:
case AUDIO_S8:
default:
spec->format = AUDIO_S16LSB;
};
if(spec->channels > 2)
spec->channels = 2;
spec->freq = CStreamPlayer::ClosestSupportedRate(spec->freq);
/* Allocate mixing buffer */
const TInt buflen = spec->size;// * bytes * spec->channels;
// audiobuf = NULL;
TRAPD(err, thisdevice->hidden = static_cast<SDL_PrivateAudioData*>(CEpocAudio::NewL(buflen, spec->silence)));
if(err != KErrNone)
return -1;
CEpocAudio::Current(thisdevice).Open(spec->freq, spec->channels, type, bytes);
CEpocAudio::Current(thisdevice).SetPause(ETrue);
// isSDLAudioPaused = 1;
thisdevice->enabled = 0; /* enable only after audio engine has been initialized!*/
/* We're ready to rock and roll. :-) */
return(0);
}
static void EPOC_CloseAudio(SDL_AudioDevice* thisdevice)
{
#ifdef DEBUG_AUDIO
SDL_TRACE("Close audio\n");
#endif
CEpocAudio::Free(thisdevice);
}
static void EPOC_ThreadInit(SDL_AudioDevice *thisdevice)
{
SDL_TRACE("SDL:EPOC_ThreadInit");
CEpocAudio::Current(thisdevice).ThreadInitL(thisdevice);
RThread().SetPriority(EPriorityMore);
thisdevice->enabled = 1;
}
/* This function waits until it is possible to write a full sound buffer */
static void EPOC_WaitAudio(SDL_AudioDevice* thisdevice)
{
#ifdef DEBUG_AUDIO
SDL_TRACE1("wait %d audio\n", CEpocAudio::AudioLib().StreamPlayer(KSfxChannel).SyncTime());
TInt tics = User::TickCount();
#endif
CEpocAudio::Current(thisdevice).Wait();
#ifdef DEBUG_AUDIO
TInt ntics = User::TickCount() - tics;
SDL_TRACE1("audio waited %d\n", ntics);
SDL_TRACE1("audio at %d\n", tics);
#endif
}
static void EPOC_PlayAudio(SDL_AudioDevice* thisdevice)
{
if(CEpocAudio::Current(thisdevice).SetPause(SDL_GetAudioStatus() == SDL_AUDIO_PAUSED))
SDL_Delay(500); //hold on the busy loop
else
CEpocAudio::Current(thisdevice).Play();
#ifdef DEBUG_AUDIO
SDL_TRACE("buffer has audio data\n");
#endif
#ifdef DEBUG_AUDIO
SDL_TRACE1("Wrote %d bytes of audio data\n", buflen);
#endif
}
static Uint8 *EPOC_GetAudioBuf(SDL_AudioDevice* thisdevice)
{
return CEpocAudio::Current(thisdevice).Buffer();
}