cleanup: mixer

This commit is contained in:
bnnm 2024-07-24 00:00:27 +02:00
parent 93de444987
commit c1dfa4d274
7 changed files with 160 additions and 127 deletions

123
src/base/mixer.c Normal file
View File

@ -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 <math.h>
#include <limits.h>
//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);
}

14
src/base/mixer.h Normal file
View File

@ -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

View File

@ -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

View File

@ -1,4 +1,4 @@
#include "mixing_priv.h"
#include "mixer_priv.h"
#include <limits.h>
#include <math.h>

View File

@ -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

View File

@ -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 <math.h>
#include <limits.h>
@ -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;

View File

@ -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