Add ansi terminal audio prototype

It's never worked very well having nesemu1.com and printvideo.com
spawning an ffmpeg or sox subprocess and streaming audio samples via
pipes. Since these programs don't work very well for that purpose, and
if you're SSH'ing into the cloud, the speaker could be very far away.

This change is part of an experiment to instead patch desktop terminals
such as PuTTY, KiTTY, gnome-terminal, etc. to support receiving inband
audio samples as ANSI code, and then playing them on the speakers of the
local machine that's being used. This way we can use printf() as a cross
platform audio playback library.
This commit is contained in:
Justine Tunney 2022-07-08 15:11:46 -07:00
parent 50a6df89b8
commit c5b9902ac9
2 changed files with 105 additions and 2 deletions

103
examples/ttyaudio.c Normal file
View file

@ -0,0 +1,103 @@
#if 0
/*─────────────────────────────────────────────────────────────────╗
To the extent possible under law, Justine Tunney has waived
all copyright and related or neighboring rights to this file,
as it is written in the following disclaimers:
http://unlicense.org/ │
http://creativecommons.org/publicdomain/zero/1.0/ │
*/
#endif
#include "libc/calls/calls.h"
#include "libc/calls/struct/sigaction.h"
#include "libc/log/check.h"
#include "libc/macros.internal.h"
#include "libc/runtime/runtime.h"
#include "libc/stdio/stdio.h"
#include "libc/sysv/consts/itimer.h"
#include "libc/sysv/consts/sig.h"
#include "libc/time/time.h"
struct Ring {
int i; // read index
int j; // write index
int n; // total samples
short* p; // samples
};
struct Speaker {
int rate; // in hertz, e.g. 8000
int channels; // 1 = mono, 2 = stereo
struct Ring buf; // audio playback buffer
};
const int maxar = 32;
const int ptime = 20;
struct Speaker s;
void LoadAudioFile(struct Speaker* s, const char* path) {
int rc;
FILE* f;
short buf[256];
if (!(f = fopen(path, "rb"))) {
fprintf(stderr, "failed to open file\n");
exit(1);
}
for (;;) {
rc = fread(buf, sizeof(short), sizeof(buf) / sizeof(short), f);
if (rc) {
s->buf.p = (short*)realloc(s->buf.p, (s->buf.n + rc) * sizeof(short));
memcpy(s->buf.p + s->buf.n, buf, rc * sizeof(short));
s->buf.n += rc;
} else if (ferror(f)) {
fprintf(stderr, "read error: %s\n", strerror(ferror(f)));
exit(2);
} else {
break;
}
}
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[");
count = MIN(samps - i, maxar);
for (j = 0; j < count; ++j) {
if (j) printf(";");
printf("%d", s.buf.p[s.buf.i++] & 0xffff);
if (s.buf.i == s.buf.n) break;
}
printf("p");
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);
s.rate = 8000;
s.channels = 1;
LoadAudioFile(&s, "/home/jart/Music/numbers.s16");
printf("\e[%d;%dy", s.rate, s.channels);
fflush(stdout);
struct sigaction sa = {.sa_handler = OnAlrm};
struct itimerval it = {{0, ptime * 1000}, {0, ptime * 1000}};
CHECK_NE(-1, sigaction(SIGALRM, &sa, 0));
CHECK_NE(-1, setitimer(ITIMER_REAL, &it, 0));
for (;;) {
pause();
}
return 0;
}

View file

@ -160,8 +160,8 @@ scall sys_setuid 0x0170170172017069 globl hidden
scall sys_setgid 0x0b50b50b520b506a globl hidden
scall sys_setresuid 0xfff11a137ffff075 globl hidden # polyfilled for xnu
scall sys_setresgid 0xfff11c138ffff077 globl hidden # polyfilled for xnu
scall sys_getresuid 0xfff119168ffff076 globl # semantics aren't well-defined
scall sys_getresgid 0xfff11b169ffff078 globl # semantics aren't well-defined
scall sys_getresuid 0xfff119168ffff076 globl hidden # semantics aren't well-defined
scall sys_getresgid 0xfff11b169ffff078 globl hidden # semantics aren't well-defined
scall sigpending 0x124034034203407f globl # a.k.a. rt_sigpending on linux
scall sys_sigsuspend 0x12606f155206f082 globl hidden # a.k.a. rt_sigsuspend on Linux; openbsd:byvalue, sigsuspend_nocancel on XNU
scall sys_sigaltstack 0x1191200352035083 globl hidden