SDL-mirror/src/cdrom/macosx/AudioFileReaderThread.c
Sam Lantinga 0c30a927ed Updated copyright date
--HG--
extra : convert_revision : svn%3Ac70aab31-4412-0410-b14c-859654838e24/trunk%403321
2008-12-08 00:27:32 +00:00

657 lines
19 KiB
C

/*
SDL - Simple DirectMedia Layer
Copyright (C) 1997-2009 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@libsdl.org
This file based on Apple sample code. We haven't changed the file name,
so if you want to see the original search for it on apple.com/developer
*/
#include "SDL_config.h"
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
AudioFileManager.cpp
*/
#include "AudioFilePlayer.h"
#include <mach/mach.h> /* used for setting policy of thread */
#include "SDLOSXCAGuard.h"
#include <pthread.h>
/*#include <list>*/
/*typedef void *FileData;*/
typedef struct S_FileData
{
AudioFileManager *obj;
struct S_FileData *next;
} FileData;
typedef struct S_FileReaderThread
{
/*public:*/
SDLOSXCAGuard *(*GetGuard) (struct S_FileReaderThread * frt);
void (*AddReader) (struct S_FileReaderThread * frt);
void (*RemoveReader) (struct S_FileReaderThread * frt,
AudioFileManager * inItem);
int (*TryNextRead) (struct S_FileReaderThread * frt,
AudioFileManager * inItem);
int mThreadShouldDie;
/*private:*/
/*typedef std::list<AudioFileManager*> FileData; */
SDLOSXCAGuard *mGuard;
UInt32 mThreadPriority;
int mNumReaders;
FileData *mFileData;
void (*ReadNextChunk) (struct S_FileReaderThread * frt);
int (*StartFixedPriorityThread) (struct S_FileReaderThread * frt);
/*static */
UInt32(*GetThreadBasePriority) (pthread_t inThread);
/*static */
void *(*DiskReaderEntry) (void *inRefCon);
} FileReaderThread;
static SDLOSXCAGuard *
FileReaderThread_GetGuard(FileReaderThread * frt)
{
return frt->mGuard;
}
/* returns 1 if succeeded */
static int
FileReaderThread_TryNextRead(FileReaderThread * frt,
AudioFileManager * inItem)
{
int didLock = 0;
int succeeded = 0;
if (frt->mGuard->Try(frt->mGuard, &didLock)) {
/*frt->mFileData.push_back (inItem); */
/* !!! FIXME: this could be faster with a "tail" member. --ryan. */
FileData *i = frt->mFileData;
FileData *prev = NULL;
FileData *newfd = (FileData *) SDL_malloc(sizeof(FileData));
newfd->obj = inItem;
newfd->next = NULL;
while (i != NULL) {
prev = i;
i = i->next;
}
if (prev == NULL)
frt->mFileData = newfd;
else
prev->next = newfd;
frt->mGuard->Notify(frt->mGuard);
succeeded = 1;
if (didLock)
frt->mGuard->Unlock(frt->mGuard);
}
return succeeded;
}
static void
FileReaderThread_AddReader(FileReaderThread * frt)
{
if (frt->mNumReaders == 0) {
frt->mThreadShouldDie = 0;
frt->StartFixedPriorityThread(frt);
}
frt->mNumReaders++;
}
static void
FileReaderThread_RemoveReader(FileReaderThread * frt,
AudioFileManager * inItem)
{
if (frt->mNumReaders > 0) {
int bNeedsRelease = frt->mGuard->Lock(frt->mGuard);
/*frt->mFileData.remove (inItem); */
FileData *i = frt->mFileData;
FileData *prev = NULL;
while (i != NULL) {
FileData *next = i->next;
if (i->obj != inItem)
prev = i;
else {
if (prev == NULL)
frt->mFileData = next;
else
prev->next = next;
SDL_free(i);
}
i = next;
}
if (--frt->mNumReaders == 0) {
frt->mThreadShouldDie = 1;
frt->mGuard->Notify(frt->mGuard); /* wake up thread so it will quit */
frt->mGuard->Wait(frt->mGuard); /* wait for thread to die */
}
if (bNeedsRelease)
frt->mGuard->Unlock(frt->mGuard);
}
}
static int
FileReaderThread_StartFixedPriorityThread(FileReaderThread * frt)
{
pthread_attr_t theThreadAttrs;
pthread_t pThread;
OSStatus result = pthread_attr_init(&theThreadAttrs);
if (result)
return 0; /*THROW_RESULT("pthread_attr_init - Thread attributes could not be created.") */
result =
pthread_attr_setdetachstate(&theThreadAttrs, PTHREAD_CREATE_DETACHED);
if (result)
return 0; /*THROW_RESULT("pthread_attr_setdetachstate - Thread attributes could not be detached.") */
result =
pthread_create(&pThread, &theThreadAttrs, frt->DiskReaderEntry, frt);
if (result)
return 0; /*THROW_RESULT("pthread_create - Create and start the thread.") */
pthread_attr_destroy(&theThreadAttrs);
/* we've now created the thread and started it
we'll now set the priority of the thread to the nominated priority
and we'll also make the thread fixed */
thread_extended_policy_data_t theFixedPolicy;
thread_precedence_policy_data_t thePrecedencePolicy;
SInt32 relativePriority;
/* make thread fixed */
theFixedPolicy.timeshare = 0; /* set to 1 for a non-fixed thread */
result =
thread_policy_set(pthread_mach_thread_np(pThread),
THREAD_EXTENDED_POLICY,
(thread_policy_t) & theFixedPolicy,
THREAD_EXTENDED_POLICY_COUNT);
if (result)
return 0; /*THROW_RESULT("thread_policy - Couldn't set thread as fixed priority.") */
/* set priority */
/* precedency policy's "importance" value is relative to spawning thread's priority */
relativePriority =
frt->mThreadPriority - frt->GetThreadBasePriority(pthread_self());
thePrecedencePolicy.importance = relativePriority;
result =
thread_policy_set(pthread_mach_thread_np(pThread),
THREAD_PRECEDENCE_POLICY,
(thread_policy_t) & thePrecedencePolicy,
THREAD_PRECEDENCE_POLICY_COUNT);
if (result)
return 0; /*THROW_RESULT("thread_policy - Couldn't set thread priority.") */
return 1;
}
static UInt32
FileReaderThread_GetThreadBasePriority(pthread_t inThread)
{
thread_basic_info_data_t threadInfo;
policy_info_data_t thePolicyInfo;
unsigned int count;
/* get basic info */
count = THREAD_BASIC_INFO_COUNT;
thread_info(pthread_mach_thread_np(inThread), THREAD_BASIC_INFO,
(integer_t *) & threadInfo, &count);
switch (threadInfo.policy) {
case POLICY_TIMESHARE:
count = POLICY_TIMESHARE_INFO_COUNT;
thread_info(pthread_mach_thread_np(inThread),
THREAD_SCHED_TIMESHARE_INFO,
(integer_t *) & (thePolicyInfo.ts), &count);
return thePolicyInfo.ts.base_priority;
break;
case POLICY_FIFO:
count = POLICY_FIFO_INFO_COUNT;
thread_info(pthread_mach_thread_np(inThread),
THREAD_SCHED_FIFO_INFO,
(integer_t *) & (thePolicyInfo.fifo), &count);
if (thePolicyInfo.fifo.depressed) {
return thePolicyInfo.fifo.depress_priority;
} else {
return thePolicyInfo.fifo.base_priority;
}
break;
case POLICY_RR:
count = POLICY_RR_INFO_COUNT;
thread_info(pthread_mach_thread_np(inThread),
THREAD_SCHED_RR_INFO,
(integer_t *) & (thePolicyInfo.rr), &count);
if (thePolicyInfo.rr.depressed) {
return thePolicyInfo.rr.depress_priority;
} else {
return thePolicyInfo.rr.base_priority;
}
break;
}
return 0;
}
static void *
FileReaderThread_DiskReaderEntry(void *inRefCon)
{
FileReaderThread *frt = (FileReaderThread *) inRefCon;
frt->ReadNextChunk(frt);
#if DEBUG
printf("finished with reading file\n");
#endif
return 0;
}
static void
FileReaderThread_ReadNextChunk(FileReaderThread * frt)
{
OSStatus result;
UInt32 dataChunkSize;
AudioFileManager *theItem = 0;
for (;;) {
{ /* this is a scoped based lock */
int bNeedsRelease = frt->mGuard->Lock(frt->mGuard);
if (frt->mThreadShouldDie) {
frt->mGuard->Notify(frt->mGuard);
if (bNeedsRelease)
frt->mGuard->Unlock(frt->mGuard);
return;
}
/*if (frt->mFileData.empty()) */
if (frt->mFileData == NULL) {
frt->mGuard->Wait(frt->mGuard);
}
/* kill thread */
if (frt->mThreadShouldDie) {
frt->mGuard->Notify(frt->mGuard);
if (bNeedsRelease)
frt->mGuard->Unlock(frt->mGuard);
return;
}
/*theItem = frt->mFileData.front(); */
/*frt->mFileData.pop_front(); */
theItem = NULL;
if (frt->mFileData != NULL) {
FileData *next = frt->mFileData->next;
theItem = frt->mFileData->obj;
SDL_free(frt->mFileData);
frt->mFileData = next;
}
if (bNeedsRelease)
frt->mGuard->Unlock(frt->mGuard);
}
if ((theItem->mFileLength - theItem->mReadFilePosition) <
theItem->mChunkSize)
dataChunkSize = theItem->mFileLength - theItem->mReadFilePosition;
else
dataChunkSize = theItem->mChunkSize;
/* this is the exit condition for the thread */
if (dataChunkSize <= 0) {
theItem->mFinishedReadingData = 1;
continue;
}
/* construct pointer */
char *writePtr = (char *) (theItem->GetFileBuffer(theItem) +
(theItem->mWriteToFirstBuffer ? 0 :
theItem->mChunkSize));
/* read data */
result = theItem->Read(theItem, writePtr, &dataChunkSize);
if (result != noErr && result != eofErr) {
AudioFilePlayer *afp =
(AudioFilePlayer *) theItem->GetParent(theItem);
afp->DoNotification(afp, result);
continue;
}
if (dataChunkSize != theItem->mChunkSize) {
writePtr += dataChunkSize;
/* can't exit yet.. we still have to pass the partial buffer back */
SDL_memset(writePtr, 0, (theItem->mChunkSize - dataChunkSize));
}
theItem->mWriteToFirstBuffer = !theItem->mWriteToFirstBuffer; /* switch buffers */
if (result == eofErr)
theItem->mReadFilePosition = theItem->mFileLength;
else
theItem->mReadFilePosition += dataChunkSize; /* increment count */
}
}
void
delete_FileReaderThread(FileReaderThread * frt)
{
if (frt != NULL) {
delete_SDLOSXCAGuard(frt->mGuard);
SDL_free(frt);
}
}
FileReaderThread *
new_FileReaderThread()
{
FileReaderThread *frt =
(FileReaderThread *) SDL_malloc(sizeof(FileReaderThread));
if (frt == NULL)
return NULL;
SDL_memset(frt, '\0', sizeof(*frt));
frt->mGuard = new_SDLOSXCAGuard();
if (frt->mGuard == NULL) {
SDL_free(frt);
return NULL;
}
#define SET_FILEREADERTHREAD_METHOD(m) frt->m = FileReaderThread_##m
SET_FILEREADERTHREAD_METHOD(GetGuard);
SET_FILEREADERTHREAD_METHOD(AddReader);
SET_FILEREADERTHREAD_METHOD(RemoveReader);
SET_FILEREADERTHREAD_METHOD(TryNextRead);
SET_FILEREADERTHREAD_METHOD(ReadNextChunk);
SET_FILEREADERTHREAD_METHOD(StartFixedPriorityThread);
SET_FILEREADERTHREAD_METHOD(GetThreadBasePriority);
SET_FILEREADERTHREAD_METHOD(DiskReaderEntry);
#undef SET_FILEREADERTHREAD_METHOD
frt->mThreadPriority = 62;
return frt;
}
static FileReaderThread *sReaderThread;
static int
AudioFileManager_DoConnect(AudioFileManager * afm)
{
if (!afm->mIsEngaged) {
OSStatus result;
/*afm->mReadFilePosition = 0; */
afm->mFinishedReadingData = 0;
afm->mNumTimesAskedSinceFinished = 0;
afm->mLockUnsuccessful = 0;
UInt32 dataChunkSize;
if ((afm->mFileLength - afm->mReadFilePosition) < afm->mChunkSize)
dataChunkSize = afm->mFileLength - afm->mReadFilePosition;
else
dataChunkSize = afm->mChunkSize;
result = afm->Read(afm, afm->mFileBuffer, &dataChunkSize);
if (result)
return 0; /*THROW_RESULT("AudioFileManager::DoConnect(): Read") */
afm->mReadFilePosition += dataChunkSize;
afm->mWriteToFirstBuffer = 0;
afm->mReadFromFirstBuffer = 1;
sReaderThread->AddReader(sReaderThread);
afm->mIsEngaged = 1;
}
/*
else
throw static_cast<OSStatus>(-1); *//* thread has already been started */
return 1;
}
static void
AudioFileManager_Disconnect(AudioFileManager * afm)
{
if (afm->mIsEngaged) {
sReaderThread->RemoveReader(sReaderThread, afm);
afm->mIsEngaged = 0;
}
}
static OSStatus
AudioFileManager_Read(AudioFileManager * afm, char *buffer, UInt32 * len)
{
return FSReadFork(afm->mForkRefNum,
fsFromStart,
afm->mReadFilePosition + afm->mAudioDataOffset,
*len, buffer, len);
}
static OSStatus
AudioFileManager_GetFileData(AudioFileManager * afm, void **inOutData,
UInt32 * inOutDataSize)
{
if (afm->mFinishedReadingData) {
++afm->mNumTimesAskedSinceFinished;
*inOutDataSize = 0;
*inOutData = 0;
return noErr;
}
if (afm->mReadFromFirstBuffer == afm->mWriteToFirstBuffer) {
#if DEBUG
printf("* * * * * * * Can't keep up with reading file\n");
#endif
afm->mParent->DoNotification(afm->mParent,
kAudioFilePlayErr_FilePlayUnderrun);
*inOutDataSize = 0;
*inOutData = 0;
} else {
*inOutDataSize = afm->mChunkSize;
*inOutData =
afm->mReadFromFirstBuffer ? afm->mFileBuffer : (afm->mFileBuffer +
afm->mChunkSize);
}
afm->mLockUnsuccessful = !sReaderThread->TryNextRead(sReaderThread, afm);
afm->mReadFromFirstBuffer = !afm->mReadFromFirstBuffer;
return noErr;
}
static void
AudioFileManager_AfterRender(AudioFileManager * afm)
{
if (afm->mNumTimesAskedSinceFinished > 0) {
int didLock = 0;
SDLOSXCAGuard *guard = sReaderThread->GetGuard(sReaderThread);
if (guard->Try(guard, &didLock)) {
afm->mParent->DoNotification(afm->mParent,
kAudioFilePlay_FileIsFinished);
if (didLock)
guard->Unlock(guard);
}
}
if (afm->mLockUnsuccessful)
afm->mLockUnsuccessful =
!sReaderThread->TryNextRead(sReaderThread, afm);
}
static void
AudioFileManager_SetPosition(AudioFileManager * afm, SInt64 pos)
{
if (pos < 0 || pos >= afm->mFileLength) {
SDL_SetError
("AudioFileManager::SetPosition - position invalid: %d filelen=%d\n",
(unsigned int) pos, (unsigned int) afm->mFileLength);
pos = 0;
}
afm->mReadFilePosition = pos;
}
static void
AudioFileManager_SetEndOfFile(AudioFileManager * afm, SInt64 pos)
{
if (pos <= 0 || pos > afm->mFileLength) {
SDL_SetError
("AudioFileManager::SetEndOfFile - position beyond actual eof\n");
pos = afm->mFileLength;
}
afm->mFileLength = pos;
}
static const char *
AudioFileManager_GetFileBuffer(AudioFileManager * afm)
{
return afm->mFileBuffer;
}
const AudioFilePlayer *
AudioFileManager_GetParent(AudioFileManager * afm)
{
return afm->mParent;
}
static int
AudioFileManager_GetByteCounter(AudioFileManager * afm)
{
return afm->mByteCounter;
}
static OSStatus
AudioFileManager_FileInputProc(void *inRefCon,
AudioUnitRenderActionFlags inActionFlags,
const AudioTimeStamp * inTimeStamp,
UInt32 inBusNumber, AudioBuffer * ioData)
{
AudioFileManager *afm = (AudioFileManager *) inRefCon;
return afm->Render(afm, ioData);
}
static OSStatus
AudioFileManager_Render(AudioFileManager * afm, AudioBuffer * ioData)
{
OSStatus result = noErr;
if (afm->mBufferOffset >= afm->mBufferSize) {
result = afm->GetFileData(afm, &afm->mTmpBuffer, &afm->mBufferSize);
if (result) {
SDL_SetError("AudioConverterFillBuffer:%ld\n", result);
afm->mParent->DoNotification(afm->mParent, result);
return result;
}
afm->mBufferOffset = 0;
}
if (ioData->mDataByteSize > afm->mBufferSize - afm->mBufferOffset)
ioData->mDataByteSize = afm->mBufferSize - afm->mBufferOffset;
ioData->mData = (char *) afm->mTmpBuffer + afm->mBufferOffset;
afm->mBufferOffset += ioData->mDataByteSize;
afm->mByteCounter += ioData->mDataByteSize;
afm->AfterRender(afm);
return result;
}
void
delete_AudioFileManager(AudioFileManager * afm)
{
if (afm != NULL) {
if (afm->mFileBuffer) {
free(afm->mFileBuffer);
}
SDL_free(afm);
}
}
AudioFileManager *
new_AudioFileManager(AudioFilePlayer * inParent,
SInt16 inForkRefNum,
SInt64 inFileLength, UInt32 inChunkSize)
{
AudioFileManager *afm;
if (sReaderThread == NULL) {
sReaderThread = new_FileReaderThread();
if (sReaderThread == NULL)
return NULL;
}
afm = (AudioFileManager *) SDL_malloc(sizeof(AudioFileManager));
if (afm == NULL)
return NULL;
SDL_memset(afm, '\0', sizeof(*afm));
#define SET_AUDIOFILEMANAGER_METHOD(m) afm->m = AudioFileManager_##m
SET_AUDIOFILEMANAGER_METHOD(Disconnect);
SET_AUDIOFILEMANAGER_METHOD(DoConnect);
SET_AUDIOFILEMANAGER_METHOD(Read);
SET_AUDIOFILEMANAGER_METHOD(GetFileBuffer);
SET_AUDIOFILEMANAGER_METHOD(GetParent);
SET_AUDIOFILEMANAGER_METHOD(SetPosition);
SET_AUDIOFILEMANAGER_METHOD(GetByteCounter);
SET_AUDIOFILEMANAGER_METHOD(SetEndOfFile);
SET_AUDIOFILEMANAGER_METHOD(Render);
SET_AUDIOFILEMANAGER_METHOD(GetFileData);
SET_AUDIOFILEMANAGER_METHOD(AfterRender);
SET_AUDIOFILEMANAGER_METHOD(FileInputProc);
#undef SET_AUDIOFILEMANAGER_METHOD
afm->mParent = inParent;
afm->mForkRefNum = inForkRefNum;
afm->mBufferSize = inChunkSize;
afm->mBufferOffset = inChunkSize;
afm->mChunkSize = inChunkSize;
afm->mFileLength = inFileLength;
afm->mFileBuffer = (char *) SDL_malloc(afm->mChunkSize * 2);
FSGetForkPosition(afm->mForkRefNum, &afm->mAudioDataOffset);
assert(afm->mFileBuffer != NULL);
return afm;
}
/* vi: set ts=4 sw=4 expandtab: */