diff options
-rw-r--r-- | include/xas/riff.h | 3 | ||||
-rw-r--r-- | src/riff.c | 175 |
2 files changed, 178 insertions, 0 deletions
diff --git a/include/xas/riff.h b/include/xas/riff.h index 6e097c3..823a9ae 100644 --- a/include/xas/riff.h +++ b/include/xas/riff.h @@ -55,4 +55,7 @@ xas_audio_stream *xas_riff_file_new(const char *path, size_t channels, int flags); +xas_audio_stream *xas_riff_file_open(const char *path, + int flags); + #endif /* _XAS_RIFF_H */ @@ -1,6 +1,8 @@ #include <stdlib.h> +#include <string.h> #include <fcntl.h> #include <unistd.h> +#include <errno.h> #include <xas/audio.h> #include <xas/riff.h> @@ -104,6 +106,139 @@ error_malloc_riff: return NULL; } +static int wave_header_parse(xas_riff *riff, + xas_riff_wave_header *header) { + if (memcmp(header->riff.header.id, + XAS_RIFF_HEADER_MAIN_ID, + strlen(XAS_RIFF_HEADER_MAIN_ID)) != 0) { + goto error_invalid_header_main_id; + } + + if (header->riff.header.size < sizeof(xas_riff_main_chunk) + + sizeof(xas_riff_wave_chunk)) { + goto error_invalid_header_wave_chunk_size; + } + + if (memcmp(header->riff.type, + XAS_RIFF_HEADER_WAVE_ID, + strlen(XAS_RIFF_HEADER_WAVE_ID)) != 0) { + goto error_invalid_wave_chunk_id; + } + + if (memcmp(header->wave.header.id, + XAS_RIFF_HEADER_WAVE_FMT_ID, + strlen(XAS_RIFF_HEADER_WAVE_FMT_ID)) != 0) { + goto error_invalid_wave_format_id; + } + + if (header->wave.header.size != sizeof(xas_riff_wave_chunk) + - sizeof(xas_riff_chunk)) { + goto error_invalid_wave_format_size; + } + + if (header->wave.type != XAS_RIFF_WAVE_DEFAULT_TYPE) { + goto error_invalid_wave_format_type; + } + + switch (header->wave.channels) { + case XAS_AUDIO_STREAM_MONO: + case XAS_AUDIO_STREAM_STEREO: + break; + + default: + goto error_invalid_wave_channels; + } + + if (header->wave.byte_rate != header->wave.channels + * header->wave.sample_size + * header->wave.sample_rate) { + goto error_invalid_wave_byte_rate; + } + + if (header->wave.sample_size_bits != header->wave.sample_size << 3) { + goto error_invalid_wave_sample_size_bits; + } + + if (memcmp(header->data.id, + XAS_RIFF_HEADER_WAVE_DATA_ID, + strlen(XAS_RIFF_HEADER_WAVE_DATA_ID)) != 0) { + goto error_invalid_wave_data_format_id; + } + + riff->sample_size = header->wave.sample_size; + riff->sample_rate = header->wave.sample_rate; + riff->channels = header->wave.channels; + + return 0; + +error_invalid_wave_data_format_id: +error_invalid_wave_sample_size_bits: +error_invalid_wave_byte_rate: +error_invalid_wave_channels: +error_invalid_wave_format_type: +error_invalid_wave_format_size: +error_invalid_wave_format_id: +error_invalid_wave_chunk_id: +error_invalid_header_wave_chunk_size: +error_invalid_header_main_id: + return -1; +} + +static xas_riff *file_open(const char *path, + int flags) { + xas_riff *riff; + xas_riff_wave_header header; + + ssize_t readlen; + + if ((riff = malloc(sizeof(*riff))) == NULL) { + goto error_malloc_riff; + } + + flags &= ~(O_CREAT | O_TRUNC); + + if ((riff->fd = open(path, flags)) < 0) { + goto error_open; + } + + riff->size = 0; + riff->sample_size = 0; + riff->sample_rate = 0; + riff->channels = 0; + + if (lseek(riff->fd, 0, SEEK_SET) < 0) { + goto error_lseek; + } + + if ((readlen = read(riff->fd, &header, sizeof(header))) < 0) { + goto error_read; + } + + if (readlen != sizeof(header)) { + errno= EINVAL; + + goto error_wave_header_short; + } + + if (wave_header_parse(riff, &header) < 0) { + goto error_wave_header_parse; + } + + return riff; + +error_wave_header_parse: +error_wave_header_short: +error_read: +error_lseek: + close(riff->fd); + +error_open: + free(riff); + +error_malloc_riff: + return NULL; +} + static void file_close(xas_riff *riff, xas_audio_stream *stream) { if (lseek(riff->fd, 0, SEEK_SET) < 0) { goto error_io; @@ -208,3 +343,43 @@ error_audio_stream_new_sink: error_file_new: return NULL; } + +xas_audio_stream *xas_riff_file_open(const char *path, + int flags) { + xas_audio_stream *stream; + xas_riff *riff; + + if ((riff = file_open(path, flags)) == NULL) { + goto error_file_open; + } + + if (flags & (O_RDWR | O_WRONLY)) { + if ((stream = xas_audio_stream_new_sink((xas_audio_drain)audio_drain, + (xas_audio_cleanup)file_close, + riff, + riff->sample_size, + riff->sample_rate, + riff->channels, + 4096)) == NULL) { + goto error_audio_stream_new_sink; + } + } else { + if ((stream = xas_audio_stream_new_source((xas_audio_fill)audio_fill, + (xas_audio_cleanup)file_close, + riff, + riff->sample_size, + riff->sample_rate, + riff->channels, + 4096)) == NULL) { + goto error_audio_stream_new_sink; + } + } + + return stream; + +error_audio_stream_new_sink: + file_close(riff, NULL); + +error_file_open: + return NULL; +} |