Beszéd átvitele hálózaton - töprengés

Fórumok

Legyen a feladat mindössze ennyi: van két gép, köztük hálózati kapcsolat, s át kellene vinni valahogy a hangot.

A feladat felületesen szemlélve egyszerű. Analóg jelet mintavételezzük, igény szerint tömörítünk, titkosítunk, hálózaton átküldjük az egészet, túloldalon kititkosítjuk, kitömörítjük, az adatokat átadjuk a hangszervernek, majd ő ugyanazzal a mintavételi frekvenciával lejátssza. Na persze. :)

Ahhoz, hogy ez működjön, a küldő és a fogadó oldalon hajszál pontosan azonos mintavételi frekvencia kell. Ha ez nem igaz, s a vevő lassabban nyeli a hangot, mint az adó küldi, akkor buffer overflow lesz. Ha viszont gyorsabban nyeljük a mintákat annál, mintsem azok beérkeznének, buffer underflow lesz a vége. Márpedig a küldő és fogadó oldalon lévő 44.1 kHz csak névlegesen annyi, nem pedig hajszál pontosan.

Felületesen nézve programokat, azt láttam, hogy buffer underflow esetében megnövelik a bufferelendő adatmennyiséget, tehát a buffer hosszát, s ezzel a latency-t. Ezzel megoldottunk volna bármit is? Alighanem áthúztuk a döglött lovat a szomszéd utcába: a küldő továbbra is lassabban küld, mint ahogyan a fogadó fél eszi az adatokat. Nagyobb bufferrel ritkábban fog recsegni a hang, de akkor, ha megáll, hosszabb ideig lesz csendben. A megnövekedett latency viszont teljesen hazavágja majd a visszhang elnyomó algoritmusokat.

Ámde találtam egy ígéretes nevű függvényt:

pa_stream_update_sample_rate()

Change the stream sampling rate during playback.

Arra gondoltam, a vételi oldalon figyelni kellene a buffer telítettségét, s az optimális értéktől való eltérést egy integráló típusú szabályozó alapjeleként lehetne felhasználni. Nyilván limiterek kellenek, de ez könnyen implementálható. Finoman állítani kellene a mintavételi frekvenciát, elérve azt, hogy az adó és vevő oldalon átlagosan - a szabályozás következtében - hajszál pontosan megegyezik majd a mintavételi frekvencia.

Ez persze a lejátszási sebesség és a hallható hang frekvenciájának finom modulálásával jár, de beszédhang esetén ez szerintem nem lesz zavaró.

Azt gondolom erről a függvényről, hogy a lelke mélyén újramintavételezéssé fajul, mert szerintem a hangkártyáknak nem lehet 1 Hz felbontással bármekkora mintavételi frekvenciát megadni.

Mi a véleményetek? Működni fog?

Első körben tervezek írni egy teszt programot, amely mondjuk egy 1 kHz-es szinuszos jelet játszik le, miközben változtatnám a mintavételi frekvenciát. Még nem kezdtem el, s nem tudom, mikor lesz rá időm, szóval türelem. :)

Hozzászólások

A Telegram forráskódja nyílt, talán a beszéd codeké is. Nézted már?

Nem. A seren-t szeretném megjavítani. Ez egy terminálos VoIP p2p alkalmazás, használom, de sokszor iszonyúan recseg-ropog a hangja, s az ember egy idő után feladja. Tudom, ebben az is benne van, hogy a Magyar Telekom meghágta a netsemlegesség szabályait, s a VoIP-ot alacsony prioritással vitte át - nem rég hír volt az Indexen -, a beszélgetőpartnerem meg épp Telekom ügyfél.

http://holdenc.altervista.org/seren/

Szerk.: Itt most nem a codec-ről van szó. A tömörítés a probléma szempontjából irreleváns. Akkor sem lesz a semmiből adat, ha kevesebbet tömünk a bufferbe, mint amennyit felszedünk onnan.

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

a kitomorites utani streamen duplazz meg / dobj el blokkonkent _1_ mintat, ha ugy latod hogy tul gyorsan / tul lassan jatszod vissza a streamet.

Igen, erre is gondoltam, s valószínűleg nagyon zavaró kopogást ez sem okozna. Viszont szerintem finomabb, folytonosabb eredményt adna a mintavételi frekvencia módosítása. Néhány ezreléket, tízezreléket legfeljebb a tökéletes hallással rendelkezők hallanak meg frekvenciában, de én nem vagyok az. Meg különben sem HiFi átvitel a cél, hanem beszéd.

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

Annál több lesz, mert ha a hálózat ingadozik, akkor a szabályzó is ingadozni fog, és kész a baj. De ki kell próbálni, lehet, hogy rosszul gondolom.

Tizenkettedik gyök kettő-szörös frekvencia egy félhang. Az 2.44 ezrelék. Szóval az ezrelékes nagyságrendet simán halljuk. Mintha vibrátózna a túloldal.

Nem P, hanem I szabályozót tervezek éppen ezért. Ráadásul limitálnám az inputot, amellyel végső soron a mintavételi frekvencia változásának sebességét korlátoznám, s limitálnám az outputot is, amellyel pedig a nyilvánvalóan irreális mintavételi frekvenciák beállításának venném elejét. Mert egy hálózati elakadásnál valóban nem volna jó, a a mintavételi frekvencia lejönne mondjuk 100 Hz-re. Aki hallott már a Shannon-féle mintavételi törvényről, azt is tudja, hogy miért. :)

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

Ha elakad a hálózat, az ígyis úgyis szar lesz. Erre az esetre nem érdemes tervezni. Mármint annyit igen, hogy ha vége a hálózati hibának, akkor álljon vissza használhatóra a stabil állapot. De valóban, ha limitálod a gyorsítást mondjuk kétszeresre, akkor még érthető lesz a végén a kimenet. Csak akkor egy fél perces leállás után 15 másodpercig még nincs rendes kommunikáció, csak a hallózás újradarálása.

aaa, regen-volt-mar-akkor-sem-tudtam. :)

azt hiszem a voice reszen nem kell ez, ott a GSM altal szetoszotott ora miatt garantalt, hogy ugyanaz az orajel van a ket vegponton.
(a modemig. hogy utana mit csinal pl az okostelefonon az android/ios/stb, azt mar plane nem tudom. persze biztos hwfuggo is.)

a PDH linkeken (telefonkozpontok / mobil tornyok kozott) viszont ez van, megy a bit-stuffing. (marmint ment valaha.)

Én mostanában írtam ilyen programot. A problémára vannak libek, amik megoldják ezt: újramintavétlezik a hangot úgy, hogy folyamatosan állítgathatod a mintavétel sebességarányát. Van belőle olyan is, ami a hangmagasságot nem módosítja. Ilyet úgy lehet csinálni, hogy frekvenciatartományban végzed a hosszabbítást (egyszerűen szólva FFT oda, lassítás és FFT vissza, de pontosan nem tudom hogyan működik).

Én a speex libre építve csináltam meg ezt a funkciót.

Szabályzóból nagyon egyszerűt csináltam: beállítottam egy ideális pufferméretet, ami annál kisebb, azt lassítja, ami nagyobb azt gyorsítja progresszíven.

Ezzel a setuppal teljesen használható volt a végeredmény még úgyis, hogy TCP-n átkergettem SSH-n keresztül a VPS szerveremen oda vissza. A két végpont pedig az otthoni hálózatomon Wifin ment.

Itt írtam blogbejegyzést a programról, bár ilyen részletekbe asszem nem merültem bele ott:

https://hup.hu/node/151733

Ez a programrész csinálja az újramintavételezést: stdio-n kapja a bemeneti hangblokkot és a ki/bemeneti frekvenciát minden periódusban. A speexet állítgatja és meghívj az újramintavételezést és köpi ki a kimenetet. Plusz kiadja, hogy hány bájtot használt fel a bemenetből, és hány hasznos van a kimeneten. Ha ez eltér a blokkmérettől, akkor a hívó oldalnak újra be kell adni a feldolgozatlan mintákat, illetve a hiányos kimeneti blokkot csak a következő körben tudja kiegészíteni. A drivernek ugye mindig tele blokkokat kell adni, és olyat is kapunk tőle:

https://github.com/rizsi/rcom/blob/master/speexcmd/cmd-speexdsp.c#L123

Az egyszerűség kedvéért külön programok összjátékával megy: a hangot a Java veszi a beépített Java audió libbel. ffmpeg veszi a képet külön streambe. És ezeket a streameket fogja össze a Java egyetlen TCP streambe és küldi egy szerveren át a másik olalra. Az ott demultiplexeli, és az ellenoldani ffmpeg-be csatornázza be a képet. A hangot a puffer méregetésével szabályzott speex lassító/gyorsítóba, végül pedig Java Audio kimenetre.

(A képet viszont szabályzatlanul amint jönnek a kockák kiteszem: így is élvezhető, csak olyan kodeket kellett az ffmpeg-ből választani - vagy beparaméterezni, ami nem pufferel)

Virtualizált Windowson valamiért akadozott az egész, mintha nem lenne tickless az ütemezés. Ehhez a működéshez ugye az kell, hogy minden azonnal ütemezve legyen, amikor lehet: a Linux jól megoldja és tök szépen működik. Pedig nem volt túlterhelve a processzor, mégsem ment simán a folyamat.

Echo canceller: ebből a Pulseaudio beépítettje egészen jó, azt csak be kell kapcsolni parancssori paraméterből, és megy is.

Az echo cancellereket legjobb HW közelben megoldani, mivel ekkor biztosan lehet tudni, hogy a kimenet és a bemenet ugyanarról az órajelről működik. Ha ez nem teljesül (pl külön USB-s mikrofon van egy másik hangkártyáról ketyegő hangszóróval), akkor sokkal bonyolultabb jól megcsinálni. Gyanúm, hogy a Skype sikere mögött az is áll, hogy erre a nehéz feladatra jó megoldást adtak, és így gagyi vagy gagyin beállított vasakon is élvezhető.

Plusz problémája az echo cancellernek, hogy ha más program is hangot ad ki, az is beleszól. Ezért is jó ha a PulseAudióra bízzuk: ő már az összes kimeneti stream mixjén tudja futtatni a visszhangelnyomást. Hasonlóan nem hülyül bele a menet közbeni hangerőállításba sem. (Amit lehet tapasztalni, ha virtuális gépben Szkájpolsz, és a host Linuxon állítgatod a hangerőt: meghülyül a visszhangelnyomója.)

Hogy Windows alatt is működjön az RCOM, ezért kísérleteztem "saját" echo cancellel is, de nem lett igazán jó, és natív Windows híján tesztelni sem nagyon tudtam. Virtualizálva a fent említett ütemezési probléma miatt akadozik az egész. Plusz probléma, hogy ehhez az egész audió kezelést át kellett alakítanom: a hangforrások mixelését nem tudtam a driverre bízni, hanem először mixelni kellett és utána belevezetni a visszhangelnyomóba.

Ekkor jöttem rá arra is, hogy a Skype miért csörög minden beszélgetés előtt még akkor is ha előbb rákattint az ellenoldal: azért, mert a csörgéssel tudja inicializálni a visszhangelnyomó alrendszerét. Ugye ez a kimenet és a bement között talál korrelációt, de csak akkor tudja inicializálni magát, ha van kiadott hang.

Emiatt van az is, hogy telefonhívás elején néha visszhangos, de amint bekalibrálta magát az elnyomórendszer, akkor elmúlik ez a zavar.

ha ilyesmit szeretnél vizsgálgatni hobbiból (abszolút nem sértésként mondva), akkor érdemes pbx szoftvereket is nézned. Ott van az asterisk és a freeswitch, azok forrása is elérhető, codec-ek és a codec-ek közti váltások is biztosan részben vagy egészen megtalálhatóak a forrásában. A pbx-eknél rengeteg féle közötti konverziót kell tudni kezelni, mert a beszélgetés két oldalán lévők elég széles codec palettából választhatnak.

Régen volt olyan projektünk, amikor egyik kolléga írt valami nagyon kis számítás és sávszélesség igényű codec-et pcm alapon (tehát semmi fancy FFT, mint a modern codec-eknél), mivel mikrokontolleren kellett működnie. Másik kollégával pedig mi írtunk gateway-t, ami a spéci formátum és a sima pcm közti konverziót végezte el, amivel már a pbx is tudott mit kezdeni. Digitális hangnál eléggé konkrétan megvan paraméterezve, hogy dekódolva milyen pcm lesz belőle. A spéci codecünkhöz legjobban passzoló pcm formátumot választottuk ki, ahol a Hz és bitrate egész számú többszörös volt talán. Az meg már legyen a sip phone szoftver vagy a pbx baja, hogy ő mivé és hogyan transzkódolja át a megfelelően felparaméterezett pcm streamet.

"A feladat felületesen szemlélve egyszerű. Analóg jelet mintavételezzük, igény szerint tömörítünk, titkosítunk, hálózaton átküldjük az egészet, túloldalon kititkosítjuk, kitömörítjük, az adatokat átadjuk a hangszervernek, majd ő ugyanazzal a mintavételi frekvenciával lejátssza."

Ez lényegében teljesen le van fedve a sip kapcsán létező szoftverekben. Titkosított rtsp hangstream is lehet, az külön megy, nem a sip parancsok adatcsatornáján. Minden le van szépen specifikálva. De egyébként elvileg akár két sip telefon is képes lehet közvetlenül egymással kommunikálni, még pbx nélkül is, csak fix ip beállítás és cross kábellel összedugás kell - ha a szoftvere nem akarja túlgondolni a beállításokat.

A SIP-pel is az a bajom, hogy kell valahol egy szerver, egy szolgáltató. Jó, tudom, a NAT... De ha nyitok portot, megy minden magától.

Amúgy ahogy nézem, az egész hercehurcát majdnem tudja a pulseaudio is, csak az a gond, hogy nem tömörít. Úgy meg eszi a sávszélt. Az opus codec jó, de mindez marginális kérdés, továbbra is az adó és a vevő mintavételi frekvenciájának eltérése a probléma, amelyre csak szabályozás és resampling a jó megoldás szerintem.

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

sip kapcsán: nem, ahogy írtam nem kell szerver. Két kliens is tud közvetlenül kommunikálni, legfeljebb némelyik szoftver implementáció túlságosan bugyuta és nem engedi úgy beállítani.

A nat kapcsán pedig csudi dolog a hole punching. Bár igaz, ahhoz is kell egy külső szerver, amire hivatkozással előállítható az az állapot, ami után már akár két nat mógötti kliens is képes egymással közvetlenül kommunikálni. Bár ez nem minden esetben lehetséges. Ha jól emlékszem a stun a kulcsszó
https://en.wikipedia.org/wiki/STUN

troll
csak tegyel a pulse tetejere egy JACK-et, abba mar biztos siman be tudod drotozni a gzip-et a tomoriteshez.
utana lehetoleg alsa-utils-szal hasznald, hogy meglegyen a full stack (ALSA + PULSE + JACK).
/troll

a serent akarod hasznalni? kiprobaltad mar? az opus elvileg kezeli neked ezt a problemat: RFC 6716 4.4. Packet Loss Concealment (PLC) es 4.4.1. Clock Drift Compensation.

alternativakent: ahogy azbest kollega is irta, a SIP mehet szerver nelkul is, pl baresip (lehet megvan csomagban a disztrodban).

A Seren-t használom évek óta, de vagy a PulseAudio interface-e van rosszul megírva, vagy a hálózaton darabosan megy az adat, de állandóan buffer underrun lesz. Van, amikor teljesen jó a hangja, de van, amikor annyira recseg, ropog, hogy feladja az ember, nem lehet érteni, mit mond a másik.

Sokszor már a saját csengőhangom is ropog, ha én hívok. Megnéztem a forrását, a poll()-lal multiplexelik a stream-eket. Ugye, van hálózat, titkosítás - ha egyszerű, gyenge is -, meg van codec, továbbá a hangszerver. Mindezek két irányban.

Aztán nem néztem meg alaposabban, de erős a gyanúm, hogy ALSA-hoz kapcsolódna, de abból lesz egy PulseAudio kompatibilitási réteg. Jobb volna natív PulseAudio, már feltéve, hogy az előbbi állításom egyáltalán igaz.

Most elfogott a lelkesedés, hogy megjavítsam.

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

Nem emlékszem már, hogy valamelyik telekonferenceia célrendszer, vagy a Skype for Business csinálta-e, de ott az történt, hogy amikor valami miatt akadás volt (ugye nem csak felvétel és lejátszás frekvenciája számít, hanem az átviteli közeg lukai és torlódásai is), akkor utána a felgyűlt hangot hirtelen jóval gyorsabban kezdte lejátszani, hogy utoljérje magát (vagy az adó felet).

Hogy magasabb frekvenciával tolta-e, vagy egyszerűen kivágott minden n-edik mintát az inputból, azt nem tudom, de talán mindegy is. A lényeg az, hogy másoknál is előfordult ez a probléma, és a megoldásuk nem teljesen más irány, mint a tiéd (bár a megvalósítás konkrétan biztosan eltér)

Most jön elő, hogy hardware-es vagyok, többnyire mikrokontrollerre programozok assembly-ben, a C-t csak kapirgálom, ami azt jelenti, elvagyok vele, amíg valami egyszerű dolgot kell csinálni magamnak. A nap nagy részében a Seren forráskódját bújom. Azt érzékelem, hogy akinek napi rutin a C, az hamar átlátja az egészet, de azt is látom, hogy nagyon durván át kellene írni ahhoz, hogy natív pulseaudio-hoz illeszkedjen - illetve még meg sem néztem, egy adott szinten túl, hogyan kezeli a hangot.

Az UDP csomagokból Opus dekódolást, a csomagvesztés valamilyen szintű kezelését megtaláltam, de nagyon át kellene látnom, hogy melyik struktúrát mire használja a szerző, mit kellene tennem, a pulseaudio-hoz kapcsolódást és arról leválasztást hogyan kellene kezelnem, poll()-t vagy callback függvényeket kellene-e használnom, bár szerintem az előbbit.

Aztán mindebben ne maradjon olyan malloc(), amelynek nincs free() párja, ne legyen double free() és hasonlók. Kicsit soknak érzem. Persze egy nagyon buta VoIP klienst írhatnék kifejezetten erre fókuszálva. Esetleg a PulseAudio-hoz lehetne Opus codec-et meg valami titkosító modult írni, az interface szerintem biztosítva van hozzá. Utána lehetne RTP-n áttolni a hangot, azt elvileg tudja a PulseAudio.

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

De figyu, lehet egy esetlegesen buta kerdesem?
Ha kizarolag beszedet szeretnel tovabbitani akkor miert 44.1k mintavetelezessel dolgozol?

Lehet, hogy a mintaveteli bitrate csokkentese direktben a problemara nem valasz de mivel a kisebb mintavetelen gyujtott kevesebb minta tovabbitasa kisebb savszeleseg mellett is megoldhato, igy jelentosen csokkanted a buffer underflow eselyet.

Overflow-t pedig ahogyan valaki fentebb emlitette "dobj el 1-et" modszerrel okosan lehet kezelni.

nem?

Ez összetett dolog. Az Opus codec különféle mintavételi frekvenciákat tud, illetve különféle hangminőséget. A sávszélességet a hangminőség fogja befolyásolni, a mintavételi frekvencia a PC hangszervere, azaz a tömörítettlen oldali interface szempontjából érdekes.

Ha alacsonyabb mintavételi frekvenciát választok, akkor a pulseaudio fog újramintavételezni, s abba belebetegszik szegény. Azért szeretnék én újramintavételezni, hogy a hangszervernek ne kelljen, mert tapasztalat, hogy ha sok futásidőt eszik, akkor szétesik a hangja.

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

Ki lehetne mindezt azzal is egészíteni, hogy figyelni lehetne a beszédben található szüneteket és ott kivárni a puffer feltöltését, így kicsit mintha tovább tartana a következő szó-szótag kimondása, de lehet, ritkábban kellene a mintavételt állítani.

Jó ötlet. Amúgy a forráskódban láttam, hogy kellően sok 'v' betű esetén sokat logol, így bekapcsoltam a -vvvv opciót, így közelebb jutok a probléma mibenlétéhez. Ha ropog a hang, ezt mondja:

Cannot write to pcm (Broken pipe)

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

Ezzel a jelenséggel (broken pipe) anno én is szoptam: audio stream, küldő-fogadó oldalon azonos mintavételi freki, e-line vonal - és mégis időnként dobálta a broken pipe-okat a logba. Icecast-darkice-páros.

--
"Nem akkor van baj amikor nincs baj, hanem amikor van!"
Népi bölcsesség

Arra jutottam, hogy szerintem ez a Linux hangrendszerének bugja. Vagy pulseaudio bug, de az is lehet, hogy az audio driver rosszul ad vissza valamilyen pointert vagy méretet. Lennart Poetteringnek meg hirhedt az a hozzáállása, hogy a pulseaudioban akkor sem hajlandó elfedni a hibát, nem hajlandó workaround-ot használni, ha amúgy ez lehetséges volna. Azt mondja, oldják meg a driver írók, ő a specifikáció szerint hívja a driver-t, s elhiszi az általa visszaadott értékeket.

Annyit haladtam vele, hogy pontosan tudom, melyik függvény tér vissza hibával. Úgy tűnik, ha nem kell a pulseaudionak újramintavételeznie, akkor kisebb futásidőt visz el, s nem jelentkezik a hiba. Ezért most azzal próbálkozom, hogy írtam C-ben egy buta, de gyors lineáris interpolációt alkalmazó újramintavételező függvényt, s a VoIP alkalmazásra bíznám az újramintavételezést, levéve ezt a terhet a pulseaudioról.

Az ősbűn az, hogy az Opus codec nem tud 44.1 kHz-es mintavételt, csak 48 kHz-et. Illetve nem csak, mert kisebbeket is tud, de a lényeg, hogy a 44.1 kHz-et nem tudja. Jelenleg a VoIP alkalmazásom az ALSA kompatibilitási layer-en keresztül 48 kHz-es stream-et nyit, de így a hangszerver szükségképpen újramintavételez.

Itt szeretném azt megoldani, hogy a pulseaudio-tól az ALSA kompatibilitási layer-en keresztül 44.1 kHz-es stream-et kérek, majd az Opus codec 48 kHz-es ki- és bemenete valamint a hangszerver közé illeszteném a saját resampler-emet. Ezzel persze még mindig nem oldottam meg azt a szabályozó algoritmust, amelyről beszéltem. Egyrészt, mert ahhoz natív pulseaudio interface kellene, másrészt azért, mert épp a hangszerver kezd recsegni-ropogni, ha újramintavételezni kell, harmadrészt pedig azért, mert kiszámoltam, hogy a gépek közötti frekvenciahiba nem okozna gyakori elakadásokat a hangban, így nem megy az érthetőség rovására. Kb. 2 percenként egy reccsenés nem nagy dráma.

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

Ez bizony könnyen lehet, még realtime kernellel is próbálkoztam, de nem jutottam dűlőre semmivel, egyszerűen néha (pár hetes, hónapos futás után) dobált pár broken pipe-ot, még néhábban pedig durva frame dobás-szerű jelenséget produkált (mint amikor cd-jatszóval beletekersz a zenébe). Ha jól emlékszem csak a reconnect oldotta meg ideiglenesen a hibát.

--
"Nem akkor van baj amikor nincs baj, hanem amikor van!"
Népi bölcsesség

Ne ez az, ha meg van kergülve, nekem is ezt csinálja. Másodpercenként vagy egy tucatszor eldobja az audio kapcsolatot, s újra kell azt építeni. Az snd_pcm_writei() negatív értékkel tér vissza.

snd_pcm_sframes_t audio_write(struct audio_data *ad, int16_t *buffer, snd_pcm_uframes_t buffersize, int silent)
{
    if ((ad->frames = snd_pcm_writei(ad->pcm_handle, buffer, buffersize)) < 0) {
        if (*ad->verbose || !silent) {
            snprintf(msgbuf, MBS, "Cannot write to pcm (%s)", snd_strerror((int)ad->frames));
            msgbook_enqueue(&mb0, MB_TYPE_ERROR, MODULE, msgbuf);
        }
    }

    return ad->frames;
}

Illetve még ez érdekes:

    if (mix_audio(pctx)) {
        /* playback decoded data */
        ret_frames = audio_write(&pctx->ad_playback, pctx->pcm, pctx->pcmlen, 1);
        if (ret_frames < 0) {
            int ret;
            /* underrun, let's handle it and try again */
            pctx->underruns++;
            ret = audio_recover(&pctx->ad_playback, (int)ret_frames, 1);
            if (ret == 0) {
                ret_frames = audio_write(&pctx->ad_playback, pctx->pcm, pctx->pcmlen, 0);
                if (ret_frames < 0)
                    die("audio_write() failed", 1);
            } else {
                die("audio_recover() on playback failed", 1);
            }
        }
    }

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

pont igy jelentkezik, ha az emlegetett nominalisan azonos, de valojaban picit eltero kuldo/fogado oldali frekvencia miatt idonkent buffer under/overflow van,
amit az ALSA az -EPIPE (= "broken pipe") hibakoddal jelez:

https://www.alsa-project.org/alsa-doc/alsa-lib/pcm.html

ket helyen irja, EPIPE-ra keress ra.

Érdekes téma.
Az az érzésem kicsit túl bonyolítod és valaki már emlegette, hogy az opensource voip szoftvereket is meg kellene nézned.
Említed, hogy az mcu -k világában otthonosabb vagy, esetleg érdemes egy voip készüléket megnézni.
Én első megközelítésben egy egyszerű streaming kapcsolatot csinálnék, a 41KHz mintavételezést pedig levenném a szabványos 8KHz -re, ami a beszédhez kell.

* Én egy indián vagyok. Minden indián hazudik.

Attól, hogy csökkentem a mintavételi frekvenciát, csak a sávszélesség igényem fog csökkenni, meg eltűnnek a magas hangjaim, de nem gyógyír az eredeti problémára, mely szerint, ha nem viszem át az órajelet, akkor vagy a csomagokba írt timestamp alapján, vagy a vételi buffer telítettsége alapján kell folyamatosan szabályozni az olvasott minták sebességét. A teljesen korrekt megoldás a resampling, s akkor nem nyávog a hang, de ez egy könnyebben implementálható megoldás volna.

Nagyobb baj, hogy nem triviális egy natív pulse interface-t, valamint a szabályozó algoritmust belefaragni az ALSA interface-re írt alkalmazásba. Pláne úgy, hogy nem vagyok egy C-zsonglőr. Bottal piszkálom a nyelvet, de azért le tudnak sokkolni a pointerekkel egymásra hivatkozó struktúrák. Nem elvileg nem értem, de egy idő után belefárad az agyam a hivatkozások lekövetésébe. Saját kód más lenne, ott az ember érti, mit miért hozott létre, nem a bonyolítás látszik mögötte.

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

Sajnos a Linux, pulse interface -ben nem vagyok otthonos.
A mintavételi frekvencia átvitele nekem kicsit homályos, miért is okoz problémát, Viszont átvinni át lehet erre voltak kitalálva a különféle kódolási eljárások az analóg világban, hogy egy PLL segítségével kinyerjék az órajelet. Lehet ezt kellene programban implementálnod?
(Én buta módon biztos annyit tennék, hogy a 8KHz mintavétel illetve a dinamika szabályozott streamet vinném át, dekódolnám ahogy jön és aztán kezdenék töprengeni, foglalkozni a minőséggel és a kapcsolati hibák okozta zavarokkal)

* Én egy indián vagyok. Minden indián hazudik.

A gond az, hogy ez a naiv elképzelés csak egy ideális világban lesz jó. Képzeld el, mi van akkor, ha a mikrofon hangját 7998 Hz-cel mintavételezed, majd a vételi oldalon a lejátszó hangkártya a beérkezett mintákat 8003 Hz-cel nyeli be, és küldi hangszóróra. Az egy dolog, hogy lényegében észrevehetetlenül magasabb lesz a hang. A nagyobb baj, hogy gyorsabban ürül a buffer, mint ahogyan telik, rendszeresen lesznek buffer alulcsordulások. Itt elakad a hang, s meg kell várni, amíg megtelik a buffer. Ez rendszeres lesz. Elvégre nem kizárt, hogy csak ritkán és alig észrevehető reccsenést kellene hallanom, de itt lényegében érthetetlenné válik a beszéd.

Ettől függetlenül lehet, hogy a pulseaudio ALSA-kompatibilis layer-ével is van gond, mert szerintem ilyen gyakori broken pipe nem lehetne. Egyáltalán, ha kiürül a buffer, az egyben azt is jelenti, hogy lezárják azt a pipe-ot? Aztán legközelebb elhasal, mert nem tudja tömni bele az adatot? Szóval lehet, hogy pulseaudio bug lesz ez.

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

Nekem ez az egész elképzelés furának tűnik.
Mikrofon 7998 és hangkártya 8003? ilyen nincs. A rögzítőeszköztől olyan adat kell jöjjön, amire fel van paraméterezve. Ha nem annyi jön, akkor hibás a drivere. A hangkártyának meg nem dedikáltan, direktben adod a hangcsomagot és az sem 8003-at nyel be, hanem 8kHz-et, ha azzal a paraméterezéssel hívod meg. Jó eséllel az alsa kell, hogy megoldja, hogy a különböző forrásokból érkező, akár különféle mintavételezésű hangcsomagokból összemixelje azt a mintavételezést, amivel aztán az egész kimegy a hangkártyára.

Vagy te most egy oprendszer és driverek nélküli megoldást akarsz írni, ami közvetlenül a vason futva, közvetlenül a hardverekkel kommunikálva feltalálja a spanyol viaszt? Nem véletlenül van annyi réteg a hardver felett, az a célja, hogy ne kelljen mindenkinek miden alkalommal mindent újra és újra megírnia. Annó dos alatt még lehet kellett ilyenekkel foglalkozni, de eltelt azóta pár évtized.

Szóval arra akarok kilyukadni, hogy biztosan ezek a valójában felmerülő problémák vagy csak a még hiányos ismeretek miatt próbálsz olyan dolgokat megoldani, amelyek nem kellene, hogy előforduljanak? Nekem inkább olyan emlékeim vannak, hogy bufferelni kell az adatokat, mert nem realtime veszi át azt a hangrendszer, plusz jellemzően körkörösen használják ilyenkor a buffert, ott is el lehet csúszni, ha nem megfelelő valami programilag. Arra is emlékszem, hogy van mód a szoftveres hangrendszer mixer kihagyására és dedikált hangkezelést kérni, de azt alacsony késleltetésű célokból szokták használni, például zenészeknek való szoftverek, hangszer szintetizálásra és élő effektezésre.

> Mikrofon 7998 és hangkártya 8003? ilyen nincs.

De van: ha nem ugyanaz az óra hajtja, és nem atomóra alapú az audió-rendszered, akkor bizony van némi frekvencia eltérés. Egyazon gépen többnyire egy óra hajtja a lejátszást és felvételt, de amikor két gép között megy a hanghívás, akkor nincs közös óra.

Ez a valóság. Mérd ki! Nem nehéz: veszel két gépet, az egyiket beállítod, hogy játssza a 440Hz-t, a másikon pedig indítasz egy gitárhangoló programot (pl lingot), és megnézed, hogy mit mér! Én valamikor régen mértem ilyeneket, és 5 Hz körüli szórást is találtam, ha jól emlékszem.

Ha közvetlen vasra, meg az oprendszer/hangalrendszer általánosan biztosított szolgáltatásokat kihagyva közvetlenül akarsz kommunikálni, akkor van. Miután adott paraméterezésű adatstream kerül ki a rögzítő eszköz driveréből és a paraméterezésnek megfelelő adatot várja el a lejátszóeszköz drivere, így ott már nem fordulhat elő. Ha mégis van, akkor vagy rosszul csinálod és nem a megfelelő erőforrásokat használod vagy valami bugos driver van ott, de akkor minden másnak is lenne baja vele.

Ahogy már írtam, azért vannak a szolgáltatások, hogy használjuk és ne akarjuk újraírni őket. Ráadásul az alrendszereket megkerülve valsz azokkal is összeakad a partizán program, ami különcködni akar. Azért kaphattok állandóan boken pipe-ot lejátszáskor, mert nem egyezik a paraméterezés az adatstream tartalmával. Ami persze lehet azért van, mert a láncban valahol valamit rosszul használtok.

"Azért kaphattok állandóan boken pipe-ot lejátszáskor, mert nem egyezik a paraméterezés az adatstream tartalmával. Ami persze lehet azért van, mert a láncban valahol valamit rosszul használtok."
- ezt egy kicsit jobban részletezhetnéd, úgy érzem hogy esetemben túl nagy hibalehetőség nincs a konfigurálásban. De egy általános digitális stream esetében is azért van pl. a szinkronjel, hogy az eltérő órajelek (jitter hiba) esetében is meg maradjon a szinkronitás - (a "broken pipe" ettől független, én valamilyen buffer-hibának gondolnám).

--
"Nem akkor van baj amikor nincs baj, hanem amikor van!"
Népi bölcsesség

Mivel valamilyen nyers adatokkal, alacsony szinten varázsolnak, ezért lehet, hogy nem ugyanannyi adatcsomag játszik, mint amire felparaméterezték az adott apit. Még az érvelésük is helyes, hogy miért fordulhat elő. Erre mondom én, hogy ha kíváncsiságból vagy mazohizmusból akarnak játszani alacsony szintű megoldással, akkor ez oké, mindenkinek lehet ilyen hobbija. Ha viszont a hangátvitel a cél, s csak akadályba ütköztek a megvalósításánál, akkor valószínűleg nem a megfelelő magasabb szintű apit / libet használják, amit éppen azért hoztak létre mások, hogy ne kelljen minden programnak újraimplementálni a megoldást a hardveres problémákra.

Magasabb szintű apikkal jellemzően csak egy buffer memóriaterületet kell biztosítani, valamilyen callback kellhet a buffer töltögetéséhez az adatokkal. Ha itt valamit rosszul kezelnek, elszámolnak, akkor persze kiürülhet a buffer és jön a broken pipe.

Ha a forrás és cél közötti adatátviteli hálózat egyenetlenségéről van szó, ott is buffer a megoldás. Meg persze le kell kezelni, ha szakad a hálózat, nem tudják feltölteni a buffert...

Nem vagy te véletlenül programozó? :)

Azt értsd meg, hogy a nominális és a tényleges sebességek nem azonosak. Az API és a programozó a nominális értékekről fog tudni. A tényleges érték közelébe akkor jutsz, ha a gépedet elviszed az Országos Mérésügyi Hivatalba, majd egy nagy pontosságú, hitelesített frekvenciamérővel megméred mindezt. De akkor is csak a valóság abban a pillanatban és olyan környezeti feltételek mellett - hőmérséklet, páratartalom, gravitációs gyorsulás, tápfeszültség - mért közelítéséről beszélhetünk.

Értem, hogy ha van mindkét gépen hálózati időszinkron, akkor lehet resampling-et csinálni, s a probléma megoldható, de részben erről beszélek, meg részben arról, hogy ha a pulseaudio-nak újramintavételeznie kell a hangot, s viszonylag sok futásidőt kezd el enni, akkor bizony szétesik a hangja, ami a hangszerver bugja, nem az alkalmazásé. Erre akarok alkalmazás oldali workaround-ot írni.

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

Igen, linuxos kernel- és hangszerver-fejlesztők olykor kezükbe vesznek oszcilloszkópot is, s azt használni is tudják. Akkor van baj, ha valaki úgy érzi, neki semmi köze az elektronikához, a hardware-hez, s szentül hisz a neki kínált library-k, API-k mindenhatóságában, s nem törődik azok fizikai korlátaival.

Olyan ez, mint az a fiatal, akinek megtanították fizika órán, hogy a gumi szigetel, majd ezt bizonyítandó gumicsizmában felüljáróról rálépett a villamos vontatás 25 kV-os felsővezetékére. Úgy tudom, túlélte égési sérülésekkel.

A gumi valóban szigetel, de vannak ennek a kijelentésnek érvényességi határai: addig, amíg nem érjük el az átütési szilárdságot, vagy a felületen akkora térerősség nem alakul ki, hogy a szennyeződésen át kialakul a villamos ív.

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

Amiről te írsz, az az ideális világban van. Attól, hogy egy kvarcra ráírták, hogy az 24 MHz-en fog rezegni, ha oszcillátort építenek belőle, csak egy felirat a fém tok oldalán, de nem a vágyott valóság. A software-ek persze azzal számolnak, hogy az pontosan 24 MHz-en jár, de azt nem tudjuk, hogy a valóságos órajelfrekvencia mekkora.

Ha már itt tartunk, a kvarcoknak van soros és párhuzamos rezonancia frekvenciája. A kettő értelemszerűen nem lehet azonos, bár nagyon közel vannak egymáshoz. Tehát pusztán a kapcsolástechnikától, attól, hogy a soros vagy a párhuzamos rezonancián rezgeted be a kvarcot, kis mértékben eltérő frekvenciát kapsz.

Tudom, vannak kész oszcillátorok, négy lábuk van, jellemzően táp, GND, kimenet és egy engedélyező láb. Ezek sem tudnak végtelen számjegy pontossággal azonos frekvencián járni, ez elvi képtelenség. És itt a mikrofon az egyik számítógépben van, ott történik a mintavételezés, majd valahol máshol van egy másik számítógép, ahol a lejátszás zajlik. A kettő között nem visszük át az órajelet, nincs közöttük szinkron. Más kérdés, hogy ha az adatcsomagok tartalmaznak időbélyeget, akkor egy újramintavételezéssel a probléma megoldható. Meg akkor is, ha mérjük, mennyi adat van átlagosan a bufferben. Éppen erről írtam. :)

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

PC-re írom, oprendszer fölött, azon belül Linuxra. A kernel nem is hagyná, hogy közvetlenül piszkáljam a hardware-t, meg kicsit nagy falat volna hangkártya driver-t írni. Használom a szolgáltatásokat, az API által biztosított függvényeket, nincs is más lehetőségem. Sőt, a Seren eredeti kódját nem én írtam. Azért nyúlok hozzá, mert recseg-ropog, s elkezdtem gondolkodni ennek lehetséges okán.

Már ne is haragudj, azt egy nagyon szemellenzős és szélsőségesen software-es meglátásnak tartom, hogy nem vagy hajlandó végiggondolni a dolgok fizikáját, abból indulsz ki, hogy az API biztosít valamit, és az úgy is van.

Én hardware fejlesztő vagyok, éppen ezért látom ezt a számítógép működése felől. Nem kell ahhoz bugosnak lennie egy kernel modulnak vagy egy software-nek, hogy belássuk, attól, hogy kérsz egy 44100 sample/s-os stream-et az operációs rendszertől, az nominálisan lesz annyi, nem pedig ténylegesen. Mégpedig ugyanazért, amiért a hálózati időszinkron nélküli órák is elkezdenek egymáshoz képest elmászni. Mert semmilyen fizikai mennyiséget, így az időt sem tudjuk végtelen pontossággal megmérni. Ennek elvi okai már a kvantummechanikában rejlenek, de gyakorlati oka is van: nem tudsz valamit úgy klónozni, hogy az a környezeti peremfeltételekkel együtt tökéletesen ugyanolyan legyen.

Tehát, ha kérsz egy 44100 Hz mintavételű stream-et a hangszervertől, az az egyik gépen például 44108 Hz-es, a másik gépen meg mondjuk 44097 Hz-es lesz. Ennek hardware okai lesznek, s közben a hangszerver teljes mellbedobással és őszintén hinni fogja, hogy ez mindkét esetben 44.1 kHz. Tehát a software azonos frekvenciát fog gondolni valamiről, ami nem azonos, de ennek nem software bug az oka.

Hogy tovább menjek, a számítógép kvarcoszcillátorainak hőmérsékletfüggése is van, de még öregedése is, azaz a környezeti hőmérséklet függvényében is változni fog a mintavételi frekvencia, továbbá a számítógép életkorától függően is lesz ennek valamekkora driftje.

Egy VoIP kommunikációban tehát lesz hatása annak, ha a két beszélgetőpartner közül az egyik kinyitja az ablakot, s megváltozik a környezeti hőmérséklet. Bár, mint mondtam, ezen hatások megfelelő software-es technikákkal, szabályozó hurkokkal kompenzálhatók, de ezért tenni kell.

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

ez mind igaz, míg a nyers adatforrással dolgozol... amit több réteg elfed, ha a megfelelő apit használsz. És ott már a 44.1kHz már tényleg 44.1kHz. Mert butaság lenne, ha a lánc minden pontján bárhol elmászhatna bármi.

Két sip telefon közt sem valós eset az, hogy össze-vissza elmászó kHz értékekkel menjen az adat. Ha vesznek csomagok vagy lassú az átvitel, az már hálózati hiba, egészen más jellegű probléma, nem mintavételezési. Persze, ha egy rosszul működő, nyers megoldásokat használó - gány - dologból jön az adatstream, akkor az okozhat problémát a lejátszó oldalon is és persze hibás adatok kezelésére szükség van. Ha a két oldal kézfogásakor abban egyeznek meg, hogy 8kHz-es 16 bites pcm stereo codec-et használ, akkor hiba, ha mégis mást küld.

Technikailag hogyan valósítod meg az operációs rendszer kernelében és a hangszerverben lévő API-k segítségével a mintavételi frekvencia hibájának elfedését, s elméleti nominális értékre történő korrigálását?

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

ez is már 15 éves leírás, ez is elég alacsonyszintű
https://www.linuxjournal.com/article/6735?page=0,2

de ez sem jó megoldás valsz, pont azért, mert nem dedikált hanglejátszás/rögzítés kell, hanem más programok mellett való működés. Viszont az elején talán érthetően leírják, hogyan működnek általánosságban ezek.
Korábban egyik kommentben hangszervert is említettél, arrafelé lehet a jó megoldás.

Ha jól sejtem a pulseaudio a legelterjedtebb manapság
https://freedesktop.org/software/pulseaudio/doxygen/parec-simple_8c-exa…

de ez valami nagyon minimál példa, ezért érdemesebb lehet belenézni valami komplexebb program forrásába. Komplexebb esetekben szerintem inkább callback-re épülő, vagy talán interruptra épülő megoldásokat használnak az adatok ki/bepakolására.

A sip-et azért is hoztam fel korábban, mert például ahhoz léteznek kész libek is, amivel a tehnikai részletek nagyja megvan és nem kell új protokolokat kitálalni a hálózati kommunikációhoz sem.

Ezért mondom, hogy ilyen nincs. Azon a szinten már pontos adatok kell menjenek. Ha például 8kHz mono 16bit pcm, az egyértelműen meghatározza, hogy milyen formátumú és mennyi adat kell átkerüljön, ami pontosan meghatározott időtartamnyi hanganyag. Ott fel sem merülhetnek az általad emlegetett mintavételezési problémák, mivel az tisztán adatfeldolgozás. Ott csak a hálózati ávitelbeli egyenetlenségek kezelése probléma, amire bufferelés a megoldás, de nem hanggal kapcsolatos szinten. Legfeljebb annyi köze van a hanghoz, hogy ha 100ms buffert akarsz, akkor a codec paramétereiből tudod kiszámolni, hogy az mennyi bájt méretű buffert jelent.

A rögzítés és lejátszás részen fordulhat elő az általad leírt hardveres probléma, de az meg el kell fedje a hangrendszer megfelelő szintű apijában az implementáció. Nem a sima programok dolga. Ha több hangstreamet kéne kezelni egy helyen és azokat közös nevezőre hozni, mint például egy konferenciahívás kezelése, ott már szükség lehet a központban a streamek összemixelésére, hogy minden klienshez csak egy hangstrema menjen, de ez egészen más dolog már.

Btw a mintavételezési problémák egy számítógépen belül is jelen vannak. Nem valósidejű a feldolgozás, hanem időosztásos. A hangeszközök valsz saját órajellel operálnak egyébként is és bufferbe téve adják át a hangmintákat, hogy amikor ráér a másik eszköz vagy cpu, akkor majd feldolgozza. Bár itt már a DMA kerül képe. De ez mind-mind lényegtelen egy olyan magas szintű programnál, mint egy hangrögzítő - streamelő - lejátszó alkalmazás. Azok mind magas szintű apit használnak, ami el kell fedje az időzítési problémákat - nem a kliens program dolga. Ahogy már írtam, a zeneiparban használnak olyan alkalmazásokat, ahol fontos az alacsony késleltetés, s ezért a közvetlenebb hardverkezelés. Azoknál szoktak saját implementációt készíteni az alacsonyabb szintű apikra.

Értem amit mondasz, de ez nem egyéb, mint idealizmus. Az volt a kérdésem, hogy a kernel driver-ben és a hangszerverben _hogyan_ feded el az eltérő sebességeket? Ugye, most nem arról beszélek, hogy az egyik hangkártya 48 kHz-cel dolgozik, s akkor átszámolja és újramintavételezi a hangszerver 44.1 kHz-re, ha az az alkalmazás igénye. Most arról beszélek, hogy a két számítógépben eltérő sebességgel telik az idő, miközben a tényleges, valóságos idő egyféleképpen telik.

Mi az a tényleges fizikai eljárás, algoritmus, hogy ha nincs a két számítógép közé kihúzva egy szál drót, amelyen átmegy egy frame szinkron jel, ennélfogva együtt jár a két gép, akkor mégis hajszál pontosan ugyanazzal a sebességgel fogja a mikrofon jelét mintavételezni az egyik gép, mint amekkorával a másik gép a bufferből felszedi és a D/A átalakítóba küldi a jelet?

Amit most mondasz, az körülbelül az, mintha azt mondanád, hogy ha építesz két digitális órát, azokat beállítod pontosan, elindítod őket egyszerre, akkor egy hét múlva is egyforma időt mutat a két órád, ha nincs bennük software bug. Azt lásd, hogy ez nincs így, olyannyira, hogy ez a két óra ugyanannak a gyártónak ugyanazzal a hibátlan software-ével is eltérő sebességgel fog járni, mert a bennük lévő, nominálisan 32768 Hz-es kvarc az egyikben 32765 Hz-en, a másikban pedig 32770 Hz-en fog járni. Egyszerűen az időalapod, az idő referenciád lesz más, s erről a software nem tud. A software mindkét órában 32768 ciklusonként fog a másodpercekhez hozzáadni egyet. A baj ezzel csak annyi, hogy egy hét elteltével a két óra már nagyon mást fog mutatni.

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

Ó, sajnálom... csak most esett le, hogy te nem konkrét dolgot akarsz megoldani: hangot átvinni, hanem csak elmélkedni valamiről. Még mindig naiv vagyok, hogy valaki problémát akar megoldani, nem pedig problémát generálni és azon rágódni. Megzavart, hogy úgy tűnt programozni akarsz.

Ha a lejátszó oldal gyorsabban akarja lejátszani, akkor idővel az buffer underrun-hoz vezet a hálózati buffernél. Mivel nem kérhet jövőbeli adatokat, ami még nincs.
Ha a lejátszó oldal lassabban játssza le, akkor csak szimplán egyre nagyobb késés kerül a lejátszásba. A küldő oldalon esetleg buffer overflow-ot okoz idővel, ha az nem tudja hová tenni a felhalmozódott adatokat. Ha olyan módon viszik át a hangot, hogy nem érdekli a küldőt, hogy a fogadónál megérkezik-e (udp), akkor időnként elvesznek csomagok.

Az ilyen problémákat pedig valószínűleg a szokásos hálózattal kapcsolatos hibakezelés lefedi, nem kell külön foglalkozni vele. Ha borul a buffer, akkor újrainicializálja azt, s kitölti a problémás időszeletet valamivel a program. Nem hangkártya szinten kell kezelni.

Gyanítom azért sem csinálnak hétköznapi esetben problémát belőle, mert a mintavételezési frekvencia nem fixen mászik el, hanem ingadozik, így átlagban kicsi a különbség. Ha meg konstans lassú, mondjuk 10Hz-el kevesebb a küldő a fogadóhoz képest, 8kHz esetén, akkor ha nem számoltam el akkor 50ms buffer esetén 40 másodpercenként lesz bufferhiba.

Ahogy neten látom, ennél sokkal pontosabbak lehetnek a hangkártyák.
http://www.baudline.com/solutions/sample_rate/index.html
http://www.leapsecond.com/pages/sound-1pps/

Ha 50 milliomod másodpercnyi a hiba, akkor 16 percenként lehet szükség buffer korrekcióra 50ms méretű bufferrel. Ráadásul, ha a program növelheti a bufferméretet, ha úgy érzékeli, hogy túl gyakran ürül az ki, szóval némi késleltetés árán csökkentheti a hibákat. 200ms körül van a felső határa az élő hangnál annak, amikor még nem kifejezetten zavaró a késés, úgy már óránként lehet mondjuk egy buffernyi (200ms) kimaradás, ami lehet épp fel sem tűnik. Nem interaktív streamingnél pedig akár több másodperces buffert is használhatnak. Előre tárolt taratlomnál pedig akár előtöltheti a teljes tartalmat is lejátszás közben.

Szóval a legtöbb hétköznapi használatnál annyira minimális hatása van ennek a jitternek, hogy nem érdemes külön foglalkozni vele, ahogy írtam a hálózatkezeléssel kapcsolatos bufferkezelés lefedheti az általa okozott hibát.

Egyébként a hangkártya pontatlanságát felülgyelheti a driver - szerintem, mivel nem ástam bele magam jobban.
A mai pécék sok MHz-es órajelével esélyes, hogy megfelelő hardveres és és szoftveres implementációval érzékelhetik a kHz-es tartományú hangkártya pontatlanságát.

Tényleg ennyi időbe tellett, amíg megértetted, hogy mi a probléma? Az, hogy ha az időalapod nem azonos, abból lehet buffer underrun vagy overflow? Ha lassan is, de azért csak sikerül egy nyekvet beszélnünk. Bár az a fajta software-es mentalitás elborzaszt, hogy „Ha borul a buffer, akkor újrainicializálja azt, s kitölti a problémás időszeletet valamivel a program.” Tudniillik egy jól megírt programtól azt várom, hogy ha most elkezdek beszélgetni, akkor két napig is csacsoghassak a hangban bekövetkező egyetlen reccsenés nélkül. Ez az a „sz.rjuk le, majd lesz valami” hozzáállás, amit ki kellene irtani a programozók fejéből, mert emiatt dőlnek össze programok, s okoznak sokszor jelentős károkat. Persze megvan a megoldás, software-re senki sem vállal felelősséget. :(

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

Nem mondtam, hogy megoldhatatlan, a buffer telítettségének folyamatos mérésével magam is egy szabályozó algoritmust javasoltam. A gond az, hogy a pulseaudio még a 13-as változatában is beteg - vagy a driver -, mert bizony recseg-ropog, ez objektív tény.

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

Ez inkább valami hardverspecifikus probléma lehet nálad. Főleg intel hda kapcsán van rengeteg bugreport és workaround az ócskább chipsetekkel, dma fix meg más paraméterek alapértelmezettől való átállítása, hogy elérje a használható szintet.
https://wiki.ubuntu.com/Audio/PositionReporting

Az órajelkülönbséges hallható hibák annyira minimálisak maximum, hogy észre sem vennéd őket. Nem arról beszélsz, hogy negyedóránként vagy egy glics... de lehet mai eszközökkel több óránként lenne csak egy pár ms-es glics, nem folyamatos recsegés-ropogás.

Az iparban eredményért fizetnek, nem pedig disszertációért. Költség-haszon... nem csak pénzbeli, de a gép erőforrásai szempontjából is. Ami nem igazi probléma, arra nem költenek milliókat, csak hogy elmondhassák minden hibátlan, csak épp használhatatlanul lassú és marha drága, mert lett egy bonyolult, de minden szempontból szép megoldás.

Azt én is írtam fentebb, hogy a gyakori recsegés-ropogás már nem írható az órajelfrkvenciák különbségének számlájára.

Csak részben van igazad. Olyan területen dolgozom, ahol ez a gondolkodásmód nem megengedett, mert közvetlenül emberéleteket veszélyeztethet, és a szabványok is szigorúbbak a szokásosnál. Ezt a gondolkodásmód miatt írom, a konkrét topic egy hobbi project-ről szól.

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

Ide írom, mert ide illik az okfejtésedhez, bár az egész téma indítóra igaz.
Teljesen rossz helyen keresed a problémádra a megoldást. Noha valóban vannak eltérések a hardverben, azok jóval minimálisabbak. Az általad leírt hipotetikus esetben 11 Hz pontatlanság van a rendszerben. Ez azt jelenti, hogy a baszús skála alján gyakorlatilag egy teljes hangjegyet hibázol (a C2-D2 hangjegy között pl ~8Hz a különbség), ráadásul ez a hibád konstans (minden frekvencián megjelenik, hiszen az az alapórajel eltérésével magyarázod). Ez egy igen durva pitch shiftet eredményez. A hipotetikus rendszered innentől képtelen zene lejátszására, sőt igazából bármilyen audió feldolgozásra alkalmatlan. Ettől függetlenül valóban van hibája a hangkártyának, csak kisebb nagyságrenden (ráadásul az ingadozó is).

Ellenben ott van egy hálózat is, ami normál körülmények között is 10-100ms-ig bármilyen késést össze tud szedni. Továbbá a választott protokolltól függően vagy megkapod minden darabját az eredeti adattartalomnak, vagy nem (UDP esetén inkább nem, TCP esetén igen, de még nagyobb késéssel az újra küldések miatt). Esetenként helytelen sorrendben jönnek meg az adataid (szintúgy UDP esetén). Ezek a hálózati problémák okozzák a puffer hibáidat, mivel ha nem jön meg az adat mire szükség lenne rá (le kellene játszani), akkor véget ért a móka, csak a puffer underrun marad. Aztán utána esetleg beesik az összes már elkésett adat, a szoftvered meg csak néz.

Szóval összességében a véleményem: miközben vizsgálgatsz egy fűszálat, hogy biztos abban botlasz el (hangkártya frekvencia eltérés) nem veszed észre a több talicskányi sódert, amiben valójában felbuktál (hálózati késések).

Upd.: Ettől független elméleti problémának érdekes a felvetésed. Ha a hálózati hibákat figyelmen kívül hagyhatjuk, mert tökéletes az átviteled, akkor én pl inkább egy szoftveres lineáris upsamplingben gondolkodnék. Az idő rendszeres (mondjuk 10ms-es) szinkronizálásával pedig el tudod dönteni, hogy normál lejátszás mehet, eldobsz a pufferből, vagy az upsamplingelt tartalombol a saját idő szerinti lejátszással egy kis pitch shift árán kitöltöd az 1-1 ms-es eltérést.

Zavard össze a világot: mosolyogj hétfőn.

Köszönöm a hozzászólásod, ez legalább konstruktív volt.

Az elején szerintem tévedsz, a többiben igazad van. Amikor a 11 Hz hibáról beszélünk, az a 44.1 kHz-hez képest értendő, ami 249 ppm. Az más kérdés, hogy a példám valóban túlzó, nagyjából 50 ppm a reális. Ennek megfelelően egy nagyon mély hang lejátszása is csak 50 ppm relatív hibával történne, amit nem hallunk meg. Akkor maradna meg Hz-re pontosan a hibánk, ha kevernénk a frekvenciát. Az analóg FM rádiók esetében épp ezt csinálják: 90 MHz környékéről lekeverik a vivőt 10.7 MHz-re, de a frekvencia löket ugyanúgy 300 kHz marad, ami viszont a 10.7 MHz-es középfrekvenciára relatív sokkal nagyobb, mint az eredeti 90 MHz-ez vivőhöz arányítva. Így könnyebb lesz demodulálni.

A másik, hogy írtam, valóban más oka van az intenzív recsegés-ropogásnak, mert néhány percenként lehetne a frekvenciahiba miatt egy reccsenés. Viszont nem úgy tűnik, hogy ez hálózati hiba, a csomagvesztés is logolásra kerül. Sajnos a pulseaudio, a hangkártya és annak kernel modulja az a három összetevő, ahol valami nincs rendben, s ebből jön a baj. Illetve a Linux kernel ütemezőjét is ide vehetjük még. Tapasztalatom szerint, ha a pulseaudio viszonylag sok futásidőt visz, akkor szétesik a hangja. Ha kell újramintavételeznie, akkor sok futásidőt eszik. Márpedig az Opus codec 48 kHz-es stream-et állít elő és ilyet fogad, a hangkártya meg 44.1 kHz-ről megy. Mehetne hardware-esen 48 kHz-ről, de akkor a legtöbb egyéb hanganyagnál kellene a resampling: csöbörből vödörbe.

Ezért döntöttem úgy, hogy a kliens oldalon újramintavételezek: a hangszervertől 44.1 kHz-es stram-et kérek, az Opus codec-nek 48 kHz-eset adok, és ilyet fogadok tőle. Lineáris interpolációt írni nem túl bonyolult. Tudom, a harmonikusok elnyomására kellene mindenféle szűrés, de beszédhang átvitelénél első közelítésben sebességre gyúrok, nem hangminőségre. Szerintem bőven rendben lesz a minőség.

Ezzel tehát semmit nem oldok meg, csak egy workaround, leveszem a pulseaudio-ról a resampling terhét, s az jótékonyan hat majd várhatóan a lelkére.

Aztán van az elméleti fejtegetés a problémára. Itt sokféle megoldás lehetséges. Gondolkodtam azon, hogy egy analóg átvitel mitől jó, míg egy mintavett digitális átvitellel mi a baj. Hát az, hogy a mintavételnek szinkronban kellene történnie, ugyanakkor nem visszük át az órajelet, s a küldő és fogadó oldalon eltérően telik az idő.

Az újramintavételezés a tökéletes megoldás, de ha van mód arra, hogy finoman állítsuk a lejátszóban a mintavételi frekvenciát, akkor lehet integráló szabályozót csinálni, ahol a szabályozó hibajele az optimális buffer telítettségtől való eltérés. Aztán egy másik megközelítés, hogy időbélyeggel látjuk el az adatcsomagokat, s erre szabályozunk. További megoldás, hogy nem nyúlunk a sebességhez, csak néha eldobunk egy-egy mintát, vagy beálmodunk egyet-egyet. Például két szomszédos minta közé azok átlagát, vagy egyszerűen az egyiket megismételjük. Nem teljes buffert, csak egy-egy hangmintát.

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

Azt már kipróbáltad mi van ha csak egy "sima", mondjuk 1KHz hangot viszel át?
Esetleg megpróbálni felvenni és visszanézni audacity -vel, ott kellene lennie a recsegésnek és talán az alakjából rájöhetsz mi lehet a forrása, az oka.
A minta pótlás jól hangzik, de ez nem a driver szintjén történik/történhet?
(Bizonyos esetekben, a forrás-nyelő probléma esetén, a stream függvényében akár teljes csomagokat eldobnak vagy valamivel pótolják.)

* Én egy indián vagyok. Minden indián hazudik.

Annyiban felesleges nézni, hogy tudom, a pulseaudio ALSA kompatibilitási layer-ének pontosan melyik függvénye tér vissza negatív értékkel, és egyúttal hibakóddal. Alulcsordul a buffer, a kis mocsok meg lezárja a stream-et, mondván, hogy broken pipe. A kliens ekkor újra megnyitja, s átadja a buffert lejátszásra, de mindez idő, s ha másodpercenként sokszor fordul elő, akkor érthetetlenné töredezik a hang.

Most azért áll a project, mert nincs rá elég időm, ezt fél szívvel nem lehet csinálni. Le kell rajzolni, melyik struktúrában melyik pointer melyik bufferre mutat, végig kell pogarászni az initet és a lezárást is, aztán csak így lehet belenyúlni. Az megvan, hogy hol kell belenyúlni, a kellemetlenebb, hogy most már van külön 48 kHz-es és 44.1 kHz-es buffer is, mindegyik capture és playback irányban. Ha lesz időm nagyon elmélyedni benne, meg fogom oldani, mert vázlatosan értem a működését, csak itt most nagyon fontos volna a tisztán látás.

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

Itt mutattam eredeti forráskód részletet. Az erőszak nem segít, mert attól, hogy úgy teszek, mintha minden rendben lenne, a hangszerver még nem fogja elvenni és lejátszani a buffer tartalmát. A kliens hibánál legalább megpróbálja megjavítani a kapcsolatot. Ez ugyan sikerül neki, de az addig eltelt időben csend van, ami ezáltal egy nagy reccsenés lesz.

A hónap végén lehet, hogy lesz időm foglalkozni vele, bár nem biztos még.

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

Szerintem ne mintavételezz újra. Mivel csak PC-kben gondolkodsz, már a streamszervertől is 48 kHz-es mintavételezést kérj. Már egy másfél évtizete minden PC-s hangchip (függetlenül attól, hogy integrált vagy dedikált) tud 48 kHz-et átvinni.

Bár rosszabb esetben maradhat a 44,1 kHz is, az Opus encoder úgyis újramintavételez.

Mindenképp mono sáv legyen, az Opusban a bitrátát 24-64 kbps közé érdemes belőni, próbálgasd, hogy melyiket hallod megfelelő minőségűnek.

“I didn’t start using Linux so I could have friends.” (Luke Smith, 2019) 🐧

Értem, amit mondasz, de ez nem ilyen egyszerű. Ha van egy 44.1 kHz-es és egy 48 kHz-es stream-ed, akkor hardware-esen az egyikre programozhatod a hangkártyát, de a keverés előtt kell a resampling a másik miatt. A pulseaudio természetesen tudja ezt, csak az a baj, hogy vagy azért, mert operációs rendszerről beszélünk, s az ütemező nem adja vissza neki időben a vezérlést, vagy valami bug folytán az a helyzet, hogy ha megemelkedik a CPU felhasználása a pulseaudio-nak, akkor ropogni kezd a hangja. Márpedig az újramintavételezés számításigényes, ha mindenféle sin(x)/x függvényeket fektetnek rá, meg esetleg fft-t csinálnak. Persze lehet, hogy Lennartnak van igaza, s a driver rosszul ad vissza bizonyos értékeket, ő meg nem használ semmilyen workaround-ot, hanem elhiszi, amit kapott, s abból számol. Aztán ebből baj lesz.

Úgy emlékszem, amikor nagyon bugzott a pulseaudio, akkoriban Lennart írt arról, hogy hív az ALSA API-ból olyan függvényt, amit épeszű felhasználó nem szokott hívni, éppen ezért sok esetben nincs is normálisan implementálva, de neki szüksége van rá. Teszem azt, hol jár a pointer a bufferen belül, meg effélék. Annak idején sértődötten be is logolta, hogy ez nem pulseaudio bug, tessék az ALSA fejlesztőknél házalni vele. Natív ALSA-val meg nem jön elő a hiba, mert a kutyának sem kell az a függvény.

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

Szerintem a Pulseaudio nem mintavételez újra. Ezt a hangchip meg tudja csinálni hardveresen, a 44,1-48kHz-re átalakítást meg az Opus encoder is megcsinálja. Nekem ilyen recsegős gondom sose volt a Pulseaudióval, pedig próbáltam 22-384 kHz-es mintavételezésű hangfájlokkal is, meg VoIP alkalmazásokkal is (Skype, Facebook Messenger). Persze, egy bloat hulladék, Pöcstering nem okozott csalódást, hozta a tőle elvárhatót, sokszor magas a CPU terhelése (bár annyira azért nem, hogy modern gépeken ez gondot okozzon), meg ha valami bug van, akkor be tud ragadni a PA server folyamat, és akkor egyáltalán nincs hang, amíg újra nem indítod, de ilyen recsegős problémám az életben nem volt vele, meg egy ótvar lassú netbookon sem.

“I didn’t start using Linux so I could have friends.” (Luke Smith, 2019) 🐧

Kénytelen újramintavételezni, ha egyszerre szólaltatsz meg különböző mintavételi frekvenciájú hangokat. Az Opus nem tudja a 44.1 kHz-et, nézd meg a doksit. Ha tudná, már rég készen lennék vele. 48 kHz-et tud, meg nem tudom fejből, mit még, de e 44.1 kHz-et nem.

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

Az Opusnak bármilyen mintavételezésű folyamot átadhatsz, bitráta függvényében újramintavételez. 64 kbps/csatorna bitrátától 48 kKz-re, és 20 kHz-es lowpass filterrel tömörít. A 44,1 kHz-et is beveszi, csak kódolni nem tud annyin, előtte újra kell mintavételeznie.

Én egyébként ezért is nem használok zene tömörítésére Opust. Hiába jobb a tömörítése az Vorbisnál, ha egyszer csak extrém alacsony bitrátán jön ez ki (annyira nem szoktam lemenni), meg ez a kötelező 48 kHz-re újramintavételezés és default 20 kHz-es lowpass is zavar, meg hogy nincs hozzá replygain megoldás, támogatás. Így hiába írja a Xiph oldala, hogy Opust ajánlott használni Vorbis helyett, én maradok zenénél az utóbbinál, oggenc -q8 profillal, ami eleve overkill, messze a transzparencia határa fölött van, de ennél lejjebb nem nagyon megyek, ritkán -q7 környékére. Azelőtt lame -V0-t használtam, még azt megelőzően lame -b320 -q0 beállítással tömörítettem, amíg a hydrogenaudio fórumán le nem beszéltek róla a LAME fejlesztői.

Gondolkoztam az aac használatán is, de ott meg nem egységesek az encoderek, a legjobb aac encoder meg az Apple-é, és ahhoz csak windowsos bináris van, ami ugyan Wine alatt tökéletesen futna, de elvi kérdés, hogy egy ilyen szutyok cég, zárt forráskódú szutykával nem tömörítek semmit. Eleve nem akarok zárt formátumot támogatni.

Viszont az alacsony latency miatt, meg nagyon alacsony bitrátán nincs ellenfele az Opusnak. Egyedül talán néhány adaptív kódek tudja verni, de csak szélőségesen alacsony sávszél esetén, amikor már amúgy is olyan rossz a minőség, hogy nem lesz erény, max. csak az átvitel nem szakad meg.

“I didn’t start using Linux so I could have friends.” (Luke Smith, 2019) 🐧

Szerintem te az opusenc parancsról beszélsz, ami egy CLI felület az Opus codec-hez. Ez a szerkezet is éppen azért újramintavételez 48 kHz-re, mert a codec bizony nem tudja a 44.1 kHz-et.

https://opus-codec.org/docs/opus_api-1.2/group__opus__encoder.html#gaa8…

Fs opus_int32: Sampling rate of input signal (Hz) This must be one of 8000, 12000, 16000, 24000, or 48000.

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

Bugreportoltam, de kétlem, hogy egy kevesek által használt, marginális software-re hatalmas erőforrásokat mozgósítanak majd. Mindegy, majd látni fogom, hogy a közelébe mennek-e egyáltalán a fejlesztők.

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

Direkt módon átvinni a hangot hálózaton? Igazából ezzel két baj van. Az egyik, hogy nincs tömörítve, a másik, hogy nincs titkosítva. Lokális hálón még esetleg, de nyilvános hálózaton ez olyan, mintha az erkélyről átkiabálnék a szomszédba.

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

errol jut eszembe -- simplex / full duplex:

arecord | ssh user@host 'aplay'
arecord | ssh user@host 'aplay& arecord' | aplay

(ha tomorites is kell, akkor lehet ssh -C is :), vagy talan opusenc / opusdec-en keresztul pipeolni.)

ez lossless, szoval hosszu ido utan itt elo fog jonni a PPM elteres. ha ezzel (nem egyezo mintavetelezes) akarsz jatszani:

arecord -t raw -r 8000 | aplay -t raw -r 7800

ami valoszinuleg szoftverben fog resamplinget csinalni, lasd --disable-resample.

Azért szeretném ha kicsit pontosítanál, a "hang" számomra 20Hz - 20KHz amiből, mondjuk a beszédhang mindössze 3KHz - klasszikus telefon szabvány alapján 8,x KSPS és előtte dinamika kompresszor.
Tehát, most HiFi minőségű zenét vagy jól érthető beszédet akarsz átvinni?

* Én egy indián vagyok. Minden indián hazudik.

teljesen értem a problémát a különbözö PC-kel (a német azt mondja "Wer mißt, mißt Mist" - azaz, "aki mér, az rosszul mér"), de nem lehet, hogy ezt a hangkártya gyártók is felmérték és saját, nagyobb pontosságú kristályokat használnak az áramköreikben? Mondjuk írtad a 2 PC-vel való tesztelést, de akkor is felmerül bennem ez az ötlet...

Illetve releváns ez egyáltalán? Amikor AudioGrabber-el CD-t digitalizálok (ami perverz, mert a CD-n is már digiálisan vannak az adatok...), akkor sem hallom másnak az mp3-at az én vagy egy másik gépen, pedig különbözö kristályok vannak bennük. Ebböl ítélve, a kvarcok által okozott különbséget egy mini-pufferel agyon lehetne csapni és inkább az adat átviteli részre kéne rágyúrni? Valami okosság (timestamps?) és UDP átvitel, aztán a fogadó oldalon egy algoritmus, ami a timestamp szerint sorrendbe rakja az adatokat - de max 1-2 sample-t vár és ha nem jön az arra a pozicióra vonatkozó adat akkor átugorja és folytatja a lejátszást a következö timestamp-nél? Ha túl nagy a puffer és a kapcsolat ingadozó, akkor a fogadó oldalnak úgy is döntenie kell, hogy catch-up legyen-e (ugyanannyi idö alatt több adatot lejátszani) vagy kihagyni a túl késön megkapott adatokat de cserébe szinkronban maradni az adóval?
Iparban erre már vannak kitalált protokollok, amiket lehet, nem is lenne nehéz integrálni ilyensmire?

Úgy néz ki, találtam egy workaround-ot a problémára. Azt csináltam, hogy a /etc/pulse/daemon.conf file-ban 48 kHz-es alapértelmezett mintavételezési frekvenciát adtam meg:

default-sample-rate = 48000
alternate-sample-rate = 44100

Ezen felül megváltoztattam a mintavételezési metódust:

resample-method = src-sinc-best-quality

Ezzel sok szenvedéstől kímélem meg a hangszervert, s lényegében hardware támogatást tolok az Opus codec alá, hiszen a hardware 48 kHz-en megy, az Opus is, a kettő között csak copy-zni kell, vagy még azt sem, csak pointert átadni. Azaz nem kell resampling. Persze kell egy rakás más dologhoz, de azok bufferelt hanganyagok, ott van idő, az nem fog recsegni. És valóban, eddig a tapasztalat azt mutatja, bevált az elképzelés. :)

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