mirror of
https://github.com/vgmstream/vgmstream.git
synced 2025-01-19 00:04:04 +01:00
cleanup: mixer
This commit is contained in:
parent
93de444987
commit
c1dfa4d274
123
src/base/mixer.c
Normal file
123
src/base/mixer.c
Normal 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
14
src/base/mixer.h
Normal 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
|
@ -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
|
@ -1,4 +1,4 @@
|
||||
#include "mixing_priv.h"
|
||||
#include "mixer_priv.h"
|
||||
#include <limits.h>
|
||||
#include <math.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
|
@ -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;
|
||||
|
||||
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user