aboutsummaryrefslogtreecommitdiff
path: root/src/mmfwd-callam.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/mmfwd-callam.cpp')
-rw-r--r--src/mmfwd-callam.cpp154
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;
}