AUv3 : how to get tempo from host?

My Audio Unit v3 is trying to get the tempo from the host, but I'm a bit lost.


Apparently there is an AUHostMusicalContextBlock block property in my Audio Unit, but I can't figure out how to use it.

It's now part of my render block, but it crashes my plugin...


- (AUInternalRenderBlock)internalRenderBlock {
    /
    __block PluginKernel *plugin = &_kernel;
    /
    // get the tempo
    double currentTempo;
    if ( self.musicalContextBlock( &currentTempo, NULL, NULL, NULL, NULL, NULL ) == YES ) {
       plugin->handleTempoSetting(currentTempo);
    }
   
    // do the render process


Anyone already got this to work?


Thanks!

Bram

Accepted Reply

You're correct. This helped me solve it. Here's how I got it to work now:


A. declare an AUMusicalContextBlock variable in your implementation:


@implementation MyAudioUnit {
    /
    PluginKernel _kernel;
    BufferedInputBus _inputBus;
    AUHostMusicalContextBlock _musicalContext;
}


B. Capture it in allocateRenderResourcesAndReturnError:


   //Tempo provided by host?
    if (self.musicalContextBlock) { _musicalContext = self.musicalContextBlock; } else _musicalContext = nil;


C. Poll it in the renderblock:


        // get tempo from host
        double currentTempo;
        if ( _musicalContext ) { // only use this if the host supports it...
            if (_musicalContext( &currentTempo, NULL, NULL, NULL, NULL, NULL ) ) {
                plugin->handleTempoSetting(currentTempo);
            }
        }


I hope this helps someone!

Replies

I just had a little play with this API, becasue I'd like to use it myself in the near future.


Ok here are my thoughts:


1. It seems you are not guranteed that the host will set the musicalContextBlock property. So it could be null. This is why you are getting crashes. It seems GarageBand doesn't set that property. Maybe a bug? But Cubasis sets it.

2. Your code should be inside the render block. i.e inside the block that's returned by the internalRenderBlock method. Otherwise it will only run once.

3. You can't call self. inside the render block. It's an obj-c call and may cause the glitch. This is what the API says:


Note that an audio unit implementation accessing this property should cache it in realtime-safe storage before beginning to render.


My guess is the best place to cache it is in allocateRenderResourcesAndReturnError method. But I'm not sure about this myself as I don't know when host sets this property.

You're correct. This helped me solve it. Here's how I got it to work now:


A. declare an AUMusicalContextBlock variable in your implementation:


@implementation MyAudioUnit {
    /
    PluginKernel _kernel;
    BufferedInputBus _inputBus;
    AUHostMusicalContextBlock _musicalContext;
}


B. Capture it in allocateRenderResourcesAndReturnError:


   //Tempo provided by host?
    if (self.musicalContextBlock) { _musicalContext = self.musicalContextBlock; } else _musicalContext = nil;


C. Poll it in the renderblock:


        // get tempo from host
        double currentTempo;
        if ( _musicalContext ) { // only use this if the host supports it...
            if (_musicalContext( &currentTempo, NULL, NULL, NULL, NULL, NULL ) ) {
                plugin->handleTempoSetting(currentTempo);
            }
        }


I hope this helps someone!

Hi, thank you very much for posting this.


Unfortunatly after many attemps we still canot get the host tempo AUHostMusicalContextBlock is always nil.


Can you confirm your solution is working on Logic 10.3.1?


thank you very much

Having the same issue here, seems to be a missing implementation in Logic

I'm just replying to say THANK YOU for the detailed explanation of how you got it to work. You definitely saved me many, many hours of headaches figuring all that out by myself!

To other people who possibly find this thread and find that AUHostMusicalContextBlock is always nil: (I hope Leonardo and jfjs already got it, since it's been 3 years...)
since AUHostMusicalContextBlock is apparently only invoked during render time, it seems it's always nil unless you poll it from the render block. Mine ended up looking like this:
Code Block Objective-C
- (AUInternalRenderBlock)internalRenderBlock {
//return _kernelAdapter.internalRenderBlock;
return ^AUAudioUnitStatus(AudioUnitRenderActionFlags *actionFlags,
const AudioTimeStamp *timestamp,
AVAudioFrameCount frameCount,
NSInteger outputBusNumber,
AudioBufferList *outputData,
const AURenderEvent *realtimeEventListHead,
AURenderPullInputBlock __unsafe_unretained pullInputBlock) {
// get tempo from host
double currentTempo;
if ( _musicalContext ) { // only use this if the host supports it...
if (_musicalContext( &currentTempo, NULL, NULL, NULL, NULL, NULL ) ) {
[self->_kernelAdapter setTempo: currentTempo];
}
}
return self->_kernelAdapter.internalRenderBlock(actionFlags, timestamp, frameCount, outputBusNumber, outputData, realtimeEventListHead, pullInputBlock);
};
}

(I hope I'm doing this right, I don't really understand exactly how Objective C blocks work, as you can very possibly tell...)