Mercurial > sdl-ios-xcode
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 } |