From 60b4ebfbf3be78dba9dc7c65ab2bdaa0b218c0c2 Mon Sep 17 00:00:00 2001 From: Andrew Opalach Date: Mon, 21 Oct 2024 19:22:50 -0400 Subject: Everything before initial synced list Signed-off-by: Andrew Opalach --- src/mixer/audio.h | 4 +- src/mixer/audio_miniaudio.c | 70 ++++++++++++++++------------ src/mixer/audio_miniaudio.h | 6 ++- src/mixer/audio_null.c | 67 +++++++++++++++++++++++++++ src/mixer/audio_null.h | 7 +++ src/mixer/meson.build | 45 ++++++++++-------- src/mixer/mixer.c | 109 +++++++++++++++++++++++++++----------------- src/mixer/mixer.h | 16 +++++-- 8 files changed, 226 insertions(+), 98 deletions(-) create mode 100644 src/mixer/audio_null.c create mode 100644 src/mixer/audio_null.h (limited to 'src/mixer') diff --git a/src/mixer/audio.h b/src/mixer/audio.h index 6624896..6e52f1a 100644 --- a/src/mixer/audio.h +++ b/src/mixer/audio.h @@ -2,12 +2,12 @@ #include -#include "../codec/ffmpeg/resampler.h" +#include "../codec/codec.h" struct camu_audio { bool (*init)(struct camu_audio *, str *); - void (*pick_format)(struct camu_audio *, struct camu_ff_resample_fmt *); + void (*pick_format)(struct camu_audio *, struct camu_resampler_format *); bool (*configure_stream)(struct camu_audio *, void *); u64 (*get_latency)(struct camu_audio *); diff --git a/src/mixer/audio_miniaudio.c b/src/mixer/audio_miniaudio.c index 26c01b2..fb4db23 100644 --- a/src/mixer/audio_miniaudio.c +++ b/src/mixer/audio_miniaudio.c @@ -1,13 +1,26 @@ #include - +#if defined(__clang__) || defined(__GNUC__) +_Pragma("GCC diagnostic push") \ +_Pragma("GCC diagnostic ignored \"-Wunused-function\"") +_Pragma("GCC diagnostic ignored \"-Wincompatible-pointer-types\"") +_Pragma("GCC diagnostic ignored \"-Wunknown-pragmas\"") +#elif _MSC_VER +#pragma warning(push, 0) +#endif #define MINIAUDIO_IMPLEMENTATION #include "audio_miniaudio.h" +#if defined(__clang__) || defined(__GNUC__) +_Pragma("GCC diagnostic pop") +#elif _MSC_VER +#pragma warning(pop) +#endif static ma_backend backends[] = { #ifndef _WIN32 ma_backend_pulseaudio, #else ma_backend_wasapi +// ma_backend_dsound #endif }; @@ -84,32 +97,34 @@ static bool audio_miniaudio_init(struct camu_audio *audio, str *name) return true; } -static enum AVSampleFormat av_sample_fmt_from_miniaudio(s32 fmt) +static s32 camu_sample_format_from_miniaudio(s32 fmt) { switch (fmt) { case ma_format_u8: - return AV_SAMPLE_FMT_U8; + return CAMU_SAMPLE_FORMAT_U8; case ma_format_s16: - return AV_SAMPLE_FMT_S16; + return CAMU_SAMPLE_FORMAT_S16; case ma_format_s24: case ma_format_s32: - return AV_SAMPLE_FMT_S32; + return CAMU_SAMPLE_FORMAT_S32; case ma_format_f32: - return AV_SAMPLE_FMT_FLT; + return CAMU_SAMPLE_FORMAT_FLT; case ma_format_unknown: default: - al_assert(false); - return AV_SAMPLE_FMT_NONE; + al_assert_and_return(false); } } -static void audio_miniaudio_pick_format(struct camu_audio *audio, struct camu_ff_resample_fmt *fmt) +static void audio_miniaudio_pick_format(struct camu_audio *audio, struct camu_resampler_format *fmt) { struct camu_audio_miniaudio *ma = (struct camu_audio_miniaudio *)audio; - fmt->req_format = av_sample_fmt_from_miniaudio(ma->device.playback.internalFormat); - fmt->req_channel_count = ma->device.playback.internalChannels; - av_channel_layout_default(&fmt->req_channel_layout, fmt->req_channel_count); - fmt->req_sample_rate = ma->device.playback.internalSampleRate; + fmt->req.format = camu_sample_format_from_miniaudio(ma->device.playback.format); + fmt->req.sample_rate = ma->device.playback.internalSampleRate; + u32 channels = ma->device.playback.channels; +#ifdef CAMU_HAVE_FFMPEG + av_channel_layout_default(&fmt->req.channel_layout, channels); +#endif + fmt->req.channel_count = channels; } static void data_callback(ma_device *device, void *output, const void *input, u32 frame_count) @@ -134,8 +149,9 @@ static bool audio_miniaudio_configure_stream(struct camu_audio *audio, void *opa ma->config.performanceProfile = ma_performance_profile_conservative; ma->config.periods = DEFAULT_PERIODS; ma->config.periodSizeInMilliseconds = DEFAULT_PERIOD_SIZE_IN_MILLISECONDS; - ma->config.playback.format = ma_format_unknown; - ma->config.playback.channels = 0; + //ma->config.playback.format = ma_format_unknown; + ma->config.playback.format = ma_format_f32; + ma->config.playback.channels = 2; ma->config.sampleRate = 0; ma->config.dataCallback = data_callback; ma->config.pUserData = ma; @@ -149,8 +165,8 @@ static bool audio_miniaudio_configure_stream(struct camu_audio *audio, void *opa al_log_error("audio_miniaudio", "Failed to initialize device (%d).", ret); return false; } - enum AVSampleFormat avfmt = av_sample_fmt_from_miniaudio(ma->device.playback.internalFormat); - const char *format_name = av_get_sample_fmt_name(avfmt); + s32 format = camu_sample_format_from_miniaudio(ma->device.playback.internalFormat); + const char *format_name = camu_audio_format_name(format); al_log_info("audio_miniaudio", "Output: %s (%dch) %dHz.", format_name, ma->device.playback.internalChannels, ma->device.playback.internalSampleRate); return true; @@ -187,11 +203,14 @@ static void audio_miniaudio_free(struct camu_audio **audio) ma_device_uninit(&ma->device); ma_context_uninit(&ma->context); al_free((void *)ma->context_config.pulse.pApplicationName); + al_free(ma); *audio = NULL; } -struct camu_audio_miniaudio audio_plugin_miniaudio = { - .a = { +struct camu_audio *camu_audio_miniaudio_create(void) +{ + struct camu_audio_miniaudio *ma = al_alloc_object(struct camu_audio_miniaudio); + ma->a = (struct camu_audio){ .init = audio_miniaudio_init, .pick_format = audio_miniaudio_pick_format, .configure_stream = audio_miniaudio_configure_stream, @@ -201,13 +220,6 @@ struct camu_audio_miniaudio audio_plugin_miniaudio = { .free = audio_miniaudio_free, .data_callback = NULL, .userdata = NULL - }, - .log = { }, - .context_config = { 0 }, - .context = { }, - .infos = NULL, - .info_count = 0, - .selected_device = NULL, - .config = { 0 }, - .device = { 0 } -}; + }; + return (struct camu_audio *)ma; +} diff --git a/src/mixer/audio_miniaudio.h b/src/mixer/audio_miniaudio.h index 3d7f010..d93c69c 100644 --- a/src/mixer/audio_miniaudio.h +++ b/src/mixer/audio_miniaudio.h @@ -1,5 +1,9 @@ #pragma once +// Include here to ensure OS related headers +// get included the way we want. +// Specifically, on Windows we want to include before . +#include #include #include "audio.h" @@ -16,4 +20,4 @@ struct camu_audio_miniaudio { ma_device device; }; -extern struct camu_audio_miniaudio audio_plugin_miniaudio; +struct camu_audio *camu_audio_miniaudio_create(void); diff --git a/src/mixer/audio_null.c b/src/mixer/audio_null.c new file mode 100644 index 0000000..ac2846a --- /dev/null +++ b/src/mixer/audio_null.c @@ -0,0 +1,67 @@ +#include "audio_null.h" + +static bool audio_null_init(struct camu_audio *audio, str *name) +{ + struct camu_audio_null *na = (struct camu_audio_null *)audio; + (void)na; + (void)name; + return true; +} + +static void audio_null_pick_format(struct camu_audio *audio, struct camu_resampler_format *fmt) +{ + struct camu_audio_null *na = (struct camu_audio_null *)audio; + (void)na; + fmt->req.format = fmt->in.format; + fmt->req.channel_count = fmt->in.channel_count; + av_channel_layout_default(&fmt->req.channel_layout, fmt->in.channel_count); + fmt->req.sample_rate = fmt->in.sample_rate; +} + +static bool audio_null_configure_stream(struct camu_audio *audio, void *opaque) +{ + struct camu_audio_null *na = (struct camu_audio_null *)audio; + (void)na; + (void)opaque; + return true; +} + +static u64 audio_null_get_latency(struct camu_audio *audio) +{ + struct camu_audio_null *na = (struct camu_audio_null *)audio; + (void)na; + return 0; +} + +static void audio_null_start(struct camu_audio *audio) +{ + struct camu_audio_null *na = (struct camu_audio_null *)audio; + (void)na; +} + +static void audio_null_stop(struct camu_audio *audio) +{ + struct camu_audio_null *na = (struct camu_audio_null *)audio; + (void)na; +} + +static void audio_null_free(struct camu_audio **audio) +{ + struct camu_audio_null *na = (struct camu_audio_null *)*audio; + (void)na; + *audio = NULL; +} + +struct camu_audio_null audio_plugin_null = { + .a = { + .init = audio_null_init, + .pick_format = audio_null_pick_format, + .configure_stream = audio_null_configure_stream, + .get_latency = audio_null_get_latency, + .start = audio_null_start, + .stop = audio_null_stop, + .free = audio_null_free, + .data_callback = NULL, + .userdata = NULL + } +}; diff --git a/src/mixer/audio_null.h b/src/mixer/audio_null.h new file mode 100644 index 0000000..b69cded --- /dev/null +++ b/src/mixer/audio_null.h @@ -0,0 +1,7 @@ +#include "audio.h" + +struct camu_audio_null { + struct camu_audio a; +}; + +extern struct camu_audio_null audio_plugin_null; diff --git a/src/mixer/meson.build b/src/mixer/meson.build index 71c003b..ea9356c 100644 --- a/src/mixer/meson.build +++ b/src/mixer/meson.build @@ -1,27 +1,32 @@ mixer_src = ['mixer.c'] mixer_deps = [common_deps] +mixer_args = [] -miniaudio = subproject('miniaudio').get_variable('miniaudio') -mixer_src += ['audio_miniaudio.c'] -mixer_deps += [miniaudio] - -miniaudio_args = [ - '-DMA_ENABLE_ONLY_SPECIFIC_BACKENDS', - '-DMA_NO_DECODING', - '-DMA_NO_ENCODING', - '-DMA_NO_RESOURCE_MANAGER', - '-DMA_NO_NODE_GRAPH', - '-DMA_NO_ENGINE', - '-DMA_NO_GENERATION', -] - -if not is_windows - libpulse = dependency('libpulse') - mixer_deps += [libpulse] - miniaudio_args += ['-DMA_ENABLE_PULSEAUDIO', '-DMA_NO_RUNTIME_LINKING'] +use_miniaudio = true +if use_miniaudio + miniaudio = subproject('miniaudio').get_variable('miniaudio') + mixer_src += ['audio_miniaudio.c'] + mixer_deps += [miniaudio] + mixer_args += [ + '-DMA_ENABLE_ONLY_SPECIFIC_BACKENDS', + '-DMA_NO_DECODING', + '-DMA_NO_ENCODING', + '-DMA_NO_RESOURCE_MANAGER', + '-DMA_NO_NODE_GRAPH', + '-DMA_NO_ENGINE', + '-DMA_NO_GENERATION', + ] + if not is_windows + libpulse = dependency('libpulse') + mixer_deps += [libpulse] + mixer_args += ['-DMA_ENABLE_PULSEAUDIO', '-DMA_NO_RUNTIME_LINKING'] + else + mixer_args += ['-DMA_ENABLE_WASAPI'] + #mixer_args += ['-DMA_ENABLE_DSOUND'] + endif else - miniaudio_args += ['-DMA_ENABLE_WASAPI'] + mixer_src += ['audio_null.c'] endif mixer = declare_dependency(sources: mixer_src, dependencies: mixer_deps, - compile_args: miniaudio_args) + compile_args: mixer_args) diff --git a/src/mixer/mixer.c b/src/mixer/mixer.c index 5caee1b..987bc8b 100644 --- a/src/mixer/mixer.c +++ b/src/mixer/mixer.c @@ -3,29 +3,43 @@ #include "mixer.h" #include "audio.h" +// Should an initial read inside of start() be marked as silence or not. +#define MIXER_WANT_INITIAL_SILENCE 1 + +// Number of silent frames to append before signaling MIXER_EMPTY. +#define MIXER_TRAILING_SILENCE 100 + static s32 data_callback(void *userdata, u8 *data, s32 frame_count, bool *silence) { struct camu_mixer *mixer = (struct camu_mixer *)userdata; - size_t req = camu_ff_resample_fmt_samples_to_bytes(&mixer->fmt, (size_t)frame_count); + size_t req = camu_audio_format_samples_to_bytes(&mixer->fmt.req, (size_t)frame_count); if (UNLIKELY(mixer->paused)) { al_memset(data, 0, req); - *silence = true; + *silence = MIXER_WANT_INITIAL_SILENCE; } else { #ifdef CAMU_MIXER_THREADED - if (al_atomic_bool_load(&mixer->queued, AL_ATOMIC_RELAXED)) { + if (al_atomic_load(u8)(&mixer->queued, AL_ATOMIC_RELAXED)) { camu_mixer_run_queue(mixer); } #endif + if (UNLIKELY(mixer->empty_after > 0)) { + if (--mixer->empty_after == 0) { + mixer->callback(mixer->userdata, CAMU_MIXER_EMPTY); + } + *silence = false; + } else { + *silence = true; + } do { - if (mixer->buffers.size == 0) { + if (al_array_is_empty(mixer->buffers)) { al_memset(data, 0, req); - *silence = true; } else { - struct camu_audio_buffer *buf = al_array_last(mixer->buffers); size_t signal; + struct camu_audio_buffer *buf = al_array_last(mixer->buffers); + *silence = false; if ((signal = camu_audio_buffer_read(buf, data, req)) < req) { #ifdef CAMU_MIXER_THREADED - if (al_atomic_bool_load(&mixer->queued, AL_ATOMIC_RELAXED)) { + if (al_atomic_load(u8)(&mixer->queued, AL_ATOMIC_RELAXED)) { camu_mixer_run_queue(mixer); } #endif @@ -33,7 +47,6 @@ static s32 data_callback(void *userdata, u8 *data, s32 frame_count, bool *silenc req = req - signal; continue; } - *silence = false; } break; } while (1); @@ -46,23 +59,20 @@ bool camu_mixer_init(struct camu_mixer *mixer, struct camu_audio *audio) mixer->audio = audio; mixer->audio->data_callback = data_callback; mixer->audio->userdata = mixer; - mixer->audio->init(mixer->audio, al_str_c("camu")); - if (!mixer->audio->configure_stream(mixer->audio, NULL)) { - return false; - } - camu_mixer_pick_format(mixer, &mixer->fmt); + mixer->audio->init(mixer->audio, al_str_c("cmv")); mixer->paused = true; + mixer->empty_after = 0; al_array_init(mixer->buffers); #ifdef CAMU_MIXER_THREADED al_array_init(mixer->add_queue); al_array_init(mixer->rem_queue); - al_atomic_bool_store(&mixer->queued, false, AL_ATOMIC_RELAXED); + al_atomic_store(u8)(&mixer->queued, 0, AL_ATOMIC_RELAXED); aki_mutex_init(&mixer->mutex); #endif return true; } -void camu_mixer_pick_format(struct camu_mixer *mixer, struct camu_ff_resample_fmt *fmt) +void camu_mixer_pick_format(struct camu_mixer *mixer, struct camu_resampler_format *fmt) { mixer->audio->pick_format(mixer->audio, fmt); } @@ -75,7 +85,7 @@ f64 camu_mixer_get_latency(struct camu_mixer *mixer) static void add_buffer_internal(struct camu_mixer *mixer, struct camu_audio_buffer *buf) { #ifdef CAMU_MIXER_THREADED - al_atomic_bool_store(&buf->ref, true, AL_ATOMIC_RELAXED); + al_atomic_store(u8)(&buf->ref, 1, AL_ATOMIC_RELAXED); #endif al_array_push(mixer->buffers, buf); } @@ -86,26 +96,14 @@ static void remove_buffer_internal(struct camu_mixer *mixer, struct camu_audio_b al_array_foreach(mixer->buffers, i, cbuf) { if (cbuf == buf) { #ifdef CAMU_MIXER_THREADED - al_atomic_bool_store(&buf->ref, false, AL_ATOMIC_RELAXED); + al_atomic_store(u8)(&buf->ref, 0, AL_ATOMIC_RELAXED); #endif - al_array_remove_at_iter(mixer->buffers, i); + al_array_remove_at(mixer->buffers, i); break; } } } -void camu_mixer_add_buffer(struct camu_mixer *mixer, struct camu_audio_buffer *buf) -{ -#ifdef CAMU_MIXER_THREADED - aki_mutex_lock(&mixer->mutex); - al_array_push(mixer->add_queue, buf); - al_atomic_bool_store(&mixer->queued, true, AL_ATOMIC_RELAXED); - aki_mutex_unlock(&mixer->mutex); -#else - add_buffer_internal(mixer, buf); -#endif -} - #ifdef CAMU_MIXER_THREADED static void run_queue_internal(struct camu_mixer *mixer) { @@ -119,22 +117,43 @@ static void run_queue_internal(struct camu_mixer *mixer) remove_buffer_internal(mixer, buf); } mixer->rem_queue.size = 0; - al_atomic_bool_store(&mixer->queued, false, AL_ATOMIC_RELAXED); + al_atomic_store(u8)(&mixer->queued, 0, AL_ATOMIC_RELAXED); + if (al_array_is_empty(mixer->buffers)) { + mixer->empty_after = MIXER_TRAILING_SILENCE; + } else { + mixer->empty_after = 0; + } } #endif +void camu_mixer_add_buffer(struct camu_mixer *mixer, struct camu_audio_buffer *buf) +{ +#ifdef CAMU_MIXER_THREADED + aki_mutex_lock(&mixer->mutex); + al_array_push(mixer->add_queue, buf); + al_atomic_store(u8)(&mixer->queued, 1, AL_ATOMIC_RELAXED); + aki_mutex_unlock(&mixer->mutex); +#else + add_buffer_internal(mixer, buf); + mixer->empty_after = 0; +#endif +} + void camu_mixer_remove_buffer(struct camu_mixer *mixer, struct camu_audio_buffer *buf) { #ifdef CAMU_MIXER_THREADED aki_mutex_lock(&mixer->mutex); al_array_push(mixer->rem_queue, buf); - al_atomic_bool_store(&mixer->queued, true, AL_ATOMIC_RELAXED); + al_atomic_store(u8)(&mixer->queued, 1, AL_ATOMIC_RELAXED); if (mixer->paused) { run_queue_internal(mixer); } aki_mutex_unlock(&mixer->mutex); #else remove_buffer_internal(mixer, buf); + if (al_array_is_empty(mixer->buffers)) { + mixer->empty_after = MIXER_TRAILING_SILENCE; + } #endif } @@ -167,17 +186,11 @@ void camu_mixer_resume(struct camu_mixer *mixer) aki_mutex_lock(&mixer->mutex); #endif if (mixer->paused) { -//#ifdef CAMU_MIXER_THREADED -// bool queued = al_atomic_bool_load(&mixer->queued, AL_ATOMIC_RELAXED); -// // Disable queue when starting audio in case start() directly calls data_callback(). -// // Locking here is necessary to 100% know the state of the audio backend at -// // any time. This allows us to act on the value of paused, like in remove_buffer(). -// if (queued) al_atomic_bool_store(&mixer->queued, false, AL_ATOMIC_RELAXED); -//#endif + // start() can internally call data_callback once before returning. + // In that call mixer->paused will still be true. So, we have a special case to + // immediately return silence and avoid any possible locking. + // Outputs can treat that silence as part of the stream with MIXER_WANT_INITIAL_SILENCE. mixer->audio->start(mixer->audio); -//#ifdef CAMU_MIXER_THREADED -// if (queued) al_atomic_bool_store(&mixer->queued, true, AL_ATOMIC_RELAXED); -//#endif mixer->paused = false; } #ifdef CAMU_MIXER_THREADED @@ -185,6 +198,18 @@ void camu_mixer_resume(struct camu_mixer *mixer) #endif } +void camu_mixer_clear(struct camu_mixer *mixer) +{ +#ifdef CAMU_MIXER_THREADED + run_queue_internal(mixer); + struct camu_audio_buffer *buf; + al_array_foreach(mixer->buffers, i, buf) { + al_atomic_store(u8)(&buf->ref, 0, AL_ATOMIC_RELAXED); + } +#endif + mixer->buffers.size = 0; +} + void camu_mixer_close(struct camu_mixer *mixer) { al_array_free(mixer->buffers); diff --git a/src/mixer/mixer.h b/src/mixer/mixer.h index 113db62..729657f 100644 --- a/src/mixer/mixer.h +++ b/src/mixer/mixer.h @@ -9,23 +9,30 @@ #include #endif -#include "../codec/ffmpeg/resampler.h" +#include "../codec/codec.h" + +enum { + CAMU_MIXER_EMPTY = 0 +}; struct camu_mixer { struct camu_audio *audio; + struct camu_resampler_format fmt; bool paused; - struct camu_ff_resample_fmt fmt; + u8 empty_after; array(struct camu_audio_buffer *) buffers; #ifdef CAMU_MIXER_THREADED array(struct camu_audio_buffer *) add_queue; array(struct camu_audio_buffer *) rem_queue; - atomic_bool queued; + atomic(u8) queued; struct aki_mutex mutex; #endif + void (*callback)(void *, u8); + void *userdata; }; bool camu_mixer_init(struct camu_mixer *mixer, struct camu_audio *audio); -void camu_mixer_pick_format(struct camu_mixer *mixer, struct camu_ff_resample_fmt *fmt); +void camu_mixer_pick_format(struct camu_mixer *mixer, struct camu_resampler_format *fmt); f64 camu_mixer_get_latency(struct camu_mixer *mixer); void camu_mixer_add_buffer(struct camu_mixer *mixer, struct camu_audio_buffer *buf); void camu_mixer_remove_buffer(struct camu_mixer *mixer, struct camu_audio_buffer *buf); @@ -34,4 +41,5 @@ void camu_mixer_run_queue(struct camu_mixer *mixer); #endif void camu_mixer_pause(struct camu_mixer *mixer); void camu_mixer_resume(struct camu_mixer *mixer); +void camu_mixer_clear(struct camu_mixer *mixer); void camu_mixer_close(struct camu_mixer *mixer); -- cgit v1.2.3-101-g0448