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.
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Ý | | | |_________________________________________________| | | / (+) \ | | / _ _ \ | | / _ (_) (_) _ \ | (+)_______/___(_)_______________________(_)___\_______(+) |
(logo public domain) |
Kompilační příkaz:
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Ý | | | |_________________________________________________| | | / (+) \ | | / _ _ \ | | / _ (_) (_) _ \ | (+)_______/___(_)_______________________(_)___\_______(+) |
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.