/* SPDX-License-Identifier: GPL-2.0 */ /** \file wav_filter.c A filter that inserts a wave header. */ #include "para.h" #include "error.h" #include "list.h" #include "sched.h" #include "buffer_tree.h" #include "filter.h" #include "string.h" #include "portable_io.h" /** A wav header is always 44 bytes. */ #define WAV_HEADER_LEN 44 /** Always write 16 bit header. */ #define BITS 16 static void make_wav_header(unsigned int channels, unsigned int sample_rate, char *headbuf) { unsigned int size = 0x7fffffff; int bytespersec = channels * sample_rate * BITS / 8; int align = channels * BITS / 8; PARA_DEBUG_LOG("writing wave header: %u channels, %u KHz\n", channels, sample_rate); memset(headbuf, 0, WAV_HEADER_LEN); memcpy(headbuf, "RIFF", 4); write_u32(headbuf + 4, size - 8); memcpy(headbuf + 8, "WAVE", 4); memcpy(headbuf + 12, "fmt ", 4); write_u32(headbuf + 16, 16); /* 16 + extra format bytes (zero) */ write_u16(headbuf + 20, 1); /* format (1 == PCM/uncompressed) */ write_u16(headbuf + 22, channels); write_u32(headbuf + 24, sample_rate); write_u32(headbuf + 28, bytespersec); write_u16(headbuf + 32, align); /* number of bytes per sample slice */ write_u16(headbuf + 34, BITS); /* significant bits per sample */ memcpy(headbuf + 36, "data", 4); /* chunk ID */ write_u32(headbuf + 40, size - 44); /* chunk size */ } static void wav_close(struct filter_node *fn) { free(fn->private_data); fn->private_data = NULL; } static void wav_open(struct filter_node *fn) { int *bof; fn->private_data = alloc(sizeof(int)); bof = fn->private_data; *bof = 1; } static void wav_pre_monitor(struct sched *s, void *context) { struct filter_node *fn = context; size_t iqs = btr_get_input_queue_size(fn->btrn); if (iqs == 0) return; sched_min_delay(s); } static int wav_post_monitor(__a_unused struct sched *s, void *context) { struct filter_node *fn = context; struct btr_node *btrn = fn->btrn; size_t iqs = btr_get_input_queue_size(btrn); int ret; char *header, *buf; int32_t rate, ch; if (iqs == 0) { ret = -E_EOF; if (btr_no_parent(btrn)) goto err; return 0; } ret = btr_exec_up(btrn, "sample_rate", &buf); if (ret < 0) { ret = -E_WAV_BAD_FC; goto err; } ret = para_atoi32(buf, &rate); free(buf); if (ret < 0) goto err; ret = btr_exec_up(btrn, "channels", &buf); if (ret < 0) { ret = -E_WAV_BAD_FC; goto err; } ret = para_atoi32(buf, &ch); free(buf); if (ret < 0) goto err; header = alloc(WAV_HEADER_LEN); make_wav_header(ch, rate, header); btr_add_output(header, WAV_HEADER_LEN, btrn); ret = -E_WAV_SUCCESS; err: if (ret == -E_WAV_SUCCESS) btr_splice_out_node(&fn->btrn); else { btr_remove_node(&fn->btrn); PARA_ERROR_LOG("%s\n", para_strerror(-ret)); } return ret; } /** \cond doxygen_ignore */ const struct filter lsg_filter_cmd_com_wav_user_data = { .close = wav_close, .open = wav_open, .pre_monitor = wav_pre_monitor, .post_monitor = wav_post_monitor, }; /** \endcond */