Про звукові API

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", потім його інстансимо в юніт. Якщо не дригатися, то за дефолтом вийде хендл вихідного девайса, далі треба потикати хендл, щоб він показував на девайс з потрібним айдішником. Але якщо одразу тикнути його в айдішник вхідного девайса, вилетимо в ашипку, тому перед тим як вказувати, куди дивиться хендл, треба на ньому вимкнути output io та вимкнути input io. Єбанутися.

Особливо радує, що скрізь у назвах констант вказано слово output, навіть якщо працюємо з вхідним девайсом. Ще більше радують триповерхові функції для отримання або встановлення значення властивості.

Колбеки

Як працює запис у девайс у нормальних API? Ну шось типу

snd_pcm_write(куди, шойно, скока);

Як це працює в найзручнішому API? У найзручнішому API ми ставимо колбек. Колбек дригається, коли API вирішило, що може грати звук. У колбеку API розказує, скільки байт воно може собі дозволити зараз проіграти та дає вказівник на буфер, куди треба ці байти покласти. Щоб мені було дохуя зручно організувати роботу з пам'яттю, кількість забираних даних буває різна від разу до разу, але завжди дуже зручна, наприклад 184 байти. А через секунду — 186. Ступені двійки? Не, не чули.

Оскільки найзручніша система любить риалтайми, то колбек дригається в іншому треді, який працює в інтеррапт контексті, а отже спати на блокуванні, і виділяти-звільняти пам'ять, і робити інші хеві операції — нізя-нізя.

Найбільше охуєнності в колбеку інпут девайса — замість вказівника на дані з девайса він присилає нуль, а щоб отримати самі дані, треба витягнути звідкись свій буфер та дригнути функцію render.

Коли девайс відкривається, йому кажуть, шо за самплрейт та формат ми від нього хочемо. Якщо самплрейт неправильний, девайс ругається ашипкою. Щоб усім було веселіше, для аутпут девайсів помилка видається в момент встановлення формату, а в інтуптів — при дриганні функції render із колбека. Ясне діло, інптут девайс нічого не знає про sample rate у 8k і вивалюється в помилку в цей момент.

Туди-сюди

Щоб працювати було ще зручніше, то в большой макосі input і output — це різні юніти, які треба конфігурити окремо, але можно поставити різні самплрейти, а в айфоні — це один і той самий.

Взагалі чого я хотів сказати — чума на обидва ваші роди. І потикайте в аду кочергою того самого, хто за все це завідував.

This entry was tagged