mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-03-03 07:29:23 +00:00
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:
parent
50a6df89b8
commit
c5b9902ac9
2 changed files with 105 additions and 2 deletions
103
examples/ttyaudio.c
Normal file
103
examples/ttyaudio.c
Normal 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;
|
||||||
|
}
|
|
@ -160,8 +160,8 @@ scall sys_setuid 0x0170170172017069 globl hidden
|
||||||
scall sys_setgid 0x0b50b50b520b506a globl hidden
|
scall sys_setgid 0x0b50b50b520b506a globl hidden
|
||||||
scall sys_setresuid 0xfff11a137ffff075 globl hidden # polyfilled for xnu
|
scall sys_setresuid 0xfff11a137ffff075 globl hidden # polyfilled for xnu
|
||||||
scall sys_setresgid 0xfff11c138ffff077 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_getresuid 0xfff119168ffff076 globl hidden # semantics aren't well-defined
|
||||||
scall sys_getresgid 0xfff11b169ffff078 globl # 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 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_sigsuspend 0x12606f155206f082 globl hidden # a.k.a. rt_sigsuspend on Linux; openbsd:byvalue, sigsuspend_nocancel on XNU
|
||||||
scall sys_sigaltstack 0x1191200352035083 globl hidden
|
scall sys_sigaltstack 0x1191200352035083 globl hidden
|
||||||
|
|
Loading…
Add table
Reference in a new issue