Kódování již preemfázovaného analogového zvuku do ASCII-artu C

4. 4. 2024
Doba čtení: 7 minut

Sdílet

 Autor: Depositphotos
Minule jsme snížili šum preemfází a dnes budeme výsledný analogový signál kódovat do digitálního nosiče a ten pak překódovávat do ASCII-artu kompatibilního s řetězci v uvozovkách ve zdrojáku v jazyce C.

sppm_preemphasized() (Sample Peak Programme Meter) pouze měří sílu záznamu na pásce, abychom věděli, zda jsme nějaký vzorek nepřepálili a nemuseli ho ořezat, čímž by se náš vytříbený audiofilní čistě analogový HiFi zvuk s kabely z bezkyslíkaté mědi zkazil.

Slovo peak znamená že se měří okamžité hodnoty nikoliv průměr v určitém čase jak je to u VU metru, srovnání je ve výše uvedeném videu.

Elektronika analogového ručkového peak programme meteru

Autor: Harumphy

Potom se lišácky pouze rozsah –1…+1 přepočte na rozsah 0…+1, který chutná procesu analogizace, a vstoupí se do tomu příslušející rutinyemit_smooth_01() v souboru c_rec.c programu VAC:

static inline void emit_smooth_11(unsigned *old_real_dig, double smooth_11)
{
        sppm_preemphasized(smooth_11);
        if (!measure){
                emit_smooth_01(old_real_dig, (smooth_11+1)/2);
        }
}

Rutina emit_smooth_01() analogizuje analogovou hodnotu na digiální symbol. Ten potom odečte od předchozího digitálního symbolu. Tím se z dat vybouchá další entropie, protože vzorky mívají hodnoty podobné předchozím vzorkům, hodnoty signálu neskáčou po celém rozsahu náhodně. Tím se vytvoří statistické rozložení, které je nahromaděné kolem 0 a dobře se následně komprimuje pomocí  bzip -9.

Chytře se přitom ještě využije cyklické nátury modulární aritmetiky, takže se nemusí téměř zdvojnásobit počet symbolů. Kdybychom měli 10 symbolů, museli bychom mít 19 symbolů pro rozdíly –10 až +10, ale protože cyklická modulární aritmetika jaksi funguje tam i zpátky tak se žádná informace neztratí a na konci vypadnou zase ta stejná data, co jsme dali na začátku, a žádnou větší sadu symbolů, která by mohla zkazit kompresi, nepotřebujeme.

Protože operace % nemá na záporných číslech ty vlastnosti, co potřebujeme, musíme se záporných číslům vyhnout, proto se tam různě přičítá analogize_n. analogize_n_half je polovička rozsahu symbolů: pokud se symboly nemění, generuje se jakási „nula“, která nemusí být přesně nula, když nula padne na půl cesty mezi dva symboly. To je z důvodu, aby neměnná křivka na ASCII artu vypadala jako samé mezery.

static inline void emit_smooth_01(unsigned *old_real_dig, double smooth_01)
{
        unsigned long real_digit;
        unsigned long diff_digit_low;

        real_digit=analogize(smooth_01, analogize_n); /* Silence produces on average analogize_n_half or analogize_n_half+0.5 */
        /* Here real digit >=0...<analogize_n */
        /* This is where clipping would occur so let's do the SPPM here too */
        diff_digit_low=(real_digit+analogize_n-*old_real_dig+analogize_n_half)%analogize_n;
        *old_real_dig=real_digit;

        emit_diff_digit(diff_digit_low);
}

Proces analogizace je standardní . Střídavý pseudonáhodný předmagnetizační signál analogizace se přičte, pak se kvantizuje, a v přijímači se potom tentýž pseudonáhodný signál odečte. Vzniklý kanál je doopravdy analogový, není to ani přidávání šumu ani dithering s distribucí chyby:

/* Returns 0...n_levels-1 */
static inline unsigned long analogize(double in_01, unsigned long n_levels)
{
        double random_half_half;
        random_half_half=generate_random_half_half();

        return analogize_specify_prs(in_01, n_levels, random_half_half);
}

Funkce generate_random_half_half generuje pseudonáhodné číslo v rozsahu –0,5…+0,5 přičemž –0,5 tam být může a +0,5 ne. Používá se Marsagliův xorshift* 64 bitů. Dr. Marsaglia již tu s námi není, nicméně jeho pseudonáhodné generátory stále ano a fungují stejně dobře jako vždycky.

uint64_t prn=xorshift64s(state);
return ldexp(prn, -64)-0.5;

Diferenciální digitální číslice – tedy diff_digit – jde do funkce emit_diff_digit(). Ta se rozhoduje, zda se číslice vejde do jednoho znaku na pásce nebo musí do 2. Protože 1 znak pojme maximálně 92 různých hodnot kvůli, které jsou čitelné na obrazovce a nemusí se zadávat jako víceznakové escape kódy.

static inline void emit_diff_digit(unsigned diff_digit_low)
{
        /* C source code emit */
        if (analogize_n>STRBASE){
                emit_diff_digit_C2(diff_digit_low);
        }else{
                emit_diff_digit_C1(diff_digit_low);
        }
}

Pro jednoznakový záznam (do 92 symbolů neboli 41 dB) je funkce emit_diff_digit_C1. Vstupní proměnná diff_digit_low je centrovaná na analogize_n_half což je digitální symbol zhruba v půlce rozsahu.

/* diff_digit_low is centered on analogize_n_half. */
static inline void emit_diff_digit_C1(unsigned diff_digit_low)
{
        unsigned long diff_digit_STRBASE_HALF;
        /* Constant signal level produces on average analogize_n_half and never goes out of >=0...<analogize_n */
        diff_digit_STRBASE_HALF=(diff_digit_low+STRBASE_HALF-analogize_n_half)%STRBASE;
        /* Constant signal level produces on average 128 */
        byte2stdout_pretty(diff_digit_STRBASE_HALF);
}

STRBASE_HALF je definovaná v pair1.h takto:

#define STRBASE 92U /* Number of characters which can be included in a C string directly: 127-32-3 (\\, \?, \") */
#define STRBASE_HALF (STRBASE/2U)

Protože jsem v jednoznakové větvi, počet digitálních číslic pro analogizaci je maximálně 92 a tak analogize_n_half nemůže překročit STRBAS_HALF a hodnota výrazu diff_digit_low+STRBASE_HALF-analogize_n_half se nedostane nikdy do záporných čísel, takže před výpočtem zbytku po dělení %STRBASE nemusíme přičítat STRBASE.

diff_digit_STRBASE_HALF je teď centrovaná na STRBASE_HALF a funkce byte2stdout_pretty zajistí ASCII-artové formátování tak, že znak ticha STRBASE_HALF odpovídá mezeře, která nemá žádný inkoust, a znaky postupně se od této hodnoty nahoru i dolu vzdalující mají více a více inkoustu – jsou podle toho seřazeny. To se zajistí překladovou tabulkou char_swap_wr (wr jako pro zápis, přehrávač používá char_swap_rd), kterou si přehrávač s sebou nese.

V případě 2-znakového záznamu již překladová tabulka ve zdrojáku v nahrávce není, protože by byla příliš velká. Místo toho se generuje stabilním třídícím algoritmem, aby při záznamu i přehrávání byla spočítána přesně stejně.

static inline void byte2stdout_pretty(unsigned char c)
{
        byte2stdout_dirty(char_swap_wr[(c)]);
}

Funkce byte2stdout_dirty nejdříve generuje obrácená lomítka na konci řádků a k nim i případně popisky sekundových značek, aby čtenář zdrojového kódu věděl, ke kterému času ono místo přísluší:

if (stdout_column+1+19>=COLUMNS){ // 1 for the data character, 19 for "/* 9999.999 s */"\           //
        static unsigned long last_marker=0;
        double second=(double)sampling_interval_counter/(double)sample_rate_Hz;
        unsigned long marker=floor(second*markers_per_sec);
        if (marker>last_marker){
                printf("\"/* %.3f s */\"\\\n", second);
                last_marker=marker;
        }else{
                fputs("\\\n",stdout);
        }
        stdout_column=0;
}

Zbytek funkce, který neukazuji, je tisk speciálních znaků do C-čkových řetězců, které se na pásce nevyskytují, pouze v popisce kazety, a normální znaky se s fanfárou do výstupu tisknou pomocí:

putchar(c);

Ukázkový poslech: Hacker Public Radio 3338: Používáme OpenSSL s_client jako telnet (anglicky)

Myslím si, že k hackerskému rádiu dobře sedne, když je šířeno ve formátu programovacího jazyka C, dokonce v ASCII-artu. A s vysílacím kompresorem zní lépe, skoro jako profesionální rádio.

 _______________________________________________________
(+)  _______________________________________________  (+)
|   / Hacker Public Radio ep. 3338: Using OpenSSL   \   |
|  | s_client like telnet, by Klaatu, CC-BY-SA 4.0   |  |
|  | s_client the new telnet. Here's how to use it.  |  |
|  | https://hackerpublicradio.org/eps.php?id=3338   |  |
|  |        _________________________________        |  |
|  |       /            _________            \       |  |
|  |      |  A " A     |         |     A " A  |      |  |
|  |      | (     )    |   | |   |    (     ) |      |  |
|  |      |  v , v     |_________|     v , v  |      |  |
|  | 75 μs \_________________________________/       |  |
|  |                                                 |  |
|  | VIRTUAL ANALOG CASSETTE        BY KAREL KULHAVÝ |  |
|  |_________________________________________________|  |
|            /             (+)             \            |
|           /        _             _        \           |
|          /   _    (_)           (_)    _   \          |
(+)_______/___(_)_______________________(_)___\_______(+)
obrázek

(logo public domain)

Kompilační příkaz:

bitcoin_skoleni

bzip2 -dc hpr_3338_256_fm.c.bz2 | cc -O3 -x c - -lm -o recording && ./recording 0 | mpv -
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
hpr_3338_256_fm.c.bz2 29 246 003 B 50 dB 50 μs (evropské FM rádio) SoX FM 256 197 kbps 1,06 Mbps 19:50 44 100 Hz mono 24-bit WAV 44 100 Hz ECDEE4A64A6C7DB3CC19C906C1B3CD54 E4D00A6B5855580B0C66F8373E39A83A
hpr_3338_256_wam.c.bz2 19 133 008 B 50 dB 75 μs (AM rádio) SoX AM 256 129 kbps 1,06 Mbps 19:50 22 050 Hz mono 24-bit WAV 44 100 Hz 2A554B8C0F9AC653120E3FD17BF7CCA4 BC8E7A0FE2BE30A7894D85CFF1A45ABB
hpr_3338_256_am.c.bz2 9 963 950 B 50 dB 75 μs (AM rádio( SoX AM 256 67 kbps 1,06 Mbps 19:50 11 025 Hz mono 24-bit WAV 44 100 Hz F0AFA5794BC2489129C20162064E6384 1C91FCDC9612A86C33FA2A92DEB7B215

Delší hudební test: Euseng Seto | Walaubagaimanapun

GCC nás možná na řádku cca. 1,7 miliónů obšťastní hláškou, ta ale neznamená chybnou kompilaci:

recording.c:1692111: note: ‘-Wmisleading-indentation’ is disabled from this point onwards, since column-tracking was disabled due to the size of the code/headers
1692111 |  *pretty_printed_byte=*read_head;
        |
 _______________________________________________________
(+)  _______________________________________________  (+)
|   / euseng seto | walaubagaimanapun | CC-BY-ND    \   |
|  | https://archive.org/details/bsc_051   Malaysian |  |
|  | composer's piece driven by synth progressions,  |  |
|  | warm piano melodies and clinical glitches.      |  |
|  |        _________________________________        |  |
|  |       /            _________            \       |  |
|  |      |  A " A     |         |     A " A  |      |  |
|  |      | (     )    |   | |   |    (     ) |      |  |
|  |      |  v , v     |_________|     v , v  |      |  |
|  | 70 μs \_________________________________/       |  |
|  |                                                 |  |
|  | VIRTUAL ANALOG CASSETTE        BY KAREL KULHAVÝ |  |
|  |_________________________________________________|  |
|            /             (+)             \            |
|           /        _             _        \           |
|          /   _    (_)           (_)    _   \          |
(+)_______/___(_)_______________________(_)___\_______(+)

obrázek

Obal singlu Walaubagaimanapun (CC-BY-ND Euseng Seto)

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
walaubagaimanapun_8464.c.bz2 79 245 863 B 80 dB 70 μs žádný 8464 521 kbps 1,411 Mbps 20:18 44 100 Hz stereo 16-bit WAV 44 100 Hz A7C603EED25AD2ADD26AA7CC7036D81B 12BC8D9F843C29838C38AD4CE71F0EC7
walaubagaimanapun_fm_512.c.bz2 55 343 190 B 56 dB 50 μs (evropské FM rádio) SoX FM 512 364 kbps 1,411 Mbps 20:18 44 100 Hz stereo 16-bit WAV 44 100 Hz E0A6CDB5AC4F02D3153D927C64BD1E6F E8D57F4BD475B373AE4694125E22D8FC

Příště popíšeme strukturu zdrojového kódu, který je samorozbalovacím analogovým záznamem zvuku v jazyce C.

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.