#include #include #include #include static xas_audio_stream *stream_new(enum xas_audio_stream_type type, void *callback, xas_audio_cleanup cleanup, void *ctx, size_t sample_size, size_t sample_rate, size_t channels, size_t buffer_size) { xas_audio_stream *stream; size_t total = sizeof(xas_audio_stream) + sample_size * channels * buffer_size; if ((stream = malloc(total)) == NULL) { goto error_malloc_stream; } stream->type = type; stream->sample_size = sample_size; stream->sample_rate = sample_rate; stream->channels = channels; stream->buffer_size = buffer_size; stream->buffer_count = 0; stream->callback = callback; stream->cleanup = cleanup; stream->ctx = ctx; return stream; error_malloc_stream: return NULL; } xas_audio_stream *xas_audio_stream_new_sink(xas_audio_drain drain, xas_audio_cleanup cleanup, void *ctx, size_t sample_size, size_t sample_rate, size_t channels, size_t buffer_size) { return stream_new(XAS_AUDIO_STREAM_SINK, drain, cleanup, ctx, sample_size, sample_rate, channels, buffer_size); } xas_audio_stream *xas_audio_stream_new_source(xas_audio_fill fill, xas_audio_cleanup cleanup, void *ctx, size_t sample_size, size_t sample_rate, size_t channels, size_t buffer_size) { return stream_new(XAS_AUDIO_STREAM_SOURCE, fill, cleanup, ctx, sample_size, sample_rate, channels, buffer_size); } void xas_audio_stream_destroy(xas_audio_stream *stream) { if (stream->cleanup) { stream->cleanup(stream->ctx, stream); } free(stream); } static inline int stream_flush(xas_audio_stream *stream) { if (stream->buffer_count == 0) { return 0; } if (stream->drain(stream->ctx, stream + 1, stream->buffer_count, stream) < 0) { goto error_stream_drain; } stream->buffer_count = 0; return 0; error_stream_drain: return -1; } static inline void *ptr(xas_audio_stream *stream, void *buf, size_t index) { return ((uint8_t *)buf) + stream->sample_size * stream->channels * index; } ssize_t xas_audio_stream_write(xas_audio_stream *stream, void *samples, size_t count) { size_t left = count, index = 0; /* * First, if the requested number of samples to write to the stream, plus * the current number in the buffer, is greater than the buffer size, fill * the buffer to capacity and flush. */ if (stream->buffer_count + count > stream->buffer_size) { size_t remainder = stream->buffer_size - stream->buffer_count; if (remainder) { memcpy(ptr(stream, stream + 1, stream->buffer_count), samples, stream->sample_size * stream->channels * remainder); } if (stream_flush(stream) < 0) { goto error_stream_flush; } if (remainder) { left -= remainder; index += remainder; } } while (left > stream->buffer_size) { ssize_t drained; if ((drained = stream->drain(stream->ctx, ptr(stream, samples, index), stream->buffer_size, stream)) < 0) { goto error_stream_drain; } left -= drained; index += drained; } memcpy(ptr(stream, stream + 1, stream->buffer_count), ptr(stream, samples, index), stream->sample_size * stream->channels * left); stream->buffer_count += left; return count; error_stream_drain: error_stream_flush: return -1; } ssize_t xas_audio_stream_read(xas_audio_stream *stream, void **samples, size_t count) { *samples = stream + 1; return stream->fill(stream->ctx, *samples, count, stream); } int xas_audio_stream_flush(xas_audio_stream *stream) { if (stream->type == XAS_AUDIO_STREAM_SINK) { return stream_flush(stream); } errno = EINVAL; return -1; }