summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorXANTRONIX Development2022-02-01 13:49:22 -0500
committerXANTRONIX Development2022-02-01 13:49:22 -0500
commitc801d7da7935731dd71fde695eb8ee8419c71d03 (patch)
tree9436f5bb4a9f1eed18db75bf0fe3b8609541ee67 /src
parent6391c03277dbe0aae6bae4c24b936ce2becf69c1 (diff)
downloadxas-c801d7da7935731dd71fde695eb8ee8419c71d03.tar.gz
xas-c801d7da7935731dd71fde695eb8ee8419c71d03.tar.bz2
xas-c801d7da7935731dd71fde695eb8ee8419c71d03.zip
Implement xas_riff_file_open()
Implement xas_riff_file_open() to open files for reading; implement a RIFF header reader to parse and validate a header, and to extract PCM sample metadata requisite to process a stream
Diffstat (limited to 'src')
-rw-r--r--src/riff.c175
1 files changed, 175 insertions, 0 deletions
diff --git a/src/riff.c b/src/riff.c
index 640f550..233d2cb 100644
--- a/src/riff.c
+++ b/src/riff.c
@@ -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;
+}