AudioUnit "Once" does not work. โ€œOnlyโ€ occurs at 6 (maybe 6 s plus, but I have not tested)

I use AudioUnitto play and record at the same time. The preferred setting is sample rate = 48 kHz, buffer duration = 0.02

The callback for playback and recording is displayed here:

static OSStatus recordingCallback(void *inRefCon, 
                                  AudioUnitRenderActionFlags *ioActionFlags, 
                                  const AudioTimeStamp *inTimeStamp, 
                                  UInt32 inBusNumber, 
                                  UInt32 inNumberFrames, 
                                  AudioBufferList *ioData) {

    IosAudioController *microphone = (__bridge IosAudioController *)inRefCon;

    // render audio into buffer
    OSStatus result = AudioUnitRender(microphone.audioUnit,
                                      ioActionFlags,
                                      inTimeStamp,
                                      inBusNumber,
                                      inNumberFrames,
                                      microphone.tempBuffer);
    checkStatus(result);
//    kAudioUnitErr_InvalidPropertyValue

    // notify delegate of new buffer list to process
    if ([microphone.dataSource respondsToSelector:@selector(microphone:hasBufferList:withBufferSize:withNumberOfChannels:)])
    {
        [microphone.dataSource microphone:microphone
                          hasBufferList:microphone.tempBuffer
                         withBufferSize:inNumberFrames
                   withNumberOfChannels:microphone.destinationFormat.mChannelsPerFrame];
    }

    return result;
}

/**
 This callback is called when the audioUnit needs new data to play through the
 speakers. If you don't have any, just don't write anything in the buffers
 */
static OSStatus playbackCallback(void *inRefCon, 
                                 AudioUnitRenderActionFlags *ioActionFlags, 
                                 const AudioTimeStamp *inTimeStamp, 
                                 UInt32 inBusNumber, 
                                 UInt32 inNumberFrames, 
                                 AudioBufferList *ioData) {    

    IosAudioController *output = (__bridge IosAudioController *)inRefCon;

    //
    // Try to ask the data source for audio data to fill out the output's
    // buffer list
    //
    if( [output.dataSource respondsToSelector:@selector(outputShouldUseCircularBuffer:)] ){

        TPCircularBuffer *circularBuffer = [output.dataSource outputShouldUseCircularBuffer:output];
        if( !circularBuffer ){
            //            SInt32 *left  = ioData->mBuffers[0].mData;
            //            SInt32 *right = ioData->mBuffers[1].mData;
            //            for(int i = 0; i < inNumberFrames; i++ ){
            //                left[  i ] = 0.0f;
            //                right[ i ] = 0.0f;
            //            }
            *ioActionFlags |= kAudioUnitRenderAction_OutputIsSilence;
            return noErr;
        };

        /**
         Thank you Michael Tyson (A Tasty Pixel) for writing the TPCircularBuffer, you are amazing!
         */

        // Get the available bytes in the circular buffer
        int32_t availableBytes;
        void *buffer = TPCircularBufferTail(circularBuffer,&availableBytes);
        int32_t amount = 0;
        //        float floatNumber = availableBytes * 0.25 / 48;
        //        float speakerNumber = ioData->mBuffers[0].mDataByteSize * 0.25 / 48;

        for (int i=0; i < ioData->mNumberBuffers; i++) {
            AudioBuffer abuffer = ioData->mBuffers[i];

            // Ideally we'd have all the bytes to be copied, but compare it against the available bytes (get min)
            amount = MIN(abuffer.mDataByteSize,availableBytes);

            // copy buffer to audio buffer which gets played after function return
            memcpy(abuffer.mData, buffer, amount);

            // set data size
            abuffer.mDataByteSize = amount;
        }
        // Consume those bytes ( this will internally push the head of the circular buffer )
        TPCircularBufferConsume(circularBuffer,amount);
    }
    else
    {
        //
        // Silence if there is nothing to output
        //
        *ioActionFlags |= kAudioUnitRenderAction_OutputIsSilence;
    }
    return noErr;
}

_tempBufferconfigured with frames 4096.

This is how I free audioUnit. Note , due to an error that VoiceProcessingIOunit may not work , if you start, stop and start it again, I need to recycle and initialize it every time. This is a known issue and is posted here, but I canโ€™t recall the link.

if (_tempBuffer != NULL) {
    for(unsigned i = 0; i < _tempBuffer->mNumberBuffers; i++)
    {
        free(_tempBuffer->mBuffers[i].mData);
    }
    free(_tempBuffer);
}
AudioComponentInstanceDispose(_audioUnit);

6, 6+ . - 6s ( 6s +). , ( . . 6-7 20 ) IOUnit, . , , , IOUnit, , .

.

UPDATE

, AudioUnit

// Describe audio component
    AudioComponentDescription desc;
    desc.componentType = kAudioUnitType_Output;
    desc.componentSubType = kAudioUnitSubType_VoiceProcessingIO;
    desc.componentFlags = 0;
    desc.componentFlagsMask = 0;
    desc.componentManufacturer = kAudioUnitManufacturer_Apple;

    // Get component
    AudioComponent inputComponent = AudioComponentFindNext(NULL, &desc);

    // Get audio units
    status = AudioComponentInstanceNew(inputComponent, &_audioUnit);
    checkStatus(status);

    // Enable IO for recording
    UInt32 flag = 1;
    status = AudioUnitSetProperty(_audioUnit,
                                  kAudioOutputUnitProperty_EnableIO,
                                  kAudioUnitScope_Input,
                                  kInputBus,
                                  &flag,
                                  sizeof(flag));
    checkStatus(status);

    // Enable IO for playback
    status = AudioUnitSetProperty(_audioUnit,
                                  kAudioOutputUnitProperty_EnableIO,
                                  kAudioUnitScope_Output,
                                  kOutputBus,
                                  &flag,
                                  sizeof(flag));
    checkStatus(status);

    // Apply format
    status = AudioUnitSetProperty(_audioUnit,
                                  kAudioUnitProperty_StreamFormat,
                                  kAudioUnitScope_Output,
                                  kInputBus,
                                  &_destinationFormat,
                                  sizeof(self.destinationFormat));
    checkStatus(status);
    status = AudioUnitSetProperty(_audioUnit,
                                  kAudioUnitProperty_StreamFormat,
                                  kAudioUnitScope_Input,
                                  kOutputBus,
                                  &_destinationFormat,
                                  sizeof(self.destinationFormat));
    checkStatus(status);


    // Set input callback
    AURenderCallbackStruct callbackStruct;
    callbackStruct.inputProc = recordingCallback;
    callbackStruct.inputProcRefCon = (__bridge void * _Nullable)(self);
    status = AudioUnitSetProperty(_audioUnit,
                                  kAudioOutputUnitProperty_SetInputCallback,
                                  kAudioUnitScope_Global,
                                  kInputBus,
                                  &callbackStruct,
                                  sizeof(callbackStruct));
    checkStatus(status);

    // Set output callback
    callbackStruct.inputProc = playbackCallback;
    callbackStruct.inputProcRefCon = (__bridge void * _Nullable)(self);
    status = AudioUnitSetProperty(_audioUnit,
                                  kAudioUnitProperty_SetRenderCallback,
                                  kAudioUnitScope_Global,
                                  kOutputBus,
                                  &callbackStruct, 
                                  sizeof(callbackStruct));
    checkStatus(status);

    // Disable buffer allocation for the recorder (optional - do this if we want to pass in our own)
    flag = 0;
    status = AudioUnitSetProperty(_audioUnit,
                                  kAudioUnitProperty_ShouldAllocateBuffer,
                                  kAudioUnitScope_Output, 
                                  kInputBus,
                                  &flag, 
                                  sizeof(flag));

    [self configureMicrophoneBufferList];

    // Initialise
    status = AudioUnitInitialize(_audioUnit);
+4
1

3 :

( underflow) inNumberFrames , .

Objective C ( : : ) Apple DTS.

AudioComponentInstanceDispose , . , ( ) , - , . , , () () , .

+1

Source: https://habr.com/ru/post/1618844/


All Articles