Про звуковой апи

written on Sunday, September 2, 2012

Мне всегда казалось, что в линуксовая ALSA - самый страшный звуковой API, а во всех остальных местах он красивый и удобный. Теперь я таки понимаю, что до того уровня откровенного пиздоблядства, который происходит в макоси, красноглазым поделкам еще расти и расти.

Открываем устройство

Как в нормальных системах открывается устройство? Ну наверное чем-то, типа

snd_pcm_open(&хендл, чооткрыть, туда или суда, куча | ебаных | флагов);

Как открывается девайс в макоси? Ну типа берем рулон туалетной бумаги и распечатываем листинг:

AudioComponentDescription desc;
desc.componentType = kAudioUnitType_Output;
component = AudioComponentFindNext(NULL, &desc);
AudioComponentInstanceNew(component, &unit);

AudioObjectPropertyAddress prop = {
    kAudioHardwarePropertyDefaultInputDevice,
    kAudioObjectPropertyScopeGlobal,
    kAudioObjectPropertyElementMaster};
UInt32 sz = sizeof(prop);
UInt32 default_id;

UInt32 enable = 1;
AudioUnitSetProperty(unit, kAudioOutputUnitProperty_EnableIO,
    kAudioUnitScope_Input, 1, &enable, sizeof(enable));
enable = 0;
AudioUnitSetProperty(unit, kAudioOutputUnitProperty_EnableIO,
    kAudioUnitScope_Output, 0, &enable, sizeof(enable));
AudioObjectGetPropertyData(kAudioObjectSystemObject, &prop, 0, NULL, &sz,
    &default_id);
AudioUnitSetProperty(unit, kAudioOutputUnitProperty_CurrentDevice,
    kAudioUnitScope_Global, 1, &default_id, sizeof(default_id));

Это типа открыли дефолтный инпут девайс. И это я еще поскипал контроль ошибок и всякие мелочи.

Чо за хуйня тут творится? Сначала мы находим нужный "component", потом его инстансим в юнит. Если не дрыгаться, то подефолту получится хендл выходного девайса, дальше нужно потыкать хендл, чтобы он указывал на устройство с нужным id. Но если сразу тыкнуть его в айдишник входного девайса, вылетим в ашипку, поэтому перед тем как указывать, куда смотрит хендл, нужно на нем выключить output io и выключить input io. Ебануться.

Особо радует, что везде в названиях констант указано слово output, даже если работаем с входным устройством. Еще больше радуют трехэтажные функции для получения или установки значения свойства.

Коллбеки

Как работаем запись в устройство в нормальных апи? Ну чото типа

snd_pcm_write(куда, чайво, скока);

Как это работает в самом удобном апи? В самом удобном апи мы ставим коллбек. Коллбек дергается, когда апи решило, что может играть звук. В коллбеке апи рассказывает, сколько байт оно может себе позволить сейчас проиграть и дает указатель на буфер, куда надо эти байты положить. Чтобы мне было дохуя удобно организовать работу с памятью, количество забираемых данных бывает разное от раза к разу, но всегда очень удобное, например 184 байта. А через секунду - 186. Степени двойки? Не, не слышали.

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

Больше всего охуенности в коллбеке input девайса - вместо указателя на данные из девайса он присылает нуль, а чтобы получить сами данные, надо вытащить откуда-то свой буфер и дрыгнуть функцию render.

Когда девайса открывается, ему говорят, чо за самплрейт и формат мы от него хотим. Если самплрейт неправильный, девайс ругается ашипкой. Чтобы всем было веселее, для аутпут девайсов ошибка выдается в момент установки формата, а у интуптов - при дрыганье функции render из коллбека. Ясное дело, инптут девайс ничего не знает про sample rate в 8k и вываливается в ошибку в этот момент.

Туда-суда

Чтобы работать было еще удобнее, то в большой макоси input и output - это разные юниты, которые надо конфигурить отдельно, но можно поствить разных самплрейт, а в айфоне - это один и тот же.

Вобщем чего я хотел сказать - чума на оба ваши рода. И потыкайте в аду кочергой этого самого, который всем заведовал.

This entry was tagged audio and code