• Open Menu Close Menu
  • Apple
  • Shopping Bag
  • Apple
  • Mac
  • iPad
  • iPhone
  • Watch
  • TV
  • Music
  • Support
  • Search apple.com
  • Shopping Bag

Lists

Open Menu Close Menu
  • Terms and Conditions
  • Lists hosted on this site
  • Email the Postmaster
  • Tips for posting to public mailing lists
Re: Buffer overrun management with TPCircularBuffer.. best way to do so?
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: Buffer overrun management with TPCircularBuffer.. best way to do so?


  • Subject: Re: Buffer overrun management with TPCircularBuffer.. best way to do so?
  • From: Kyle Sluder <email@hidden>
  • Date: Sat, 03 May 2014 13:34:05 -0700

On May 3, 2014, at 1:17 PM, Douglas Carmichael <email@hidden> wrote:

Kyle:

Would I need to pass the semaphore down to the rendering callback along with the TPCircularBuffer?

How would I do that?

Perhaps I should have avoided the term semaphore. You don't want to actually use a real semaphore because you must not block the render callback thread. The render callback is run on a real time thread by the operating system on a fixed schedule. If there's no useful work for it to do, it must generate the requisite amount of silence or else you will get audio glitching.

The idea I was trying to explain by analogy is to atomically and locklessly increment and decrement a global integer variable that counts the number of free buffers in the ring. Because it's a global variable, there’s no object that needs to be passed around. Both threads just take the address of the global variable.

The tricky part is in using the correct atomic primitives in the right order to ensure you don't enqueue or use a buffer without properly updating the counter. And that I can't help you with.

--Kyle Sluder


This is an example I found: http://stackoverflow.com/questions/20844429/core-audio-render-thread-and-thread-signalling

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


On Thu, May 1, 2014 at 8:55 AM, Douglas Carmichael <email@hidden> wrote:
Kevin:

Thanks for the advice!

Currently, I’m not using two threads, but have the buffer reading in my rendering callback.

Proof-of-concept: https://gist.github.com/douglas-carmichael/37abe32b9341e5a17a46
 
If I uncomment the second xmp_play_frame/TPCircularBufferProduceBytes bit after the usleep in the playback loop, I do get coherently-sounding audio. But, it plays faster than usual and it has crackle/pop sounds as well.

If I comment it out, I get one frame of ‘good’ audio, but then smaller fragments get sent to the callback:
2014-05-01 10:54:48.189 xmpAUTest-badLoop[38221:12c0b] Outputting zeroes. 2004 bytes available, 2048 bytes expected.
2014-05-01 10:55:26.590 xmpAUTest-badLoop[38234:12c0b] Outputting zeroes. 1720 bytes available, 2048 bytes expected.

Would there be any way to tell the callback to ‘sleep’, so to speak?
Any ideas on how I can improve what I have?

Thanks in advance!

—Douglas

On Apr 30, 2014, at 7:07 PM, Kevin Dixon <email@hidden> wrote:

Do "length" - "fillCount" to see how much space is available. If this is less than your threshold, cause the producer thread to sleep until the buffer is sufficiently drained.

-Kevin


On Wed, Apr 16, 2014 at 3:49 AM, Douglas Carmichael <email@hidden> wrote:
To whom it may concern:

I’ve started working on using TPCircularBuffer to manage the output of a music playback library (libxmp: http://xmp.sourceforge.net/libxmp.html), but I’m stumped on the best way to manage overruns.

Currently, my code does take the audio data into the buffer and render it out on the rendering callback, but there doesn’t seem to be a way to check if the buffer is full with a TPCircularBuffer before pushing data into the buffer.

What should I be doing in my code to make it work?

(I was able to send equal-size chunks of audio from the library in my rendering callback without issue, but the ring buffer will work better for the app I want to build.)

Thanks in advance for any help!

—Douglas

 _______________________________________________
Do not post admin requests to the list. They will be ignored.
Coreaudio-api mailing list      (email@hidden)
Help/Unsubscribe/Update your Subscription:

This email sent to email@hidden

  • Follow-Ups:
    • Re: Buffer overrun management with TPCircularBuffer.. best way to do so?
      • From: Ross Bencina <email@hidden>
    • Re: Buffer overrun management with TPCircularBuffer.. best way to do so?
      • From: Kyle Sluder <email@hidden>
References: 
 >Re: Buffer overrun management with TPCircularBuffer.. best way to do so? (From: Douglas Carmichael <email@hidden>)
 >Re: Buffer overrun management with TPCircularBuffer.. best way to do so? (From: Kevin Dixon <email@hidden>)
 >Re: Buffer overrun management with TPCircularBuffer.. best way to do so? (From: Douglas Carmichael <email@hidden>)
 >Re: Buffer overrun management with TPCircularBuffer.. best way to do so? (From: Kyle Sluder <email@hidden>)
 >Re: Buffer overrun management with TPCircularBuffer.. best way to do so? (From: Douglas Carmichael <email@hidden>)

  • Prev by Date: Re: Buffer overrun management with TPCircularBuffer.. best way to do so?
  • Next by Date: Re: Buffer overrun management with TPCircularBuffer.. best way to do so?
  • Previous by thread: Re: Buffer overrun management with TPCircularBuffer.. best way to do so?
  • Next by thread: Re: Buffer overrun management with TPCircularBuffer.. best way to do so?
  • Index(es):
    • Date
    • Thread