Kyle:
Would I need to pass the semaphore down to the rendering callback along with the TPCircularBuffer?
How would I do that?
Still unclear on how you would get it down to the callback, though.
--Douglas On May 3, 2014, at 2:38 PM, Kyle Sluder < email@hidden> wrote: On May 3, 2014, at 10:08 AM, Douglas Carmichael < email@hidden> wrote:
Kevin:
How would I pass the NSCondition down to the rendering callback along with the TPCircularBuffer?
Don’t. NSCondition isn’t useful to you here. It probably just calls sleep() internally. Plus, you should not be sending Objective-C messages from realtime threads, as they may block on malloc().
I'm certain there are existing implementations out there that show how to deal with over/under-runs. Alas, I’m on my phone and don’t have quick access to any. To my memory, the general idea is that since both your decoding and rendering threads know how many buffers are being generated, you can keep a “free buffer” counter synchronized between them (essentially a lockless semaphore). If the semaphore count drops to zero, the render thread generates silence. If the semaphore count reaches the number of buffers, the decoding thread yields to the scheduler. (Sleeping by some multiple of the duration of audio contained in a sample buffer seems like a reasonable idea.)
--Kyle Sluder
—Douglas On May 1, 2014, at 8:49 PM, Kevin Dixon < email@hidden> wrote: Your output callback is called in a more or less "real time" sense. The callback is invoked by the OS as needed to satisfy the requirements of the output device.
See https://developer.apple.com/library/mac/documentation/musicaudio/Conceptual/AudioUnitProgrammingGuide/AudioUnitDevelopmentFundamentals/AudioUnitDevelopmentFundamentals.html
and read the bit about "Pull Model"
libxmp, or other similar decoding type of software typically generate data faster than real time. On the flip side, they are normally too expensive to call on a realtime thread as well, not to mention have buffer sizes different than your generator.
Basically your "main" method is already running on a separate thread.
You need to accurately detect when the buffer is getting full, and then sleep before generating the next block. You can more or less count on that your buffer will be drained in real time, so if you have a 2 second buffer, then you should sleep for less than 2 seconds. Your margin of safety may vary depending on hardware.
Remember that usleep sleeps AT LEAST for the amount of time you specify. You should consider using a proper thread synchronization mechanism. Have a look at NSCondition, which has the "waitUntilDate:" method
-Kevin
|