#include #include #include #include #include #include struct _xas_riff { int fd; size_t size; size_t sample_size, sample_rate, channels; }; static int header_write(xas_riff *riff) { xas_riff_wave_header header = { .riff = { { .id = "RIFF", .size = sizeof(xas_riff_main_chunk) + sizeof(xas_riff_wave_chunk) + riff->size }, .type = "WAVE" }, .wave = { { .id = "fmt ", .size = 16 }, .type = XAS_RIFF_WAVE_DEFAULT_TYPE, .channels = riff->channels, .sample_rate = riff->sample_rate, .byte_rate = riff->channels * riff->sample_size * riff->sample_rate, .sample_size = riff->sample_size, .sample_size_bits = riff->sample_size << 3 }, { .id = "data", .size = riff->size } }; if (lseek(riff->fd, 0, SEEK_SET) < 0) { goto error_io; } if (write(riff->fd, &header, sizeof(header)) < 0) { goto error_io; } if (lseek(riff->fd, 0, SEEK_END) < 0) { goto error_io; } return 0; error_io: return -1; } static xas_riff *file_new(const char *path, size_t sample_size, size_t sample_rate, size_t channels, int flags) { xas_riff *riff; if ((riff = malloc(sizeof(*riff))) == NULL) { goto error_malloc_riff; } riff->fd = (flags & O_CREAT)? open(path, flags, 0644): open(path, flags); if (riff->fd < 0) { goto error_open; } riff->size = 0; riff->sample_size = sample_size; riff->sample_rate = sample_rate; riff->channels = channels; if (flags & (O_CREAT | O_TRUNC)) { if (header_write(riff) < 0) { goto error_header_write; } } return riff; error_header_write: 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; } if (header_write(riff) < 0) { goto error_header_write; } (void)close(riff->fd); free(riff); error_header_write: error_io: return; } static int audio_drain(xas_riff *riff, void *samples, size_t count, xas_audio_stream *stream) { size_t len = count * stream->sample_size * stream->channels; ssize_t wrlen; if ((wrlen = write(riff->fd, samples, len)) < 0) { goto error_write; } riff->size += wrlen; return wrlen / stream->sample_size / stream->channels; error_write: return -1; } static ssize_t audio_fill(xas_riff *riff, void *samples, size_t count, xas_audio_stream *stream) { size_t len = count * stream->sample_size * stream->channels; ssize_t rdlen; if ((rdlen = read(riff->fd, samples, len)) < 0) { goto error_read; } return rdlen / stream->sample_size / stream->channels; error_read: return -1; } xas_audio_stream *xas_riff_file_new(const char *path, size_t sample_size, size_t sample_rate, size_t channels, int flags) { xas_audio_stream *stream; xas_riff *riff; if ((riff = file_new(path, sample_size, sample_rate, channels, flags)) == NULL) { goto error_file_new; } if (flags & (O_RDWR | O_WRONLY)) { if ((stream = xas_audio_stream_new_sink((xas_audio_drain)audio_drain, (xas_audio_cleanup)file_close, riff, sample_size, sample_rate, 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, sample_size, sample_rate, channels, 4096)) == NULL) { goto error_audio_stream_new_sink; } } return stream; error_audio_stream_new_sink: file_close(riff, NULL); error_file_new: return NULL; }