cleanup: mixing

This commit is contained in:
bnnm 2024-07-23 22:50:36 +02:00
parent 693b4685bb
commit 4ed61e3740
6 changed files with 129 additions and 135 deletions

View File

@ -103,10 +103,10 @@ void mix_vgmstream(sample_t *outbuf, int32_t sample_count, VGMSTREAM* vgmstream)
*/
data->current_channels = vgmstream->channels;
for (int m = 0; m < data->mixing_count; m++) {
mix_command_data* mix = &data->mixing_chain[m];
mix_op_t* mix = &data->mixing_chain[m];
//TODO: set callback
switch(mix->command) {
switch(mix->type) {
case MIX_SWAP: mixer_op_swap(data, sample_count, mix); break;
case MIX_ADD: mixer_op_add(data, sample_count, mix); break;
case MIX_VOLUME: mixer_op_volume(data, sample_count, mix); break;
@ -166,7 +166,6 @@ void mixing_update_channel(VGMSTREAM* vgmstream) {
/* ******************************************************************* */
static int fix_layered_channel_layout(VGMSTREAM* vgmstream) {
int i;
mixer_data_t* data = vgmstream->mixing_data;
layered_layout_data* layout_data;
uint32_t prev_cl;
@ -185,7 +184,7 @@ static int fix_layered_channel_layout(VGMSTREAM* vgmstream) {
if (prev_cl == 0)
return 0;
for (i = 1; i < layout_data->layer_count; i++) {
for (int i = 1; i < layout_data->layer_count; i++) {
uint32_t layer_cl = layout_data->layers[i]->channel_layout;
if (prev_cl != layer_cl)
return 0;

View File

@ -5,7 +5,7 @@
#include <limits.h>
static bool add_mixing(VGMSTREAM* vgmstream, mix_command_data *mix) {
static bool add_mixing(VGMSTREAM* vgmstream, mix_op_t* op) {
mixer_data_t* data = vgmstream->mixing_data;
if (!data)
return false;
@ -21,11 +21,11 @@ static bool add_mixing(VGMSTREAM* vgmstream, mix_command_data *mix) {
return false;
}
data->mixing_chain[data->mixing_count] = *mix; /* memcpy */
data->mixing_chain[data->mixing_count] = *op; /* memcpy */
data->mixing_count++;
if (mix->command == MIX_FADE) {
if (op->type == MIX_FADE) {
data->has_fade = true;
}
else {
@ -39,20 +39,20 @@ static bool add_mixing(VGMSTREAM* vgmstream, mix_command_data *mix) {
void mixing_push_swap(VGMSTREAM* vgmstream, int ch_dst, int ch_src) {
mixer_data_t* data = vgmstream->mixing_data;
mix_command_data mix = {0};
mix_op_t op = {0};
if (ch_dst < 0 || ch_src < 0 || ch_dst == ch_src) return;
if (!data || ch_dst >= data->output_channels || ch_src >= data->output_channels) return;
mix.command = MIX_SWAP;
mix.ch_dst = ch_dst;
mix.ch_src = ch_src;
op.type = MIX_SWAP;
op.ch_dst = ch_dst;
op.ch_src = ch_src;
add_mixing(vgmstream, &mix);
add_mixing(vgmstream, &op);
}
void mixing_push_add(VGMSTREAM* vgmstream, int ch_dst, int ch_src, double volume) {
mixer_data_t* data = vgmstream->mixing_data;
mix_command_data mix = {0};
mix_op_t op = {0};
if (!data) return;
//if (volume < 0.0) return; /* negative volume inverts the waveform */
@ -60,35 +60,35 @@ void mixing_push_add(VGMSTREAM* vgmstream, int ch_dst, int ch_src, double volume
if (ch_dst < 0 || ch_src < 0) return;
if (!data || ch_dst >= data->output_channels || ch_src >= data->output_channels) return;
mix.command = MIX_ADD;
mix.ch_dst = ch_dst;
mix.ch_src = ch_src;
mix.vol = volume;
op.type = MIX_ADD;
op.ch_dst = ch_dst;
op.ch_src = ch_src;
op.vol = volume;
//;VGM_LOG("MIX: add %i+%i*%f\n", ch_dst,ch_src,volume);
add_mixing(vgmstream, &mix);
add_mixing(vgmstream, &op);
}
void mixing_push_volume(VGMSTREAM* vgmstream, int ch_dst, double volume) {
mixer_data_t* data = vgmstream->mixing_data;
mix_command_data mix = {0};
mix_op_t op = {0};
//if (ch_dst < 0) return; /* means all channels */
//if (volume < 0.0) return; /* negative volume inverts the waveform */
if (volume == 1.0) return; /* no change */
if (!data || ch_dst >= data->output_channels) return;
mix.command = MIX_VOLUME; //if (volume == 0.0) MIX_VOLUME0 /* could simplify */
mix.ch_dst = ch_dst;
mix.vol = volume;
op.type = MIX_VOLUME; //if (volume == 0.0) MIX_VOLUME0 /* could simplify */
op.ch_dst = ch_dst;
op.vol = volume;
//;VGM_LOG("MIX: volume %i*%f\n", ch_dst,volume);
add_mixing(vgmstream, &mix);
add_mixing(vgmstream, &op);
}
void mixing_push_limit(VGMSTREAM* vgmstream, int ch_dst, double volume) {
mixer_data_t* data = vgmstream->mixing_data;
mix_command_data mix = {0};
mix_op_t op = {0};
//if (ch_dst < 0) return; /* means all channels */
if (volume < 0.0) return;
@ -96,26 +96,26 @@ void mixing_push_limit(VGMSTREAM* vgmstream, int ch_dst, double volume) {
if (!data || ch_dst >= data->output_channels) return;
//if (volume == 0.0) return; /* dumb but whatevs */
mix.command = MIX_LIMIT;
mix.ch_dst = ch_dst;
mix.vol = volume;
op.type = MIX_LIMIT;
op.ch_dst = ch_dst;
op.vol = volume;
add_mixing(vgmstream, &mix);
add_mixing(vgmstream, &op);
}
void mixing_push_upmix(VGMSTREAM* vgmstream, int ch_dst) {
mixer_data_t* data = vgmstream->mixing_data;
mix_command_data mix = {0};
mix_op_t op = {0};
int ok;
if (ch_dst < 0) return;
if (!data || ch_dst > data->output_channels || data->output_channels +1 > VGMSTREAM_MAX_CHANNELS) return;
/* dst can be == output_channels here, since we are inserting */
mix.command = MIX_UPMIX;
mix.ch_dst = ch_dst;
op.type = MIX_UPMIX;
op.ch_dst = ch_dst;
ok = add_mixing(vgmstream, &mix);
ok = add_mixing(vgmstream, &op);
if (ok) {
data->output_channels += 1;
if (data->mixing_channels < data->output_channels)
@ -125,16 +125,16 @@ void mixing_push_upmix(VGMSTREAM* vgmstream, int ch_dst) {
void mixing_push_downmix(VGMSTREAM* vgmstream, int ch_dst) {
mixer_data_t* data = vgmstream->mixing_data;
mix_command_data mix = {0};
mix_op_t op = {0};
int ok;
if (ch_dst < 0) return;
if (!data || ch_dst >= data->output_channels || data->output_channels - 1 < 1) return;
mix.command = MIX_DOWNMIX;
mix.ch_dst = ch_dst;
op.type = MIX_DOWNMIX;
op.ch_dst = ch_dst;
ok = add_mixing(vgmstream, &mix);
ok = add_mixing(vgmstream, &op);
if (ok) {
data->output_channels -= 1;
}
@ -142,31 +142,29 @@ void mixing_push_downmix(VGMSTREAM* vgmstream, int ch_dst) {
void mixing_push_killmix(VGMSTREAM* vgmstream, int ch_dst) {
mixer_data_t* data = vgmstream->mixing_data;
mix_command_data mix = {0};
int ok;
mix_op_t op = {0};
if (ch_dst <= 0) return; /* can't kill from first channel */
if (!data || ch_dst >= data->output_channels) return;
mix.command = MIX_KILLMIX;
mix.ch_dst = ch_dst;
op.type = MIX_KILLMIX;
op.ch_dst = ch_dst;
//;VGM_LOG("MIX: killmix %i\n", ch_dst);
ok = add_mixing(vgmstream, &mix);
bool ok = add_mixing(vgmstream, &op);
if (ok) {
data->output_channels = ch_dst; /* clamp channels */
}
}
static mix_command_data* get_last_fade(mixer_data_t* data, int target_channel) {
int i;
for (i = data->mixing_count; i > 0; i--) {
mix_command_data *mix = &data->mixing_chain[i-1];
if (mix->command != MIX_FADE)
static mix_op_t* get_last_fade(mixer_data_t* data, int target_channel) {
for (int i = data->mixing_count; i > 0; i--) {
mix_op_t* op = &data->mixing_chain[i-1];
if (op->type != MIX_FADE)
continue;
if (mix->ch_dst == target_channel)
return mix;
if (op->ch_dst == target_channel)
return op;
}
return NULL;
@ -176,8 +174,8 @@ static mix_command_data* get_last_fade(mixer_data_t* data, int target_channel) {
void mixing_push_fade(VGMSTREAM* vgmstream, int ch_dst, double vol_start, double vol_end, char shape,
int32_t time_pre, int32_t time_start, int32_t time_end, int32_t time_post) {
mixer_data_t* data = vgmstream->mixing_data;
mix_command_data mix = {0};
mix_command_data *mix_prev;
mix_op_t op = {0};
mix_op_t* op_prev;
//if (ch_dst < 0) return; /* means all channels */
@ -192,15 +190,15 @@ void mixing_push_fade(VGMSTREAM* vgmstream, int ch_dst, double vol_start, double
if (shape == '(' || shape == ')')
shape = 'H';
mix.command = MIX_FADE;
mix.ch_dst = ch_dst;
mix.vol_start = vol_start;
mix.vol_end = vol_end;
mix.shape = shape;
mix.time_pre = time_pre;
mix.time_start = time_start;
mix.time_end = time_end;
mix.time_post = time_post;
op.type = MIX_FADE;
op.ch_dst = ch_dst;
op.vol_start = vol_start;
op.vol_end = vol_end;
op.shape = shape;
op.time_pre = time_pre;
op.time_start = time_start;
op.time_end = time_end;
op.time_post = time_post;
/* cancel fades and optimize a bit when using negative pre/post:
@ -216,33 +214,33 @@ void mixing_push_fade(VGMSTREAM* vgmstream, int ch_dst, double vol_start, double
* as they're uncommon and hard to optimize
* fades cancel fades of the same channel, and 'all channel' (-1) fades also cancel 'all channels'
*/
mix_prev = get_last_fade(data, mix.ch_dst);
if (mix_prev == NULL) {
op_prev = get_last_fade(data, op.ch_dst);
if (op_prev == NULL) {
if (vol_start == 1.0 && time_pre < 0)
time_pre = time_start; /* fade-out helds default volume before fade start can be clamped */
if (vol_end == 1.0 && time_post < 0)
time_post = time_end; /* fade-in helds default volume after fade end can be clamped */
}
else if (mix_prev->time_post < 0 || mix.time_pre < 0) {
else if (op_prev->time_post < 0 || op.time_pre < 0) {
int is_prev = 1;
/* test if prev is really cancelled by this */
if ((mix_prev->time_end > mix.time_start) ||
(mix_prev->time_post >= 0 && mix_prev->time_post > mix.time_start) ||
(mix.time_pre >= 0 && mix.time_pre < mix_prev->time_end))
if ((op_prev->time_end > op.time_start) ||
(op_prev->time_post >= 0 && op_prev->time_post > op.time_start) ||
(op.time_pre >= 0 && op.time_pre < op_prev->time_end))
is_prev = 0;
if (is_prev) {
/* change negative values to actual points */
if (mix_prev->time_post < 0 && mix.time_pre < 0) {
mix_prev->time_post = mix_prev->time_end;
mix.time_pre = mix_prev->time_post;
if (op_prev->time_post < 0 && op.time_pre < 0) {
op_prev->time_post = op_prev->time_end;
op.time_pre = op_prev->time_post;
}
if (mix_prev->time_post >= 0 && mix.time_pre < 0) {
mix.time_pre = mix_prev->time_post;
if (op_prev->time_post >= 0 && op.time_pre < 0) {
op.time_pre = op_prev->time_post;
}
else if (mix_prev->time_post < 0 && mix.time_pre >= 0) {
mix_prev->time_post = mix.time_pre;
else if (op_prev->time_post < 0 && op.time_pre >= 0) {
op_prev->time_post = op.time_pre;
}
/* else: both define start/ends, do nothing */
}
@ -250,5 +248,5 @@ void mixing_push_fade(VGMSTREAM* vgmstream, int ch_dst, double vol_start, double
}
//;VGM_LOG("MIX: fade %i^%f~%f=%c@%i~%i~%i~%i\n", ch_dst, vol_start, vol_end, shape, time_pre, time_start, time_end, time_post);
add_mixing(vgmstream, &mix);
add_mixing(vgmstream, &op);
}

View File

@ -76,8 +76,8 @@ static int is_layered_auto(VGMSTREAM* vgmstream, int max, char mode) {
/* no channel down/upmixing (cannot guess output) */
mixer_data_t* data = vgmstream->mixing_data;
for (int i = 0; i < data->mixing_count; i++) {
mix_command_t mix = data->mixing_chain[i].command;
if (mix == MIX_UPMIX || mix == MIX_DOWNMIX || mix == MIX_KILLMIX) /*mix == MIX_SWAP || ??? */
mix_type_t type = data->mixing_chain[i].type;
if (type == MIX_UPMIX || type == MIX_DOWNMIX || type == MIX_KILLMIX) /*type == MIX_SWAP || ??? */
return 0;
}

View File

@ -5,60 +5,60 @@
// when there are no actual float ops (ex. 'swap', if no ' volume' )
// Performance gain is probably fairly small, though.
void mixer_op_swap(mixer_data_t* data, int32_t sample_count, mix_command_data* mix) {
void mixer_op_swap(mixer_data_t* data, int32_t sample_count, mix_op_t* op) {
float* sbuf = data->mixbuf;
for (int s = 0; s < sample_count; s++) {
float temp_f = sbuf[mix->ch_dst];
sbuf[mix->ch_dst] = sbuf[mix->ch_src];
sbuf[mix->ch_src] = temp_f;
float temp_f = sbuf[op->ch_dst];
sbuf[op->ch_dst] = sbuf[op->ch_src];
sbuf[op->ch_src] = temp_f;
sbuf += data->current_channels;
}
}
void mixer_op_add(mixer_data_t* data, int32_t sample_count, mix_command_data* mix) {
void mixer_op_add(mixer_data_t* data, int32_t sample_count, mix_op_t* op) {
float* sbuf = data->mixbuf;
/* could optimize when vol == 1 to avoid one multiplication but whatevs (not common) */
for (int s = 0; s < sample_count; s++) {
sbuf[mix->ch_dst] = sbuf[mix->ch_dst] + sbuf[mix->ch_src] * mix->vol;
sbuf[op->ch_dst] = sbuf[op->ch_dst] + sbuf[op->ch_src] * op->vol;
sbuf += data->current_channels;
}
}
void mixer_op_volume(mixer_data_t* data, int32_t sample_count, mix_command_data* mix) {
void mixer_op_volume(mixer_data_t* data, int32_t sample_count, mix_op_t* op) {
float* sbuf = data->mixbuf;
if (mix->ch_dst < 0) {
if (op->ch_dst < 0) {
/* "all channels", most common case */
for (int s = 0; s < sample_count * data->current_channels; s++) {
sbuf[s] = sbuf[s] * mix->vol;
sbuf[s] = sbuf[s] * op->vol;
}
}
else {
for (int s = 0; s < sample_count; s++) {
sbuf[mix->ch_dst] = sbuf[mix->ch_dst] * mix->vol;
sbuf[op->ch_dst] = sbuf[op->ch_dst] * op->vol;
sbuf += data->current_channels;
}
}
}
void mixer_op_limit(mixer_data_t* data, int32_t sample_count, mix_command_data* mix) {
void mixer_op_limit(mixer_data_t* data, int32_t sample_count, mix_op_t* op) {
float* sbuf = data->mixbuf;
const float limiter_max = 32767.0f;
const float limiter_min = -32768.0f;
const float temp_max = limiter_max * mix->vol;
const float temp_min = limiter_min * mix->vol;
const float temp_max = limiter_max * op->vol;
const float temp_min = limiter_min * op->vol;
/* could optimize when vol == 1 to avoid one multiplication but whatevs (not common) */
for (int s = 0; s < sample_count; s++) {
if (mix->ch_dst < 0) {
if (op->ch_dst < 0) {
for (int ch = 0; ch < data->current_channels; ch++) {
if (sbuf[ch] > temp_max)
sbuf[ch] = temp_max;
@ -67,17 +67,17 @@ void mixer_op_limit(mixer_data_t* data, int32_t sample_count, mix_command_data*
}
}
else {
if (sbuf[mix->ch_dst] > temp_max)
sbuf[mix->ch_dst] = temp_max;
else if (sbuf[mix->ch_dst] < temp_min)
sbuf[mix->ch_dst] = temp_min;
if (sbuf[op->ch_dst] > temp_max)
sbuf[op->ch_dst] = temp_max;
else if (sbuf[op->ch_dst] < temp_min)
sbuf[op->ch_dst] = temp_min;
}
sbuf += data->current_channels;
}
}
void mixer_op_upmix(mixer_data_t* data, int32_t sample_count, mix_command_data* mix) {
void mixer_op_upmix(mixer_data_t* data, int32_t sample_count, mix_op_t* op) {
int max_channels = data->current_channels;
data->current_channels += 1;
@ -91,7 +91,7 @@ void mixer_op_upmix(mixer_data_t* data, int32_t sample_count, mix_command_data*
int sbuf_ch = max_channels - 1;
for (int ch = data->current_channels - 1; ch >= 0; ch--) {
if (ch == mix->ch_dst) {
if (ch == op->ch_dst) {
sbuf_tmp[ch] = 0; /* inserted as silent */
}
else {
@ -102,7 +102,7 @@ void mixer_op_upmix(mixer_data_t* data, int32_t sample_count, mix_command_data*
}
}
void mixer_op_downmix(mixer_data_t* data, int32_t sample_count, mix_command_data* mix) {
void mixer_op_downmix(mixer_data_t* data, int32_t sample_count, mix_op_t* op) {
int max_channels = data->current_channels;
data->current_channels -= 1;
@ -111,11 +111,11 @@ void mixer_op_downmix(mixer_data_t* data, int32_t sample_count, mix_command_data
for (int s = 0; s < sample_count; s++) {
for (int ch = 0; ch < mix->ch_dst; ch++) {
for (int ch = 0; ch < op->ch_dst; ch++) {
sbuf_tmp[ch] = sbuf[ch]; /* copy untouched channels */
}
for (int ch = mix->ch_dst; ch < max_channels; ch++) {
for (int ch = op->ch_dst; ch < max_channels; ch++) {
sbuf_tmp[ch] = sbuf[ch + 1]; /* 'pull' dropped channels back */
}
@ -124,9 +124,9 @@ void mixer_op_downmix(mixer_data_t* data, int32_t sample_count, mix_command_data
}
}
void mixer_op_killmix(mixer_data_t* data, int32_t sample_count, mix_command_data* mix) {
void mixer_op_killmix(mixer_data_t* data, int32_t sample_count, mix_op_t* op) {
int max_channels = data->current_channels;
data->current_channels = mix->ch_dst; /* clamp channels */
data->current_channels = op->ch_dst; /* clamp channels */
float* sbuf = data->mixbuf;
float* sbuf_tmp = sbuf;

View File

@ -54,28 +54,28 @@ static inline float get_fade_gain_curve(char shape, float index) {
return gain;
}
static bool get_fade_gain(mix_command_data *mix, float *out_cur_vol, int32_t current_subpos) {
static bool get_fade_gain(mix_op_t* op, float* out_cur_vol, int32_t current_subpos) {
float cur_vol = 0.0f;
if ((current_subpos >= mix->time_pre || mix->time_pre < 0) && current_subpos < mix->time_start) {
cur_vol = mix->vol_start; /* before */
if ((current_subpos >= op->time_pre || op->time_pre < 0) && current_subpos < op->time_start) {
cur_vol = op->vol_start; /* before */
}
else if (current_subpos >= mix->time_end && (current_subpos < mix->time_post || mix->time_post < 0)) {
cur_vol = mix->vol_end; /* after */
else if (current_subpos >= op->time_end && (current_subpos < op->time_post || op->time_post < 0)) {
cur_vol = op->vol_end; /* after */
}
else if (current_subpos >= mix->time_start && current_subpos < mix->time_end) {
else if (current_subpos >= op->time_start && current_subpos < op->time_end) {
/* in between */
float range_vol, range_dur, range_idx, index, gain;
if (mix->vol_start < mix->vol_end) { /* fade in */
range_vol = mix->vol_end - mix->vol_start;
range_dur = mix->time_end - mix->time_start;
range_idx = current_subpos - mix->time_start;
if (op->vol_start < op->vol_end) { /* fade in */
range_vol = op->vol_end - op->vol_start;
range_dur = op->time_end - op->time_start;
range_idx = current_subpos - op->time_start;
index = range_idx / range_dur;
} else { /* fade out */
range_vol = mix->vol_end - mix->vol_start;
range_dur = mix->time_end - mix->time_start;
range_idx = mix->time_end - current_subpos;
range_vol = op->vol_end - op->vol_start;
range_dur = op->time_end - op->time_start;
range_idx = op->time_end - current_subpos;
index = range_idx / range_dur;
}
@ -95,12 +95,12 @@ static bool get_fade_gain(mix_command_data *mix, float *out_cur_vol, int32_t cur
* curves are complementary (exponential fade-in ~= logarithmic fade-out); the following
* are described taking fade-in = normal.
*/
gain = get_fade_gain_curve(mix->shape, index);
gain = get_fade_gain_curve(op->shape, index);
if (mix->vol_start < mix->vol_end) { /* fade in */
cur_vol = mix->vol_start + range_vol * gain;
if (op->vol_start < op->vol_end) { /* fade in */
cur_vol = op->vol_start + range_vol * gain;
} else { /* fade out */
cur_vol = mix->vol_end - range_vol * gain; //mix->vol_start - range_vol * (1 - gain);
cur_vol = op->vol_end - range_vol * gain; //mix->vol_start - range_vol * (1 - gain);
}
}
else {
@ -112,7 +112,7 @@ static bool get_fade_gain(mix_command_data *mix, float *out_cur_vol, int32_t cur
return true;
}
void mixer_op_fade(mixer_data_t* data, int32_t sample_count, mix_command_data* mix) {
void mixer_op_fade(mixer_data_t* data, int32_t sample_count, mix_op_t* mix) {
float* sbuf = data->mixbuf;
float new_gain = 0.0f;
@ -145,11 +145,11 @@ void mixer_op_fade(mixer_data_t* data, int32_t sample_count, mix_command_data* m
bool mixer_op_fade_is_active(mixer_data_t* data, int32_t current_start, int32_t current_end) {
for (int i = 0; i < data->mixing_count; i++) {
mix_command_data* mix = &data->mixing_chain[i];
mix_op_t* mix = &data->mixing_chain[i];
int32_t fade_start, fade_end;
float vol_start = mix->vol_start;
if (mix->command != MIX_FADE)
if (mix->type != MIX_FADE)
continue;
/* check is current range falls within a fade

View File

@ -4,9 +4,6 @@
#define VGMSTREAM_MAX_MIXING 512
//TODO rename
/* mixing info */
typedef enum {
MIX_SWAP,
MIX_ADD,
@ -16,10 +13,10 @@ typedef enum {
MIX_DOWNMIX,
MIX_KILLMIX,
MIX_FADE
} mix_command_t;
} mix_type_t;
typedef struct {
mix_command_t command;
mix_type_t type;
/* common */
int ch_dst;
int ch_src;
@ -33,7 +30,7 @@ typedef struct {
int32_t time_start; /* fade start position where vol changes from vol_start to vol_end */
int32_t time_end; /* fade end position where vol changes from vol_start to vol_end */
int32_t time_post; /* position after time_end where vol_end applies (-1 = end) */
} mix_command_data;
} mix_op_t;
typedef struct {
int mixing_channels; /* max channels needed to mix */
@ -43,7 +40,7 @@ typedef struct {
int mixing_count; /* mixing number */
size_t mixing_size; /* mixing max */
mix_command_data mixing_chain[VGMSTREAM_MAX_MIXING]; /* effects to apply (could be alloc'ed but to simplify...) */
mix_op_t mixing_chain[VGMSTREAM_MAX_MIXING]; /* effects to apply (could be alloc'ed but to simplify...) */
float* mixbuf; /* internal mixing buffer */
int current_channels; /* state: channels may increase/decrease during ops */
@ -54,13 +51,13 @@ typedef struct {
bool has_fade;
} mixer_data_t;
void mixer_op_swap(mixer_data_t* data, int32_t sample_count, mix_command_data* mix);
void mixer_op_add(mixer_data_t* data, int32_t sample_count, mix_command_data* mix);
void mixer_op_volume(mixer_data_t* data, int32_t sample_count, mix_command_data* mix);
void mixer_op_limit(mixer_data_t* data, int32_t sample_count, mix_command_data* mix);
void mixer_op_upmix(mixer_data_t* data, int32_t sample_count, mix_command_data* mix);
void mixer_op_downmix(mixer_data_t* data, int32_t sample_count, mix_command_data* mix);
void mixer_op_killmix(mixer_data_t* data, int32_t sample_count, mix_command_data* mix);
void mixer_op_fade(mixer_data_t* data, int32_t sample_count, mix_command_data* mix);
void mixer_op_swap(mixer_data_t* data, int32_t sample_count, mix_op_t* op);
void mixer_op_add(mixer_data_t* data, int32_t sample_count, mix_op_t* op);
void mixer_op_volume(mixer_data_t* data, int32_t sample_count, mix_op_t* op);
void mixer_op_limit(mixer_data_t* data, int32_t sample_count, mix_op_t* op);
void mixer_op_upmix(mixer_data_t* data, int32_t sample_count, mix_op_t* op);
void mixer_op_downmix(mixer_data_t* data, int32_t sample_count, mix_op_t* op);
void mixer_op_killmix(mixer_data_t* data, int32_t sample_count, mix_op_t* op);
void mixer_op_fade(mixer_data_t* data, int32_t sample_count, mix_op_t* op);
bool mixer_op_fade_is_active(mixer_data_t* data, int32_t current_start, int32_t current_end);
#endif