diff src/cdrom/macosx/AudioFileReaderThread.cpp @ 613:9c6717a1c66f

Added MacOS X CD-ROM audio support (thanks Max and Darrell)
author Sam Lantinga <slouken@libsdl.org>
date Tue, 15 Apr 2003 16:33:56 +0000
parents
children de1b2c3063b9
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/cdrom/macosx/AudioFileReaderThread.cpp	Tue Apr 15 16:33:56 2003 +0000
@@ -0,0 +1,419 @@
+/*
+    SDL - Simple DirectMedia Layer
+    Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002  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
+*/
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// AudioFileReaderThread.cpp
+//
+#include "AudioFilePlayer.h"
+#include <mach/mach.h> //used for setting policy of thread
+#include "CAGuard.h"
+#include <pthread.h>
+
+#include <list>
+
+class FileReaderThread {
+public:
+	FileReaderThread ();
+
+	CAGuard&					GetGuard() { return mGuard; }
+    
+	void						AddReader();
+	
+	void						RemoveReader (const AudioFileReaderThread* inItem);
+		
+		// returns true if succeeded
+	bool						TryNextRead (AudioFileReaderThread* inItem)
+	{
+		bool didLock = false;
+		bool succeeded = false;
+		if (mGuard.Try (didLock))
+		{
+			mFileData.push_back (inItem);
+			mGuard.Notify();
+			succeeded = true;
+
+			if (didLock)
+				mGuard.Unlock();
+		}
+				
+		return succeeded;
+	}	
+	
+    int		mThreadShouldDie;
+    
+private:
+	typedef	std::list<AudioFileReaderThread*> FileData;
+
+	CAGuard				mGuard;
+	UInt32				mThreadPriority;
+	
+	int					mNumReaders;	
+	FileData			mFileData;
+
+
+	void 						ReadNextChunk ();
+	
+	void 						StartFixedPriorityThread ();
+    static UInt32				GetThreadBasePriority (pthread_t inThread);
+    
+	static void*				DiskReaderEntry (void *inRefCon);
+};
+
+FileReaderThread::FileReaderThread ()
+	  : mThreadPriority (62),
+		mNumReaders (0)
+{
+}
+
+void	FileReaderThread::AddReader()
+{
+	if (mNumReaders == 0)
+	{
+		mThreadShouldDie = false;
+	
+		StartFixedPriorityThread ();
+	}
+	mNumReaders++;
+}
+
+void	FileReaderThread::RemoveReader (const AudioFileReaderThread* inItem)
+{
+	if (mNumReaders > 0)
+	{
+		CAGuard::Locker fileReadLock (mGuard);
+
+		for (FileData::iterator iter = mFileData.begin(); iter != mFileData.end(); ++iter)
+		{
+			if ((*iter) == inItem) {	
+				mFileData.erase (iter);
+			}
+		}
+		
+		if (--mNumReaders == 0) {
+			mThreadShouldDie = true;
+			mGuard.Notify(); // wake up thread so it will quit
+            mGuard.Wait();   // wait for thread to die
+		}
+	}	
+}
+
+void 	FileReaderThread::StartFixedPriorityThread ()
+{
+	pthread_attr_t		theThreadAttrs;
+	pthread_t			pThread;
+	
+	OSStatus result = pthread_attr_init(&theThreadAttrs);
+		THROW_RESULT("pthread_attr_init - Thread attributes could not be created.")
+	
+	result = pthread_attr_setdetachstate(&theThreadAttrs, PTHREAD_CREATE_DETACHED);
+		THROW_RESULT("pthread_attr_setdetachstate - Thread attributes could not be detached.")
+	
+	result = pthread_create (&pThread, &theThreadAttrs, DiskReaderEntry, this);
+		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 = false;	// set to true 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);
+        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 = mThreadPriority - FileReaderThread::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);
+        THROW_RESULT("thread_policy - Couldn't set thread priority.")
+}
+
+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;
+}
+
+void	*FileReaderThread::DiskReaderEntry (void *inRefCon)
+{
+	FileReaderThread *This = (FileReaderThread *)inRefCon;
+	This->ReadNextChunk();
+	#if DEBUG
+	printf ("finished with reading file\n");
+	#endif
+	
+	return 0;
+}
+
+void 	FileReaderThread::ReadNextChunk ()
+{
+	OSStatus result;
+	UInt32	dataChunkSize;
+	AudioFileReaderThread* theItem = 0;
+
+	for (;;) 
+	{
+		{ // this is a scoped based lock
+			CAGuard::Locker fileReadLock (mGuard);
+			
+			if (this->mThreadShouldDie) {
+            
+                mGuard.Notify();
+                return;
+            }
+			
+			if (mFileData.empty())
+			{
+				mGuard.Wait();
+			}
+			            
+			// kill thread
+			if (this->mThreadShouldDie) {
+            
+                mGuard.Notify();
+                return;
+            }
+
+			theItem = mFileData.front();
+			mFileData.pop_front();
+		}
+	
+		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 = true;
+			continue;
+		}
+			// construct pointer
+		char* writePtr = const_cast<char*>(theItem->GetFileBuffer() + 
+								(theItem->mWriteToFirstBuffer ? 0 : theItem->mChunkSize));
+	
+/*
+        printf ("AudioFileReadBytes: theItem=%.8X fileID=%.8X pos=%.8X sz=%.8X flen=%.8X ptr=%.8X\n", 
+            (unsigned int)theItem, (unsigned int)theItem->GetFileID(),
+            (unsigned int)theItem->mReadFilePosition, (unsigned int)dataChunkSize, 
+            (unsigned int)theItem->mFileLength, (unsigned int)writePtr);
+*/            
+		result = AudioFileReadBytes (theItem->GetFileID(), 
+									false,
+									theItem->mReadFilePosition, 
+									&dataChunkSize, 
+									writePtr);
+		if (result) {
+			theItem->GetParent().DoNotification(result);
+			continue;
+		}
+		
+		if (dataChunkSize != theItem->mChunkSize)
+		{
+			writePtr += dataChunkSize;
+
+            // can't exit yet.. we still have to pass the partial buffer back
+            memset (writePtr, 0, (theItem->mChunkSize - dataChunkSize));
+        }
+		
+		theItem->mWriteToFirstBuffer = !theItem->mWriteToFirstBuffer;	// switch buffers
+		
+		theItem->mReadFilePosition += dataChunkSize;		// increment count
+	}
+}
+
+
+static FileReaderThread sReaderThread;
+
+AudioFileReaderThread::AudioFileReaderThread (AudioFilePlayer	&inParent, 
+										AudioFileID 			&inFile, 
+										SInt64 					inFileLength,
+										UInt32					inChunkSize)
+	: AudioFileManager (inParent, inFile),
+	  mChunkSize (inChunkSize),
+	  mFileLength (inFileLength),
+	  mReadFilePosition (0),
+	  mWriteToFirstBuffer (false),
+	  mFinishedReadingData (false),
+
+	  mLockUnsuccessful (false),
+	  mIsEngaged (false)
+{
+	mFileBuffer = (char*) malloc (mChunkSize * 2);
+    assert (mFileBuffer != NULL);
+}
+
+void	AudioFileReaderThread::DoConnect ()
+{
+	if (!mIsEngaged)
+	{
+		//mReadFilePosition = 0;
+		mFinishedReadingData = false;
+
+		mNumTimesAskedSinceFinished = -1;
+		mLockUnsuccessful = false;
+		
+		UInt32 dataChunkSize;
+        
+        if ((mFileLength - mReadFilePosition) < mChunkSize)
+			dataChunkSize = mFileLength - mReadFilePosition;
+		else
+			dataChunkSize = mChunkSize;
+        
+		OSStatus result = AudioFileReadBytes ( mAudioFileID, 
+												false, 
+												mReadFilePosition, 
+												&dataChunkSize, 
+												mFileBuffer);
+            THROW_RESULT("AudioFileReadBytes")
+            
+		mReadFilePosition += dataChunkSize;
+				
+		mWriteToFirstBuffer = false;
+		mReadFromFirstBuffer = true;
+
+		sReaderThread.AddReader();
+		
+		mIsEngaged = true;
+	}
+	else
+		throw static_cast<OSStatus>(-1); //thread has already been started
+}
+
+void	AudioFileReaderThread::Disconnect ()
+{
+	if (mIsEngaged) 
+	{
+		sReaderThread.RemoveReader (this);
+		mIsEngaged = false;
+	}
+}
+
+OSStatus AudioFileReaderThread::GetFileData (void** inOutData, UInt32 *inOutDataSize)
+{
+	if (mFinishedReadingData) 
+	{
+		++mNumTimesAskedSinceFinished;
+		*inOutDataSize = 0;
+		*inOutData = 0;
+		return noErr;
+	}
+	
+	if (mReadFromFirstBuffer == mWriteToFirstBuffer) {
+		#if DEBUG
+		printf ("* * * * * * * Can't keep up with reading file:%ld\n", mParent.GetBusNumber());
+		#endif
+		
+		mParent.DoNotification (kAudioFilePlayErr_FilePlayUnderrun);
+		*inOutDataSize = 0;
+		*inOutData = 0;
+	} else {
+		*inOutDataSize = mChunkSize;
+		*inOutData = mReadFromFirstBuffer ? mFileBuffer : (mFileBuffer + mChunkSize);
+	}
+
+	mLockUnsuccessful = !sReaderThread.TryNextRead (this);
+	
+	mReadFromFirstBuffer = !mReadFromFirstBuffer;
+
+	return noErr;
+}
+
+void 	AudioFileReaderThread::AfterRender ()
+{
+	if (mNumTimesAskedSinceFinished > 0)
+	{
+		bool didLock = false;
+		if (sReaderThread.GetGuard().Try (didLock)) {
+			mParent.DoNotification (kAudioFilePlay_FileIsFinished);
+			if (didLock)
+				sReaderThread.GetGuard().Unlock();
+		}
+	}
+
+	if (mLockUnsuccessful)
+		mLockUnsuccessful = !sReaderThread.TryNextRead (this);
+}
+
+void 	AudioFileReaderThread::SetPosition (SInt64 pos)
+{
+    if (pos < 0 || pos >= mFileLength) {
+        SDL_SetError ("AudioFileReaderThread::SetPosition - position invalid: %d filelen=%d\n", 
+            (unsigned int)pos, (unsigned int)mFileLength);
+        pos = 0;
+    }
+        
+    mReadFilePosition = pos;
+}
+    
+void 	AudioFileReaderThread::SetEndOfFile (SInt64 pos)
+{
+    if (pos <= 0 || pos > mFileLength) {
+        SDL_SetError ("AudioFileReaderThread::SetEndOfFile - position beyond actual eof\n");
+        pos = mFileLength;
+    }
+    
+    mFileLength = pos;
+}
\ No newline at end of file