summaryrefslogtreecommitdiff
path: root/src/mixer
diff options
context:
space:
mode:
authorAndrew Opalach <andrew@akon.city> 2024-10-21 19:22:50 -0400
committerAndrew Opalach <andrew@akon.city> 2024-10-21 19:22:50 -0400
commit60b4ebfbf3be78dba9dc7c65ab2bdaa0b218c0c2 (patch)
tree08ff2ce7975f523112e7ad2fe4f797b4fc7db5de /src/mixer
parent2f9a0945bfeee3296cec3d38d094e4c49f9cb65f (diff)
downloadcamu-60b4ebfbf3be78dba9dc7c65ab2bdaa0b218c0c2.tar.gz
camu-60b4ebfbf3be78dba9dc7c65ab2bdaa0b218c0c2.tar.bz2
camu-60b4ebfbf3be78dba9dc7c65ab2bdaa0b218c0c2.zip
Everything before initial synced list
Signed-off-by: Andrew Opalach <andrew@akon.city>
Diffstat (limited to 'src/mixer')
-rw-r--r--src/mixer/audio.h4
-rw-r--r--src/mixer/audio_miniaudio.c70
-rw-r--r--src/mixer/audio_miniaudio.h6
-rw-r--r--src/mixer/audio_null.c67
-rw-r--r--src/mixer/audio_null.h7
-rw-r--r--src/mixer/meson.build45
-rw-r--r--src/mixer/mixer.c109
-rw-r--r--src/mixer/mixer.h16
8 files changed, 226 insertions, 98 deletions
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 <al/str.h>
-#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 <al/log.h>
-
+#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 <aki/thread.h> here to ensure OS related headers
+// get included the way we want.
+// Specifically, on Windows we want to include <winsock2.h> before <windows.h>.
+#include <aki/thread.h>
#include <miniaudio.h>
#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 <aki/thread.h>
#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);