Re: How does one update a view position during a core audio render callback?
Re: How does one update a view position during a core audio render callback?
- Subject: Re: How does one update a view position during a core audio render callback?
- From: Brian Willoughby <email@hidden>
- Date: Sat, 07 Mar 2015 18:10:06 -0800
Iain is correct: you should not actually be calling any significant API during your audio callback, and certainly nothing that draws on the screen. However, you don't need a fancy solution. CoreAudio has a couple of easy ways around the communications problem.
1) The best way to get a number from your audio engine to your UI engine is to use a parameter. You can make it a read-only parameter so that only the audio engine can set it. Then, every time your audio engine wants the value to advance, just set the value of the parameter. In your UI engine, you can subscribe to changes in that parameter, and you will be notified on each change - but it will be on the correct thread. There's no need to roll your own lock-free ring-buffer when CoreAudio has already solved the problem of getting data between the audio engine and UI engine (even when they're on different machines).
SetParameter(kParam_Name, value);
2) Playback position is a special case where you don't even need to pass data. The CoreAudio time line will progress in a predictable way. You merely need to make a note of the start time, and then all time stamps afterwards are relative to your start time, and thus reflect your playback position. Loops will require an update to the start time. There is a header that details the timing calls. If you want to get fancy, you can even calculate the presentation time of your AudioUnit chain, to adjust for the difference between the time audio data is placed in the buffer versus when it is actually heard, and then your view position will be quite accurate.
<CoreAudio/HostTime.h>
Brian Willoughby
Sound Consulting
On Mar 7, 2015, at 4:20 PM, Iain Holmes <email@hidden> wrote:
> This is a more specific version of the question of how to communicate between the realtime thread and the UI thread.
> The core audio renderer callback shouldn’t be calling any ObjC classes or methods, as these can be non-deterministic WRT to locking and may block the realtime thread, so using performSelectorOnMainThread: and NSTimer on the renderer thread is out.
>
> The common solution is to use a lock free ring buffer: I ended up using the implementation in PortAudio: https://subversion.assembla.com/svn/portaudio/portaudio/trunk/src/common/pa_ringbuffer.c
>
> When playback is started, the main thread creates a ring buffer and it is passed to the core audio functions so that the renderer thread can access it. The main thread then starts a timer with whatever resolution needed: for my case I set it to 20times a second, that seemed to be good enough for me. This timer checks the ring buffer to see if there was any message from the render thread, and if so, it acts upon it.
>
> Then every time the render callback completes, it posts a message to the ring buffer and carries on rendering the audio.
>
>> On 7 Mar 2015, at 18:20, Patrick J. Collins <email@hidden> wrote:
>>
>> Hmmm... So yeah, I tried using a timer-- and strangely, it's similar to
>> when I did the performSelectorOnMainThread... The update only happens a
>> handful of times and I don't get why...
>>
>> self.timer = [NSTimer scheduledTimerWithTimeInterval:(1.0f / 60) target:self selector:@selector(updatePlayhead) userInfo:nil repeats:YES];
>>
>> For a 1.6 second audio clip, the callback is fired only 5 times..
>>
>> ?? Why is this happening?
>>
>> here is the actual code for my sample player:
>> https://gist.github.com/patrick99e99/72712182158ecbfcbfea
>>
>> Patrick J. Collins
>> http://collinatorstudios.com
>>
>>
>> On Sat, 7 Mar 2015, Dave O'Neill wrote:
>>
>>>
>>> ---------- Forwarded message ----------
>>> From: Dave O'Neill <email@hidden>
>>> Date: Sat, Mar 7, 2015 at 9:44 AM
>>> Subject: Re: How does one update a view position during a core audio render callback?
>>> To: "Patrick J. Collins" <email@hidden>
>>>
>>>
>>> The simplest way to do is to just set a variable to your current time in your render callback.
>>> And then employ a repeating timer to check that value and update your view on the main thread.
>>> @implementation MyObject{
>>> float currentTime;
>>> }
>>>
>>> OSStatus myRenderCallback{
>>> MyObject *object = (MyObject *)inRefCon;
>>> object->currentTime = CalculateTimeOnRenderThread(); //with no Obj-C messaging!
>>> }
>>>
>>> -(void)myTimerCallback{
>>> self.playhead.position = currentTime;
>>> }
>>>
>>> If you want it to refresh the currentTime faster than the render callback you should get the
>>> mach_absolute_time() at the start of playing and again in your timer and calculate the difference.
>>>
>>> @implementation MyObject{
>>> UInt64 startMachTime;
>>> }
>>> // if you need accuracy get the mHostTime from your render callback
>>> // otherwise do this
>>>
>>> -(void)startAudio{
>>> startMachTime = mach_absolute_time();
>>> }
>>>
>>> -(void)myTimerCallback{
>>> UInt64 ticksSinceStart = mach_absolute_time() - startMachTime;
>>> self.playhead.position = convertTicksToSeconds(ticksSinceStart);
>>> }
>>>
>>> A CADisplyLink is an excellent timer for this
>>>
>>> On Sat, Mar 7, 2015 at 9:17 AM, Patrick J. Collins <email@hidden> wrote:
>>> Hi,
>>>
>>> So I have plotted out a waveform, and simply want to have a vertical
>>> line represent a playhead which will move across the waveform's X-axis
>>> as it plays.
>>>
>>> I am using an NSBox as my playhead, and in my callback proc, I tried doing inside the
>>> loop:
>>>
>>> player->sampler.playheadView.position = player->sampler.playheadView.containerWidth
>>> / player->buffer.size * currentSampleIndex
>>>
>>> my playheadView's position setter just does this:
>>>
>>> -(void)setPosition:(NSUInteger)position {
>>> if (position == self.position) return;
>>> [self setFrameOrigin:NSMakePoint(position, 0)];
>>> }
>>>
>>> However, this causes everything to slow down to the point that the audio plays
>>> with clicks and gaps inbetween the frames..
>>>
>>> I changed this to do a performSelectorOnMainThread, where this operation
>>> occurs, but it seems like the playhead only gets updated a handful of times
>>> during playback, so it does not look good..
>>>
>>> What is the ideal way to get visual feedback like this during playback?
>>>
>>> Thanks!
>>>
>>> Patrick J. Collins
>>> http://collinatorstudios.com
>
_______________________________________________
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