cleanup: mixer

This commit is contained in:
bnnm 2024-07-23 23:51:44 +02:00
parent 4ed61e3740
commit 93de444987
12 changed files with 287 additions and 262 deletions

View File

@ -9,6 +9,8 @@
void decode_free(VGMSTREAM* vgmstream) {
if (!vgmstream->codec_data)
return;
#ifdef VGM_USE_VORBIS
if (vgmstream->coding_type == coding_OGG_VORBIS) {
@ -124,6 +126,9 @@ void decode_free(VGMSTREAM* vgmstream) {
void decode_seek(VGMSTREAM* vgmstream) {
if (!vgmstream->codec_data)
return;
if (vgmstream->coding_type == coding_CIRCUS_VQ) {
seek_circus_vq(vgmstream->codec_data, vgmstream->loop_current_sample);
}
@ -222,6 +227,8 @@ void decode_seek(VGMSTREAM* vgmstream) {
void decode_reset(VGMSTREAM* vgmstream) {
if (!vgmstream->codec_data)
return;
#ifdef VGM_USE_VORBIS
if (vgmstream->coding_type == coding_OGG_VORBIS) {

View File

@ -2,7 +2,7 @@
#include "../util/channel_mappings.h"
#include "mixing.h"
#include "mixing_priv.h"
#include "plugins.h"
#include "sbuf.h"
#include <math.h>
#include <limits.h>
@ -17,7 +17,7 @@
*
* 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 whichever of input/output channels is bigger.
* 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).
@ -30,28 +30,6 @@
/* ******************************************************************* */
// TODO decide if using float 1.0 style or 32767 style (fuzzy PCM changes when doing that)
static void sbuf_copy_s16_to_f32(float* buf_f32, int16_t* buf_s16, int samples, int channels) {
for (int s = 0; s < samples * channels; s++) {
buf_f32[s] = buf_s16[s]; // / 32767.0f
}
}
static void sbuf_copy_f32_to_s16(int16_t* buf_s16, float* buf_f32, int samples, int channels) {
/* when casting float to int, value is simply truncated:
* - (int)1.7 = 1, (int)-1.7 = -1
* alts for more accurate rounding could be:
* - (int)floor(f)
* - (int)(f < 0 ? f - 0.5f : f + 0.5f)
* - (((int) (f1 + 32768.5)) - 32768)
* - etc
* but since +-1 isn't really audible we'll just cast as it's the fastest
*/
for (int s = 0; s < samples * channels; s++) {
buf_s16[s] = clamp16( buf_f32[s]); // * 32767.0f
}
}
static int32_t get_current_pos(VGMSTREAM* vgmstream, int32_t sample_count) {
int32_t current_pos;
@ -74,99 +52,116 @@ 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_data_t* data = vgmstream->mixing_data;
mixer_t* mixer = vgmstream->mixer;
/* no support or not need to apply */
if (!data || !data->mixing_on || data->mixing_count == 0)
if (!mixer || !mixer->active || mixer->chain_count == 0)
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) */
data->current_subpos = 0;
if (data->has_fade) {
int32_t current_pos = get_current_pos(vgmstream, sample_count);
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 (!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);
data->current_subpos = 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(data->mixbuf, outbuf, sample_count, vgmstream->channels);
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)
*/
data->current_channels = vgmstream->channels;
for (int m = 0; m < data->mixing_count; m++) {
mix_op_t* mix = &data->mixing_chain[m];
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(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;
case MIX_LIMIT: mixer_op_limit(data, sample_count, mix); break;
case MIX_UPMIX: mixer_op_upmix(data, sample_count, mix); break;
case MIX_DOWNMIX: mixer_op_downmix(data, sample_count, mix); break;
case MIX_KILLMIX: mixer_op_killmix(data, sample_count, mix); break;
case MIX_FADE: mixer_op_fade(data, sample_count, mix);
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, data->mixbuf, sample_count, data->output_channels);
sbuf_copy_f32_to_s16(outbuf, mixer->mixbuf, sample_count, mixer->output_channels);
}
/* ******************************************************************* */
void mixing_init(VGMSTREAM* vgmstream) {
mixer_data_t* data = calloc(1, sizeof(mixer_data_t));
if (!data) goto fail;
data->mixing_size = VGMSTREAM_MAX_MIXING; /* fixed array for now */
data->mixing_channels = vgmstream->channels;
data->output_channels = vgmstream->channels;
vgmstream->mixing_data = data;
return;
fail:
free(data);
return;
}
void mixing_close(VGMSTREAM* vgmstream) {
if (!vgmstream)
return;
mixer_data_t* data = vgmstream->mixing_data;
if (!data) return;
free(data->mixbuf);
free(data);
}
void mixing_update_channel(VGMSTREAM* vgmstream) {
mixer_data_t* data = vgmstream->mixing_data;
if (!data) return;
/* lame hack for dual stereo, but dual stereo is pretty hack-ish to begin with */
data->mixing_channels++;
data->output_channels++;
}
/* ******************************************************************* */
static int fix_layered_channel_layout(VGMSTREAM* vgmstream) {
mixer_data_t* data = vgmstream->mixing_data;
mixer_t* data = vgmstream->mixer;
layered_layout_data* layout_data;
uint32_t prev_cl;
@ -198,7 +193,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_data_t* data = vgmstream->mixing_data;
mixer_t* data = vgmstream->mixer;
if (fix_layered_channel_layout(vgmstream))
goto done;
@ -216,7 +211,7 @@ done:
void mixing_setup(VGMSTREAM* vgmstream, int32_t max_sample_count) {
mixer_data_t* data = vgmstream->mixing_data;
mixer_t* data = vgmstream->mixer;
if (!data)
return;
@ -230,7 +225,7 @@ void mixing_setup(VGMSTREAM* vgmstream, int32_t max_sample_count) {
if (!mixbuf_re) goto fail;
data->mixbuf = mixbuf_re;
data->mixing_on = true;
data->active = true;
fix_channel_layout(vgmstream);
@ -245,7 +240,7 @@ fail:
}
void mixing_info(VGMSTREAM* vgmstream, int* p_input_channels, int* p_output_channels) {
mixer_data_t* data = vgmstream->mixing_data;
mixer_t* data = vgmstream->mixer;
int input_channels, output_channels;
if (!data)

View File

@ -9,17 +9,19 @@ 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 mixing_init(VGMSTREAM* vgmstream);
void mixing_close(VGMSTREAM* vgmstream);
void mixing_update_channel(VGMSTREAM* vgmstream);
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). */
void mixing_setup(VGMSTREAM * vgmstream, int32_t max_sample_count);
void mixing_setup(VGMSTREAM* vgmstream, int32_t max_sample_count);
/* gets current mixing info */
void mixing_info(VGMSTREAM * vgmstream, int *input_channels, int *output_channels);
void mixing_info(VGMSTREAM* vgmstream, int *input_channels, int *output_channels);
/* adds mixes filtering and optimizing if needed */
void mixing_push_swap(VGMSTREAM* vgmstream, int ch_dst, int ch_src);

View File

@ -6,43 +6,43 @@
static bool add_mixing(VGMSTREAM* vgmstream, mix_op_t* op) {
mixer_data_t* data = vgmstream->mixing_data;
if (!data)
mixer_t* mixer = vgmstream->mixer;
if (!mixer)
return false;
if (data->mixing_on) {
VGM_LOG("MIX: ignoring new mixes when mixing active\n");
if (mixer->active) {
VGM_LOG("MIX: ignoring new ops when mixer is active\n");
return false; /* to avoid down/upmixing after activation */
}
if (data->mixing_count + 1 > data->mixing_size) {
if (mixer->chain_count + 1 > mixer->chain_size) {
VGM_LOG("MIX: too many mixes\n");
return false;
}
data->mixing_chain[data->mixing_count] = *op; /* memcpy */
data->mixing_count++;
mixer->chain[mixer->chain_count] = *op; /* memcpy */
mixer->chain_count++;
if (op->type == MIX_FADE) {
data->has_fade = true;
mixer->has_fade = true;
}
else {
data->has_non_fade = true;
mixer->has_non_fade = true;
}
//;VGM_LOG("MIX: total %i\n", data->mixing_count);
//;VGM_LOG("MIX: total %i\n", data->chain_count);
return true;
}
void mixing_push_swap(VGMSTREAM* vgmstream, int ch_dst, int ch_src) {
mixer_data_t* data = vgmstream->mixing_data;
mixer_t* mixer = vgmstream->mixer;
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;
if (!mixer || ch_dst >= mixer->output_channels || ch_src >= mixer->output_channels) return;
op.type = MIX_SWAP;
op.ch_dst = ch_dst;
op.ch_src = ch_src;
@ -51,14 +51,14 @@ void mixing_push_swap(VGMSTREAM* vgmstream, int ch_dst, int ch_src) {
}
void mixing_push_add(VGMSTREAM* vgmstream, int ch_dst, int ch_src, double volume) {
mixer_data_t* data = vgmstream->mixing_data;
mixer_t* mixer = vgmstream->mixer;
mix_op_t op = {0};
if (!data) return;
if (!mixer) return;
//if (volume < 0.0) return; /* negative volume inverts the waveform */
if (volume == 0.0) return; /* ch_src becomes silent and nothing is added */
if (ch_dst < 0 || ch_src < 0) return;
if (!data || ch_dst >= data->output_channels || ch_src >= data->output_channels) return;
if (!mixer || ch_dst >= mixer->output_channels || ch_src >= mixer->output_channels) return;
op.type = MIX_ADD;
op.ch_dst = ch_dst;
@ -70,13 +70,13 @@ void mixing_push_add(VGMSTREAM* vgmstream, int ch_dst, int ch_src, double volume
}
void mixing_push_volume(VGMSTREAM* vgmstream, int ch_dst, double volume) {
mixer_data_t* data = vgmstream->mixing_data;
mixer_t* mixer = vgmstream->mixer;
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;
if (!mixer || ch_dst >= mixer->output_channels) return;
op.type = MIX_VOLUME; //if (volume == 0.0) MIX_VOLUME0 /* could simplify */
op.ch_dst = ch_dst;
@ -87,13 +87,13 @@ void mixing_push_volume(VGMSTREAM* vgmstream, int ch_dst, double volume) {
}
void mixing_push_limit(VGMSTREAM* vgmstream, int ch_dst, double volume) {
mixer_data_t* data = vgmstream->mixing_data;
mixer_t* mixer = vgmstream->mixer;
mix_op_t op = {0};
//if (ch_dst < 0) return; /* means all channels */
if (volume < 0.0) return;
if (volume == 1.0) return; /* no actual difference */
if (!data || ch_dst >= data->output_channels) return;
if (!mixer || ch_dst >= mixer->output_channels) return;
//if (volume == 0.0) return; /* dumb but whatevs */
op.type = MIX_LIMIT;
@ -104,12 +104,12 @@ void mixing_push_limit(VGMSTREAM* vgmstream, int ch_dst, double volume) {
}
void mixing_push_upmix(VGMSTREAM* vgmstream, int ch_dst) {
mixer_data_t* data = vgmstream->mixing_data;
mixer_t* mixer = vgmstream->mixer;
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;
if (!mixer || ch_dst > mixer->output_channels || mixer->output_channels +1 > VGMSTREAM_MAX_CHANNELS) return;
/* dst can be == output_channels here, since we are inserting */
op.type = MIX_UPMIX;
@ -117,35 +117,35 @@ void mixing_push_upmix(VGMSTREAM* vgmstream, int ch_dst) {
ok = add_mixing(vgmstream, &op);
if (ok) {
data->output_channels += 1;
if (data->mixing_channels < data->output_channels)
data->mixing_channels = data->output_channels;
mixer->output_channels += 1;
if (mixer->mixing_channels < mixer->output_channels)
mixer->mixing_channels = mixer->output_channels;
}
}
void mixing_push_downmix(VGMSTREAM* vgmstream, int ch_dst) {
mixer_data_t* data = vgmstream->mixing_data;
mixer_t* mixer = vgmstream->mixer;
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;
if (!mixer || ch_dst >= mixer->output_channels || mixer->output_channels - 1 < 1) return;
op.type = MIX_DOWNMIX;
op.ch_dst = ch_dst;
ok = add_mixing(vgmstream, &op);
if (ok) {
data->output_channels -= 1;
mixer->output_channels -= 1;
}
}
void mixing_push_killmix(VGMSTREAM* vgmstream, int ch_dst) {
mixer_data_t* data = vgmstream->mixing_data;
mixer_t* mixer = vgmstream->mixer;
mix_op_t op = {0};
if (ch_dst <= 0) return; /* can't kill from first channel */
if (!data || ch_dst >= data->output_channels) return;
if (!mixer || ch_dst >= mixer->output_channels) return;
op.type = MIX_KILLMIX;
op.ch_dst = ch_dst;
@ -153,14 +153,14 @@ void mixing_push_killmix(VGMSTREAM* vgmstream, int ch_dst) {
//;VGM_LOG("MIX: killmix %i\n", ch_dst);
bool ok = add_mixing(vgmstream, &op);
if (ok) {
data->output_channels = ch_dst; /* clamp channels */
mixer->output_channels = ch_dst; /* clamp channels */
}
}
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];
static mix_op_t* get_last_fade(mixer_t* mixer, int target_channel) {
for (int i = mixer->chain_count; i > 0; i--) {
mix_op_t* op = &mixer->chain[i-1];
if (op->type != MIX_FADE)
continue;
if (op->ch_dst == target_channel)
@ -173,13 +173,13 @@ static mix_op_t* 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;
mixer_t* mixer = vgmstream->mixer;
mix_op_t op = {0};
mix_op_t* op_prev;
//if (ch_dst < 0) return; /* means all channels */
if (!data || ch_dst >= data->output_channels) return;
if (!mixer || ch_dst >= mixer->output_channels) return;
if (time_pre > time_start || time_start > time_end || (time_post >= 0 && time_end > time_post)) return;
if (time_start < 0 || time_end < 0) return;
//if (time_pre < 0 || time_post < 0) return; /* special meaning of file start/end */
@ -214,7 +214,7 @@ 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'
*/
op_prev = get_last_fade(data, op.ch_dst);
op_prev = get_last_fade(mixer, 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 */

View File

@ -11,8 +11,8 @@
#define MIX_MACRO_BGM 'b'
void mixing_macro_volume(VGMSTREAM* vgmstream, double volume, uint32_t mask) {
mixer_data_t* data = vgmstream->mixing_data;
if (!data)
mixer_t* mixer = vgmstream->mixer;
if (!mixer)
return;
if (mask == 0) {
@ -20,7 +20,7 @@ void mixing_macro_volume(VGMSTREAM* vgmstream, double volume, uint32_t mask) {
return;
}
for (int ch = 0; ch < data->output_channels; ch++) {
for (int ch = 0; ch < mixer->output_channels; ch++) {
if (!((mask >> ch) & 1))
continue;
mixing_push_volume(vgmstream, ch, volume);
@ -28,8 +28,8 @@ void mixing_macro_volume(VGMSTREAM* vgmstream, double volume, uint32_t mask) {
}
void mixing_macro_track(VGMSTREAM* vgmstream, uint32_t mask) {
mixer_data_t* data = vgmstream->mixing_data;
if (!data)
mixer_t* mixer = vgmstream->mixer;
if (!mixer)
return;
if (mask == 0) {
@ -37,7 +37,7 @@ void mixing_macro_track(VGMSTREAM* vgmstream, uint32_t mask) {
}
/* reverse remove all channels (easier this way as when removing channels numbers change) */
for (int ch = data->output_channels - 1; ch >= 0; ch--) {
for (int ch = mixer->output_channels - 1; ch >= 0; ch--) {
if ((mask >> ch) & 1)
continue;
mixing_push_downmix(vgmstream, ch);
@ -74,9 +74,9 @@ static int is_layered_auto(VGMSTREAM* vgmstream, int max, char mode) {
return 0;
/* no channel down/upmixing (cannot guess output) */
mixer_data_t* data = vgmstream->mixing_data;
for (int i = 0; i < data->mixing_count; i++) {
mix_type_t type = data->mixing_chain[i].type;
mixer_t* mixer = vgmstream->mixer;
for (int i = 0; i < mixer->chain_count; i++) {
mix_type_t type = mixer->chain[i].type;
if (type == MIX_UPMIX || type == MIX_DOWNMIX || type == MIX_KILLMIX) /*type == MIX_SWAP || ??? */
return 0;
}
@ -208,10 +208,10 @@ static void mixing_macro_layer_auto(VGMSTREAM* vgmstream, int max, char mode) {
void mixing_macro_layer(VGMSTREAM* vgmstream, int max, uint32_t mask, char mode) {
mixer_data_t* data = vgmstream->mixing_data;
mixer_t* mixer = vgmstream->mixer;
int current, ch, output_channels, selected_channels;
if (!data)
if (!mixer)
return;
if (is_layered_auto(vgmstream, max, mode)) {
@ -224,7 +224,7 @@ void mixing_macro_layer(VGMSTREAM* vgmstream, int max, uint32_t mask, char mode)
if (max == 0) /* auto calculate */
max = get_layered_max_channels(vgmstream);
if (max <= 0 || data->output_channels <= max)
if (max <= 0 || mixer->output_channels <= max)
return;
/* set all channels (non-existant channels will be ignored) */
@ -233,7 +233,7 @@ void mixing_macro_layer(VGMSTREAM* vgmstream, int max, uint32_t mask, char mode)
}
/* save before adding fake channels */
output_channels = data->output_channels;
output_channels = mixer->output_channels;
/* count possibly set channels */
selected_channels = 0;
@ -292,19 +292,19 @@ void mixing_macro_layer(VGMSTREAM* vgmstream, int max, uint32_t mask, char mode)
}
void mixing_macro_crosstrack(VGMSTREAM* vgmstream, int max) {
mixer_data_t* data = vgmstream->mixing_data;
mixer_t* mixer = vgmstream->mixer;
int current, ch, track, track_ch, track_num, output_channels;
int32_t change_pos, change_next, change_time;
if (!data)
if (!mixer)
return;
if (max <= 0 || data->output_channels <= max)
if (max <= 0 || mixer->output_channels <= max)
return;
if (!vgmstream->loop_flag) /* maybe force loop? */
return;
/* this probably only makes sense for even channels so upmix before if needed) */
output_channels = data->output_channels;
output_channels = mixer->output_channels;
if (output_channels % 2) {
mixing_push_upmix(vgmstream, output_channels);
output_channels += 1;
@ -356,19 +356,19 @@ void mixing_macro_crosstrack(VGMSTREAM* vgmstream, int max) {
}
void mixing_macro_crosslayer(VGMSTREAM* vgmstream, int max, char mode) {
mixer_data_t* data = vgmstream->mixing_data;
mixer_t* mixer = vgmstream->mixer;
int current, ch, layer, layer_ch, layer_num, loop, output_channels;
int32_t change_pos, change_time;
if (!data)
if (!mixer)
return;
if (max <= 0 || data->output_channels <= max)
if (max <= 0 || mixer->output_channels <= max)
return;
if (!vgmstream->loop_flag) /* maybe force loop? */
return;
/* this probably only makes sense for even channels so upmix before if needed) */
output_channels = data->output_channels;
output_channels = mixer->output_channels;
if (output_channels % 2) {
mixing_push_upmix(vgmstream, output_channels);
output_channels += 1;
@ -469,7 +469,7 @@ typedef enum {
} mixing_position_t;
void mixing_macro_downmix(VGMSTREAM* vgmstream, int max /*, mapping_t output_mapping*/) {
mixer_data_t* data = vgmstream->mixing_data;
mixer_t* mixer = vgmstream->mixer;
int ch, output_channels, mp_in, mp_out, ch_in, ch_out;
channel_mapping_t input_mapping, output_mapping;
const double vol_max = 1.0;
@ -478,15 +478,15 @@ void mixing_macro_downmix(VGMSTREAM* vgmstream, int max /*, mapping_t output_map
double matrix[16][16] = {{0}};
if (!data)
if (!mixer)
return;
if (max <= 1 || data->output_channels <= max || max >= 8)
if (max <= 1 || mixer->output_channels <= max || max >= 8)
return;
/* assume WAV defaults if not set */
input_mapping = vgmstream->channel_layout;
if (input_mapping == 0) {
switch(data->output_channels) {
switch(mixer->output_channels) {
case 1: input_mapping = mapping_MONO; break;
case 2: input_mapping = mapping_STEREO; break;
case 3: input_mapping = mapping_2POINT1; break;
@ -532,7 +532,7 @@ void mixing_macro_downmix(VGMSTREAM* vgmstream, int max /*, mapping_t output_map
}
/* save and make N fake channels at the beginning for easier calcs */
output_channels = data->output_channels;
output_channels = mixer->output_channels;
for (ch = 0; ch < max; ch++) {
mixing_push_upmix(vgmstream, 0);
}

View File

@ -5,35 +5,35 @@
// 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_op_t* op) {
float* sbuf = data->mixbuf;
void mixer_op_swap(mixer_t* mixer, int32_t sample_count, mix_op_t* op) {
float* sbuf = mixer->mixbuf;
for (int s = 0; s < sample_count; s++) {
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;
sbuf += mixer->current_channels;
}
}
void mixer_op_add(mixer_data_t* data, int32_t sample_count, mix_op_t* op) {
float* sbuf = data->mixbuf;
void mixer_op_add(mixer_t* mixer, int32_t sample_count, mix_op_t* op) {
float* sbuf = mixer->mixbuf;
/* could optimize when vol == 1 to avoid one multiplication but whatevs (not common) */
for (int s = 0; s < sample_count; s++) {
sbuf[op->ch_dst] = sbuf[op->ch_dst] + sbuf[op->ch_src] * op->vol;
sbuf += data->current_channels;
sbuf += mixer->current_channels;
}
}
void mixer_op_volume(mixer_data_t* data, int32_t sample_count, mix_op_t* op) {
float* sbuf = data->mixbuf;
void mixer_op_volume(mixer_t* mixer, int32_t sample_count, mix_op_t* op) {
float* sbuf = mixer->mixbuf;
if (op->ch_dst < 0) {
/* "all channels", most common case */
for (int s = 0; s < sample_count * data->current_channels; s++) {
for (int s = 0; s < sample_count * mixer->current_channels; s++) {
sbuf[s] = sbuf[s] * op->vol;
}
}
@ -41,13 +41,13 @@ void mixer_op_volume(mixer_data_t* data, int32_t sample_count, mix_op_t* op) {
for (int s = 0; s < sample_count; s++) {
sbuf[op->ch_dst] = sbuf[op->ch_dst] * op->vol;
sbuf += data->current_channels;
sbuf += mixer->current_channels;
}
}
}
void mixer_op_limit(mixer_data_t* data, int32_t sample_count, mix_op_t* op) {
float* sbuf = data->mixbuf;
void mixer_op_limit(mixer_t* mixer, int32_t sample_count, mix_op_t* op) {
float* sbuf = mixer->mixbuf;
const float limiter_max = 32767.0f;
const float limiter_min = -32768.0f;
@ -59,7 +59,7 @@ void mixer_op_limit(mixer_data_t* data, int32_t sample_count, mix_op_t* op) {
for (int s = 0; s < sample_count; s++) {
if (op->ch_dst < 0) {
for (int ch = 0; ch < data->current_channels; ch++) {
for (int ch = 0; ch < mixer->current_channels; ch++) {
if (sbuf[ch] > temp_max)
sbuf[ch] = temp_max;
else if (sbuf[ch] < temp_min)
@ -73,24 +73,24 @@ void mixer_op_limit(mixer_data_t* data, int32_t sample_count, mix_op_t* op) {
sbuf[op->ch_dst] = temp_min;
}
sbuf += data->current_channels;
sbuf += mixer->current_channels;
}
}
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;
void mixer_op_upmix(mixer_t* mixer, int32_t sample_count, mix_op_t* op) {
int max_channels = mixer->current_channels;
mixer->current_channels += 1;
float* sbuf_tmp = data->mixbuf + sample_count * data->current_channels;
float* sbuf = data->mixbuf + sample_count * max_channels;
float* sbuf_tmp = mixer->mixbuf + sample_count * mixer->current_channels;
float* sbuf = mixer->mixbuf + sample_count * max_channels;
/* copy 'backwards' as otherwise would overwrite samples before moving them forward */
for (int s = 0; s < sample_count; s++) {
sbuf_tmp -= data->current_channels;
sbuf_tmp -= mixer->current_channels;
sbuf -= max_channels;
int sbuf_ch = max_channels - 1;
for (int ch = data->current_channels - 1; ch >= 0; ch--) {
for (int ch = mixer->current_channels - 1; ch >= 0; ch--) {
if (ch == op->ch_dst) {
sbuf_tmp[ch] = 0; /* inserted as silent */
}
@ -102,11 +102,11 @@ 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) {
int max_channels = data->current_channels;
data->current_channels -= 1;
void mixer_op_downmix(mixer_t* mixer, int32_t sample_count, mix_op_t* op) {
int max_channels = mixer->current_channels;
mixer->current_channels -= 1;
float* sbuf = data->mixbuf;
float* sbuf = mixer->mixbuf;
float* sbuf_tmp = sbuf;
for (int s = 0; s < sample_count; s++) {
@ -119,24 +119,24 @@ void mixer_op_downmix(mixer_data_t* data, int32_t sample_count, mix_op_t* op) {
sbuf_tmp[ch] = sbuf[ch + 1]; /* 'pull' dropped channels back */
}
sbuf_tmp += data->current_channels;
sbuf_tmp += mixer->current_channels;
sbuf += max_channels;
}
}
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 = op->ch_dst; /* clamp channels */
void mixer_op_killmix(mixer_t* mixer, int32_t sample_count, mix_op_t* op) {
int max_channels = mixer->current_channels;
mixer->current_channels = op->ch_dst; /* clamp channels */
float* sbuf = data->mixbuf;
float* sbuf = mixer->mixbuf;
float* sbuf_tmp = sbuf;
for (int s = 0; s < sample_count; s++) {
for (int ch = 0; ch < data->current_channels; ch++) {
for (int ch = 0; ch < mixer->current_channels; ch++) {
sbuf_tmp[ch] = sbuf[ch];
}
sbuf_tmp += data->current_channels;
sbuf_tmp += mixer->current_channels;
sbuf += max_channels;
}
}

View File

@ -112,12 +112,12 @@ static bool get_fade_gain(mix_op_t* op, float* out_cur_vol, int32_t current_subp
return true;
}
void mixer_op_fade(mixer_data_t* data, int32_t sample_count, mix_op_t* mix) {
float* sbuf = data->mixbuf;
void mixer_op_fade(mixer_t* mixer, int32_t sample_count, mix_op_t* mix) {
float* sbuf = mixer->mixbuf;
float new_gain = 0.0f;
int channels = data->current_channels;
int32_t current_subpos = data->current_subpos;
int channels = mixer->current_channels;
int32_t current_subpos = mixer->current_subpos;
//TODO optimize for case 0?
for (int s = 0; s < sample_count; s++) {
@ -138,14 +138,14 @@ void mixer_op_fade(mixer_data_t* data, int32_t sample_count, mix_op_t* mix) {
current_subpos++;
}
data->current_subpos = current_subpos;
mixer->current_subpos = current_subpos;
}
bool mixer_op_fade_is_active(mixer_data_t* data, int32_t current_start, int32_t current_end) {
bool mixer_op_fade_is_active(mixer_t* mixer, int32_t current_start, int32_t current_end) {
for (int i = 0; i < data->mixing_count; i++) {
mix_op_t* mix = &data->mixing_chain[i];
for (int i = 0; i < mixer->chain_count; i++) {
mix_op_t* mix = &mixer->chain[i];
int32_t fade_start, fade_end;
float vol_start = mix->vol_start;

View File

@ -33,31 +33,33 @@ typedef struct {
} mix_op_t;
typedef struct {
int mixing_channels; /* max channels needed to mix */
int input_channels; /* starting channels before mixing */
int output_channels; /* resulting channels after mixing */
int mixing_channels; /* max channels needed to mix */
bool mixing_on; /* mixing allowed */
bool active; /* mixing working */
int mixing_count; /* mixing number */
size_t mixing_size; /* mixing max */
mix_op_t mixing_chain[VGMSTREAM_MAX_MIXING]; /* effects to apply (could be alloc'ed but to simplify...) */
int chain_count; /* op number */
size_t chain_size; /* max ops */
mix_op_t chain[VGMSTREAM_MAX_MIXING]; /* effects to apply (could be alloc'ed but to simplify...) */
/* fades only apply at some points, other mixes are active */
bool has_non_fade;
bool has_fade;
float* mixbuf; /* internal mixing buffer */
int current_channels; /* state: channels may increase/decrease during ops */
int32_t current_subpos; /* state: current sample pos in the stream */
/* fades only apply at some points, other mixes are active */
bool has_non_fade;
bool has_fade;
} mixer_data_t;
} mixer_t;
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);
void mixer_op_swap(mixer_t* data, int32_t sample_count, mix_op_t* op);
void mixer_op_add(mixer_t* data, int32_t sample_count, mix_op_t* op);
void mixer_op_volume(mixer_t* data, int32_t sample_count, mix_op_t* op);
void mixer_op_limit(mixer_t* data, int32_t sample_count, mix_op_t* op);
void mixer_op_upmix(mixer_t* data, int32_t sample_count, mix_op_t* op);
void mixer_op_downmix(mixer_t* data, int32_t sample_count, mix_op_t* op);
void mixer_op_killmix(mixer_t* data, int32_t sample_count, mix_op_t* op);
void mixer_op_fade(mixer_t* data, int32_t sample_count, mix_op_t* op);
bool mixer_op_fade_is_active(mixer_t* data, int32_t current_start, int32_t current_end);
#endif

View File

@ -223,6 +223,8 @@ void setup_state_vgmstream(VGMSTREAM* vgmstream) {
/*****************************************************************************/
void render_free(VGMSTREAM* vgmstream) {
if (!vgmstream->layout_data)
return;
if (vgmstream->layout_type == layout_segmented) {
free_layout_segmented(vgmstream->layout_data);

28
src/base/sbuf.h Normal file
View File

@ -0,0 +1,28 @@
#ifndef _SBUF_H
#define _SBUF_H
#include "../streamtypes.h"
// TODO decide if using float 1.0 style or 32767 style (fuzzy PCM changes when doing that)
static inline void sbuf_copy_s16_to_f32(float* buf_f32, int16_t* buf_s16, int samples, int channels) {
for (int s = 0; s < samples * channels; s++) {
buf_f32[s] = buf_s16[s]; // / 32767.0f
}
}
static inline void sbuf_copy_f32_to_s16(int16_t* buf_s16, float* buf_f32, int samples, int channels) {
/* when casting float to int, value is simply truncated:
* - (int)1.7 = 1, (int)-1.7 = -1
* alts for more accurate rounding could be:
* - (int)floor(f)
* - (int)(f < 0 ? f - 0.5f : f + 0.5f)
* - (((int) (f1 + 32768.5)) - 32768)
* - etc
* but since +-1 isn't really audible we'll just cast as it's the fastest
*/
for (int s = 0; s < samples * channels; s++) {
buf_s16[s] = clamp16( buf_f32[s]); // * 32767.0f
}
}
#endif

View File

@ -642,8 +642,8 @@ static VGMSTREAM* init_vgmstream_internal(STREAMFILE* sf) {
/* some players are picky with incorrect channel layouts */
if (vgmstream->channel_layout > 0) {
int output_channels = vgmstream->channels;
int ch, count = 0, max_ch = 32;
for (ch = 0; ch < max_ch; ch++) {
int count = 0, max_ch = 32;
for (int ch = 0; ch < max_ch; ch++) {
int bit = (vgmstream->channel_layout >> ch) & 1;
if (ch > 17 && bit) {
VGM_LOG("VGMSTREAM: wrong bit %i in channel_layout %x\n", ch, vgmstream->channel_layout);
@ -750,12 +750,12 @@ void reset_vgmstream(VGMSTREAM* vgmstream) {
}
/* Allocate memory and setup a VGMSTREAM */
VGMSTREAM* allocate_vgmstream(int channel_count, int loop_flag) {
VGMSTREAM* allocate_vgmstream(int channels, int loop_flag) {
VGMSTREAM* vgmstream;
/* up to ~16-24 aren't too rare for multilayered files, more is probably a bug */
if (channel_count <= 0 || channel_count > VGMSTREAM_MAX_CHANNELS) {
VGM_LOG("VGMSTREAM: error allocating %i channels\n", channel_count);
/* up to ~16-24 aren't too rare for multilayered files, 50+ is probably a bug */
if (channels <= 0 || channels > VGMSTREAM_MAX_CHANNELS) {
VGM_LOG("VGMSTREAM: error allocating %i channels\n", channels);
return NULL;
}
@ -777,20 +777,20 @@ VGMSTREAM* allocate_vgmstream(int channel_count, int loop_flag) {
*/
/* create vgmstream + main structs (other data is 0'ed) */
vgmstream = calloc(1,sizeof(VGMSTREAM));
vgmstream = calloc(1, sizeof(VGMSTREAM));
if (!vgmstream) return NULL;
vgmstream->start_vgmstream = calloc(1,sizeof(VGMSTREAM));
vgmstream->start_vgmstream = calloc(1, sizeof(VGMSTREAM));
if (!vgmstream->start_vgmstream) goto fail;
vgmstream->ch = calloc(channel_count,sizeof(VGMSTREAMCHANNEL));
vgmstream->ch = calloc(channels, sizeof(VGMSTREAMCHANNEL));
if (!vgmstream->ch) goto fail;
vgmstream->start_ch = calloc(channel_count,sizeof(VGMSTREAMCHANNEL));
vgmstream->start_ch = calloc(channels, sizeof(VGMSTREAMCHANNEL));
if (!vgmstream->start_ch) goto fail;
if (loop_flag) {
vgmstream->loop_ch = calloc(channel_count,sizeof(VGMSTREAMCHANNEL));
vgmstream->loop_ch = calloc(channels, sizeof(VGMSTREAMCHANNEL));
if (!vgmstream->loop_ch) goto fail;
}
@ -798,27 +798,20 @@ VGMSTREAM* allocate_vgmstream(int channel_count, int loop_flag) {
* in theory the bigger the better but in practice there isn't much difference */
vgmstream->tmpbuf_size = 0x10000; /* for all channels */
vgmstream->tmpbuf = malloc(sizeof(sample_t) * vgmstream->tmpbuf_size);
if (!vgmstream->tmpbuf) goto fail;
vgmstream->channels = channel_count;
vgmstream->channels = channels;
vgmstream->loop_flag = loop_flag;
mixing_init(vgmstream); /* pre-init */
vgmstream->mixer = mixer_init(vgmstream->channels); /* pre-init */
//if (!vgmstream->mixer) goto fail;
/* BEWARE: try_dual_file_stereo does some free'ing too */
//vgmstream->stream_name_size = STREAM_NAME_SIZE;
return vgmstream;
fail:
if (vgmstream) {
mixing_close(vgmstream);
free(vgmstream->tmpbuf);
free(vgmstream->ch);
free(vgmstream->start_ch);
free(vgmstream->loop_ch);
free(vgmstream->start_vgmstream);
}
free(vgmstream);
close_vgmstream(vgmstream);
return NULL;
}
@ -834,25 +827,21 @@ void close_vgmstream(VGMSTREAM* vgmstream) {
/* now that the special cases have had their chance, clean up the standard items */
{
int i,j;
for (i = 0; i < vgmstream->channels; i++) {
if (vgmstream->ch[i].streamfile) {
close_streamfile(vgmstream->ch[i].streamfile);
/* Multiple channels might have the same streamfile. Find the others
* that are the same as this and clear them so they won't be closed again. */
for (j = 0; j < vgmstream->channels; j++) {
if (i != j && vgmstream->ch[j].streamfile == vgmstream->ch[i].streamfile) {
vgmstream->ch[j].streamfile = NULL;
}
for (int i = 0; i < vgmstream->channels; i++) {
if (vgmstream->ch[i].streamfile) {
close_streamfile(vgmstream->ch[i].streamfile);
/* Multiple channels might have the same streamfile. Find the others
* that are the same as this and clear them so they won't be closed again. */
for (int j = 0; j < vgmstream->channels; j++) {
if (i != j && vgmstream->ch[j].streamfile == vgmstream->ch[i].streamfile) {
vgmstream->ch[j].streamfile = NULL;
}
vgmstream->ch[i].streamfile = NULL;
}
vgmstream->ch[i].streamfile = NULL;
}
}
mixing_close(vgmstream);
mixer_free(vgmstream->mixer);
free(vgmstream->tmpbuf);
free(vgmstream->ch);
free(vgmstream->start_ch);
@ -898,13 +887,13 @@ void vgmstream_force_loop(VGMSTREAM* vgmstream, int loop_flag, int loop_start_sa
/* propagate changes to layouts that need them */
if (vgmstream->layout_type == layout_layered) {
int i;
layered_layout_data* data = vgmstream->layout_data;
/* layered loops using the internal VGMSTREAMs */
for (i = 0; i < data->layer_count; i++) {
if (!data->layers[i]->config_enabled) /* only in simple mode */
for (int i = 0; i < data->layer_count; i++) {
if (!data->layers[i]->config_enabled) { /* only in simple mode */
vgmstream_force_loop(data->layers[i], loop_flag, loop_start_sample, loop_end_sample);
}
/* layer's force_loop also calls setup_vgmstream, no need to do it here */
}
}
@ -924,9 +913,8 @@ void vgmstream_set_loop_target(VGMSTREAM* vgmstream, int loop_target) {
/* propagate changes to layouts that need them */
if (vgmstream->layout_type == layout_layered) {
int i;
layered_layout_data *data = vgmstream->layout_data;
for (i = 0; i < data->layer_count; i++) {
for (int i = 0; i < data->layer_count; i++) {
vgmstream_set_loop_target(data->layers[i], loop_target);
}
}
@ -960,7 +948,7 @@ static void try_dual_file_stereo(VGMSTREAM* opened_vgmstream, STREAMFILE* sf, in
int dfs_pair = -1; /* -1=no stereo, 0=opened_vgmstream is left, 1=opened_vgmstream is right */
VGMSTREAM* new_vgmstream = NULL;
STREAMFILE* dual_sf = NULL;
int i,j, dfs_pair_count, extension_len, filename_len;
int dfs_pair_count, extension_len, filename_len;
int sample_variance, loop_variance;
if (opened_vgmstream->channels != 1)
@ -986,8 +974,8 @@ static void try_dual_file_stereo(VGMSTREAM* opened_vgmstream, STREAMFILE* sf, in
/* find pair from base name and modify new_filename with the opposite (tries L>R then R>L) */
dfs_pair_count = (sizeof(dfs_pairs)/sizeof(dfs_pairs[0]));
for (i = 0; dfs_pair == -1 && i < dfs_pair_count; i++) {
for (j = 0; dfs_pair == -1 && j < 2; j++) {
for (int i = 0; dfs_pair == -1 && i < dfs_pair_count; i++) {
for (int j = 0; dfs_pair == -1 && j < 2; j++) {
const char* this_suffix = dfs_pairs[i][j];
const char* that_suffix = dfs_pairs[i][j^1];
size_t this_suffix_len = strlen(dfs_pairs[i][j]);
@ -1136,12 +1124,12 @@ static void try_dual_file_stereo(VGMSTREAM* opened_vgmstream, STREAMFILE* sf, in
opened_vgmstream->layout_type = layout_none; /* fixes some odd cases */
/* discard the second VGMSTREAM */
mixing_close(new_vgmstream);
mixer_free(new_vgmstream->mixer);
free(new_vgmstream->tmpbuf);
free(new_vgmstream->start_vgmstream);
free(new_vgmstream);
mixing_update_channel(opened_vgmstream); /* notify of new channel hacked-in */
mixer_update_channel(opened_vgmstream); /* notify of new channel hacked-in */
}
return;
@ -1160,6 +1148,7 @@ fail:
int vgmstream_open_stream(VGMSTREAM* vgmstream, STREAMFILE* sf, off_t start_offset) {
return vgmstream_open_stream_bf(vgmstream, sf, start_offset, 0);
}
int vgmstream_open_stream_bf(VGMSTREAM* vgmstream, STREAMFILE* sf, off_t start_offset, int force_multibuffer) {
STREAMFILE* file = NULL;
char filename[PATH_LIMIT];

View File

@ -221,7 +221,7 @@ typedef struct {
VGMSTREAMCHANNEL* loop_ch; /* shallow copy of channels as they were at the loop point (for loops) */
void* start_vgmstream; /* shallow copy of the VGMSTREAM as it was at the beginning of the stream (for resets) */
void* mixing_data; /* state for mixing effects */
void* mixer; /* state for mixing effects */
/* Optional data the codec needs for the whole stream. This is for codecs too
* different from vgmstream's structure to be reasonably shoehorned.