/* * Copyright (C) 2005-2009 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ /** \file write.c Paraslash's standalone wav/raw player. */ #include #include #include "para.h" #include "string.h" #include "write.cmdline.h" #include "list.h" #include "sched.h" #include "ggo.h" #include "stdin.h" #include "write.h" #include "write_common.h" #include "fd.h" #include "error.h" INIT_WRITE_ERRLISTS; /** Check if given buffer contains a valid wave header. */ struct check_wav_task { /** The buffer to check. */ char *buf; /** Number of bytes loaded in \a buf. */ size_t *loaded; /** Non-zero if an error occurred or end of file was reached. */ int *input_error; /** Number of channels specified in wav header given by \a buf. */ unsigned channels; /** Sample rate specified in wav header given by \a buf. */ unsigned samplerate; /** The task structure used by the scheduler. */ struct task task; }; /** Delay writing until given time. */ struct initial_delay_task { /** The time the first data should be written out. */ struct timeval start_time; /** The task structure for this task. */ struct task task; }; static struct write_args_info conf; static struct stdin_task sit; static struct check_wav_task the_check_wav_task; static struct initial_delay_task the_initial_delay_task; static struct writer_node_group *wng; /** Length of a standard wav header. */ #define WAV_HEADER_LEN 44 /** * Test if audio buffer contains a valid wave header. * * \return If not, return -E_NO_WAV_HEADER, otherwise, return zero. If * there is less than WAV_HEADER_LEN bytes available, return one. */ static void check_wav_pre_select(__a_unused struct sched *s, struct task *t) { struct check_wav_task *cwt = container_of(t, struct check_wav_task, task); unsigned char *a; int ret; if (*cwt->loaded < WAV_HEADER_LEN) { if (*cwt->input_error < 0) t->error = *cwt->input_error; return; } cwt->channels = 2; cwt->samplerate = 44100; a = (unsigned char*)cwt->buf; if (a[0] != 'R' || a[1] != 'I' || a[2] != 'F' || a[3] != 'F') { PARA_NOTICE_LOG("wav header not found\n"); t->error = -E_NO_WAV_HEADER; goto out; } cwt->channels = (unsigned) a[22]; cwt->samplerate = a[24] + (a[25] << 8) + (a[26] << 16) + (a[27] << 24); *cwt->loaded -= WAV_HEADER_LEN; memmove(cwt->buf, cwt->buf + WAV_HEADER_LEN, *cwt->loaded); t->error = -E_WAV_HEADER_SUCCESS; PARA_INFO_LOG("channels: %d, sample rate: %d\n", cwt->channels, cwt->samplerate); out: wng->channels = &cwt->channels; wng->samplerate = &cwt->samplerate; ret = wng_open(wng); if (ret < 0) t->error = ret; s->timeout.tv_sec = 0; s->timeout.tv_usec = 1; } static void initial_delay_pre_select(struct sched *s, struct task *t) { struct initial_delay_task *idt = container_of(t, struct initial_delay_task, task); struct timeval diff; if (!idt->start_time.tv_sec && !idt->start_time.tv_usec) { t->error = -E_NO_DELAY; goto register_check_wav; } if (tv_diff(now, &idt->start_time, &diff) > 0) { t->error = -E_DELAY_TIMEOUT; goto register_check_wav; } if (tv_diff(&s->timeout , &diff, NULL) > 0) s->timeout = diff; return; register_check_wav: register_task(&the_check_wav_task.task); s->timeout.tv_sec = 0; s->timeout.tv_usec = 1; } static int loglevel; INIT_STDERR_LOGGING(loglevel) static struct writer_node_group *check_args(void) { int i, ret = -E_WRITE_SYNTAX; struct writer_node_group *g = NULL; struct initial_delay_task *idt = &the_initial_delay_task; loglevel = get_loglevel_by_name(conf.loglevel_arg); if (conf.start_time_given) { long unsigned sec, usec; if (sscanf(conf.start_time_arg, "%lu:%lu", &sec, &usec) != 2) goto out; idt->start_time.tv_sec = sec; idt->start_time.tv_usec = usec; } if (!conf.writer_given) { g = setup_default_wng(); ret = 1; goto out; } g = wng_new(conf.writer_given); ret = -E_WRITE_SYNTAX; for (i = 0; i < conf.writer_given; i++) { int writer_num; g->writer_nodes[i].conf = check_writer_arg( conf.writer_arg[i], &writer_num); if (!g->writer_nodes[i].conf) goto out; g->writer_nodes[i].writer_num = writer_num; } ret = 1; out: if (ret > 0) return g; free(g); return NULL; } __noreturn static void print_help_and_die(void) { int d = conf.detailed_help_given; const char **p = d? write_args_info_detailed_help : write_args_info_help; printf_or_die("%s\n\n", WRITE_CMDLINE_PARSER_PACKAGE "-" WRITE_CMDLINE_PARSER_VERSION); printf_or_die("%s\n\n", write_args_info_usage); for (; *p; p++) printf_or_die("%s\n", *p); print_writer_helps(d); exit(0); } /** * Para_write's main function. * * \param argc The usual argument counter. * \param argv The usual argument vector. * * It registers the stdin task, the check_wav_task, the task for initial delay * and all tasks for actually writing out the stream. * * \return \p EXIT_SUCCESS or EXIT_FAILURE */ int main(int argc, char *argv[]) { int ret = -E_WRITE_SYNTAX; static struct sched s; struct check_wav_task *cwt = &the_check_wav_task; struct initial_delay_task *idt = &the_initial_delay_task; writer_init(); write_cmdline_parser(argc, argv, &conf); HANDLE_VERSION_FLAG("write", conf); if (conf.help_given || conf.detailed_help_given) print_help_and_die(); wng = check_args(); if (!wng) goto out; stdin_set_defaults(&sit); ret = -ERRNO_TO_PARA_ERROR(EINVAL); if (conf.bufsize_arg < 0) goto out; if (conf.bufsize_arg >= INT_MAX / 1024) goto out; sit.bufsize = conf.bufsize_arg * 1024; sit.buf = para_malloc(sit.bufsize); wng->bufp = &sit.buf; wng->loaded = &sit.loaded; wng->input_error = &sit.task.error; register_task(&sit.task); cwt->buf = sit.buf; cwt->loaded = &sit.loaded; cwt->input_error = &sit.task.error; sprintf(cwt->task.status, "check wav"); cwt->task.pre_select = check_wav_pre_select; idt->task.pre_select = initial_delay_pre_select; sprintf(idt->task.status, "initial_delay"); register_task(&idt->task); s.default_timeout.tv_sec = 10; s.default_timeout.tv_usec = 0; ret = schedule(&s); wng_close(wng); out: if (ret < 0) { PARA_ERROR_LOG("%s\n", para_strerror(-ret)); exit(EXIT_FAILURE); } exit(EXIT_SUCCESS); }