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
4ed61e3740
commit
93de444987
@ -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) {
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
|
@ -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 */
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
28
src/base/sbuf.h
Normal 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
|
@ -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];
|
||||
|
@ -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.
|
||||
|
Loading…
x
Reference in New Issue
Block a user