diff --git a/examples/ttyaudio.c b/examples/ttyaudio.c index 2374157ec..326b0359d 100644 --- a/examples/ttyaudio.c +++ b/examples/ttyaudio.c @@ -8,16 +8,41 @@ ╚─────────────────────────────────────────────────────────────────*/ #endif #include "dsp/core/core.h" +#include "libc/bits/bits.h" #include "libc/calls/calls.h" #include "libc/calls/struct/sigaction.h" +#include "libc/fmt/nf32.h" #include "libc/log/check.h" #include "libc/macros.internal.h" #include "libc/runtime/runtime.h" +#include "libc/stdio/append.internal.h" #include "libc/stdio/stdio.h" #include "libc/sysv/consts/itimer.h" #include "libc/sysv/consts/sig.h" #include "libc/time/time.h" +/** + * @fileoverview experimental way to play audio in terminals + * + * This is an stdio application that prints audio samples. The terminal + * on the other end, needs to be able to understand the Nf sequences we + * use here, which should be invisible to the terminal sort of like out + * of band signalling. + * + * To play audio with a supporting terminal: + * + * make -j8 o//examples/ttyaudio.com + * wget https://justine.lol/numbers.s16 + * o//examples/ttyaudio.com numbers.s16 + * + * To reveal the inband ansi audio transmission: + * + * o//examples/ttyaudio.com numbers.s16 2>/dev/null | + * o//tool/viz/bing.com | + * o//tool/viz/fold.com + * + */ + #define CSI "s" #define SGR1 "?80" #define SGR2 "?81" @@ -31,6 +56,7 @@ struct Ring { struct Speaker { int rate; // in hertz, e.g. 8000 + int codec; // 0 = s16, 2 = µ-Law int channels; // 1 = mono, 2 = stereo struct Ring buf; // audio playback buffer }; @@ -40,10 +66,13 @@ const int ptime = 20; struct Speaker s; +void OnAlrm(int sig) { +} + void LoadAudioFile(struct Speaker* s, const char* path) { int rc; FILE* f; - short buf[256]; + short buf[1024]; if (!(f = fopen(path, "rb"))) { fprintf(stderr, "failed to open file\n"); exit(1); @@ -64,36 +93,26 @@ void LoadAudioFile(struct Speaker* s, const char* path) { fclose(f); } -void OnAlrm(int sig) { - int i, j; - int count; - int samps = s.rate / (1000 / ptime); - for (i = 0; i < samps; i += count) { - printf("\e[" SGR2); - count = MIN(samps - i, maxar); - for (j = 0; j < count; ++j) { - printf(";%d", s.buf.p[s.buf.i++] & 0xffff); - /* printf(";%d", mulaw(s.buf.p[s.buf.i++])); */ - if (s.buf.i == s.buf.n) break; - } - printf(CSI); - if (s.buf.i == s.buf.n) break; - } - fflush(stdout); - if (s.buf.i == s.buf.n) exit(0); - fprintf(stderr, "\r\e[K%d / %d", s.buf.i, s.buf.n); - fflush(stderr); -} - int main(int argc, char* argv[]) { - if (!isatty(1)) exit(1); + if (argc < 2) return 1; + if (!isatty(0)) exit(1); s.rate = 8000; s.channels = 1; - LoadAudioFile(&s, "/home/jart/Music/numbers.s16"); + s.codec = 0; + LoadAudioFile(&s, argv[1]); - printf("\e[" SGR1 "%d;%d;0" CSI, s.rate, s.channels); - fflush(stdout); + char nf[21]; + char* obuf = 0; + appendw(&obuf, READ16LE("\e%")); + appendd(&obuf, nf, EncodeNf32(nf, s.rate) - nf); + appendw(&obuf, '/'); + appendd(&obuf, nf, EncodeNf32(nf, s.channels) - nf); + appendw(&obuf, '/'); + appendd(&obuf, nf, EncodeNf32(nf, s.codec) - nf); + appendw(&obuf, '0'); + write(1, obuf, appendz(obuf).i); + free(obuf); struct sigaction sa = {.sa_handler = OnAlrm}; struct itimerval it = {{0, ptime * 1000}, {0, ptime * 1000}}; @@ -101,8 +120,30 @@ int main(int argc, char* argv[]) { CHECK_NE(-1, setitimer(ITIMER_REAL, &it, 0)); for (;;) { + char* p; + int count; + char nf[22]; + int i, j, x; + char* obuf = 0; + int samps = s.rate / (1000 / ptime); + appendw(&obuf, READ16LE("\e ")); + for (i = 0; i < samps; ++i) { + if (s.codec == 1) { + x = mulaw(s.buf.p[s.buf.i++]); + } else { + x = s.buf.p[s.buf.i++] & 0xffff; + } + *(p = EncodeNf32(nf, x)) = '/'; + appendd(&obuf, nf, p + 1 - nf); + if (s.buf.i == s.buf.n) break; + } + appendw(&obuf, '0'); + write(1, obuf, appendz(obuf).i); + free(obuf); + fprintf(stderr, "\r\e[K%d / %d", s.buf.i, s.buf.n); + fflush(stderr); pause(); } - return 0; + return 1; } diff --git a/libc/fmt/decodenf32.c b/libc/fmt/decodenf32.c new file mode 100644 index 000000000..259352c41 --- /dev/null +++ b/libc/fmt/decodenf32.c @@ -0,0 +1,41 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2022 Justine Alexandra Roberts Tunney │ +│ │ +│ Permission to use, copy, modify, and/or distribute this software for │ +│ any purpose with or without fee is hereby granted, provided that the │ +│ above copyright notice and this permission notice appear in all copies. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ +│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ +│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ +│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ +│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ +│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ +│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ +│ PERFORMANCE OF THIS SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/errno.h" +#include "libc/fmt/nf32.h" +#include "libc/str/str.h" + +/** + * Decodes u32 from ANSI Nf sequence. + */ +uint32_t DecodeNf32(const char *s, char **e) { + int diglet; + uint32_t x; + for (x = 0; (diglet = kNfcimal[*s & 255]) != -1; s++) { + if (__builtin_mul_overflow(x, 10, &x) || + __builtin_add_overflow(x, diglet, &x)) { + errno = ERANGE; + x = -1; + break; + } + } + if (e) { + *e = (char *)s; + } + return x; +} diff --git a/libc/fmt/encodenf32.c b/libc/fmt/encodenf32.c new file mode 100644 index 000000000..50edb20e4 --- /dev/null +++ b/libc/fmt/encodenf32.c @@ -0,0 +1,41 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2022 Justine Alexandra Roberts Tunney │ +│ │ +│ Permission to use, copy, modify, and/or distribute this software for │ +│ any purpose with or without fee is hereby granted, provided that the │ +│ above copyright notice and this permission notice appear in all copies. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ +│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ +│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ +│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ +│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ +│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ +│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ +│ PERFORMANCE OF THIS SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/fmt/nf32.h" + +/** + * Encodes u32 as ANSI Nf sequence content. + */ +char *EncodeNf32(char p[hasatleast 12], uint32_t x) { + char t; + size_t i, a, b; + i = 0; + do { + p[i++] = " ,.-+*#%&$"[x % 10]; + x = x / 10; + } while (x > 0); + p[i] = '\0'; + if (i) { + for (a = 0, b = i - 1; a < b; ++a, --b) { + t = p[a]; + p[a] = p[b]; + p[b] = t; + } + } + return p + i; +} diff --git a/libc/fmt/knfcimal.S b/libc/fmt/knfcimal.S new file mode 100644 index 000000000..f2ef5abc9 --- /dev/null +++ b/libc/fmt/knfcimal.S @@ -0,0 +1,75 @@ +#include "libc/macros.internal.h" + +// generated by: +// o/tool/build/xlat.com -s kNfcimal -inx -1 -I ' ,.-+*#%&$' +// +// present absent +// ──────────────── ──────────────── +// ∅☺☻♥♦♣♠•◘○◙♂♀♪♫☼ 0x00 +// ►◄↕‼¶§▬↨↑↓→←∟↔▲▼ 0x10 +// ␠ #§%& *+,-. !“ ‘() / 0x20 +// 0123456789:;<=>⁇ 0x30 +// @ABCDEFGHIJKLMNO 0x40 +// PQRSTUVWXYZ[⭝]^_ 0x50 +// `abcdefghijklmno 0x60 +// pqrstuvwxyz{|}~⌂ 0x70 +// ÇüéâäàåçêëèïîìÄÅ 0x80 +// ÉæÆôöòûùÿÖÜ¢£¥€ƒ 0x90 +// áíóúñѪº¿⌐¬½¼¡«» 0xa0 +// ░▒▓│┤╡╢╖╕╣║╗╝╜╛┐ 0xb0 +// └┴┬├─┼╞╟╚╔╩╦╠═╬╧ 0xc0 +// ╨╤╥╙╘╒╓╫╪┘┌█▄▌▐▀ 0xd0 +// αßΓπΣσμτΦΘΩδ∞φε∩ 0xe0 +// ≡±≥≤⌠⌡÷≈°∙×√ⁿ²■λ 0xf0 +// +// const char kNfcimal[256] = { +// -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, // 0x00 +// -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, // 0x10 +// 0,-1,-1, 6, 9, 7, 8,-1,-1,-1, 5, 4, 1, 3, 2,-1, // 0x20 +// -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, // 0x30 +// -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, // 0x40 +// -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, // 0x50 +// -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, // 0x60 +// -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, // 0x70 +// -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, // 0x80 +// -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, // 0x90 +// -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, // 0xa0 +// -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, // 0xb0 +// -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, // 0xc0 +// -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, // 0xd0 +// -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, // 0xe0 +// -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, // 0xf0 +// }; + + .initbss 300,_init_kNfcimal +kNfcimal: + .zero 256 + .endobj kNfcimal,globl + .previous + + .initro 300,_init_kNfcimal +kNfcimal.rom: + .byte 32,255 # 00-1f ∅-▼ + .byte 1,0 # 20-20 ␠-␠ + .byte 2,255 # 21-22 !-“ + .byte 1,6 # 23-23 #-# + .byte 1,9 # 24-24 §-§ + .byte 1,7 # 25-25 %-% + .byte 1,8 # 26-26 &-& + .byte 3,255 # 27-29 ‘-) + .byte 1,5 # 2a-2a *-* + .byte 1,4 # 2b-2b +-+ + .byte 1,1 # 2c-2c ,-, + .byte 1,3 # 2d-2d --- + .byte 1,2 # 2e-2e .-. + .byte 209,255 # 2f-ff /-λ + .byte 0,0 # terminator + .byte 0,0 # padding + .endobj kNfcimal.rom,globl + + .init.start 300,_init_kNfcimal + call rldecode + lodsw + .init.end 300,_init_kNfcimal + +// 39 bytes total (15% original size) diff --git a/libc/fmt/nf32.h b/libc/fmt/nf32.h new file mode 100644 index 000000000..2df366776 --- /dev/null +++ b/libc/fmt/nf32.h @@ -0,0 +1,13 @@ +#ifndef COSMOPOLITAN_LIBC_FMT_NF32_H_ +#define COSMOPOLITAN_LIBC_FMT_NF32_H_ +#if !(__ASSEMBLER__ + __LINKER__ + 0) +COSMOPOLITAN_C_START_ + +extern const char kNfcimal[256]; + +uint32_t DecodeNf32(const char *, char **); +char *EncodeNf32(char[hasatleast 12], uint32_t); + +COSMOPOLITAN_C_END_ +#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ +#endif /* COSMOPOLITAN_LIBC_FMT_NF32_H_ */ diff --git a/test/libc/fmt/encodenf32_test.c b/test/libc/fmt/encodenf32_test.c new file mode 100644 index 000000000..f244f40e1 --- /dev/null +++ b/test/libc/fmt/encodenf32_test.c @@ -0,0 +1,40 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2022 Justine Alexandra Roberts Tunney │ +│ │ +│ Permission to use, copy, modify, and/or distribute this software for │ +│ any purpose with or without fee is hereby granted, provided that the │ +│ above copyright notice and this permission notice appear in all copies. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ +│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ +│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ +│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ +│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ +│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ +│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ +│ PERFORMANCE OF THIS SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/fmt/fmt.h" +#include "libc/fmt/nf32.h" +#include "libc/rand/rand.h" +#include "libc/testlib/testlib.h" + +TEST(EncodeNf32, test) { + char b[21]; + EncodeNf32(b, 31337); + ASSERT_STREQ("-,--%", b); +} + +TEST(EncodeNf32, roundTrip) { + int i; + char b[21]; + uint32_t x, y; + for (i = 0; i < 1000; ++i) { + x = lemur64(); + EncodeNf32(b, x); + y = DecodeNf32(b, 0); + ASSERT_EQ(x, y); + } +} diff --git a/tool/build/xlat.c b/tool/build/xlat.c index 5b988d0c8..f6cd30680 100644 --- a/tool/build/xlat.c +++ b/tool/build/xlat.c @@ -36,6 +36,7 @@ int dig; int xlat[256]; +bool identity; const char *symbol; static int Bing(int c) { @@ -50,7 +51,7 @@ static void Fill(int f(int)) { int i; for (i = 0; i < 256; ++i) { if (f(i)) { - xlat[i] = 1; + xlat[i] = identity ? i : 1; } } } @@ -143,7 +144,8 @@ int main(int argc, char *argv[]) { for (k = 1; k < argc; ++k) { if (argv[k][0] != '-') { for (i = 0; argv[k][i]; ++i) { - xlat[argv[k][i] & 255] = dig; + /* xlat[argv[k][i] & 255] = identity ? i : dig; */ + xlat[argv[k][i] & 255] = identity ? (argv[k][i] & 255) : dig; } } else { i = 0; @@ -160,6 +162,9 @@ int main(int argc, char *argv[]) { case 'i': Invert(); goto moar; + case 'I': + identity = !identity; + goto moar; case 'n': Negative(); goto moar; @@ -239,7 +244,7 @@ int main(int argc, char *argv[]) { for (i = 0; i < 16; ++i) { printf(" "); for (j = 0; j < 16; ++j) { - printf("%2d,", xlat[i * 16 + j]); + printf("%2d,", (char)xlat[i * 16 + j]); } printf(" // 0x%02x\n//\t", i * 16); }