Hi,
I just started to develop audio unit hosting support in my application.
Offline rendering seems to work except that I hear no output, but why?
I suspect with the player goes something wrong.
I connect to CoreAudio in a different location in the code.
Here are some error messages I faced so far:
2025-08-14 19:42:04.132930+0200 com.gsequencer.GSequencer[34358:18611871] [avae]     AVAudioEngineGraph.mm:4668  Can't retrieve source node to play sequence because there is no output node!
2025-08-14 19:42:04.151171+0200 com.gsequencer.GSequencer[34358:18611871] [avae]     AVAudioEngineGraph.mm:4668  Can't retrieve source node to play sequence because there is no output node!
2025-08-14 19:43:08.344530+0200 com.gsequencer.GSequencer[34358:18614927]            AUAudioUnit.mm:1417  Cannot set maximumFramesToRender while render resources allocated.
2025-08-14 19:43:08.346583+0200 com.gsequencer.GSequencer[34358:18614927] [avae]            AVAEInternal.h:104   [AVAudioSequencer.mm:121:-[AVAudioSequencer(AVAudioSequencer_Player) startAndReturnError:]: (impl->Start()): error -10852
** (<unknown>:34358): WARNING **: 19:43:08.346: error during audio sequencer start - -10852
I have implemented an AVAudioEngine based AudioUnit host. Here I instantiate player and effect:
/* audio engine */
audio_engine = [[AVAudioEngine alloc] init];
  
fx_audio_unit_audio->audio_engine = (gpointer) audio_engine;
av_format = (AVAudioFormat *) fx_audio_unit_audio->av_format;
/* av audio player node */
av_audio_player_node = [[AVAudioPlayerNode alloc] init];
/* av audio unit */
av_audio_unit_effect = [[AVAudioUnitEffect alloc] initWithAudioComponentDescription:[((AVAudioUnitComponent *) AGS_AUDIO_UNIT_PLUGIN(base_plugin)->component) audioComponentDescription]];
av_audio_unit = (AVAudioUnit *) av_audio_unit_effect;
  
fx_audio_unit_audio->av_audio_unit = av_audio_unit;
  
/* audio sequencer */
av_audio_sequencer = [[AVAudioSequencer alloc] initWithAudioEngine:audio_engine];
  
fx_audio_unit_audio->av_audio_sequencer = (gpointer) av_audio_sequencer;
/* output node */
[[AVAudioOutputNode alloc] init];
  
/* audio player and audio unit */
[audio_engine attachNode:av_audio_player_node];
[audio_engine attachNode:av_audio_unit];
[audio_engine connect:av_audio_player_node to:av_audio_unit format:av_format];
[audio_engine connect:av_audio_unit to:[audio_engine outputNode] format:av_format];
ns_error = NULL;
  
[audio_engine enableManualRenderingMode:AVAudioEngineManualRenderingModeOffline
   format:av_format
   maximumFrameCount:buffer_size error:&ns_error];
if(ns_error != NULL &&
   [ns_error code] != noErr){
  g_warning("enable manual rendering mode error - %d", [ns_error code]);
}
ns_error = NULL;
      
[[av_audio_unit AUAudioUnit] allocateRenderResourcesAndReturnError:&ns_error];
if(ns_error != NULL &&
   [ns_error code] != noErr){
  g_warning("Audio Unit allocate render resources returned error - ErrorCode %d", [ns_error code]);
}
Then I render in a dedicated thread.
ns_error = NULL;
[audio_engine startAndReturnError:&ns_error];
if(ns_error != NULL &&
   [ns_error code] != noErr){
  g_warning("error during audio engine start - %d", [ns_error code]);
}
  
[av_audio_sequencer prepareToPlay];
ns_error = NULL;
  
[av_audio_sequencer startAndReturnError:&ns_error];
if(ns_error != NULL &&
   [ns_error code] != noErr){
  g_warning("error during audio sequencer start - %d", [ns_error code]);
}
[av_audio_player_node play];
while(is_running){
  /* pre sync */
  /* IO buffers */
  av_output_buffer = (AVAudioPCMBuffer *) scope_data->av_output_buffer;
  av_input_buffer = (AVAudioPCMBuffer *) scope_data->av_input_buffer;
  /* fill input buffer */
  /* schedule av input buffer */
  frame_position = 0; // (gint64) ((note_offset * absolute_delay) + delay_counter) * buffer_size;
  av_audio_player_node = (AVAudioPlayerNode *) fx_audio_unit_audio->av_audio_player_node;
  AVAudioTime *av_audio_time = [[AVAudioTime alloc] initWithHostTime:frame_position sampleTime:frame_position atRate:((double) samplerate)];
  [av_audio_player_node scheduleBuffer:av_input_buffer atTime:av_audio_time options:0 completionHandler:nil];
	  
  /* render */
  ns_error = NULL;
	  
  status = [audio_engine renderOffline:AGS_FX_AUDIO_UNIT_AUDIO_FIXED_BUFFER_SIZE toBuffer:av_output_buffer error:&ns_error];
    
  if(ns_error != NULL &&
     [ns_error code] != noErr){
    g_warning("render offline error - %d", [ns_error code]);
  }
}
regards, Joël
Hi,
Now, I have a working Audio Unit v3 host, using these objects:
AVAudioEngine *audio_engine;
AVAudioOutputNode *av_output_node;
AVAudioInputNode *av_input_node;
AVAudioUnit *av_audio_unit;
AVAudioSequencer *av_audio_sequencer;
AVAudioFormat *av_format;
You can make use of output and input node of AVAudioEngine while in offline rendering mode.
/* output node */
av_output_node = [audio_engine outputNode];
/* input node */
av_input_node = [audio_engine inputNode];
/* mixer node */
av_audio_mixer_node = [audio_engine mainMixerNode];
  
/* audio player and audio unit */
[audio_engine attachNode:av_audio_unit];
[audio_engine connect:av_input_node to:av_audio_unit format:av_format];
[audio_engine connect:av_audio_unit to:av_audio_mixer_node format:av_format];
[audio_engine connect:av_audio_mixer_node to:av_output_node format:av_format];
The thing with the input node is you have to provide a block before start AVAudioEngine.
input_success = [av_input_node setManualRenderingInputPCMFormat:av_format
inputBlock:^(AVAudioFrameCount inNumberOfFrames){
  AudioBufferList *audio_buffer_list;
  guint audio_channels;
  guint i;
  audio_channels = [av_format channelCount];
  audio_buffer_list = (AudioBufferList *) ags_fx_audio_unit_iterate_channel_data->audio_buffer_list;
				
  /* fill av input buffer */
  if(ags_fx_audio_unit_iterate_channel_data != NULL){
    if(AGS_FX_AUDIO_UNIT_AUDIO_FIXED_BUFFER_SIZE <= inNumberOfFrames){
      ags_audio_buffer_util_copy_buffer_to_buffer(NULL,
          audio_buffer_list->mBuffers[0].mData, 1, 0,
          ags_fx_audio_unit_iterate_channel_data->input + (ags_fx_audio_unit_iterate_sub_block * AGS_FX_AUDIO_UNIT_AUDIO_FIXED_BUFFER_SIZE), 1, 0,
                                                                                     AGS_FX_AUDIO_UNIT_AUDIO_FIXED_BUFFER_SIZE, AGS_AUDIO_BUFFER_UTIL_COPY_FLOAT_TO_FLOAT);
    }else{
	   ags_audio_buffer_util_copy_buffer_to_buffer(NULL,
          audio_buffer_list->mBuffers[0].mData, 1, 0,
          ags_fx_audio_unit_iterate_channel_data->input + (ags_fx_audio_unit_iterate_sub_block * AGS_FX_AUDIO_UNIT_AUDIO_FIXED_BUFFER_SIZE), 1, 0,
          inNumberOfFrames, AGS_AUDIO_BUFFER_UTIL_COPY_FLOAT_TO_FLOAT);
        }
      }
    
    return((const AudioBufferList *) audio_buffer_list);
  }];
if(input_success != YES){
  g_warning("set manual rendering input failed");
}
