Как записывать и воспроизводить с помощью NAudio с помощью AsioOut

Я пытаюсь получить ввод звука и отправить вывод напрямую с меньшей задержкой, возможной с C#.

Я использую библиотеку NAudio, которая поддерживает ASIO для уменьшения задержки.

В частности, я использую объект AsioOut для записи и другой объект для воспроизведения, инициализированный с помощью BufferedWaveProvider, который заполняется функцией обратного вызова: OnAudioAvailable, что позволяет мне использовать буферы ASIO.

Проблема в том, что я слышу звук с различными глюками и с небольшой задержкой. Я думаю, что проблема в функции OnAudioAvailable, где буфер заполняется данными, взятыми на вход со звуковой карты.

Декларации:

NAudio.Wave.AsioOut playAsio;
NAudio.Wave.AsioOut recAsio;
NAudio.Wave.BufferedWaveProvider buffer;

Процедура воспроизведения:

if (sourceList.SelectedItems.Count == 0) return;

int deviceNumber = sourceList.SelectedItems[0].Index;

recAsio = new NAudio.Wave.AsioOut(deviceNumber);
recAsio.InitRecordAndPlayback(null, 2, 44100); //rec channel = 1

NAudio.Wave.WaveFormat formato = new NAudio.Wave.WaveFormat();
buffer = new NAudio.Wave.BufferedWaveProvider(formato);

recAsio.AudioAvailable += new EventHandler<NAudio.Wave.AsioAudioAvailableEventArgs>(OnAudioAvailable);

//Collego l'output col buffer
playAsio = new NAudio.Wave.AsioOut(deviceNumber);
playAsio.Init(buffer);

//Registro
recAsio.Play();
//Playback
playAsio.Play();

OnAudioAvailable():

//Callback
private unsafe void OnAudioAvailable(object sender, NAudio.Wave.AsioAudioAvailableEventArgs e)
{
    //Copio tutti gli elementi di InputBuffers in buf e li aggiungo in coda al buffer
    byte[] buf = new byte[e.SamplesPerBuffer];
    for (int i = 0; i < e.InputBuffers.Length; i++)
    {
        Marshal.Copy(e.InputBuffers[i], buf, 0, e.SamplesPerBuffer);
        //Aggiungo in coda al buffer
        buffer.AddSamples(buf, 0, buf.Length);
    }
}

Определение AsioAudioAvailableEventArgs:

public AsioAudioAvailableEventArgs(IntPtr[] inputBuffers, int samplesPerBuffer, AsioSampleType asioSampleType);
public float[] GetAsInterleavedSamples();

Кто-нибудь знает, как это исправить? Спасибо вам всем.


person Shafa95    schedule 18.03.2014    source источник


Ответы (1)


Вы не должны использовать два экземпляра AsioOut для одного и того же устройства. Я удивлен, что это вообще работает. Просто используйте тот, с InitRecordAndPlayback.

Для абсолютно минимальной задержки сквозного мониторинга в AudioAvailableEvent скопируйте непосредственно в OutputBuffers и установите WrittenToOutputBuffers = true. Это означает, что вам не нужен BufferedWaveProvider.

Также помните, что любые сбои возникают просто из-за того, что вам не удается достаточно быстро обработать событие AudioAvailable. Когда вы работаете с ASIO, вы можете иметь очень низкие задержки (например, менее 10 мс) и иметь дело с большим количеством данных (например, частота дискретизации 96 кГц, 8 каналов ввода и вывода). Таким образом, вам нужно выполнить большое перемещение данных в короткий промежуток времени. В .NET вы должны учитывать тот неприятный факт, что сборщик мусора может сработать в любой момент и время от времени вызывать пропуск буфера.

person Mark Heath    schedule 19.03.2014
comment
Я понимаю, что вы говорите, и на самом деле это именно то, что я хотел сделать, но все еще не понимаю, как.. Я не могу найти никаких OutputBuffers и в AsioAudioAvailableEventArgs эти двух параметров не существует. Можете ли вы сказать мне, где его найти или, может быть, показать мне какой-то кусок кода? Спасибо большое. - person Shafa95; 19.03.2014
comment
Вы используете последнюю версию NAudio? - person Mark Heath; 19.03.2014
comment
Спасибо, что заставили меня заметить, что я использую более старую версию. Но я все еще не могу сделать то, что вы сказали, потому что OutputBuffers всегда имеет length=0, в отличие от InputBuffers, у которого length=2< /б>. Поэтому, если я попытаюсь скопировать данные между ними, я выйду за пределы допустимого диапазона. Что я должен делать? Наверное, я забыл что-то важное. - person Shafa95; 19.03.2014
comment
ах да, это немного хакерская работа (нужно сильно почистить API для будущей версии). Передайте буферизованный провайдер волны в InitRecordAndPlayback, который будет использоваться для определения количества выходных каналов, которые вы хотите, даже если вы на самом деле ничего не будете считывать из него. - person Mark Heath; 20.03.2014
comment
Я пытался сделать, как ты сказал мне, но ничего не слышу. Тем не менее, я также проверил отладчиком, и он выполняет все инструкции без ошибок. Что я сделал не так? OnAudioAvailable: for (int i = 0; i < e.InputBuffers.Length; i++) e.OutputBuffers[i] = e.InputBuffers[i]; e.WrittenToOutputBuffers = true; - person Shafa95; 22.03.2014
comment
на самом деле вы не копируете туда какие-либо данные. Это низкоуровневое кодирование, у вас есть указатели на буферы, и вы должны использовать Marshal.Copy для копирования байтов между ними. - person Mark Heath; 22.03.2014