Admin
19.12.2016

Multitoolbox — новый сайт от создателей этого поратала
ASIO (Audio Stream Input/Output) – это протокол для драйверов звуковых карт … разработанный фирмой Steinberg, предоставляющий приложениям унифицированный интерфейс к аппаратным ресурсам. А также технология, решающая две существующие на сегодняшний момент проблемы:
1. Минимизация задержек записи и воспроизведения
2. Предоставление универсального интерфейса для мультиканального ввода и вывода звуковой информации
Стандартные драйвера Linux способны обеспечивать практически такие же малые задержки как и ASIO, но для Windows это является настоящим бичом. Так, например, не особенно утешительные заявления Microsoft о том что DirectSound обеспечивает задержки в 20мс не являются истиной. На практике достигаются лишь задержки в 50-80 мс. Достаточно низкоуровневая технология Kernel Streaming обеспечивает задержки не менее 20-21мс (пример тому программа Guitar FXBox). Такая задержка уже практически не заметна для неопытного человека, однако к ней добавляются задержки вносимые фильтрами обработки аудио (для процессоров эффектов), сетевые задержки, задержки вносимые работой других приложений (в не real-time ОС) и т.д.
Насчет сетевых задержек, возможно, будет отдельная статья, когда они будут мною достаточно изучены.
Технология ASIO способна обеспечить задержки менее 2мс оставляя достаточно времени для процессирования и создавая существенный задел для запаздывающих по сети пакетов.
Процесс работы приложения с ASIO разделяется на несколько основных этапов:
1. Инициализация, выбор драйвера
2. Создания аудио буферов
3. Запуск драйвера
4. Работаем с драйвером, копируя данные из входных буферов и в выходные
5. Останавливаем драйвер
6. Удаляем аудио буферы
7. Отключаем драйвер от приложения
Следующий рисунок (из описания ASIO SDK 2.2) хорошо иллюстрирует этот процесс.

Для .NET существует API драйвера написанный на смеси C++ и C#, хотя для реальной работы мне пришлось над ним немного поработать. Скачать исходники и дёмку можно тут (для скачки придется зарегистрироваться, но ресурс отличный, по тому не влом).
Ниже я приведу несколько кусков кода, которые помогут разобраться в использовании этой библиотечки (а еще ниже пару полезных ее изменений которые мне пришлось сделать).
Получение списка драйверов:
for (int i = 0; i < AsioDriver.InstalledDrivers.Length; i++)
driversList.Add(AsioDriver.InstalledDrivers[i].Name);
return driversList;
}
Список драйверов постоянно находится в статическом компоненте AsioDriver.InstalledDrivers. Его можно например поместить в ComboBox и предоставить возможность пользователю выбрать нужный из существующих в системе.
Для выбора драйвера методу AsioDriver.SelectDriver нужно передать один из объектов списка AsioDriver.InstalledDrivers, примерно так:
Для полной инициализации драйвера нужно вызвать следующую последовательность методов:
Метод воспроизведения:
Здесь мы просто копируем имеющиеся у нас данные в выходной аудио буфер. Метод достаточно гибкий: он воспроизводит данные только в один канал, по этому если у нас стерео система или квадро, то мы можем им воспроизводить в каждый канал свои данные, или по своему обработанные данные. Переменная channel содержит номер канала и может выбираться в диапазоне существующих выходных каналов. Например, в одном из режимов моей звуковой карты (Creative Live 5.1) есть режим 8+8 каналов (8 входных и 8 выходных), следовательно я могу выбирать номера выходных каналов из диапазона 0..7. Если на физических выводах к ним подключены устройства воспроизведения, то я буду слышать звук. Так для воспроизведения на гарнитуру я могу написать:
Соответственно для записи звука со входа используем следующий метод:
Здесь все аналогично, только копирование данных происходит в обратном направлении.
Вызывать эти методы следует из обработчика сообщения драйвера BufferUpdate, который мы зарегистрировали при инициализации драйвера. Ниже определим обработчик который просто копирует вход на выход:
Как правило, выходные каналы существуют, по этому, для определения размера аудио буферов драйвера, когда это требуется, можно использовать строчку driver.OutputChannels[0].BufferSize. Все буфера (и входные и выходные) имеют одинаковые размеры.
Это основные моменты, которые необходимо знать, остальная информация уже легко извлекается из исходников этой библиотеки.
Пара исправлений
Библиотека работает только в 32-х битном режиме звука. Имея старенькую 16-ти битную звуковушку мне пришлось немного исправить драйвер. А именно в файлах Channel.h и Channel.cpp заменить
На
И также исправить все работающие с этими массивами методы.
Второе исправление касалось невозможности прямо указывать размер буффера. По тому пришлось перегрузить метод void CreateBuffers(bool useMaxBufferSize) так:
// create our input and output channel arrays
_inputChannels = gcnew array
_outputChannels = gcnew array
// each channel needs a buffer info
ASIOBufferInfo* pBufferInfos = new ASIOBufferInfo[totalChannels];
// now create each input channel and set up its buffer
for (int index = 0; index < _nInputs; index++)
{
pBufferInfos[index].isInput = 1;
pBufferInfos[index].channelNum = index;
pBufferInfos[index].buffers[0] = 0;
pBufferInfos[index].buffers[1] = 0;
}
// and do the same for output channels
for (int index = 0; index < _nOutputs; index++)
{
pBufferInfos[index + _nInputs].isInput = 0;
pBufferInfos[index + _nInputs].channelNum = index;
pBufferInfos[index + _nInputs].buffers[0] = 0;
pBufferInfos[index + _nInputs].buffers[1] = 0;
}
// get the driver to create its buffers
_pDriver->createBuffers(pBufferInfos, totalChannels, bufferSize, _pCallbacks);
// now go and create the managed channel objects to manipulate these buffers
for (int index = 0; index < _nInputs; index++)
{
_inputChannels[index] = gcnew Channel(_pDriver, true, index,
pBufferInfos[index].buffers[0],
pBufferInfos[index].buffers[1],
bufferSize);
}
for (int index = 0; index < _nOutputs; index++)
{
_outputChannels[index] = gcnew Channel(_pDriver, false, index,
pBufferInfos[index + _nInputs].buffers[0],
pBufferInfos[index + _nInputs].buffers[1],
bufferSize);
}
_outputReadySupport = _pDriver->outputReady();
#if ENABLETRACE
Trace::WriteLine(String::Format(«outputReady(): {0}», _outputReadySupport));
#endif
}
Единственное ограничение при использовании этого перегруженного метода – в качестве размеров буферов нужно выберать только поддерживаемые звуковыми картами значения! Разные звуковые карты могут поддерживать разные размеры, так Live 5.1 поддерживает размеры буферов кратные степени двойки, длительность аудио данных в которых выражется дробным количеством миллисекунд, карты E-MU 0404 только буферы длительностью в целые миллисекунды.
На этом пока все. Хотя есть еще одна проблема с созданием релизной версии библиотеки, но сейчас я ее описывать не стану. А когда появится файлообменник, приаттачу (если вспомню 🙂 ) файл с исходниками релизной библиотечки.