earshot, мой лакальны транскрыбатар, стаў публічным у жніўні. У яго README ёсць табліца пра запіс гуку, яе пісаў я. Мікрафон працуе на ўсіх 3 сістэмах. Пра сістэмны гук, гэта значыць пра тое, што граюць дынамікі, у табліцы напісана “works out of the box” для Linux. Для Windows патрэбны Stereo Mix або VB-CABLE, а для “macOS 12 and later” патрэбны BlackHole. Пад табліцай я напісаў, што macOS увогуле не дае звычайнаму працэсу API для запісу сістэмнага гуку.
Гэты радок я напісаў па памяці. Памяць з тых гадоў, калі ўсе ставілі Soundflower, каб запісаць гук на Маку. Ці праўда гэта дагэтуль, я не правяраў. Радок пра Linux я таксама не ставіў пад сумнеў, бо на маёй машыне ўсё проста працавала. На гэтым тыдні я прайшоўся па абодвух радках.
Што насамрэч даюць аперацыйныя сістэмы
Машыны з Windows або з Макам для гэтага ў мяне няма, таму гэтая частка паводле дакументацыі і зыходнікаў.
У Windows запіс з выхаду ёсць у WASAPI з часоў Vista. Адкрываеш паток запісу на прыладзе прайгравання са сцягам AUDCLNT_STREAMFLAGS_LOOPBACK. Яшчэ ёсць process loopback, ён запісвае гук адной праграмы і яе даччыных працэсаў. Мінімальнай версіяй Microsoft называе Windows 10 зборкі 20348. Ніводзін кліенцкі выпуск Windows 10 да гэтай зборкі не дайшоў, гэта зборка Windows Server 2022, так што на справе гаворка пра Windows 11 або гэты сервер. У macOS гук з’явіўся ў ScreenCaptureKit з macOS 13. З macOS 14.2 у Core Audio ёсць яшчэ AudioHardwareCreateProcessTap, адвод з выхаду аднаго працэсу або ўсіх адразу. Так што мая фраза пра macOS няправільная для ўсіх версій пачынаючы з 13.
earshot усё роўна патрэбна віртуальная прылада з-за ffmpeg. Сам earshot ніякую аўдыяпрыладу не адкрывае, ён запускае ffmpeg, чытае з яго stdout PCM 16 кГц мона і пакідае платформу на ffmpeg. Сёння, 11 кастрычніка, я паглядзеў спіс libavdevice у галіне master у ffmpeg. Для гуку ў Windows там ёсць dshow, а ўваходу WASAPI няма. На macOS ва ўваходзе avfoundation няма кода ScreenCaptureKit, таму з крыніц гуку ён бачыць толькі прылады запісу.
Linux, з замерам
Радок пра Linux я мог памераць. Нода, на якой я працую, гэта сервер без гукавой карты. Таму я распакаваў PulseAudio 16.1 і PipeWire 1.0.5 з WirePlumber 0.4.17 з пакетаў Ubuntu 24.04 у лакальную папку і стварыў віртуальныя выхады. Абодва серверы я ганяў адными і тымі ж pacat і pactl, так што кліенцкі бок аднолькавы. Тэставы гук гэта 3 секунды шуму з фіксаваным зернем плюс тон 1 кГц, 48 кГц стэрэа. Кожны запіс я выраўноўваю па арыгінале і параўноўваю адлік за адлікам.
Пры гучнасці 100% monitor вяртае гук біт у біт. Гэта 144 000 кадраў са 144 000, на абодвух серверах і ў кожным прагоне канчатковага стэнда.
Потым я пасунуў паўзунок на PulseAudio. На выхадзе з гучнасцю 50% запіс роўны арыгіналу, памножанаму на 0,125, з дакладнасцю да акруглення апошняга біта. Гэта 0,5 у кубе, крывая гучнасці PulseAudio. Выключаны гук на выхадзе дае чыстыя нулі. Гучнасць адной праграмы на 50% падчас прайгравання таксама дае 0,125. Значыць, на PulseAudio monitor чытае гук пасля ўсіх гэтых рэгулятараў.
І сервер гэта запамінае. Пасля таго як я сцішыў адну праграму, яе наступны паток зноў пачаўся з 0,125. Сервер захоўвае гэтую гучнасць на дыску па ролі патоку, а калі ролі няма, то па ідэнтыфікатары або назве праграмы. Для earshot гэта значыць, што старая налада праграмы дзейнічае і на яе наступны званок.
Я ледзь не напісаў, што PipeWire паводзіць сябе гэтак жа. На маіх першых выхадах так і было. Потым праверка па зыходніках паказала прычыну. Гэтыя выхады я ствараў праз слой сумяшчальнасці PipeWire з PulseAudio. Гэты слой уключае ім monitor.channel-volumes. У самога PipeWire па змаўчанні гэтая налада выключаная. WirePlumber 0.4.17 пакідае яе выключанай для сапраўдных гукавых карт. Тады я зрабіў яшчэ адзін выхад наўпрост у PipeWire, з наладай па змаўчанні. На ім выхад на 50% даў запіс біт у біт, выключаны гук таксама. Гучнасць праграмы па-ранейшаму давала 0,125 і па-ранейшаму запаміналася.
Пераключэнне выхаду на абодвух працуе нармальна. Я пачаў запіс з @DEFAULT_MONITOR@, пераключыў выхад па змаўчанні на іншы і граў туды. Абодва серверы перавялі запіс на новы monitor. Запіс зноў супаў біт у біт.
Дзе пачынае балець
earshot адпраўляе ў мадэль не любы гук. Ён рэжа паток на кадры па 20 мс. Кадр лічыцца маўленнем, калі яго RMS не меншы за 600 па шкале 16 біт. Фрагмент застаецца, толькі калі ў ім набралася хаця б 300 мс такіх кадраў. Гэтыя значэнні па змаўчанні выбіраў я сам і ні разу не думаў пра іх разам з паўзунком гучнасці.
Таму я прайграў запіс маўлення на рознай гучнасці. Запісваў ffmpeg 6.1.1 з тымі ж аргументамі, якія earshot збірае на Linux, толькі ў файл, а не ў stdout. Потым аддаў вынік сегментару earshot на каміце 15da817 з наладамі па змаўчанні. Запіс гэта 10 гарвардскіх фраз з Open Speech Repository, для прайгравання я ператварыў яго ў стэрэа. На 100% пік каля -12 dBFS, а ў самага гучнага кадра ў 20 мс RMS ад 3500 да 3650, у залежнасці ад прагону.
На PulseAudio з паўзунком выхаду earshot нарэзвае з 10 фраз 10 фрагментаў да 80% уключна. На 70% нарэзвае 8. З 60% і ніжэй ніводнага. На 60% самыя гучныя кадры ўсё яшчэ праходзяць парог 600, бо 3650, памножанае на 0,216, гэта каля 790, але такіх кадраў ва ўсім запісе ўсяго 18. Ніводзін фрагмент не набірае з іх 300 мс. На PipeWire паўзунок выхаду нічога не мяняе. earshot нарэзвае ўсе 10 фрагментаў нават на 30%. Паўзунок праграмы вяртае там той жа абрыў: 9 фрагментаў на 70%, ніводнага з 60% і ніжэй. Розніцу 8 супраць 9 на 70% я спісваю на акругленне і межы кадраў і далей не правяраў. Ні ў адным з гэтых прагонаў нічога не падае, ffmpeg і сегментар працуюць далей, а расшыфроўка застаецца пустой.
Мяне нейкі час падманваў і ўласны стэнд. У першых прагонах запіс пачынаўся за 1 секунду да прайгравання і спыняўся праз 1 секунду пасля. Некаторыя запісы гублялі да секунды ў пачатку або кавалак у канцы. З запасам у 3 секунды з кожнага боку гэта больш не паўтаралася. Канцы губляліся таму, што pipewire-pulse аддае запісу даныя буйнымі кавалкамі. Чаму прападаў пачатак, я не ведаю і не капаў.
Што мяняецца для earshot
У README радок пра Linux не павінен казаць толькі “works out of the box”. Працуе, але што менавіта запісваецца, залежыць ад гучнасці. На PulseAudio паўзунок выхаду і выключэнне гуку вырашаюць, ці пачуе earshot маўленне ўвогуле. На PipeWire гэта вырашае толькі гучнасць праграмы. У абодвух выпадках пустая расшыфроўка прыходзіць без аніякай памылкі. Радок пра macOS проста няправільны. Яго трэба прыбраць з README і з docs/audio-capture.md таксама.
Для Windows і macOS сумленнае рашэнне гэта ўласны шлях запісу побач з ffmpeg. Гэта значыць WASAPI loopback з аднаго боку і process tap з другога. Гэта больш кода, чым ёсць у earshot цяпер. Я яго не напісаў.
Што я не правяраў
Нічога з гэтага не запускалася ні на Windows, ні на macOS. Я не ведаю, ці бачыць WASAPI loopback або process tap гук да сістэмнай гучнасці ці пасля.
Усе выхады былі віртуальнымі. PulseAudio можа прымяняць частку гучнасці ў жалезе гукавой карты. Паводле зыходнікаў гэтая частка ідзе пасля monitor, а выключэнне гуку заўсёды робіцца праграмна. Значыць, на ноўтбуку з PulseAudio абрыў можа апынуцца ніжэй па паўзунку, а выключаны гук усё роўна павінен даваць цішыню. Гэта я прачытаў у кодзе, запусціць не мог. Мой выхад у PipeWire толькі паўтарае наладу па змаўчанні для сапраўднай карты. Сапраўднай картай ён ад гэтага не становіцца.
Сам абрыў адносіцца толькі да гэтага запісу. Цішэйшы званок зрушвае яго ўверх па паўзунку, гучнейшы ўніз. Парог 600 да таго ж мяняецца сцягам --threshold.