Точный таймер с помощью AudioUnit

Я пытаюсь сделать точный таймер для анализа ввода. Я хотел бы иметь возможность измерять отклонение 1% в сигналах ~ 200 мс. Насколько я понимаю, использование AudioUnit позволит получить ‹1 мс. Я попытался реализовать код из примера Стефана Поппа. работайте над xcode 6.3, однако у меня работает пример:

  1. Хотя я в конечном итоге хочу захватить звук, я подумал, что должен быть какой-то способ получить уведомление, например NSTimer, поэтому я попробовал AudioUnitAddRenderNotify, но он делает именно то, что должен, — т.е. он привязан к рендерингу, а не просто произвольный таймер. Есть ли способ вызвать обратный вызов без необходимости записи или воспроизведения?

  2. Когда я изучаю mSampleTime, я обнаруживаю, что интервал между срезами соответствует inNumberFrames — 512 — что составляет 11,6 мс. Я вижу одинаковый интервал как для записи, так и для воспроизведения. Мне нужно большее разрешение, чем это.

Я попытался поиграть с kAudioSessionProperty_PreferredHardwareIOBufferDuration, но все примеры, которые я смог найти, используют устаревшие AudioSessions, поэтому я попытался преобразовать их в AudioUnits:

Float32 preferredBufferSize = .001; // in seconds
status = AudioUnitSetProperty(audioUnit, kAudioSessionProperty_PreferredHardwareIOBufferDuration, kAudioUnitScope_Output, kOutputBus, &preferredBufferSize, sizeof(preferredBufferSize));

Но я получаю OSStatus -10879, kAudioUnitErr_InvalidProperty.

Затем я попробовал kAudioUnitProperty_MaximumFramesPerSlice со значениями 128 и 256, но inNumberFrames всегда 512.

UInt32 maxFrames = 128;
status = AudioUnitSetProperty(audioUnit, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global, 0, &maxFrames, sizeof(maxFrames));

[EDIT] Я пытаюсь сравнить синхронизацию ввода (выбор пользователем MIDI или микрофона) с тем, когда это должно быть. В частности, играет ли инструмент до или после такта/метронома и насколько сильно? Это для музыкантов, а не для игр, поэтому точность требуется.

[EDIT] Ответы кажутся реактивными на события. т.е. они позволяют мне точно видеть, когда что-то произошло, однако я не вижу, как я делаю что-то точно. Моя вина, что не ясно. Мое приложение также должно быть метрономом — синхронизировать воспроизведение клика в такт и мигать точкой в ​​такт — тогда я могу проанализировать действия пользователя, чтобы сравнить время. Но если я не смогу точно сыграть бит, все остальное развалится. Может быть, я должен записывать звук — даже если я этого не хочу — просто для того, чтобы получить отметку времени из обратного вызова?

[EDIT] В настоящее время мой метроном:

- (void) setupAudio
{
    AVAudioPlayer *audioPlayer;
    NSString *path = [NSString stringWithFormat:@"%@/click.mp3", [[NSBundle mainBundle] resourcePath]];
    NSURL *soundUrl = [NSURL fileURLWithPath:path];
    audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:soundUrl error:nil];
    [audioPlayer prepareToPlay];

    CADisplayLink *syncTimer;
    syncTimer = [CADisplayLink displayLinkWithTarget:self selector:@selector(syncFired:)];
    syncTimer.frameInterval = 30;
    [syncTimer addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
}

-(void)syncFired:(CADisplayLink *)displayLink
{
    [audioPlayer play];
}   

person Greg    schedule 05.07.2015    source источник
comment
Как вы воспроизводите аудио в этот момент? это миди? Или вы буферизуете звук вручную? Есть много API на выбор. Если вы будете делать приложение в стиле караоке, то вам определенно нужно изучить основной звук внутри и снаружи, и это займет некоторое время. Если вы делаете что-то более простое, например, определяете, насколько близок пользователь к клику, вы можете использовать комбинацию основного звука и MusicPlayer API   -  person dave234    schedule 12.07.2015


Ответы (2)


Вы должны использовать кольцевой буфер и выполнять анализ сигнала фрагментами, которые соответствуют желаемому количеству кадров, на собственном таймере. Для этого вы настраиваете обратный вызов рендеринга, а затем загружаете в циклический буфер входной звук в обратном вызове. Затем вы устанавливаете свой собственный таймер, который будет извлекать данные из хвоста буфера и выполнять ваш анализ. Таким образом, вы можете загружать в буфер 1024 кадра каждые 0,23 секунды, а ваш таймер анализа может срабатывать каждые 0,000725 секунды и анализировать 32 образца. Здесь связанный с этим вопрос о кольцевых буферах.

ИЗМЕНИТЬ

Чтобы получить точную синхронизацию с помощью кольцевого буфера, вы также можете сохранить временную метку, соответствующую аудиобуферу. Для этого я использую TPCircularBuffer. TPCircularBufferPrepareEmptyAudioBufferList, TPCircularBufferProduceAudioBufferList и TPCircularBufferNextBufferList копируют и извлекают аудиобуфер и метку времени в кольцевой буфер и из него. Затем, когда вы выполняете анализ, каждому буферу будет соответствовать временная метка, что избавит вас от необходимости выполнять всю вашу работу в потоке рендеринга и позволит вам выбирать окно анализа.

person dave234    schedule 06.07.2015
comment
Я понимаю математику и получаю кольцевой буфер, но я не понимаю, как так быстро настроить таймер анализа. Как запланировать обратный вызов каждые 0,000725 секунды? Спасибо. - person Greg; 06.07.2015
comment
Просто используйте NSTimer [NSTimer scheduledTimerWithTimeInterval:32 / 44100.0 target:self selector:@selector(analyze) userInfo:nil repeats:1]; Затем используйте метод анализа, в котором вы извлекаете данные из кольцевого буфера. -(void)analyze{ //do analysis } - person dave234; 06.07.2015
comment
NSTimer хорош только до 50-100 мс. CADisplayLink работает примерно до 1/60 секунды = 16,7 мс. Иногда лучше иногда намного хуже. developer.apple.com/library/ ios/документация/Какао/Справочник/ - person Greg; 06.07.2015
comment
Что именно вы пытаетесь сделать, что требует точного таймера? Преимущество кольцевого буфера заключается в том, что вы можете отделить обратный вызов рендеринга от анализа. Ваш NSTimer должен проверить, достаточно ли данных в кольцевом буфере для проведения анализа, и просто пропустить его, если нет. Вы не можете получить звук быстрее, чем это предлагает аппаратное обеспечение. Если вам действительно нужен точный таймер, вы должны отредактировать свой вопрос, включив в него то, что вы пытаетесь сделать, для чего он требуется. - person dave234; 06.07.2015

Если вы используете что-то вроде кросс-корреляции и/или пикового детектора для поиска совпадающего вектора семплов в буфере аудиосэмплов (или кольцевом буфере, содержащем сэмплы), то вы должны иметь возможность подсчитывать сэмплы между резкими событиями с точностью до одного семпла. (1/44100 или 0,0226757 миллисекунд при частоте дискретизации 44,1 кГц), плюс или минус некоторая ошибка оценки времени. Для событий, разделенных более чем одним буфером Audio Unit, вы можете суммировать и добавлять количество сэмплов в промежуточных буферах, чтобы получить более точный временной интервал, чем просто использование (гораздо более грубого) буферного хронирования.

Однако обратите внимание, что существует задержка или задержка между выходом каждого буфера сэмпла и звуком динамика, а также между приемом звука микрофона и обратными вызовами буфера. Это должно быть измерено, так как вы можете измерить время прохождения туда и обратно между отправкой буфера выборки и тем, когда функция оценки автокорреляции входного буфера возвращает его. Именно столько времени требуется оборудованию для буферизации, преобразования (аналогового в цифровое и наоборот) и передачи данных. Эта задержка может составлять от 2 до 6 раз по 5,8 миллисекунды при использовании соответствующих настроек аудиосессии, но может отличаться для разных устройств iOS.

Да, самый точный способ измерения звука — это захватить звук и просмотреть данные в реальном сэмплированном аудиопотоке.

person hotpaw2    schedule 07.07.2015