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;
}