Egy kedves történet a Pulseaudioval

A hétvégén rendberaktam a gépem hangját; átgondoltam, hogy mi ad ki hangot, mik az igényeim és nyélbe vágtam az egészet Pulseaudioval.

Összefoglalva kevés dologgal csapok zajt:
1. videókat, vlogokat nézek, zenét hallgatok - média
2. telefon, telegram, viber - voip
3. értesítések, egyebek

A média hangerejét gyakran állítom szélsőséges értékek között (néha egész lehalkítom), a voip-t és az értesítéseket ezzel szemben leginkább állandó, magasabb hangerőn akarom. Voip-nél extra igények vannak: visszhang kioltás, beszélgetés közbeni átkapcsolás a kézibeszélő/headset és a hangfal között, továbbá a hangfalra kimenő voip hívás hangszínét emelni kell, másképp túl mély lesz a telefonban megszokotthoz képest. Az is fontos volt, hogy az összes médiát egy billentyűvel el tudjam némítani (pl bejövő hívásnál), ne kelljen keresgélni hogy melyk böngészőablak melyk tabján szól valami.

TL;DR: mindezt könnyedén megoldható Pulseaudioval.

A setup PA-ban leképezve, leegyszerűsítve így néz ki:


┌───────┐     ┌───────────┐     ┌──────────┐
│ Média ├─────┤ null-sink ├─────┤ loopback ├─────┐
└───────┘     └───────────┘     └──────────┘     │
┌───────┐    ┌─────────────┐   ┌─────────────┐   │
│ VOIP  ├────┤ echo-cancel ├───┤ ladspa-sink ├───┼─── Output
└───────┘    └─────────────┘   └─────────────┘   │
┌─────────┐                                      │
│ Default ├──────────────────────────────────────┘
└─────────┘

A

~/.pulse/default.pa

konfig fájlban pedig így:


load-module module-ladspa-sink sink_name=ladspa_output-voice plugin=mbeq_1197 label=mbeq control=-30.0,-19.1,-18.6,-18.6,-18.6,-10.0,-8.0,-6.5,1.5,1.5,1.5,8.5,10.6,10.6,10.6
load-module module-echo-cancel source_name=voip_input sink_master=ladspa_output-voice sink_name=voip_output aec_method=webrtc
load-module module-null-sink sink_name=media_sink sink_properties='device.description="Media sink"'
load-module module-loopback source=media_sink.monitor

illetve a

~/.config/pulse/daemon.conf

fájlban kikapcsoltam a flat-volumes -t.

A loopback az ahol a gyakori hangerő állítás történik a multimédia billentyűkkel, két másik hotkeyt az Output hangerejének állítására konfiguráltam (ezeket igen ritkán használom). Az echo-cancel és a ladspa-sink eszközök csak öröklik a kimenet hangerejét.

Írtam 2 wrapper scriptet, az alkalmazásokat ezen át indítom, attól függően hogy melyk osztályba akarom sorolni a hangjukat:


$ cat ~/bin/execvoipapp
export PULSE_SINK=voip_output
export PULSE_SOURCE=voip_input
exec "$@"
$ cat ~/bin/execmediaapp
export PULSE_SINK=media_sink
exec "$@"

És már csak a telefonáláshoz használt eszközök közötti váltás volt hátra: ezt scriptbe foglalt

pactl move-(sink-input|source-output) ...

paranccsal oldottam meg amit hotkeyre tettem.

Ezzel a hangrendszerem épp úgy szól ahogy elképzeltem - közben nem találkoztam PA buggal, ellenben minden igényem remekül lefedte. Nem lett volna egyszerű nélküle megoldani.

Hozzászólások

Szia!

Mivel látom, hogy Te értesz ( nálam biztos jobban ) a PulseAudiohoz kérdezhetnék egy-két dolgot?

1. Miért kell a Pulseaudio, ha van Alsa? Mit add pluszba?
2. Hogyan lehet megadni a default 'hangkártyát', ha több is van ? Ha menet közben bedugok egy USB-s hangadaptert vagy jack headset-et akkor automatikusan átáll arra a hang?

Tud, ha a hangkártya nem tudja hardveresen, akkor betölti a dmix plugint az alap konfigja. A PulseAudio elterjedése előtt simán működött ez is, évekig használtam anélkül, hogy tudtam volna a létezéséről.

A PulseAudio hátránya pont ez (mellesleg Win Vista óta a win-eknek is), hogy nem tud hardveres mixelést és effekteket használni. Mondjuk egy mai CPU röhögve számolja ezeket, de anno P3-P4 idején (előtte meg pláne) még észrevehető volt a teljesítmény-vesztés játékok alatt.
Az effektek viszont hiányoznak, de ez a rohadék hangkártya-gyártó cégek (elsősorban a Creative) hibája.

Nem azért. Az OSS nem volt a kernel része, és fejlesztők nem is akarták azzá tenni. A felépítése és az API-ja is eléggé szar volt az OSSv4-es előtt. Az ALSA azért jött létre, hogy legyen egy modern API a linux kernelben.

20 éve pedig az alaplapi hangkártyák még csak elkezdtek elterjedni (AC'97 Codec ugyebár), de az elterjedt diszkrét kártyák nagy része sem tudott hardveresen mixelni (régi SoundBlaster és klónjai, stb)

Logokat kell olvasni. Amúgy ebben az a rossz, hogy ha a pulseaudio recseg, az ALSA nem, az még lehet az ALSA hibája. Azért, mert a pulseaudio használ olyan függvényeket az alsa API-ból, amelyet amúgy senki soha, s lehet, hogy az implementáció el van szúrva, vagy trehányul van megcsinálva az ALSA-ban. A pulseaudio meg nem használ workaround-ot, ha visszakap egy értéket a függvénytől, azt szentül elhiszi, azzal számol, lesz is belőle baj.

tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE

Nem hozzám jött a kérdés, de erre reagálok.

Miért kell a Pulseaudio, ha van Alsa?

Nekem a router-embe van dugva USB-s „hangkártya”. Oda kell stream-elnem a hangot akár a nagy gépemről, akár a notebook-omról. Bónusz nehezítés, hogy VoIP alkalmazás esetén kell visszhang elnyomás is.

Ezt hogyan oldanád meg Alsa-val? Amúgy komolyan érdekel, mert a router alkalmazásai mind alsa interface-szel rendelkeznek, de nekem így még nem sikerült ezt megoldanom. Pulseaudio-val viszont megy. Kár, hogy egyes hanganyagok kétszer járják meg most a router-t: egyszer tömörítve, egyszer tömörítetlenül.

tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE

1. Nem ismerem alaposan az ALSA-t, de szerintem abban nem lehet megoldani a stream átdobását másik kimenetre anélkül, hogy az alkalmazás támogatná.

2. Parancssorból valahogy így (erre a PA mixerek is képesek GUI-n):


$ pactl list sinks short
$ pactl set-default-sink {output_sink_name}

Új USB adapter esetén nem áll át automatikusan, de van megoldás: udev konfiggal el tudod lőni a scripted ami átdobja az összes streamet az adapterre. Egyébként a Plasma desktop is képes erre, a Hardware / Multimedia / Audio Volume / Advanced fül alatt lehet bekapcsolni (Automatically switch all running streams when a new output becomes available).
A jack dugós váltásnak állítólag automatikusan kéne működnie ha az ALSA driver támogatja - én sose próbáltam.

Azt a null-sink-es, loopback-es részt nem értem. Miért így kell?

tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE

A null-sink csak arra jó, hogy létrejön egy virtuális output és hozzá a monitor source. Minden ráküldött stream megjelenik a monitoron, majd megy a devnullba. Itt segít a loopback, aminek megadhatsz egy source-t (monitor) és egy sinket (a valódi output device) amire átküldi - így tud megszólalni a null sink.

Még mindig nem értem. Közvetlenül a default sink-be nem mehetne a hang? Minek a semmibe küldeni, amit azért mégis felszedsz, hogy aztán a kimenetre küldd? Van ennek sima memória másolgatáson túl valami szerepe? Az miért jó, ha buffereket másolgatunk?

tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE

Ha a default sinket használná minden app, nem tudnám elnémítani az összeset úgy, hogy közben a bejövő hívás ne legyen néma.

KMix-ben beállítottam a "Media" sinket mint "Master channel", így a multimédia billentyűk a loopback device hangerejét állítják és vele együtt az összes média appot, miközben a VoIP appok hangerejét nem bántják.

Én már értem. Valamivel be kell nyeletni a hangot, de az nem lehet hardware, valódi hangkártya. Csak azért kell, hogy legyen neve, hivatkozni lehessen rá, s így állíthatóvá váljék a hangereje illetve némítani lehessen. Ha a defaulttal csinálja, akkor minden állítódik, de ő úgy akarta, hogy a VoIP hangereje ne változzék, csak a multimédiás alkalmazásoké.

Szerk.: a második kérdésedre pedig az a válasz, hogy a loopback source-t köt sinkre, s a source itt a null-sink monitora, ha jól értem.

tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE

locsemege jól mondja, de megpróbálom még egyszerűbben megfogalmazni;
A null és a loopback együtt alkalmas egy virtualis hangkártya létrehozására: a null kvázi egy sink-source alagút, a loopback pedig source-sink alagút. Mindkettő kell, hogy létrejöjjön a virtuális sink ami aztán egy létező eszközön meg is szólal.

Valójában azt hiányolom, hogy pusztán attól, hogy a defaul.pa - vagy system.pa - file-ban leírja az ember, milyen modult milyen paraméterrel töltsön be a pulseaudio, még cseppet sem látszik világosan a topológia. Olyan leíró nyelv kellene, amelyben vannak dobozok, a doboznak vannak bemenetei, kimenetei, s ezek között valósít meg függvénykapcsolatot. A keverés is az, a szintszabályozás is, a visszhang elnyomás, vagy a route-olás, meg persze az equalizer is. Aztán ezen dobozok be- és kimeneteit lehetne valahogyan összekötözni.

Szerintem ilyen topológiát konfig file-ban látványosan leíró nyelvet nem túl nehéz kitalálni.

Jelen esetben a pulseaudio-nál sokszor az is gondot okoz nekem, hogy a sink, source megnevezésből lássam a jel útját - jó, beszédes az elnevezés -, mert kérdés, honnan nézve az. Meg egy protokoll esetén is kipottyan a hang például a hálózaton, de ott már nem beszélnek erről, hallgatólagosan van csak ez ott. Nem teljességgel érthetetlen, de zavaró a konfigfile nyelvezete, ráadásul vannak nem a topológiát leíró modulok is közé vegyítve, hogy áttekinthetetlenebb legyen az egész.

tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE

Bonyolult a topológia, nem lehet egyszerű fa struktúrába rendezni, inkább irányított körmentes gráffal lehet leírni. Erre szerintem nincs szép megoldás text fájlban. Viszont semmi nem akadályoz meg abban, hogy kommentben megszámozd a sorokat és rajzolj egy DAG-ot pl Dia-val amivel átlátható lesz az egész.

A jelölés nem tetszik. Például itt van a module-native-protocol-tcp, amiből nem derül ki, hogy azon beesik hang, azon kimegy hang, sőt, akár vezérlés is, ha client.conf-ban úgy akarom - bár az lehet, menne akkor is, ha ezt a modult nem tölteném be. Szóval nem egyértelmű. Itt nem látom, hogy source, sink, pedig kimegy a dobozból, hisz látom, most is folyamatosan kb. 192 kB/s-mal.

Tehát ne komment legyen, hanem a tényleges konfig, az a lényeg. Az, hogy valamit kommentben rajzolok, csak magam szórakoztatása, de attól nem fog úgy is működni.

tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE

Amúgy miért ne lehetne?

mixer (in1, in2, out1) {
out1=f(in1, in2);
}

volume (in1, vol, out1) {
out1=g(in1, vol);
}

connect (volume.out1, mixer.in1);

Valami ilyesmit el bírnék képzelni, s látnám, mi hova van kötve. Az f() és g() függvényeket nem definiáltam, ezekre lehetne a hangszervernek kész belső függvényrendszere, amelyet az ember felhasznál kedve szerint. De felőlem lehetne egyszerűsíteni is akár:

main.out=mixer(volume(in1, vol), in2);

tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE

- Nekem ez a szintaxis sem elég intuitív, legszívesebben ezt is inkább lerajzolnám, kommentelném.
- A PA dinamikus szemléletű, rugalmasan újrakonfigurálhatóak a láncok. Egy leírónyelv mellett ott a kérdőjel, hogy a változások hogy kezelhetőek könnyen, állapotvesztés és hiccup nélkül (realtime adatfeldolgozásról van szó).
- A PA scope-ja a tömegigény, miközben tökéletesen illeszthető a JACK-hez aminek célja épp a bonyolult feldolgozási láncok kezelése: users are enabled to build powerful systems for signal processing. Simán ki tudod küldeni a streamed JACK-re akár GUI-ról konfigurált feldolgozásra, majd onnan vissza PA-ba. A PA eszközei ezen a területen egyébként is jóval szerényebbek.

Én nem vagyok meggyőzve hogy a PA-nak szüksége van egy leírónyelvre, a juzerek 98%-a szerintem azt se tudja hogy van konfig fájlja.