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 - это разные юниты, которые надо конфигурить отдельно, но можно поствить разных самплрейт, а в айфоне - это один и тот же.
Вобщем чего я хотел сказать - чума на оба ваши рода. И потыкайте в аду кочергой этого самого, который всем заведовал.