#include <sys/time.h>
#include <sys/un.h>
#include <unistd.h>
+#include <sys/mman.h>
+#include <fcntl.h>
#include "log.h"
#include "mem.h"
/** @brief Control socket or NULL */
const char *control_socket;
+/** @brief Buffer for debugging dump
+ *
+ * The debug dump is enabled by the @c --dump option. It records the last 20s
+ * of audio to the specified file (which will be about 3.5Mbytes). The file is
+ * written as as ring buffer, so the start point will progress through it.
+ *
+ * Use clients/dump2wav to convert this to a WAV file, which can then be loaded
+ * into (e.g.) Audacity for further inspection.
+ *
+ * All three backends (ALSA, OSS, Core Audio) now support this option.
+ *
+ * The idea is to allow the user a few seconds to react to an audible artefact.
+ */
+int16_t *dump_buffer;
+
+/** @brief Current index within debugging dump */
+size_t dump_index;
+
+/** @brief Size of debugging dump in samples */
+size_t dump_size = 44100/*Hz*/ * 2/*channels*/ * 20/*seconds*/;
+
static const struct option options[] = {
{ "help", no_argument, 0, 'h' },
{ "version", no_argument, 0, 'V' },
#if HAVE_COREAUDIO_AUDIOHARDWARE_H
{ "core-audio", no_argument, 0, 'c' },
#endif
+ { "dump", required_argument, 0, 'r' },
{ "socket", required_argument, 0, 's' },
{ "config", required_argument, 0, 'C' },
{ 0, 0, 0, 0 }
/* display version number and terminate */
static void version(void) {
- xprintf("disorder-playrtp version %s\n", disorder_version_string);
+ xprintf("%s", disorder_version_string);
xfclose(stdout);
exit(0);
}
struct sockaddr_in6 in6;
};
union any_sockaddr mgroup;
+ const char *dumpfile = 0;
static const struct addrinfo prefs = {
AI_PASSIVE,
mem_init();
if(!setlocale(LC_CTYPE, "")) fatal(errno, "error calling setlocale");
- while((n = getopt_long(argc, argv, "hVdD:m:b:x:L:R:M:aocC:", options, 0)) >= 0) {
+ while((n = getopt_long(argc, argv, "hVdD:m:b:x:L:R:M:aocC:r", options, 0)) >= 0) {
switch(n) {
case 'h': help();
case 'V': version();
#endif
case 'C': configfile = optarg; break;
case 's': control_socket = optarg; break;
+ case 'r': dumpfile = optarg; break;
default: fatal(0, "invalid option");
}
}
if((err = pthread_create(&tid, 0, control_thread, 0)))
fatal(err, "pthread_create control_thread");
}
+ if(dumpfile) {
+ int fd;
+ unsigned char buffer[65536];
+ size_t written;
+
+ if((fd = open(dumpfile, O_RDWR|O_TRUNC|O_CREAT, 0666)) < 0)
+ fatal(errno, "opening %s", dumpfile);
+ /* Fill with 0s to a suitable size */
+ memset(buffer, 0, sizeof buffer);
+ for(written = 0; written < dump_size * sizeof(int16_t);
+ written += sizeof buffer) {
+ if(write(fd, buffer, sizeof buffer) < 0)
+ fatal(errno, "clearing %s", dumpfile);
+ }
+ /* Map the buffer into memory for convenience */
+ dump_buffer = mmap(0, dump_size * sizeof(int16_t), PROT_READ|PROT_WRITE,
+ MAP_SHARED, fd, 0);
+ if(dump_buffer == (void *)-1)
+ fatal(errno, "mapping %s", dumpfile);
+ info("dumping to %s", dumpfile);
+ }
play_rtp();
return 0;
}