Подтвердить что ты не робот

Я хочу вызывать 20 раз в секунду installTapOnBus: bufferSize: format: block:

Я хочу отображать сигнал в режиме реального времени с микрофона. Я был реализован с помощью installTapOnBus: bufferSize: format: block:, Эта функция вызывается три раза за одну секунду. Я хочу, чтобы эту функцию вызывали 20 раз в секунду. Где я могу установить?

AVAudioSession *audioSession = [AVAudioSession sharedInstance];

NSError* error = nil;
if (audioSession.isInputAvailable) [audioSession setCategory:AVAudioSessionCategoryPlayAndRecord error:&error];
if(error){
    return;
}

[audioSession setActive:YES error:&error];
if(error){
    retur;
}

self.engine = [[[AVAudioEngine alloc] init] autorelease];

AVAudioMixerNode* mixer = [self.engine mainMixerNode];
AVAudioInputNode* input = [self.engine inputNode];
[self.engine connect:input to:mixer format:[input inputFormatForBus:0]];

// tap ... 1 call in 16537Frames
// It does not change even if you change the bufferSize
[input installTapOnBus:0 bufferSize:4096 format:[input inputFormatForBus:0] block:^(AVAudioPCMBuffer* buffer, AVAudioTime* when) {

    for (UInt32 i = 0; i < buffer.audioBufferList->mNumberBuffers; i++) {
        Float32 *data = buffer.audioBufferList->mBuffers[i].mData;
        UInt32 frames = buffer.audioBufferList->mBuffers[i].mDataByteSize / sizeof(Float32);

        // create waveform
        ...
    }
}];

[self.engine startAndReturnError:&error];
if (error) {
    return;
}
4b9b3361

Ответ 1

говорят, Apple Support ответила нет: (по sep 2014)

Да, в настоящее время внутренне мы имеем фиксированный размер буферного буфера (0,375 с), и указанный размер буфера клиента для крана не вступает в силу.

но кто-то изменяет размер буфера и получает 40 мс https://devforums.apple.com/thread/249510?tstart=0

Не могу проверить это, neen в ObjC: (

UPD работает! только одна строка:

    [input installTapOnBus:0 bufferSize:1024 format:[mixer outputFormatForBus:0] block:^(AVAudioPCMBuffer *buffer, AVAudioTime *when) {
    buffer.frameLength = 1024; //here

Ответ 2

Ссылка на класс AVAudioNode указывает, что реализация может выбрать размер буфера, отличный от того, который вы поставляете, насколько я знаю, мы застряли в очень большом размере буфера. Это печально, потому что AVAudioEngine - отличная оболочка Core Audio. Поскольку мне тоже нужно использовать входной сигнал для чего-то другого, кроме записи, я смотрю на The Amazing Audio Engine, а также на Core Audio C API (см. IBook Learning Core Audio для отличных обучающих программ на нем) в качестве альтернативы.

*** Обновление. Оказывается, вы можете получить доступ к AudioUnit AVAudioInputNode и установить на него обратный вызов визуализации. Через AVAudioSession вы можете установить желаемый размер буфера аудиосеанса (не гарантируется, но, конечно, лучше, чем node краны). До сих пор я получил размеры буфера до 64 образцов, используя этот подход. Я отправлю сообщение здесь с кодом, как только у меня была возможность проверить это.

Ответ 3

Не знаю, почему или даже если это работает, просто попробуйте несколько вещей. Но наверняка NSLogs указывают интервал 21 мс, 1024 образца, поступающих в каждый буфер...

        AVAudioEngine* sEngine = NULL;
        - (void)applicationDidBecomeActive:(UIApplication *)application 
        {
            /*
             Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
             */

            [glView startAnimation];

            AVAudioSession *audioSession = [AVAudioSession sharedInstance];

            NSError* error = nil;
            if (audioSession.isInputAvailable) [audioSession setCategory:AVAudioSessionCategoryPlayAndRecord error:&error];
            if(error){
                return;
            }

            [audioSession setActive:YES error:&error];
            if(error){
                return;
            }

            sEngine = [[AVAudioEngine alloc] init];

            AVAudioMixerNode* mixer = [sEngine mainMixerNode];
            AVAudioInputNode* input = [sEngine inputNode];
            [sEngine connect:input to:mixer format:[input inputFormatForBus:0]];

            __block NSTimeInterval start = 0.0;

            // tap ... 1 call in 16537Frames
            // It does not change even if you change the bufferSize
            [input installTapOnBus:0 bufferSize:1024 format:[input inputFormatForBus:0] block:^(AVAudioPCMBuffer* buffer, AVAudioTime* when) {

                if (start == 0.0)
                    start = [AVAudioTime secondsForHostTime:[when hostTime]];

                // why does this work? because perhaps the smaller buffer is reused by the audioengine, with the code to dump new data into the block just using the block size as set here?
                // I am not sure that this is supported by apple?
                NSLog(@"buffer frame length %d", (int)buffer.frameLength);
                buffer.frameLength = 1024;
                UInt32 frames = 0;
                for (UInt32 i = 0; i < buffer.audioBufferList->mNumberBuffers; i++) {
                    Float32 *data = buffer.audioBufferList->mBuffers[i].mData;
                    frames = buffer.audioBufferList->mBuffers[i].mDataByteSize / sizeof(Float32);
                    // create waveform
                    ///
                }
                NSLog(@"%d frames are sent at %lf", (int) frames, [AVAudioTime secondsForHostTime:[when hostTime]] - start);
            }];

            [sEngine startAndReturnError:&error];
            if (error) {
                return;
            }

        }

Ответ 4

Начиная с iOS 13 в 2019 году, существует AVAudioSinkNode, который может лучше выполнить то, что вы ищете. Хотя вы могли бы также создать обычный AVAudioUnit/Node и присоединить его к входу/выходу, разница с AVAudioSinkNode заключается в том, что вывод не требуется. Это делает его более похожим на сигнал и позволяет обойти проблемы с неполными цепями, которые могут возникнуть при использовании обычного аудиоустройства/узла.

Для получения дополнительной информации:

Соответствующий код Swift приведен на стр. 10 (с небольшой ошибкой, исправленной ниже) PDF файла сеанса.

// Create Engine
let engine = AVAudioEngine()
// Create and Attach AVAudioSinkNode
let sinkNode = AVAudioSinkNode() { (timeStamp, frames, audioBufferList) ->
OSStatus in
 …
}
engine.attach(sinkNode) 

Я полагаю, что при этом вам все равно придется соблюдать типичные правила звука в реальном времени (например, нет выделения/освобождения памяти, нет вызовов ObjC, нет блокировки или ожидания блокировок и т.д.). Кольцевой буфер все еще может быть полезен здесь.

Ответ 5

Возможно, вы сможете использовать CADisplayLink для достижения этого. A CADisplayLink даст вам обратный вызов каждый раз, когда экран обновляется, что обычно будет намного больше 20 раз в секунду (так что может потребоваться дополнительная логика для дросселирования или ограничения количества раз, когда ваш метод выполняется в вашем случае).

Это, очевидно, решение, которое довольно дискретно от вашей аудиоработы, и в той степени, в которой вам требуется решение, которое отражает вашу сессию, оно может не сработать. Но когда нам нужны частые повторяющиеся обратные вызовы в iOS, это часто подход к выбору, поэтому это идея.

Ответ 6

Эта строка устраняет проблему:

[input installTapOnBus:0 bufferSize:4096 format:[input inputFormatForBus:0] block:^(AVAudioPCMBuffer* buffer, AVAudioTime* when)