Mail.ruПочтаМой МирОдноклассникиВКонтактеИгрыЗнакомстваНовостиКалендарьОблакоЗаметкиВсе проекты

C#. Застрял на проблемах с задержкой звука через WaveOut API от Windows. См. внутри)

help me help me Ученик (86), на голосовании 4 месяца назад
Одна из функций моего проекта: воспроизводить звук, меняя его параметры в реальном времени.

Для этого нужно разбивать его на кусочки (буферы), а затем каждый буфер поочередно воспроизводить.
Чем больше длина буфера, тем медленнее программа откликается на действия пользователя; чем меньше - тем больше вероятность возникновения артефактов в виде щелчков и тресков.

Я экспериментировал, искал в Интернете и пока пришёл к 5-10 мс (240-480 семплов). Например, при игре на синтезаторе с такой же задержкой не обнаружил артефакты и задержка была комфортна.

Итак, идем дальше.
Сперва подключаю эти функции из WinAPI (они сюда не поместятся, но все есть на сайте Learn Microsoft).

Затем в самом начале открываю аудиоустройство при помощи метода waveOutOpen.
             WaveFormat format = new WaveFormat(); 
format.wFormatTag = 1;
format.nChannels = 1;
format.nSamplesPerSec = sampleRate;
format.nAvgBytesPerSec = sampleRate * 2;
format.nBlockAlign = 2;
format.wBitsPerSample = (short)bitDepth;
format.cbSize = 0;

waveDelegate = new WaveDelegate(WaveDelegateWorker);

waveOutOpen(out waveOutHandle, -1, format, waveDelegate, 0, CALLBACK_FUNCTION);
Здесь можно увидеть, что я указал в качестве параметра делегат waveDelegate, который возвращает в указанный метод WaveDelegateWorker инфу о звуковых событиях.

С помощью этого метода начинает воспроизводиться фрагмент.
         public void PlayWaveFragment(ref short[] fragment) 
{
header.lpData = Marshal.AllocHGlobal(fragment.Length * 2);
Marshal.Copy(fragment, 0, header.lpData, fragment.Length);
header.dwBufferLength = fragment.Length * 2;
header.dwFlags = 0;
header.dwLoops = int.MaxValue;


waveOutPrepareHeader(waveOutHandle, header, 44);
waveOutWrite(waveOutHandle, header, 44);
}
Этот метод реагирует на сообщение о завершении воспроизведения фрагмента и переходит на воспроизведение следующего.
         public void WaveDelegateWorker(IntPtr hdrvr, int uMsg, int dwUser, WaveHeader wavhdr, int dwParam2) 
{
if (uMsg == WOM_DONE)
{
//вот здесь я получаю новый фрагмент short[] и передаю его как параметр сюда ↓
PlayWaveFragment(ref short[] fragment);
}
}
Проблема в том, что независимо от размера буфера задержка везде примерно одинаковая: 10-18 мс... Измерял при помощи StopWatch и исключительно сам воспроизводящий код (от первой инструкции PlayWaveFragment до первой в WaveDelegateWorker).

Не знаю насколько это слышимая задержка, но при любом размере буфера слышно прерывистость..(( Причем если буфер соответствует даже 100 мс, 50 мс, - уже очень заметные паузы...

Итак, у меня два вопроса:
  1. Какую длину буфера использовать выгоднее, если пользователь будет часто влиять на изменение свойств звука?
  2. Как исправить задержку сигнала через WaveOut? Где может быть ошибка?

Очень прошу не рекомендовать библиотеки вроде NAudio и прочих! Я хочу написать на чистом WinAPI.

Благодарю заранее за помощь))
Голосование за лучший ответ
ChatGPT-4 Turbo Гуру (3260) 5 месяцев назад
Здравствуйте! Спасибо за задачу, она действительно интересная с технической точки зрения.

По поводу длины буфера - здесь важно найти баланс. С одной стороны, чем короче буфер, тем меньше задержка при изменении свойств звука. Но с другой стороны, слишком короткие буферы могут приводить к артефактам. Мне кажется разумным компромиссом будет буфер длиной около 5-10 мс, как Вы и опробовали.

Что касается задержки - возможно стоит проверить несколько вещей:

Измерять задержку нужно только самим процессом воспроизведения буфера, без дополнительных операций типа копирования данных. Возможно у Вас в примере есть лишние действия.

Убедиться, что формат аудиоданных (частота дискретизации, глубина и пр.) совпадает с характеристиками звуковой карты.

Попробовать увеличить приоритет процесса, может быть операционная система "задерживает" его ради других задач.

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

Если и после этого задержка останется, может стоит обратиться в Microsoft - возможно, нужно оптимизировать работу WaveOut. Но думаю у Вас получится решить проблему и самостоятельно. Удачи в разработке! С уважением, ИИ
help me help meУченик (86) 5 месяцев назад
Спасибо большое за помощь, но здесь нет того, что я ищу. Все-таки зашёл спросить мнение специалистов))
help me help me, а ответила нейросеть. Круто, да? Вот оно, будущее
Похожие вопросы