diff options
Diffstat (limited to 'src/mmfwd-callam.cpp')
-rw-r--r-- | src/mmfwd-callam.cpp | 154 |
1 files changed, 130 insertions, 24 deletions
diff --git a/src/mmfwd-callam.cpp b/src/mmfwd-callam.cpp index 90ad4d2..3104dd7 100644 --- a/src/mmfwd-callam.cpp +++ b/src/mmfwd-callam.cpp @@ -31,10 +31,7 @@ struct { const char *hello_sample_pat; // glob patterns const char *outfile_prefix; struct { - const char *sample_rate; - const char *channels; - const char *fmt; - bool playback; // TODO + bool enabled[2]; } playback; int vl; } params; @@ -66,9 +63,9 @@ size_t getrndsize (void) { void init_params (void) { params.hello_sample_pat = "hello/*.pcm"; - params.playback.sample_rate = "8000"; - params.playback.channels = "1"; - params.playback.fmt = "S16_LE"; + // params.playback.sample_rate = "8000"; + // params.playback.channels = "1"; + // params.playback.fmt = "S16_LE"; } void init_g (void) { @@ -77,6 +74,108 @@ void init_g (void) { g.playback[0] = g.playback[1] = -1; } +void parse_env (void) { + const char *env; + + env = getenv("MMFWD_CALLAM_PLAYBACK"); + if (env != NULL) { + int v = 0; + + sscanf(env, "%d", &v); + params.playback.enabled[0] = v & 1 ? true : false; + params.playback.enabled[1] = v & 2 ? true : false; + } +} + +pid_t do_playback_exec (const int p_stdin, const int p_stdout) { + pid_t pid; + int fr; + + pid = fork(); + if (pid < 0) { + return -1; + } + else if (pid > 0) { + return pid; + } + + close(STDIN_FILENO); + close(STDOUT_FILENO); + dup2(p_stdin, STDIN_FILENO); + dup2(p_stdout, STDOUT_FILENO); + close(p_stdin); + close(p_stdout); + + fr = system( + "ffmpeg -loglevel error -f s16le -ar 16000 -ac 1 -i pipe: -filter:a loudnorm -ar 16000 -ac 1 -f s16le - | " + "aplay -q -r 16000 -c 1 -f S16_LE -" + ); + if (fr < 0) { + perror(ARGV0 ": system()"); + abort(); + } + exit(fr); + + assert("unreachable" && false); +} + +bool open_playback (void) { + int p[2]; + int fr; + pid_t pid[2] = { -1, -1 }; + int fd_in[2] = { -1, -1 }; + int fd_out[2] = { -1, -1 }; + int blackhole; + + fr = pipe(p); + if (fr < 0) { + perror(ARGV0 ": pipe()"); + goto ERR; + } + close(p[0]); + blackhole = p[1]; + + for (size_t i = 0; i < 2; i += 1) { + if (!params.playback.enabled[i]) { + continue; + } + + fr = pipe(p); + if (fr < 0) { + perror(ARGV0 ": pipe()"); + goto ERR; + } + fd_in[i] = p[0]; + fd_out[i] = p[1]; + + pid[i] = do_playback_exec(p[0], blackhole); + if (pid[i] < 0) { + goto ERR; + } + + g.playback[i] = p[1]; + close(fd_in[i]); + fd_in[i] = -1; + + // Let audio skip when the buffer gets full. + // Should be fine as long as the size of the buffer is aligned to 2 byte + // boundary which is always the case. + fr = fcntl(g.playback[i], F_GETFL); + fr |= O_NONBLOCK; + fr = fcntl(g.playback[i], F_SETFL); + assert(fr == 0); + } + + return true; +ERR: // house-keeping + for (size_t i = 0; i < 2; i += 1) { + close(fd_in[i]); + close(fd_out[i]); + kill(pid[i], SIGHUP); + } + return false; +} + bool open_dev (void) { struct termios tis; const int fd = open(params.dev, O_RDWR | O_NONBLOCK); @@ -209,6 +308,23 @@ bool write_full (const int fd, const void *m, size_t len, const std::string &fn) return true; } +void sink_playback_audio (int *fd, const void *buf, const size_t len) { + ssize_t iofr; + + if (*fd < 0) { + return; + } + + iofr = write(*fd, buf, len); + // Errors are not fatal: + // - skip some samples when the child process struggle/stopped + // - EPIPE in case of death of child process + if (iofr < 0 && errno == EPIPE) { + close(*fd); + *fd = -1; + } +} + bool loop_main (void) { uint8_t buf[4096]; size_t sample_pos = 0; @@ -259,14 +375,7 @@ bool loop_main (void) { if (!write_full(g.fd[0], buf, blen, g.outfile[0])) { return false; } - - if (g.playback[0] >= 0) { - iofr = write(g.playback[0], buf, blen); - if (iofr < 0) { - close(g.playback[0]); - g.playback[0] = -1; - } - } + sink_playback_audio(&g.playback[0], buf, blen); } if (pfd.events & POLLOUT) { @@ -294,14 +403,7 @@ bool loop_main (void) { if (!write_full(g.fd[1], ptr, blen, g.outfile[1])) { return false; } - - if (g.playback[1] >= 0) { - iofr = write(g.playback[1], ptr, blen); - if (iofr < 0) { - close(g.playback[1]); - g.playback[1] = -1; - } - } + sink_playback_audio(&g.playback[1], buf, blen); sample_pos += blen; if (sample_pos >= g.hsample.len) { @@ -328,6 +430,8 @@ void handle_termsig (int) { int main (const int argc, const char **argv) { init_params(); init_g(); + + parse_env(); // TODO: getopt() if (argc <= 2) { @@ -340,7 +444,9 @@ int main (const int argc, const char **argv) { params.dev = argv[1]; params.outfile_prefix = argv[2]; - if (!open_dev() || !open_hsample() || !open_fds()) { + signal(SIGPIPE, SIG_IGN); + + if (!open_playback() || !open_dev() || !open_hsample() || !open_fds()) { return 1; } |