From c1dfa4d274a68e9647b941a8547b3a32598d1dd6 Mon Sep 17 00:00:00 2001 From: bnnm Date: Wed, 24 Jul 2024 00:00:27 +0200 Subject: [PATCH] cleanup: mixer --- src/base/mixer.c | 123 ++++++++++++++++ src/base/mixer.h | 14 ++ ...mixing_ops_common.c => mixer_ops_common.c} | 2 +- .../{mixing_ops_fade.c => mixer_ops_fade.c} | 2 +- src/base/{mixing_priv.h => mixer_priv.h} | 4 +- src/base/mixing.c | 132 +++--------------- src/base/mixing.h | 10 +- 7 files changed, 160 insertions(+), 127 deletions(-) create mode 100644 src/base/mixer.c create mode 100644 src/base/mixer.h rename src/base/{mixing_ops_common.c => mixer_ops_common.c} (99%) rename src/base/{mixing_ops_fade.c => mixer_ops_fade.c} (99%) rename src/base/{mixing_priv.h => mixer_priv.h} (97%) diff --git a/src/base/mixer.c b/src/base/mixer.c new file mode 100644 index 00000000..c98cfd0c --- /dev/null +++ b/src/base/mixer.c @@ -0,0 +1,123 @@ +#include "../vgmstream.h" +#include "../util/channel_mappings.h" +#include "mixing.h" +#include "mixer_priv.h" +#include "mixer.h" +#include "sbuf.h" +#include +#include + +//TODO simplify +/** + * Mixer modifies decoded sample buffer before final output. This is implemented + * mostly with simplicity in mind rather than performance. Process: + * - detect if mixing applies at current moment or exit (mini performance optimization) + * - copy/upgrade buf to float mixbuf if needed + * - do mixing ops + * - copy/downgrade mixbuf to original buf if needed + * + * Mixing may add or remove channels. input_channels is the buf's original channels, + * and output_channels the resulting buf's channels. buf and mixbuf must be + * as big as max channels (mixing_channels). + * + * Mixing ops are added by a meta (ex. TXTP) or plugin through the API. Non-sensical + * mixes are ignored (to avoid rechecking every time). + * + * Currently, mixing must be manually enabled before starting to decode, because plugins + * need to setup bigger bufs when upmixing. (to be changed) + * + * segmented/layered layouts handle mixing on their own. + */ + +void* mixer_init(int channels) { + mixer_t* mixer = calloc(1, sizeof(mixer_t)); + if (!mixer) goto fail; + + mixer->chain_size = VGMSTREAM_MAX_MIXING; /* fixed array for now */ + mixer->mixing_channels = channels; + mixer->output_channels = channels; + mixer->input_channels = channels; + + return mixer; + +fail: + mixer_free(mixer); + return NULL; +} + +void mixer_free(void* _mixer) { + mixer_t* mixer = _mixer; + if (!mixer) return; + + free(mixer->mixbuf); + free(mixer); +} + +void mixer_update_channel(void* _mixer) { + mixer_t* mixer = _mixer; + if (!mixer) return; + + /* lame hack for dual stereo, but dual stereo is pretty hack-ish to begin with */ + mixer->mixing_channels++; + mixer->output_channels++; +} + +bool mixer_is_active(void* _mixer) { + mixer_t* mixer = _mixer; + + /* no support or not need to apply */ + if (!mixer || !mixer->active || mixer->chain_count == 0) + return false; + + return true; +} + + +void mixer_process(void* _mixer, sample_t *outbuf, int32_t sample_count, int32_t current_pos) { + mixer_t* mixer = _mixer; + + /* no support or not need to apply */ + if (!mixer || !mixer->active || mixer->chain_count == 0) + return; + + /* try to skip if no fades apply (set but does nothing yet) + only has fades + * (could be done in mix op but avoids upgrading bufs in some cases) */ + mixer->current_subpos = 0; + if (mixer->has_fade) { + //;VGM_LOG("MIX: fade test %i, %i\n", data->has_non_fade, mixer_op_fade_is_active(data, current_pos, current_pos + sample_count)); + if (!mixer->has_non_fade && !mixer_op_fade_is_active(mixer, current_pos, current_pos + sample_count)) + return; + + //;VGM_LOG("MIX: fade pos=%i\n", current_pos); + mixer->current_subpos = current_pos; + } + + // upgrade buf for mixing (somehow using float buf rather than int32 is faster?) + sbuf_copy_s16_to_f32(mixer->mixbuf, outbuf, sample_count, mixer->input_channels); + + /* apply mixing ops in order. Some ops change total channels they may change around: + * - 2ch w/ "1+2,1u" = ch1+ch2, ch1(add and push rest) = 3ch: ch1' ch1+ch2 ch2 + * - 2ch w/ "1u" = downmix to 1ch (current_channels decreases once) + */ + mixer->current_channels = mixer->input_channels; + for (int m = 0; m < mixer->chain_count; m++) { + mix_op_t* mix = &mixer->chain[m]; + + //TODO: set callback + switch(mix->type) { + case MIX_SWAP: mixer_op_swap(mixer, sample_count, mix); break; + case MIX_ADD: mixer_op_add(mixer, sample_count, mix); break; + case MIX_VOLUME: mixer_op_volume(mixer, sample_count, mix); break; + case MIX_LIMIT: mixer_op_limit(mixer, sample_count, mix); break; + case MIX_UPMIX: mixer_op_upmix(mixer, sample_count, mix); break; + case MIX_DOWNMIX: mixer_op_downmix(mixer, sample_count, mix); break; + case MIX_KILLMIX: mixer_op_killmix(mixer, sample_count, mix); break; + case MIX_FADE: mixer_op_fade(mixer, sample_count, mix); + default: + break; + } + } + + /* downgrade mix to original output */ + sbuf_copy_f32_to_s16(outbuf, mixer->mixbuf, sample_count, mixer->output_channels); +} diff --git a/src/base/mixer.h b/src/base/mixer.h new file mode 100644 index 00000000..45764ec6 --- /dev/null +++ b/src/base/mixer.h @@ -0,0 +1,14 @@ +#ifndef _MIXER_H_ +#define _MIXER_H_ + +#include "../streamtypes.h" + +/* internal mixing pre-setup for vgmstream (doesn't imply usage). + * If init somehow fails next calls are ignored. */ +void* mixer_init(int channels); +void mixer_free(void* mixer); +void mixer_update_channel(void* mixer); +void mixer_process(void* _mixer, sample_t *outbuf, int32_t sample_count, int32_t current_pos); +bool mixer_is_active(void* mixer); + +#endif diff --git a/src/base/mixing_ops_common.c b/src/base/mixer_ops_common.c similarity index 99% rename from src/base/mixing_ops_common.c rename to src/base/mixer_ops_common.c index cfe84d37..3d57cd2b 100644 --- a/src/base/mixing_ops_common.c +++ b/src/base/mixer_ops_common.c @@ -1,4 +1,4 @@ -#include "mixing_priv.h" +#include "mixer_priv.h" // TO-DO: some ops can be done with original PCM sbuf to avoid copying to the float sbuf diff --git a/src/base/mixing_ops_fade.c b/src/base/mixer_ops_fade.c similarity index 99% rename from src/base/mixing_ops_fade.c rename to src/base/mixer_ops_fade.c index 3287d6a8..1ee6e484 100644 --- a/src/base/mixing_ops_fade.c +++ b/src/base/mixer_ops_fade.c @@ -1,4 +1,4 @@ -#include "mixing_priv.h" +#include "mixer_priv.h" #include #include diff --git a/src/base/mixing_priv.h b/src/base/mixer_priv.h similarity index 97% rename from src/base/mixing_priv.h rename to src/base/mixer_priv.h index f09c82db..076e7c7d 100644 --- a/src/base/mixing_priv.h +++ b/src/base/mixer_priv.h @@ -1,5 +1,5 @@ -#ifndef _MIXING_PRIV_H_ -#define _MIXING_PRIV_H_ +#ifndef _MIXER_PRIV_H_ +#define _MIXER_PRIV_H_ #include "../streamtypes.h" #define VGMSTREAM_MAX_MIXING 512 diff --git a/src/base/mixing.c b/src/base/mixing.c index c26602a2..6ed4f275 100644 --- a/src/base/mixing.c +++ b/src/base/mixing.c @@ -1,7 +1,8 @@ #include "../vgmstream.h" #include "../util/channel_mappings.h" #include "mixing.h" -#include "mixing_priv.h" +#include "mixer.h" +#include "mixer_priv.h" #include "sbuf.h" #include #include @@ -52,116 +53,19 @@ static int32_t get_current_pos(VGMSTREAM* vgmstream, int32_t sample_count) { } void mix_vgmstream(sample_t *outbuf, int32_t sample_count, VGMSTREAM* vgmstream) { - mixer_t* mixer = vgmstream->mixer; - /* no support or not need to apply */ - if (!mixer || !mixer->active || mixer->chain_count == 0) + if (!mixer_is_active(vgmstream->mixer)) return; int32_t current_pos = get_current_pos(vgmstream, sample_count); - mixer_process(mixer, outbuf, sample_count, current_pos); -} - -/* ******************************************************************* */ - -void* mixer_init(int channels) { - mixer_t* mixer = calloc(1, sizeof(mixer_t)); - if (!mixer) goto fail; - - mixer->chain_size = VGMSTREAM_MAX_MIXING; /* fixed array for now */ - mixer->mixing_channels = channels; - mixer->output_channels = channels; - mixer->input_channels = channels; - - return mixer; - -fail: - mixer_free(mixer); - return NULL; -} - -void mixer_free(void* _mixer) { - mixer_t* mixer = _mixer; - if (!mixer) return; - - free(mixer->mixbuf); - free(mixer); -} - -void mixer_update_channel(void* _mixer) { - mixer_t* mixer = _mixer; - if (!mixer) return; - - /* lame hack for dual stereo, but dual stereo is pretty hack-ish to begin with */ - mixer->mixing_channels++; - mixer->output_channels++; -} - -bool mixer_is_active(void* _mixer) { - mixer_t* mixer = _mixer; - - /* no support or not need to apply */ - if (!mixer || !mixer->active || mixer->chain_count == 0) - return false; - - return true; -} - - -void mixer_process(void* _mixer, sample_t *outbuf, int32_t sample_count, int32_t current_pos) { - mixer_t* mixer = _mixer; - - /* no support or not need to apply */ - if (!mixer || !mixer->active || mixer->chain_count == 0) - return; - - /* try to skip if no fades apply (set but does nothing yet) + only has fades - * (could be done in mix op but avoids upgrading bufs in some cases) */ - mixer->current_subpos = 0; - if (mixer->has_fade) { - //;VGM_LOG("MIX: fade test %i, %i\n", data->has_non_fade, mixer_op_fade_is_active(data, current_pos, current_pos + sample_count)); - if (!mixer->has_non_fade && !mixer_op_fade_is_active(mixer, current_pos, current_pos + sample_count)) - return; - - //;VGM_LOG("MIX: fade pos=%i\n", current_pos); - mixer->current_subpos = current_pos; - } - - // upgrade buf for mixing (somehow using float buf rather than int32 is faster?) - sbuf_copy_s16_to_f32(mixer->mixbuf, outbuf, sample_count, mixer->input_channels); - - /* apply mixing ops in order. Some ops change total channels they may change around: - * - 2ch w/ "1+2,1u" = ch1+ch2, ch1(add and push rest) = 3ch: ch1' ch1+ch2 ch2 - * - 2ch w/ "1u" = downmix to 1ch (current_channels decreases once) - */ - mixer->current_channels = mixer->input_channels; - for (int m = 0; m < mixer->chain_count; m++) { - mix_op_t* mix = &mixer->chain[m]; - - //TODO: set callback - switch(mix->type) { - case MIX_SWAP: mixer_op_swap(mixer, sample_count, mix); break; - case MIX_ADD: mixer_op_add(mixer, sample_count, mix); break; - case MIX_VOLUME: mixer_op_volume(mixer, sample_count, mix); break; - case MIX_LIMIT: mixer_op_limit(mixer, sample_count, mix); break; - case MIX_UPMIX: mixer_op_upmix(mixer, sample_count, mix); break; - case MIX_DOWNMIX: mixer_op_downmix(mixer, sample_count, mix); break; - case MIX_KILLMIX: mixer_op_killmix(mixer, sample_count, mix); break; - case MIX_FADE: mixer_op_fade(mixer, sample_count, mix); - default: - break; - } - } - - /* downgrade mix to original output */ - sbuf_copy_f32_to_s16(outbuf, mixer->mixbuf, sample_count, mixer->output_channels); + mixer_process(vgmstream->mixer, outbuf, sample_count, current_pos); } /* ******************************************************************* */ static int fix_layered_channel_layout(VGMSTREAM* vgmstream) { - mixer_t* data = vgmstream->mixer; + mixer_t* mixer = vgmstream->mixer; layered_layout_data* layout_data; uint32_t prev_cl; @@ -171,7 +75,7 @@ static int fix_layered_channel_layout(VGMSTREAM* vgmstream) { layout_data = vgmstream->layout_data; /* mainly layer-v (in cases of layers-within-layers should cascade) */ - if (data->output_channels != layout_data->layers[0]->channels) + if (mixer->output_channels != layout_data->layers[0]->channels) return 0; /* check all layers share layout (implicitly works as a channel check, if not 0) */ @@ -193,7 +97,7 @@ static int fix_layered_channel_layout(VGMSTREAM* vgmstream) { /* channel layout + down/upmixing = ?, salvage what we can */ static void fix_channel_layout(VGMSTREAM* vgmstream) { - mixer_t* data = vgmstream->mixer; + mixer_t* mixer = vgmstream->mixer; if (fix_layered_channel_layout(vgmstream)) goto done; @@ -201,7 +105,7 @@ static void fix_channel_layout(VGMSTREAM* vgmstream) { /* segments should share channel layout automatically */ /* a bit wonky but eh... */ - if (vgmstream->channel_layout && vgmstream->channels != data->output_channels) { + if (vgmstream->channel_layout && vgmstream->channels != mixer->output_channels) { vgmstream->channel_layout = 0; } @@ -211,9 +115,9 @@ done: void mixing_setup(VGMSTREAM* vgmstream, int32_t max_sample_count) { - mixer_t* data = vgmstream->mixer; + mixer_t* mixer = vgmstream->mixer; - if (!data) + if (!mixer) return; /* special value to not actually enable anything (used to query values) */ @@ -221,11 +125,11 @@ void mixing_setup(VGMSTREAM* vgmstream, int32_t max_sample_count) { return; /* create or alter internal buffer */ - float *mixbuf_re = realloc(data->mixbuf, max_sample_count * data->mixing_channels * sizeof(float)); + float* mixbuf_re = realloc(mixer->mixbuf, max_sample_count * mixer->mixing_channels * sizeof(float)); if (!mixbuf_re) goto fail; - data->mixbuf = mixbuf_re; - data->active = true; + mixer->mixbuf = mixbuf_re; + mixer->active = true; fix_channel_layout(vgmstream); @@ -240,15 +144,15 @@ fail: } void mixing_info(VGMSTREAM* vgmstream, int* p_input_channels, int* p_output_channels) { - mixer_t* data = vgmstream->mixer; + mixer_t* mixer = vgmstream->mixer; int input_channels, output_channels; - if (!data) + if (!mixer) goto fail; - output_channels = data->output_channels; - if (data->output_channels > vgmstream->channels) - input_channels = data->output_channels; + output_channels = mixer->output_channels; + if (mixer->output_channels > vgmstream->channels) + input_channels = mixer->output_channels; else input_channels = vgmstream->channels; diff --git a/src/base/mixing.h b/src/base/mixing.h index 727bc247..4d8705bb 100644 --- a/src/base/mixing.h +++ b/src/base/mixing.h @@ -7,14 +7,6 @@ * outbuf must big enough to hold output_channels*samples_to_do */ void mix_vgmstream(sample_t *outbuf, int32_t sample_count, VGMSTREAM* vgmstream); -/* internal mixing pre-setup for vgmstream (doesn't imply usage). - * If init somehow fails next calls are ignored. */ -void* mixer_init(int channels); -void mixer_free(void* mixer); -void mixer_update_channel(void* mixer); -void mixer_process(void* _mixer, sample_t *outbuf, int32_t sample_count, int32_t current_pos); -bool mixer_is_active(void* mixer); - /* Call to let vgmstream apply mixing, which must handle input/output_channels. * Once mixing is active any new mixes are ignored (to avoid the possibility * of down/upmixing without querying input/output_channels). */ @@ -41,4 +33,4 @@ void mixing_macro_crosslayer(VGMSTREAM* vgmstream, int max, char mode); void mixing_macro_downmix(VGMSTREAM* vgmstream, int max /*, mapping_t output_mapping*/); -#endif /* _MIXING_H_ */ +#endif