Preemfáze nahrávání analogového zvuku do ASCII-artu v C

28. 11. 2023
Doba čtení: 10 minut

Sdílet

 Autor: Depositphotos
Popíšeme řízení nahrávací úrovně a jak se promítnou různé mikrosekundové hodnoty preemfáze na sílu šumu ve zvuku a na vzhled ASCII-artu nesoucího svůj protiklad, nekvantizovaný, analogový záznam.

Dekódování vstupního WAVu

Interní zpracování signálu probíhá v plovoucí čárce s přesností double (mantisa 53 bitů+1 bit znaménko = celková LPCM přesnost 54 bitů) a někdy v rozsahu od –1 do +1 (oba konce včetně), jindy od 0 do +1 (oba konce včetně). Podle vstupní přesnosti WAVu se použije jedna z několika rutin, zde je příklad 24bitové.

Přípona _11 jména rutiny značí rozsah od –1 do +1. Vzorky jsou ve WAVu ukládány nejméně významný byte nejdřív a se znamínkem v dvojkovém doplňku. Tichu odpovídá vstupní hodnota 0 (nikoliv o půl kvanta nižší –0,5, které je v půlce dostupného rozsahu!) a dává výstupní 0, nejnižší vstupní –0×800000 dává výstupní –1,0 a nejvyšší vstupní 0×7FFFFF dává +0.999999881. Je to tak, aby se –0×800000 neořezávalo a tím nezkreslovalo.

Kvůli byrokratickému pravidlu K&R A.6.1 se unsigned char automaticky povyšuje na int, jenže bitové operace na intu nemají definovaný výsledek (int může být implementován jako znamínko-absolutní hodnota, jedničkový doplněk nebo dvojkový doplněk, a dokonce může mít hodnoty které vyvolávají výjimky jako –0 nebo –0×8000) tak jsem použil explicitní &0×FFUL, aby se z unsigned charu udělal unsigned long a nevznikal typ se znamínkem. &0×FF jsem tam dal pro jistotu pro architektury které mají 9-bitové chary a podobně (nejsem si jist, co bude v tom 9. bitu).

Konverze je založená na faktu, že když máme hodnotu vzroku v unsigned typu a záporná čísla jsou tak nad kladnými, stačí obrátit hodnotu nejvyššího bitu, překonvertovat do double a pak tu váhu zmíněného nejvyššího bitu odečíst. Pak zmíněnou váhou nejvyššího bitu vydělíme a získáme požadovaný výstupní rozsah.

Otevřeme si soubor c_rec.c z programu VAC:

static inline double b24_to_double_11(unsigned char *samples)
{
        unsigned long ul;
        double d;
#define SHIFT_24      0x800000UL
#define SCALE_24      0x800000UL /* Not 0x7FFFFFUL to prevent producing values outside +/-1 which would lead to clipping */

        ul=     ((((unsigned long)samples[0])&0xFFUL)<< 0UL)+
                ((((unsigned long)samples[1])&0xFFUL)<< 8UL)+
                ((((unsigned long)samples[2])&0xFFUL)<<16UL);
        ul^=SHIFT_24;

        /* Input              Signed       Unsig. |  ul here
         * ----------------------------+----------+---------
         * Minimum           -0x800000 | 0x800000 | 0x000000
         * Symmetric minimum -0x7FFFFF | 0x800001 | 0x000001
         * Zero (silence)    +0x000000 | 0x000000 | 0x800000
         * Maximum           +0x7FFFFF | 0x7FFFFF | 0xFFFFFF
         */

        d=ul;
        d-=(double)SHIFT_24;

        /* Input              Signed       Unsig. |    d here
         * ----------------------------+----------+----------
         * Minimum           -0x800000 | 0x800000 | -0x800000
         * Symmetric minimum -0x7FFFFF | 0x800001 | -0x7FFFFF
         * Zero (silence)    +0x000000 | 0x000000 |         0
         * Maximum           +0x7FFFFF | 0x7FFFFF | +0x7FFFFF
         */

        d/=(double)SCALE_24;

        /* Input              Signed       Unsig. |       d here
         * ----------------------------+----------+-------------
         * Minimum           -0x800000 | 0x800000 | -1.000000000
         * Symmetric minimum -0x7FFFFF | 0x800001 | -0.999999881
         * Zero (silence)    +0x000000 | 0x000000 |  0.000000000
         * Maximum           +0x7FFFFF | 0x7FFFFF | +0.999999881
         */

        return d;

Řízení nahrávací úrovně

V tomto okamžiku se nastaví nahrávací úroveň. Pokud je nahrávací úroveň slabá, signál je relativně slabý vůči šumu a plýtvá se odstupem signálu od šumu. Některá YouTube videa tím trpí, že člověk musí vytočit hlasitost na doraz aby vůbec něco slyšel, a následující video potom protrhne reproduktory. Pokud je signál příliš silný, nevejde se do rozsahu, ořezává se (clipping) a zní zkresleně. Pokud to nastane v SoXu, napíše varovnou hlášku. V předchozím průchodu na nečisto se změřila největší úrověn a nahrávací úroveň se nastaví jako reciproká hodnota předáním v bashovém skriptu test_c.sh jako parametr 2. spuštění C programu na nahrávání na virtuální analogovou pásku.

l_11*=recording_gain_linear;

Preemfáze

Informace o síle předmagnetizace a časové konstantě preemfáze na různých kazetách. Preemfáze má hodnotu buď 120 μs nebo 70 μs

CC-BY-SA Karel Kulhavý, Havelbaude, stuart.childs, Joost J. Bakker IJmuiden

Starší kazety používaly časovou konstantu (RC konstantu) 120 μs, novější 70 μs. Vynásobíme-li ji 2π, dostaneme periodu přechodové frekvence: 1,326 kHz a 2,274 kHz. Celkem to odpovídá tvaru hudebních spekter hudby ze studie. Idea je, že vyšší a vyšší kmitočty jsou v hudbě slabší a slabší. Nevyužili bychom tak plnou možnou zaznamenávání hlasitosti (protože nízké kmitočty ji už využívají), a tak se zbytečně plýtvá přesností záznamu (odstupem od šumu).

Tzv. preemfáze (zvýraznění před záznamem) vyšší kmitočty zesílí, pak se zaznamená, přečte a provede se deemfáze (odstranění zvýraznění) – zařadí se filtr, co působí přesně opačně. Signál tak projde nezměněn, šum na vysokých kmitočtech se zeslabí a tak je ho celkově méně. Šum také díky tomu dostává spektrum podobnější hudbě a tak se za hudbou lépe kamufluje.

Proces preemfáze je identický jako na analogových kazetách a potřebuje stavovou informaci – předchozí analogovou „elektronickou“ hodnotu, a předchozí nefiltrovaný – „skutečný“ digitální symbol – výstup procesu analogizace. Předchozí nefiltrovaný digitální symbol musí být nastaven do půlky rozsahu digitálních symbolů, protože to je nejbližší tichu:

static unsigned old_real_dig_0; /* Preset these to analogize_n_half */
static double old_electronic_chan0_11=0;
emit_smooth_11(&old_real_dig_0, preemphasize(&old_electronic_chan0_11, chan0_11));

Copak nám dělá ta funkce preemphasize? Pojmem elektrický signál zde označuji signál na který nebyla uplatněna preemfáze (tedy elektrický co jde do magnetofonu zvenku) a pojmem magnetický signál, na který preemfáze již uplatněna byla (co jde do magnetické hlavy zmagnetizovat virtuální částice z dimenze disco).

static inline double preemphasize(double *old_electronic, double new_electronic)
{
        double new_magnetic;
        new_magnetic=(new_electronic-*old_electronic)*decay_shift_inverted+*old_electronic;
        /* Now we can overwrite *old_electronic because we won't need it in a calculation anymore */
        *old_electronic=new_electronic;
        return new_magnetic;
}

Řádka new_magnetic=(new_electronic-*old_electronic)*decay_shift_inverted+*old_electronic provozuje jednoduchý RC filtr dolní propusti obráceně v čase. Tedy máme výstup a řešíme rovnici, abychom dedukovali, jaký musel být vstup. Místo, aby rozdíl mezi novou a starou hodnotou redukovala řekněme na 80 %, naopak ho zvětšuje na 1/80%=125%. Místo zeslabování výšky zesilujeme. V přehrávači je stejný obvod RC dolní propusti ale běží již normálně v čase dopředu, takže výšky stejnou měrou zeslabuje, navzájem se jejich efekt vyruší a zbude rovná frekvenční charakteristika. decay_shift_inverted se v inicializaci nastavuje takto:

void init_decay_shift_inverted(void)
{
        decay_shift_inverted=1/decay_time_to_decay_shift(preemphasis_usec*1e-6);
}

double decay_time_to_decay_shift(double decay_time_sec)
{
        double decay_shift, decay_time_samples;

        decay_time_samples=sample_rate_Hz*decay_time_sec;
        if (decay_time_samples<=0) decay_shift=1;
        else decay_shift=-expm1(-1/decay_time_samples);
        return decay_shift;
}

decay_time_to_decay_shift je pro ten RC dolnopásmový filtr co bude v přehrávači, ale my ho provozujeme pozpátku v čase tak hodnotu v nadřazené funkci init_decay_shift_inverted invertujeme. Funkce expm1 je použita pro spolehlivou numerickou přesnost. Funkci hodnoty  decay_shift ilustrujeme:

Hodnota decay_shift Efekt na RC dolní propust co bude v přehrávači
0,0 Starý výstupní vzorek zůstane jak je, stane se novým výstupním vzorkem, a nový vstupní vzorek je kompletně ignorován. RC obvod je nekonečně pomalý, vůbec nereaguje.
0,1 Nový výstupní vzorek je 10% nový vstupní vzorek a 90% starý výstupní vzorek. Reaguje pomalu.
0,9 Nový výstupní vzorek je 90% nový vstupní vzorek a 10% starý výstupní vzorek. Reaguje rychle.
1,0 Starý výstupní vzorek je ignorován, nový výstupní vzorek je nový vstupní vzorek. Vůbec nefiltruje, implementuje operaci identity na signálu – hodnoty beze změny prochází.
Autor: Karel Kulhavý
Autor: Karel Kulhavý
Autor: Karel Kulhavý

Efekt preemfáze na vzhled zvuku v ASCII-artu ve zdrojáku C. Postupně: 0 μs, 70 μs, 2000 μs.

Testujeme různé preemfáze

Pro srovnání různých preemfází jsem musel nastavit odstup signálu od šumu na velmi nízkou hodnotu 21 dB (9 symbolů), aby byl šum dobře slyšet. Přijde mi, že skutečně 70 μs působí nejméně rušivě, nebo možná 90 μs.

Nízké hodnoty propichují mé bubínky ječivým syčením, zatímto vysoké hodnoty utápějí hudbu za oponou burácejícího vodopádu.

Vysoké hodnoty mikrosekund preemfáze zeslabují hlasitost signálu z přehrávače, a to z následujícího bizarního, překomplikovaného důvodu: Signál nelze více zesílit, protože v extrémně nepravděpodobném případě by se mohlo stát, že šum způsobí přebuzení (zkreslení) signálu jdoucího z přehrávače – nevešel by se do rozsahu.

Extrémní deemfáze extrémně zeslabuje výšky a preemfáze je tak musí před nahráváním preventivně extrémně zesilovat. Tím se ale nahrávka na kazetu nevejde a musí se zeslabit a při přehrávání zase zesílit. Deemfáze z nahrávky všechno kromě nejhlubších basů zeslabí. U šumu se ale může stát extrémně nepravděpodobná událost, že se sejde tolik po sobě náhodných hodnot stejným směrem, že to vyrobí šumový signál tak nízkofrekvenční, že ho deemfáze vůbec nezeslabí. Tudíž je nutné kalkulovat, že amplitudu šumu deemfáze nezeslabuje. Máme-li 9 symbolů, i prázdnou kazetu (přehrávající pouze šum) nemůžeme zesílit víc než 9 krát, jinak by kvůli tomuto jevu mohlo nastat přebuzení. Nicméně signál hudby, extrémně zeslabený extrémní deemfází, potřebuje mnohem větší zesílení, které mu takto nemůžeme poskytnout.

Ke zkompilování všech souborů na porovnání preemfází můžete použít:

bitcoin školení listopad 24

ls fear_of_falling_9_*.c.bz2 | LC_ALL=C sed -E 's/\.bz2$//' | parallel "bzip2 -dc {}.bz2 | cc -O3 -x c - -lm -o {.}; echo {} zkompilován"
Zakomprimovaný zdroják ke stažení Délka souboru zakomprimovaná Odstup analogového signálu od šumu analogové nahrávky analogizované na digitální nosič Preemfáze Modulační procesor Počet symbolů digitálního nosiče analogové nahrávky Bitová rychlost ve zkomprimovaném stavu Bitová rychlost zdroje Délka min:sec vzorkovací frekvence Počet kanálů Kvalita zdroje SHA256 .c.bz2 souboru
fear_of_falling_9_0us.c.bz2 2 007 530 B 21 dB 0 μs (bez preemfáze) žádný 9 127 kbps 1,4 Mbps 2:06 44 100 Hz stereo 16-bit WAV 44 100 Hz D33A686A567A8DAB6CBE7A8B9­7B248B2 9265C7081A0D013CCA7A20A58635A83E
fear_of_falling_9_1us.c.bz2 2 007 334 B 21 dB 1 μs žádný 9 127 kbps 1,4 Mbps 2:06 44 100 Hz stereo 16-bit WAV 44 100 Hz D88BF5F70B6A2B5697E1E5A07DB8173A BD56016F7FF40787077E0172BBD6CBBB
fear_of_falling_9_2us.c.bz2 2 006 550 B 21 dB 2 μs žádný 9 127 kbps 1,4 Mbps 2:06 44 100 Hz stereo 16-bit WAV 44 100 Hz 0785A28EC923C064F6742E726E111948 91B95481CF4C55C6CEB9CA128EFB85D
fear_of_falling_9_7us.c.bz2 2 006 492 B 21 dB 7 μs žádný 9 127 kbps 1,4 Mbps 2:06 44 100 Hz stereo 16-bit WAV 44 100 Hz 38988D77918EAAB4255A92141334B4BF F6CCCC363C3C51B7B14CE79A73709E0A
fear_of_falling_9_20us.c.bz2 1 982 693 B 21 dB 20 μs žádný 9 125 kbps 1,4 Mbps 2:06 44 100 Hz stereo 16-bit WAV 44 100 Hz 24ABD325A589274E27F870E8EC712057 69B0A17E7644BA94C405EF1F84D3503E
fear_of_falling_9_50us.c.bz2 1 915 695 B 21 dB 50 μs žádný 9 121 kbps 1,4 Mbps 2:06 44 100 Hz stereo 16-bit WAV 44 100 Hz 8D5C55A01570B556C4AB25C0D6E1922B E433449A9D57183930EF288E087EDFA4
fear_of_falling_9_70us.c.bz2 1 867 627 B 21 dB 70 μs žádný 9 118 kbps 1,4 Mbps 2:06 44 100 Hz stereo 16-bit WAV 44 100 Hz DAFA69DA9183124DD36A047D6A698338 9D3D00141246741BF5D3C621C01F9F11
fear_of_falling_9_90us.c.bz2 1 830 353 B 21 dB 90 μs žádný 9 116 kbps 1,4 Mbps 2:06 44 100 Hz stereo 16-bit WAV 44 100 Hz BEE226B54895E160BCD1BD96667CDB09 8BA440CEF6EEA5C47148BDBF7DD131C8
fear_of_falling_9_120us.c.bz2 1 769 437 B 21 dB 120 μs žádný 9 112 kbps 1,4 Mbps 2:06 44 100 Hz stereo 16-bit WAV 44 100 Hz 1C66C7B40114125543B5BB53E23634FF F97AA1CEC3217B4B2668C58D1C367F6F
fear_of_falling_9_200us.c.bz2 1 675 833 B 21 dB 200 μs žádný 9 106 kbps 1,4 Mbps 2:06 44 100 Hz stereo 16-bit WAV 44 100 Hz 6D3651941C62E5EE424E382290A58F1D 26573F2A855E4535D9131075C4BC6BE9
fear_of_falling_9_700us.c.bz2 1 867 627 B 21 dB 700 μs žádný 9 97 kbps 1,4 Mbps 2:06 44 100 Hz stereo 16-bit WAV 44 100 Hz 9D83C1D655D1DFB32A0BD17CD­8C7BA86 8169A2E122563EED6E­A9F3863254EF89
fear_of_falling_9_2000us.c.bz2 1 506 478 B 21 dB 2000 μs žádný 9 95 kbps 1,4 Mbps 2:06 44 100 Hz stereo 16-bit WAV 44 100 Hz AFD92BF2CE432362B9D132C39BD52F9B 3DE5060527C0E84F44033112B83D7FC0
fear_of_falling_9_7000us.c.bz2 1 500 979 B 21 dB 7000 μs žádný 9 95 kbps 1,4 Mbps 2:06 44 100 Hz stereo 16-bit WAV 44 100 Hz 1A19146990032EF8B42DE64546DD9F27 16A0536F303202A193317049B1F9505E
fear_of_falling_9_20000us.c.bz2 1 497 773 B 21 dB 20000 μs žádný 9 95 kbps 1,4 Mbps 2:06 44 100 Hz stereo 16-bit WAV 44 100 Hz 33E7CB5B25D06582FA28D21C434B6056 52DA69EA467D8C49421FD0C73EBB7381

Skladba: Last Day On Earth(UK)/Peter McCormick: Fear Of Falling, CC-BY-SA 4.0 International

Příště budeme kódovat do ASCII-artu.

Autor článku

Karel Kulhavý vystudoval operační systémy, sítě a překladače na MFF UK a je autorem optického pojítka Twibright Ronja a spoluautorem textového a grafického webového prohlížeče Twibright Links.