comparison 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
comparison
equal deleted inserted replaced
612:0648505b1f8b 613:9c6717a1c66f
1 /*
2 SDL - Simple DirectMedia Layer
3 Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002 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@libsdl.org
21
22 This file based on Apple sample code. We haven't changed the file name,
23 so if you want to see the original search for it on apple.com/developer
24 */
25
26 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
27 // AudioFileReaderThread.cpp
28 //
29 #include "AudioFilePlayer.h"
30 #include <mach/mach.h> //used for setting policy of thread
31 #include "CAGuard.h"
32 #include <pthread.h>
33
34 #include <list>
35
36 class FileReaderThread {
37 public:
38 FileReaderThread ();
39
40 CAGuard& GetGuard() { return mGuard; }
41
42 void AddReader();
43
44 void RemoveReader (const AudioFileReaderThread* inItem);
45
46 // returns true if succeeded
47 bool TryNextRead (AudioFileReaderThread* inItem)
48 {
49 bool didLock = false;
50 bool succeeded = false;
51 if (mGuard.Try (didLock))
52 {
53 mFileData.push_back (inItem);
54 mGuard.Notify();
55 succeeded = true;
56
57 if (didLock)
58 mGuard.Unlock();
59 }
60
61 return succeeded;
62 }
63
64 int mThreadShouldDie;
65
66 private:
67 typedef std::list<AudioFileReaderThread*> FileData;
68
69 CAGuard mGuard;
70 UInt32 mThreadPriority;
71
72 int mNumReaders;
73 FileData mFileData;
74
75
76 void ReadNextChunk ();
77
78 void StartFixedPriorityThread ();
79 static UInt32 GetThreadBasePriority (pthread_t inThread);
80
81 static void* DiskReaderEntry (void *inRefCon);
82 };
83
84 FileReaderThread::FileReaderThread ()
85 : mThreadPriority (62),
86 mNumReaders (0)
87 {
88 }
89
90 void FileReaderThread::AddReader()
91 {
92 if (mNumReaders == 0)
93 {
94 mThreadShouldDie = false;
95
96 StartFixedPriorityThread ();
97 }
98 mNumReaders++;
99 }
100
101 void FileReaderThread::RemoveReader (const AudioFileReaderThread* inItem)
102 {
103 if (mNumReaders > 0)
104 {
105 CAGuard::Locker fileReadLock (mGuard);
106
107 for (FileData::iterator iter = mFileData.begin(); iter != mFileData.end(); ++iter)
108 {
109 if ((*iter) == inItem) {
110 mFileData.erase (iter);
111 }
112 }
113
114 if (--mNumReaders == 0) {
115 mThreadShouldDie = true;
116 mGuard.Notify(); // wake up thread so it will quit
117 mGuard.Wait(); // wait for thread to die
118 }
119 }
120 }
121
122 void FileReaderThread::StartFixedPriorityThread ()
123 {
124 pthread_attr_t theThreadAttrs;
125 pthread_t pThread;
126
127 OSStatus result = pthread_attr_init(&theThreadAttrs);
128 THROW_RESULT("pthread_attr_init - Thread attributes could not be created.")
129
130 result = pthread_attr_setdetachstate(&theThreadAttrs, PTHREAD_CREATE_DETACHED);
131 THROW_RESULT("pthread_attr_setdetachstate - Thread attributes could not be detached.")
132
133 result = pthread_create (&pThread, &theThreadAttrs, DiskReaderEntry, this);
134 THROW_RESULT("pthread_create - Create and start the thread.")
135
136 pthread_attr_destroy(&theThreadAttrs);
137
138 // we've now created the thread and started it
139 // we'll now set the priority of the thread to the nominated priority
140 // and we'll also make the thread fixed
141 thread_extended_policy_data_t theFixedPolicy;
142 thread_precedence_policy_data_t thePrecedencePolicy;
143 SInt32 relativePriority;
144
145 // make thread fixed
146 theFixedPolicy.timeshare = false; // set to true for a non-fixed thread
147 result = thread_policy_set (pthread_mach_thread_np(pThread), THREAD_EXTENDED_POLICY, (thread_policy_t)&theFixedPolicy, THREAD_EXTENDED_POLICY_COUNT);
148 THROW_RESULT("thread_policy - Couldn't set thread as fixed priority.")
149 // set priority
150 // precedency policy's "importance" value is relative to spawning thread's priority
151 relativePriority = mThreadPriority - FileReaderThread::GetThreadBasePriority (pthread_self());
152
153 thePrecedencePolicy.importance = relativePriority;
154 result = thread_policy_set (pthread_mach_thread_np(pThread), THREAD_PRECEDENCE_POLICY, (thread_policy_t)&thePrecedencePolicy, THREAD_PRECEDENCE_POLICY_COUNT);
155 THROW_RESULT("thread_policy - Couldn't set thread priority.")
156 }
157
158 UInt32 FileReaderThread::GetThreadBasePriority (pthread_t inThread)
159 {
160 thread_basic_info_data_t threadInfo;
161 policy_info_data_t thePolicyInfo;
162 unsigned int count;
163
164 // get basic info
165 count = THREAD_BASIC_INFO_COUNT;
166 thread_info (pthread_mach_thread_np (inThread), THREAD_BASIC_INFO, (integer_t*)&threadInfo, &count);
167
168 switch (threadInfo.policy) {
169 case POLICY_TIMESHARE:
170 count = POLICY_TIMESHARE_INFO_COUNT;
171 thread_info(pthread_mach_thread_np (inThread), THREAD_SCHED_TIMESHARE_INFO, (integer_t*)&(thePolicyInfo.ts), &count);
172 return thePolicyInfo.ts.base_priority;
173 break;
174
175 case POLICY_FIFO:
176 count = POLICY_FIFO_INFO_COUNT;
177 thread_info(pthread_mach_thread_np (inThread), THREAD_SCHED_FIFO_INFO, (integer_t*)&(thePolicyInfo.fifo), &count);
178 if (thePolicyInfo.fifo.depressed) {
179 return thePolicyInfo.fifo.depress_priority;
180 } else {
181 return thePolicyInfo.fifo.base_priority;
182 }
183 break;
184
185 case POLICY_RR:
186 count = POLICY_RR_INFO_COUNT;
187 thread_info(pthread_mach_thread_np (inThread), THREAD_SCHED_RR_INFO, (integer_t*)&(thePolicyInfo.rr), &count);
188 if (thePolicyInfo.rr.depressed) {
189 return thePolicyInfo.rr.depress_priority;
190 } else {
191 return thePolicyInfo.rr.base_priority;
192 }
193 break;
194 }
195
196 return 0;
197 }
198
199 void *FileReaderThread::DiskReaderEntry (void *inRefCon)
200 {
201 FileReaderThread *This = (FileReaderThread *)inRefCon;
202 This->ReadNextChunk();
203 #if DEBUG
204 printf ("finished with reading file\n");
205 #endif
206
207 return 0;
208 }
209
210 void FileReaderThread::ReadNextChunk ()
211 {
212 OSStatus result;
213 UInt32 dataChunkSize;
214 AudioFileReaderThread* theItem = 0;
215
216 for (;;)
217 {
218 { // this is a scoped based lock
219 CAGuard::Locker fileReadLock (mGuard);
220
221 if (this->mThreadShouldDie) {
222
223 mGuard.Notify();
224 return;
225 }
226
227 if (mFileData.empty())
228 {
229 mGuard.Wait();
230 }
231
232 // kill thread
233 if (this->mThreadShouldDie) {
234
235 mGuard.Notify();
236 return;
237 }
238
239 theItem = mFileData.front();
240 mFileData.pop_front();
241 }
242
243 if ((theItem->mFileLength - theItem->mReadFilePosition) < theItem->mChunkSize)
244 dataChunkSize = theItem->mFileLength - theItem->mReadFilePosition;
245 else
246 dataChunkSize = theItem->mChunkSize;
247
248 // this is the exit condition for the thread
249 if (dataChunkSize == 0) {
250 theItem->mFinishedReadingData = true;
251 continue;
252 }
253 // construct pointer
254 char* writePtr = const_cast<char*>(theItem->GetFileBuffer() +
255 (theItem->mWriteToFirstBuffer ? 0 : theItem->mChunkSize));
256
257 /*
258 printf ("AudioFileReadBytes: theItem=%.8X fileID=%.8X pos=%.8X sz=%.8X flen=%.8X ptr=%.8X\n",
259 (unsigned int)theItem, (unsigned int)theItem->GetFileID(),
260 (unsigned int)theItem->mReadFilePosition, (unsigned int)dataChunkSize,
261 (unsigned int)theItem->mFileLength, (unsigned int)writePtr);
262 */
263 result = AudioFileReadBytes (theItem->GetFileID(),
264 false,
265 theItem->mReadFilePosition,
266 &dataChunkSize,
267 writePtr);
268 if (result) {
269 theItem->GetParent().DoNotification(result);
270 continue;
271 }
272
273 if (dataChunkSize != theItem->mChunkSize)
274 {
275 writePtr += dataChunkSize;
276
277 // can't exit yet.. we still have to pass the partial buffer back
278 memset (writePtr, 0, (theItem->mChunkSize - dataChunkSize));
279 }
280
281 theItem->mWriteToFirstBuffer = !theItem->mWriteToFirstBuffer; // switch buffers
282
283 theItem->mReadFilePosition += dataChunkSize; // increment count
284 }
285 }
286
287
288 static FileReaderThread sReaderThread;
289
290 AudioFileReaderThread::AudioFileReaderThread (AudioFilePlayer &inParent,
291 AudioFileID &inFile,
292 SInt64 inFileLength,
293 UInt32 inChunkSize)
294 : AudioFileManager (inParent, inFile),
295 mChunkSize (inChunkSize),
296 mFileLength (inFileLength),
297 mReadFilePosition (0),
298 mWriteToFirstBuffer (false),
299 mFinishedReadingData (false),
300
301 mLockUnsuccessful (false),
302 mIsEngaged (false)
303 {
304 mFileBuffer = (char*) malloc (mChunkSize * 2);
305 assert (mFileBuffer != NULL);
306 }
307
308 void AudioFileReaderThread::DoConnect ()
309 {
310 if (!mIsEngaged)
311 {
312 //mReadFilePosition = 0;
313 mFinishedReadingData = false;
314
315 mNumTimesAskedSinceFinished = -1;
316 mLockUnsuccessful = false;
317
318 UInt32 dataChunkSize;
319
320 if ((mFileLength - mReadFilePosition) < mChunkSize)
321 dataChunkSize = mFileLength - mReadFilePosition;
322 else
323 dataChunkSize = mChunkSize;
324
325 OSStatus result = AudioFileReadBytes ( mAudioFileID,
326 false,
327 mReadFilePosition,
328 &dataChunkSize,
329 mFileBuffer);
330 THROW_RESULT("AudioFileReadBytes")
331
332 mReadFilePosition += dataChunkSize;
333
334 mWriteToFirstBuffer = false;
335 mReadFromFirstBuffer = true;
336
337 sReaderThread.AddReader();
338
339 mIsEngaged = true;
340 }
341 else
342 throw static_cast<OSStatus>(-1); //thread has already been started
343 }
344
345 void AudioFileReaderThread::Disconnect ()
346 {
347 if (mIsEngaged)
348 {
349 sReaderThread.RemoveReader (this);
350 mIsEngaged = false;
351 }
352 }
353
354 OSStatus AudioFileReaderThread::GetFileData (void** inOutData, UInt32 *inOutDataSize)
355 {
356 if (mFinishedReadingData)
357 {
358 ++mNumTimesAskedSinceFinished;
359 *inOutDataSize = 0;
360 *inOutData = 0;
361 return noErr;
362 }
363
364 if (mReadFromFirstBuffer == mWriteToFirstBuffer) {
365 #if DEBUG
366 printf ("* * * * * * * Can't keep up with reading file:%ld\n", mParent.GetBusNumber());
367 #endif
368
369 mParent.DoNotification (kAudioFilePlayErr_FilePlayUnderrun);
370 *inOutDataSize = 0;
371 *inOutData = 0;
372 } else {
373 *inOutDataSize = mChunkSize;
374 *inOutData = mReadFromFirstBuffer ? mFileBuffer : (mFileBuffer + mChunkSize);
375 }
376
377 mLockUnsuccessful = !sReaderThread.TryNextRead (this);
378
379 mReadFromFirstBuffer = !mReadFromFirstBuffer;
380
381 return noErr;
382 }
383
384 void AudioFileReaderThread::AfterRender ()
385 {
386 if (mNumTimesAskedSinceFinished > 0)
387 {
388 bool didLock = false;
389 if (sReaderThread.GetGuard().Try (didLock)) {
390 mParent.DoNotification (kAudioFilePlay_FileIsFinished);
391 if (didLock)
392 sReaderThread.GetGuard().Unlock();
393 }
394 }
395
396 if (mLockUnsuccessful)
397 mLockUnsuccessful = !sReaderThread.TryNextRead (this);
398 }
399
400 void AudioFileReaderThread::SetPosition (SInt64 pos)
401 {
402 if (pos < 0 || pos >= mFileLength) {
403 SDL_SetError ("AudioFileReaderThread::SetPosition - position invalid: %d filelen=%d\n",
404 (unsigned int)pos, (unsigned int)mFileLength);
405 pos = 0;
406 }
407
408 mReadFilePosition = pos;
409 }
410
411 void AudioFileReaderThread::SetEndOfFile (SInt64 pos)
412 {
413 if (pos <= 0 || pos > mFileLength) {
414 SDL_SetError ("AudioFileReaderThread::SetEndOfFile - position beyond actual eof\n");
415 pos = mFileLength;
416 }
417
418 mFileLength = pos;
419 }