/* * Copyright (C) 2005-2007 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ /** \file write.c Paraslash's standalone wav/raw player */ #include "para.h" #include "string.h" #include "write.cmdline.h" #include "list.h" #include "sched.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 end of file was reached */ int *eof; /** number of channels specified in wav header given by \a buf */ unsigned channels; /** samplerate specified in wav header given by \a buf */ unsigned samplerate; /** the task structure for this task */ 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 cwt; static struct initial_delay_task idt; 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 awailable, return one. */ static void check_wav_pre_select(__a_unused struct sched *s, struct task *t) { struct check_wav_task *wt = t->private_data; unsigned char *a; if (*wt->loaded < WAV_HEADER_LEN) { t->ret = *wt->eof? -E_PREMATURE_END : 1; return; } wt->channels = 2; wt->samplerate = 44100; a = (unsigned char*)wt->buf; t->ret = -E_NO_WAV_HEADER; if (a[0] != 'R' || a[1] != 'I' || a[2] != 'F' || a[3] != 'F') return; wt->channels = (unsigned) a[22]; wt->samplerate = a[24] + (a[25] << 8) + (a[26] << 16) + (a[27] << 24); *wt->loaded -= WAV_HEADER_LEN; memmove(wt->buf, wt->buf + WAV_HEADER_LEN, *wt->loaded); t->ret = -E_WAV_HEADER_SUCCESS; PARA_INFO_LOG("channels: %d, sample rate: %d\n", wt->channels, wt->samplerate); } static void initial_delay_pre_select(struct sched *s, struct task *t) { struct initial_delay_task *dt = t->private_data; struct timeval diff; t->ret = -E_NO_DELAY; if (!dt->start_time.tv_sec && !dt->start_time.tv_usec) return; t->ret = -E_DELAY_TIMEOUT; if (tv_diff(now, &dt->start_time, &diff) > 0) return; t->ret = 1; if (tv_diff(&s->timeout , &diff, NULL) > 0) s->timeout = diff; } INIT_STDERR_LOGGING(conf.loglevel_arg) static struct writer_node_group *check_args(void) { int i, ret = -E_WRITE_SYNTAX; struct writer_node_group *g = NULL; if (conf.list_writers_given) { char *msg = NULL; FOR_EACH_WRITER(i) { char *tmp = make_message("%s%s%s", i? msg : "", i? " " : "", writer_names[i]); free(msg); msg = tmp; } fprintf(stderr, "%s\n", msg); free(msg); exit(EXIT_SUCCESS); } 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 = &writers[writer_num]; } ret = 1; out: if (ret > 0) return g; free(g); return NULL; } static void wng_event_handler(struct task *t) { struct writer_node_group *g = t->private_data; PARA_INFO_LOG("%s\n", PARA_STRERROR(-t->ret)); unregister_task(t); wng_close(g); } static void idt_event_handler(struct task *t) { int ret; PARA_INFO_LOG("%s\n", PARA_STRERROR(-t->ret)); unregister_task(t); wng->buf = sit.buf; wng->loaded = &sit.loaded; wng->input_eof = &sit.eof; wng->task.event_handler = wng_event_handler; wng->channels = &cwt.channels; wng->samplerate = &cwt.samplerate; ret = wng_open(wng); if (ret < 0) { PARA_ERROR_LOG("%s\n", PARA_STRERROR(-ret)); exit(EXIT_FAILURE); } } static void cwt_event_handler(struct task *t) { if (t->ret != -E_NO_WAV_HEADER && t->ret != -E_WAV_HEADER_SUCCESS) { PARA_ERROR_LOG("%s\n", PARA_STRERROR(-t->ret)); exit(EXIT_FAILURE); } PARA_INFO_LOG("%s\n", PARA_STRERROR(-t->ret)); unregister_task(t); // if (t->ret == -E_WAV_HEADER_SUCCESS) { // conf.channels_arg = cwt.channels; // conf.sample_rate_arg = cwt.sample_rate; // } idt.task.pre_select = initial_delay_pre_select; idt.task.private_data = &idt; idt.task.event_handler = idt_event_handler; sprintf(idt.task.status, "initial_delay"); register_task(&idt.task); } /** * 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; struct sched s; write_cmdline_parser(argc, argv, &conf); HANDLE_VERSION_FLAG("write", conf); init_supported_writers(); wng = check_args(); if (!wng) goto out; stdin_set_defaults(&sit); if (conf.bufsize_given) sit.bufsize = conf.bufsize_arg; sit.buf = para_malloc(sit.bufsize), register_task(&sit.task); cwt.task.pre_select = check_wav_pre_select; cwt.task.private_data = &cwt; cwt.task.event_handler = cwt_event_handler; cwt.buf = sit.buf; cwt.loaded = &sit.loaded; cwt.eof = &sit.eof; sprintf(cwt.task.status, "check wav"); register_task(&cwt.task); s.default_timeout.tv_sec = 1; s.default_timeout.tv_usec = 0; ret = sched(&s); out: if (ret < 0) { PARA_ERROR_LOG("%s\n", PARA_STRERROR(-ret)); ret = EXIT_FAILURE; } else ret = EXIT_SUCCESS; return ret; }