Enable TXTP mixing

This commit is contained in:
bnnm 2019-03-24 01:21:09 +01:00
parent 248ec056e3
commit 8739631792
12 changed files with 270 additions and 260 deletions

View File

@ -407,10 +407,8 @@ int main(int argc, char ** argv) {
channels = vgmstream->channels;
input_channels = vgmstream->channels;
#ifdef VGMSTREAM_MIXING
/* enable after config but before outbuf */
vgmstream_mixing_enable(vgmstream, SAMPLE_BUFFER_SIZE, &input_channels, &channels);
#endif
if (cfg.play_forever && (!vgmstream->loop_flag || vgmstream->loop_target > 0)) {
fprintf(stderr,"I could play a nonlooped track forever, but it wouldn't end well.");

View File

@ -97,7 +97,7 @@ bgm.sxd2#12
```
### Play segmented subsong ranges as one
**`#m(number)~(number)` or `#ms(number)~(number)`**: set multiple subsong segments at a time, to avoid so much C&P
**`#(number)~(number)` or `#s(number)~(number)`**: set multiple subsong segments at a time, to avoid so much C&P
**Prince of Persia Sands of Time**: *song_01.txtp*
```
@ -204,6 +204,115 @@ Use this feature responsibly, though. If you find a format that should loop usin
Note that a few codecs may not work with arbitrary loop values since they weren't tested with loops. Misaligned loops will cause audible "clicks" at loop point too.
### Channel mixing
**`#m(op),(...),(op)`**: mix channels in various ways by specifying multiple comma-separated sub-commands:
Possible operations:
- `N-M`: swaps M with N
- `N+M*(volume)`: mixes M * volume to N
- `N+M`: mixes M to N
- `N*(volume)`: changes volume of N
- `N=(volume)`: limits volume of N
- `Nu`: upmix (insert) N ('pushing' all following channels forward)
- `Nd`: downmix (remove) N ('pulling' all following channels backward)
- `ND`: downmix (remove) N and all following channels
- `N(type)(time-start)+(time-length)`: defines a fade
* `type` can be `{` = fade-in, `}` = fade-out, `(` = crossfade-in, `)` = crossfade-out
* crossfades are better tuned to use when changing between tracks
* using multiple fades in the same channel will cancel previous fades
* may only cancel when fade is after previous one
* `}` then `{` makes sense, but `}` then `}` will make funny volume bumps
- `N^(volume-start)~(volume-end)=(shape)@(time-pre)~(time-start)+(time-length)~(time-last)`: defines full fade envelope
* full definition of the above to allow precise volume changes over time
* not necessarily fades, as you could set length 0 for volume "bumps", or make volumes 1.0~0.5
* pre/post may be -1 to set "file start" and "file end", cancelled by next fade
* `(shape)` can be `{` = fade, `(` = crossfade, other values are reserved for internal testing and may change anytime
Considering:
- `N` and `M` are channels (*current* value after previous operators are applied)
- channel 1 is first
- channel 0 is shorthand for all channels where applicable (`0*V`, `0=V`, `0^...`)
- may use `x` instead of `*` and `_` instead of `:` (for mini-TXTP)
- `(volume)` is a `N.N` decimal value where 1.0 is 100% base volume
- negative volume inverts the waveform (for weird effects)
- `(time)` can be `N:NN(.n)` (minutes:seconds), `N.N` (seconds) or `N` (samples)
- represents the file's global play time, so it may be set after N loops
- beware of `10.0` (ten seconds) vs `10` (ten samples)
- may not work with huge numbers (like several hours)
- adding trailing channels must be done 1 by 1 at the end (for stereo: `3u,4u,(...)`
- nonsensical values are ignored (like referencing channel 3 in a stereo file)
Main usage would be creating stereo files for games that layer channels.
```
# quad to stereo: all layers must play at the same time
# - mix 75% of channel 3/4 into channel 1/2, then drop channel 3 and 4
song#m1+3*0.75,2+4*.75,3D
# quad to stereo: only channel 3 and 4 should play
# - swap channel 1/2 with 3/4, then drop channel 3/4
song#m1-3,2-4,3D
# also equivalent, but notice the order
# - drop channel 1 then 2 (now 1)
song#m1d,1d
```
Proper mixing requires some basic knowledge though, it's further explained later. Order matters and operations are applied sequentially, for extra flexibility at the cost of complexity and user-friendliness, and may result in surprising mixes. Try to stick to macros and simple combos, using later examples as a base.
This can be applied to individual layers and segments, but normally you want to use `commands` to apply mixing to the resulting file (see examples). Per-segment mixing should be reserved to specific up/downmixings.
Mixing must be supported by the plugin, otherwise it's ignored (there is a negligible performance penalty per mix operation though).
### Macros
**`#@(macro name and parameters)`**: adds a new macro
Manually setting values gets old, so TXTP supports a bunch of simple macros. They automate some of the above commands (analyzing the file), and may be combined, so order still matters.
- `volume N (channels)`: sets volume V to selected channels
- `track (channels)`: makes a file of selected channels
- `layer-v N (channels)`: mixes selected channels to N channels with default volume (for layered vocals)
- `layer-b N (channels)`: same, but adjusts volume depending on layers (for layered bgm)
- `layer-e N (channels)`: same, but adjusts volume equally for all layers (for generic downmixing)
- `remix N (channels)`: same, but mixes selected channels to N channels properly adjusting volume (for layered bgm)
- `crosstrack N`: crossfades between Nch tracks after every loop (loop count is adjusted as needed)
- `crosslayer-v/b/e N`: crossfades Nch layers to the main track after every loop (loop count is adjusted as needed)
`channels` can be multiple comma-separated channels or N~M ranges and may be ommited were applicable to mean "all channels" (channel order doesn't matter but it's internally fixed).
Examples:
```
# plays 2ch layer1 (base melody)
okami-ryoshima_coast.aix#@track 1,2
# plays 2ch layer1+2 (base melody+percussion)
okami-ryoshima_coast.aix#@layer-b 2 1~4 #1~4 may be skipped
# uses 2ch layer1 (base melody) in the first loop, adds 2ch layer2 (percussion) to layer1 in the second
okami-ryoshima_coast.aix#@crosslayer-b 2
# uses 2ch track1 (exploration) in the first loop, changes to 2ch track2 (combat) in the second
ffxiii2-eclipse.scd#@crosstrack 2
# plays 2ch from 4ch track1 (sneaking)
mgs4-bgm_ee_alert_01.mta2#@layer-e 2 1~4
# downmix bgm + vocals to stereo
nier_automata-BGM_0_012_04.wem
nier_automata-BGM_0_012_07.wem
mode = layers
commands = #@layer-v 2
# can be combined with normal mixes too for creative results
# (add channel clone of ch1, then 50% of range)
song#m4u,4+1#@volume 0.5 2~4
# equivalent to #@layer-e 2 1~4
mgs4-bgm_ee_alert_01.mta2#@track 1~4#@layer-b 2
# equivalent to #@track 1,2
okami-ryoshima_coast.aix#@layer-b 2 1,2
```
## OTHER FEATURES
### Default commands
@ -326,3 +435,137 @@ To simplify TXTP creation, if the .txtp is empty (0 bytes) its filename is used
- *Ryoshima Coast 1 & 2.aix#c1,2.txtp*: channel mask
- *boss2_3ningumi_ver6.adx#l2#F.txtp*: loop twice then play song end file normally
- etc
## MIXING
Sometimes games use multiple channels in uncommon ways, for example as layered tracks for dynamic music (like main+vocals), or crossfading a stereo song to another stereo song. In those cases we normally would want a stereo track, but vgmstream can't guess how channels are used (since it's game-dependant). To solve this via TXTP you can set mixing output and volumes manually.
A song file is just data that can contain a (sometimes unlimited) number of channels, that must play in physical speakers. Standard audio formats define how to "map" known channels to speakers:
- `1.0: FC`
- `2.0: FL, FR`
- `2.1: FL, FR, LF`
- `4.0: FL, FR, SL, SR`
- `5.1: FL, FR, FC, LF, SL, SR`
- ... (channels in order, where FL=front left, FC=front center, etc)
If you only have stereo speakers, when playing a 5.1 file your player may silently transform to stereo, as otherwise you would miss some channels. But a game song's channels can be various things: standard mapped, per-format map, per-game, multilayers combined ("downmixed") to a final stereo file, music then all language tracks, etc. So you need to decide which channels drop or combine and their individual volumes via mixing.
Say you want to mix 4ch to 2ch (ch3 to ch1, ch4 to ch2). Due to how audio signals work, mixing just combines (adds) sounds. So if channels 1/2 are LOUD, and channels 3/4 are LOUD, you get a LOUDER channel 1/2. To fix this we set mixing volume, for example: `mix channel 3/4 * 0.707 (-3db/30% lower volume) to channel 1/2`: the resulting stereo file is now more listenable. Those volumes are just for standard audio and may work ok for every game though.
All this means there is no simple, standard way to mix, so you must experiment a bit.
### MIXING EXAMPLES
For most common usages you can stick with macros but actual mixing is quite flexible:
```
# boost volume of all channels by 30%
song#m0*1.3
# boost but limit volume (highs don't go too high, while lows sound louder)
song#m0*1.3,0=0.9
# downmix 4ch layers to stereo (this may sound too loud)
song#m1+3,2+4,3D
# downmix 4ch layers to stereo with adjusted volume for latter channels (common 4.0 to 2.0 mixdown)
song#m1+3*0.7,2+4*0.7,3D
# downmix 4ch layers to stereo with equal adjusted volume (common layer mixdown)
song#m0*0.7,m1*0.7,1+3*0.7,2+4*0.7,3D
# downmix stereo to mono (ignored if file is 1ch)
zelda-cdi.xa#m1d
# upmix mono to stereo by copying ch1 to ch2
zelda-cdi.xa#m2*0.0,2+1
# downmix 5.1 wav (FL FR FC LFE BL BR) to stereo
# (uses common -3db mixing formula: Fx=Fx + FC*0.707 + Rx*0.707)
song#m1+3*0.707,2+3*0.707,1+5*0.707,2+6*0.707,3D
# mask sfx track ch3 in a 6ch file
song#m3*0.0
# add a fake silent channel to a 5ch file (FL FR FC BL BR) and move it to LFE position
# "make ch6, swap BL with LFE (now FL FR FC LFE BR BL), swap BR with BL (now FL FR FC LFE BL BR)
sf5.hca#m6u,4-6,5-6
# mix 50% of channel 3 into 1 and 2, drop 3
song#m1+3*0.5,2+3*0.5,3d
# swap ch1 and 2 then change volume to the resulting swapped channel
song#m1-2,2*0.5
# fade-in ch3+4 percussion track layer into main track, downmix to stereo
# (may be split in multiple lines, no difference)
okami-ryoshima_coast.aix#l2
commands = #m3(1:10~0:05 # loop happens after ~1:10
commands = #m4(1:10~0:05 # ch3/4 are percussion tracks
commands = #m1+3*0.707,2+4*0.707 # ch3/4 always mixed but silent until 1:10
commands = #m3D # remove channels after all mixing
# same but fade-out percussion after second loop
okami-ryoshima_coast.aix#l3
commands = #m3(1:10~0:05,3)2:20~0:05
commands = #m4(1:10~0:05,4)2:20~0:05
commands = #m1+3*0.707,2+4*0.707,3D
# crossfade exploration and combat sections after loop
ffxiii-2~eclipse.aix
commands = #m1)1:50~0:10,m2)1:50~0:10
commands = #m3(1:50~0:10,m4(1:50~0:10
commands = #m1+3,2+4,3D # won't play at the same time, no volume needed
# ghetto voice removal (invert channel + other channel removes duplicated parts, and vocals are often layered)
song
commands = #m3u,3+1*-1.0,4u,4+2*-1.0
commands = #m1+4,2+3,3d,3d
# crosstrack 4ch file 3 times, going back to first track by creating a fake 3rd track with ch1 and 2:
ffxiii2-eclipse.scd#m5u,6u,5+1,6+2#@crosstrack 2
```
Segment/layer downmixing is allowed but try to keep it simple, some mixes accomplish the same things but are a bit strange.
```
# mix one stereo segment with a mono segment
intro-stereo.hca
loop-mono.hca#m2u
# this makes mono file
intro-stereo.hca#m2u
loop-stereo.hca#m2u
# but you normally should do this instead as it's more natural
intro-stereo.hca
loop-stereo.hca
commands = #m2u
# fading segments work rather unexpectedly
# fades out 1 minute into the _segment_ (could be 2 minutes into the resulting file)
segment1.hca#m0{0:10+10.0
segment2.hca#m0}1:00+10.0
# better use: commands = #m0{0:10+10.0,0}2:00+10.0
# it would work ok it they were layers, but still, better to use commands with the resulting file
```
Note how order subtly affects end results:
```
# after silencing channel 1 mixing is meaningless
song#m1*0.0,2+1
# allowed but useless or ignored
song#m1u,1d,1-1,1*1.0,11d,7D
# this creates a new ch1 with 50% of ch2 (actually old ch1), total 3ch
song#m1u,1+2*0.5
# so does this
song#m3u,3+1*0.5,1-3,2-3
# this may not be what you want
# (result is a silent ch1, and ch2 with 50% of ch3)
song#m1+2*0.5,1u
# for a 2ch file 2nd command is ignored, since ch2 is removed after 1st command
song#m1d,2+1*0.5
```

View File

@ -269,13 +269,8 @@ bool input_vgmstream::decode_run(audio_chunk & p_chunk,abort_callback & p_abort)
/* fade! */
if (vgmstream->loop_flag && fade_samples > 0 && !loop_okay) {
int fade_channels;
int fade_channels = output_channels;
int samples_into_fade = decode_pos_samples - (stream_length_samples - fade_samples);
#ifdef VGMSTREAM_MIXING
fade_channels = output_channels;
#else
fade_channels = vgmstream->channels;
#endif
if (samples_into_fade + samples_to_do > 0) {
int j,k;
for (j=0;j<samples_to_do;j++,samples_into_fade++) {
@ -290,49 +285,11 @@ bool input_vgmstream::decode_run(audio_chunk & p_chunk,abort_callback & p_abort)
}
}
#ifdef VGMSTREAM_MIXING
unsigned channel_config = vgmstream->channel_layout;
if (!channel_config)
channel_config = audio_chunk::g_guess_channel_config(output_channels);
bytes = (samples_to_do*output_channels * sizeof(sample_buffer[0]));
p_chunk.set_data_fixedpoint((char*)sample_buffer, bytes, vgmstream->sample_rate, output_channels, 16, channel_config);
#else
/* downmix enabled (foobar refuses to do more than 8 channels) */
if (downmix_channels > 0 && downmix_channels < vgmstream->channels) {
short temp_buffer[SAMPLE_BUFFER_SIZE * VGMSTREAM_MAX_CHANNELS];
int s, ch;
for (s = 0; s < samples_to_do; s++) {
/* copy channels up to max */
for (ch = 0; ch < downmix_channels; ch++) {
temp_buffer[s*downmix_channels + ch] = sample_buffer[s*vgmstream->channels + ch];
}
/* then mix the rest */
for (ch = downmix_channels; ch < vgmstream->channels; ch++) {
int downmix_ch = ch % downmix_channels;
int new_sample = ((int)temp_buffer[s*downmix_channels + downmix_ch] + (int)sample_buffer[s*vgmstream->channels + ch]);
new_sample = (int)(new_sample * 0.7); /* limit clipping without removing too much loudness... hopefully */
if (new_sample > 32767) new_sample = 32767;
else if (new_sample < -32768) new_sample = -32768;
temp_buffer[s*downmix_channels + downmix_ch] = (short)new_sample;
}
}
/* copy back to global buffer... in case of multithreading stuff? */
memcpy(sample_buffer,temp_buffer, samples_to_do*downmix_channels*sizeof(short));
unsigned channel_config = audio_chunk::g_guess_channel_config(downmix_channels);
bytes = (samples_to_do*downmix_channels * sizeof(sample_buffer[0]));
p_chunk.set_data_fixedpoint((char*)sample_buffer, bytes, vgmstream->sample_rate, downmix_channels, 16, channel_config);
}
else {
unsigned channel_config = vgmstream->channel_layout;
if (!channel_config)
channel_config = audio_chunk::g_guess_channel_config(vgmstream->channels);
bytes = (samples_to_do*vgmstream->channels * sizeof(sample_buffer[0]));
p_chunk.set_data_fixedpoint((char*)sample_buffer, bytes, vgmstream->sample_rate, vgmstream->channels, 16, channel_config);
}
#endif
decode_pos_samples+=samples_to_do;
decode_pos_ms=decode_pos_samples*1000LL/vgmstream->sample_rate;
@ -471,13 +428,10 @@ void input_vgmstream::setup_vgmstream(abort_callback & p_abort) {
set_config_defaults(&config);
apply_config(vgmstream, &config);
#ifdef VGMSTREAM_MIXING
/* enable after all config but before outbuf (though ATM outbuf is not dynamic so no need to read input_channels) */
vgmstream_mixing_autodownmix(vgmstream, downmix_channels);
vgmstream_mixing_enable(vgmstream, SAMPLE_BUFFER_SIZE, NULL /*&input_channels*/, &output_channels);
#else
output_channels = vgmstream->channels;
#endif
decode_pos_ms = 0;
decode_pos_samples = 0;
paused = 0;
@ -503,12 +457,9 @@ void input_vgmstream::get_subsong_info(t_uint32 p_subsong, pfc::string_base & ti
set_config_defaults(&infoconfig);
apply_config(infostream,&infoconfig);
is_infostream = true;
#ifdef VGMSTREAM_MIXING
vgmstream_mixing_autodownmix(infostream, downmix_channels);
vgmstream_mixing_enable(infostream, 0, NULL /*&input_channels*/, &info_channels);
#else
*channels = infostream->channels;
#endif
} else {
// vgmstream ready as get_info is valid after open() with any reason
infostream = vgmstream;

View File

@ -1,8 +1,6 @@
#include "layout.h"
#include "../vgmstream.h"
#ifdef VGMSTREAM_MIXING
#include "../mixing.h"
#endif
/* NOTE: if loop settings change the layered vgmstreams must be notified (preferably using vgmstream_force_loop) */
@ -31,12 +29,9 @@ void render_vgmstream_layered(sample_t * outbuf, int32_t sample_count, VGMSTREAM
/* each layer will handle its own looping/mixing internally */
#ifdef VGMSTREAM_MIXING
/* layers may have its own number of channels */
mixing_info(data->layers[layer], NULL, &layer_channels);
#else
layer_channels = data->layers[layer]->channels;
#endif
render_vgmstream(
data->buffer,
samples_to_do,
@ -96,12 +91,9 @@ int setup_layout_layered(layered_layout_data* data) {
if (data->layers[i]->num_samples <= 0)
goto fail;
#ifdef VGMSTREAM_MIXING
/* different layers may have different input/output channels */
mixing_info(data->layers[i], &layer_input_channels, &layer_output_channels);
#else
layer_input_channels = layer_output_channels = data->layers[i]->channels;
#endif
max_output_channels += layer_output_channels;
if (max_input_channels < layer_input_channels)
max_input_channels = layer_input_channels;
@ -122,9 +114,8 @@ int setup_layout_layered(layered_layout_data* data) {
setup_vgmstream(data->layers[i]); /* final setup in case the VGMSTREAM was created manually */
#ifdef VGMSTREAM_MIXING
mixing_setup(data->layers[i], VGMSTREAM_LAYER_SAMPLE_BUFFER); /* init mixing */
#endif
}
if (max_output_channels > VGMSTREAM_MAX_CHANNELS || max_input_channels > VGMSTREAM_MAX_CHANNELS)

View File

@ -1,8 +1,6 @@
#include "layout.h"
#include "../vgmstream.h"
#ifdef VGMSTREAM_MIXING
#include "../mixing.h"
#endif
#define VGMSTREAM_MAX_SEGMENTS 255
#define VGMSTREAM_SEGMENT_SAMPLE_BUFFER 8192
@ -149,13 +147,9 @@ int setup_layout_segmented(segmented_layout_data* data) {
data->segments[i]->loop_flag = 0;
}
#ifdef VGMSTREAM_MIXING
/* different segments may have different input channels, though output should be
* the same for all (ex. 2ch + 1ch segments, but 2ch segment is downmixed to 1ch) */
mixing_info(data->segments[i], &segment_input_channels, &segment_output_channels);
#else
segment_input_channels = segment_output_channels = data->segments[i]->channels;
#endif
if (max_input_channels < segment_input_channels)
max_input_channels = segment_input_channels;
if (max_output_channels < segment_output_channels)
@ -164,11 +158,7 @@ int setup_layout_segmented(segmented_layout_data* data) {
if (i > 0) {
int prev_output_channels;
#ifdef VGMSTREAM_MIXING
mixing_info(data->segments[i-1], NULL, &prev_output_channels);
#else
prev_output_channels = data->segments[i-1]->channels;
#endif
if (segment_output_channels != prev_output_channels)
goto fail;
@ -184,9 +174,8 @@ int setup_layout_segmented(segmented_layout_data* data) {
setup_vgmstream(data->segments[i]); /* final setup in case the VGMSTREAM was created manually */
#ifdef VGMSTREAM_MIXING
mixing_setup(data->segments[i], VGMSTREAM_SEGMENT_SAMPLE_BUFFER); /* init mixing */
#endif
}
if (max_output_channels > VGMSTREAM_MAX_CHANNELS || max_input_channels > VGMSTREAM_MAX_CHANNELS)

View File

@ -1,15 +1,12 @@
#include "meta.h"
#include "../coding/coding.h"
#include "../layout/layout.h"
#ifdef VGMSTREAM_MIXING
#include "../mixing.h"
#endif
#define TXTP_LINE_MAX 1024
#define TXTP_MIXING_MAX 128
#ifdef VGMSTREAM_MIXING
/* mixing info */
typedef enum {
MIX_SWAP,
@ -55,21 +52,14 @@ typedef struct {
uint32_t mask;
char mode;
} txtp_mix_data;
#endif
typedef struct {
char filename[TXTP_LINE_MAX];
int subsong;
uint32_t channel_mask;
#ifndef VGMSTREAM_MIXING
int channel_mappings_on;
int channel_mappings[32];
#endif
#ifdef VGMSTREAM_MIXING
int mixing_count;
txtp_mix_data mixing[TXTP_MIXING_MAX];
#endif
double config_loop_count;
double config_fade_time;
@ -106,9 +96,7 @@ typedef struct {
static txtp_header* parse_txtp(STREAMFILE* streamFile);
static void clean_txtp(txtp_header* txtp);
static void apply_config(VGMSTREAM *vgmstream, txtp_entry *current);
#ifdef VGMSTREAM_MIXING
void add_mixing(txtp_entry* cfg, txtp_mix_data* mix, txtp_mix_t command);
#endif
/* TXTP - an artificial playlist-like format to play files with segments/layers/config */
VGMSTREAM * init_vgmstream_txtp(STREAMFILE *streamFile) {
@ -263,17 +251,6 @@ fail:
}
static void apply_config(VGMSTREAM *vgmstream, txtp_entry *current) {
#ifndef VGMSTREAM_MIXING
vgmstream->channel_mask = current->channel_mask;
vgmstream->channel_mappings_on = current->channel_mappings_on;
if (vgmstream->channel_mappings_on) {
int ch;
for (ch = 0; ch < 32; ch++) {
vgmstream->channel_mappings[ch] = current->channel_mappings[ch];
}
}
#endif
vgmstream->config_loop_count = current->config_loop_count;
vgmstream->config_fade_time = current->config_fade_time;
@ -302,7 +279,6 @@ static void apply_config(VGMSTREAM *vgmstream, txtp_entry *current) {
vgmstream_force_loop(vgmstream, current->loop_install, current->loop_start_sample, current->loop_end_sample);
}
#ifdef VGMSTREAM_MIXING
/* add macro to mixing list */
if (current->channel_mask) {
int ch;
@ -361,7 +337,6 @@ static void apply_config(VGMSTREAM *vgmstream, txtp_entry *current) {
}
}
}
#endif
}
/* ********************************** */
@ -537,7 +512,6 @@ static int get_mask(const char * config, uint32_t *value) {
}
#ifdef VGMSTREAM_MIXING
static int get_fade(const char * config, txtp_mix_data *mix, int *out_n) {
int n, m, tn = 0;
char type, separator;
@ -634,9 +608,7 @@ static int get_fade(const char * config, txtp_mix_data *mix, int *out_n) {
fail:
return 0;
}
#endif
#ifdef VGMSTREAM_MIXING
void add_mixing(txtp_entry* cfg, txtp_mix_data* mix, txtp_mix_t command) {
if (cfg->mixing_count + 1 > TXTP_MIXING_MAX) {
VGM_LOG("TXTP: too many mixes\n");
@ -652,7 +624,6 @@ void add_mixing(txtp_entry* cfg, txtp_mix_data* mix, txtp_mix_t command) {
cfg->mixing[cfg->mixing_count] = *mix; /* memcpy'ed */
cfg->mixing_count++;
}
#endif
static void add_config(txtp_entry* current, txtp_entry* cfg, const char* filename) {
@ -662,16 +633,6 @@ static void add_config(txtp_entry* current, txtp_entry* cfg, const char* filenam
current->channel_mask = cfg->channel_mask;
#ifndef VGMSTREAM_MIXING
if (cfg->channel_mappings_on) {
int ch;
current->channel_mappings_on = cfg->channel_mappings_on;
for (ch = 0; ch < 32; ch++) {
current->channel_mappings[ch] = cfg->channel_mappings[ch];
}
}
#endif
#ifdef VGMSTREAM_MIXING
//*current = *cfg; /* don't memcopy to allow list additions */ //todo save list first then memcpy
if (cfg->mixing_count > 0) {
@ -681,7 +642,6 @@ static void add_config(txtp_entry* current, txtp_entry* cfg, const char* filenam
current->mixing_count++;
}
}
#endif
current->config_loop_count = cfg->config_loop_count;
current->config_fade_time = cfg->config_fade_time;
@ -752,33 +712,6 @@ static int add_filename(txtp_header * txtp, char *filename, int is_default) {
config += get_mask(config, &cfg.channel_mask);
//;VGM_LOG("TXTP: channel_mask ");{int i; for (i=0;i<16;i++)VGM_LOG("%i ",(cfg.channel_mask>>i)&1);}VGM_LOG("\n");
}
#ifndef VGMSTREAM_MIXING
else if (strcmp(command,"m") == 0) {
/* channel mappings: file.ext#m1-2,3-4 = swaps channels 1<>2 and 3<>4 */
int ch_from = 0, ch_to = 0;
cfg.channel_mappings_on = 1;
while (config[0] != '\0') {
if (sscanf(config, " %d%n", &ch_from, &n) != 1)
break;
config += n;
if (config[0]== ',' || config[0]== '-')
config++;
if (sscanf(config, " %d%n", &ch_to, &n) != 1)
break;
config += n;
if (config[0]== ',' || config[0]== '-')
config++;
if (ch_from > 0 && ch_from <= 32 && ch_to > 0 && ch_to <= 32) {
cfg.channel_mappings[ch_from-1] = ch_to-1;
}
//;VGM_LOG("TXTP: channel_swap %i-%i\n", ch_from, ch_to);
}
}
#endif
#ifdef VGMSTREAM_MIXING
else if (strcmp(command,"m") == 0) {
/* channel mixing: file.ext#m(sub-command),(sub-command),etc */
char cmd;
@ -864,7 +797,6 @@ static int add_filename(txtp_header * txtp, char *filename, int is_default) {
break; /* unknown mix/new command/end */
}
}
#endif
else if (strcmp(command,"s") == 0 || (nc == 1 && config[0] >= '0' && config[0] <= '9')) {
/* subsongs: file.ext#s2 = play subsong 2, file.ext#2~10 = play subsong range */
int subsong_start = 0, subsong_end = 0;
@ -933,7 +865,6 @@ static int add_filename(txtp_header * txtp, char *filename, int is_default) {
//;VGM_LOG("TXTP: loop_install %i (max=%i): %i %i / %f %f\n", cfg.loop_install, cfg.loop_end_max,
// cfg.loop_start_sample, cfg.loop_end_sample, cfg.loop_start_second, cfg.loop_end_second);
}
#ifdef VGMSTREAM_MIXING
//todo cleanup
else if (strcmp(command,"@volume") == 0) {
txtp_mix_data mix = {0};
@ -992,7 +923,6 @@ static int add_filename(txtp_header * txtp, char *filename, int is_default) {
add_mixing(&cfg, &mix, type);
}
#endif
else if (config[nc] == ' ') {
//;VGM_LOG("TXTP: comment\n");
break; /* comment, ignore rest */

View File

@ -4,8 +4,6 @@
#include <math.h>
#ifdef VGMSTREAM_MIXING
/**
* Mixing lets vgmstream modify the resulting sample buffer before final output.
* This can be implemented in a number of ways but it's done like it is considering
@ -978,5 +976,3 @@ void mixing_info(VGMSTREAM * vgmstream, int *out_input_channels, int *out_output
fail:
return;
}
#endif

View File

@ -1,8 +1,6 @@
#include "vgmstream.h"
#include "plugins.h"
#ifdef VGMSTREAM_MIXING
#include "mixing.h"
#endif
#define VGMSTREAM_TAGS_LINE_MAX 2048
@ -227,7 +225,6 @@ void vgmstream_tags_reset(VGMSTREAM_TAGS* tags, const char* target_filename) {
}
}
#ifdef VGMSTREAM_MIXING
void vgmstream_mixing_enable(VGMSTREAM* vgmstream, int32_t max_sample_count, int *input_channels, int *output_channels) {
mixing_setup(vgmstream, max_sample_count);
mixing_info(vgmstream, input_channels, output_channels);
@ -246,4 +243,3 @@ void vgmstream_mixing_autodownmix(VGMSTREAM *vgmstream, int max_channels) {
return;
}
#endif

View File

@ -76,7 +76,6 @@ int vgmstream_tags_next_tag(VGMSTREAM_TAGS* tags, STREAMFILE* tagfile);
void vgmstream_tags_close(VGMSTREAM_TAGS* tags);
#ifdef VGMSTREAM_MIXING
/* ****************************************** */
/* MIXING: modifies vgmstream output */
/* ****************************************** */
@ -92,6 +91,5 @@ void vgmstream_mixing_autodownmix(VGMSTREAM *vgmstream, int max_channels);
/* sets a fadeout */
//void vgmstream_mixing_fadeout(VGMSTREAM *vgmstream, float start_second, float duration_seconds);
#endif
#endif /* _PLUGINS_H_ */

View File

@ -9,9 +9,7 @@
#include "meta/meta.h"
#include "layout/layout.h"
#include "coding/coding.h"
#ifdef VGMSTREAM_MIXING
#include "mixing.h"
#endif
static void try_dual_file_stereo(VGMSTREAM * opened_vgmstream, STREAMFILE *streamFile, VGMSTREAM* (*init_vgmstream_function)(STREAMFILE*));
@ -780,20 +778,17 @@ VGMSTREAM * allocate_vgmstream(int channel_count, int loop_flag) {
vgmstream->channels = channel_count;
vgmstream->loop_flag = loop_flag;
#ifdef VGMSTREAM_MIXING
mixing_init(vgmstream); /* pre-init */
#endif
//vgmstream->stream_name_size = STREAM_NAME_SIZE;
return vgmstream;
fail:
if (vgmstream) {
mixing_close(vgmstream);
free(vgmstream->ch);
free(vgmstream->start_ch);
free(vgmstream->loop_ch);
free(vgmstream->start_vgmstream);
#ifdef VGMSTREAM_MIXING
mixing_close(vgmstream);
#endif
}
free(vgmstream);
return NULL;
@ -932,9 +927,8 @@ void close_vgmstream(VGMSTREAM * vgmstream) {
}
}
}
#ifdef VGMSTREAM_MIXING
mixing_close(vgmstream);
#endif
free(vgmstream->ch);
free(vgmstream->start_ch);
free(vgmstream->loop_ch);
@ -1084,44 +1078,7 @@ void render_vgmstream(sample * buffer, int32_t sample_count, VGMSTREAM * vgmstre
break;
}
#ifndef VGMSTREAM_MIXING
/* swap channels if set, to create custom channel mappings */
if (vgmstream->channel_mappings_on) {
int ch_from,ch_to,s;
sample_t temp;
for (s = 0; s < sample_count; s++) {
for (ch_from = 0; ch_from < vgmstream->channels; ch_from++) {
if (ch_from > 32)
continue;
ch_to = vgmstream->channel_mappings[ch_from];
if (ch_to < 1 || ch_to > 32 || ch_to > vgmstream->channels-1 || ch_from == ch_to)
continue;
temp = buffer[s*vgmstream->channels + ch_from];
buffer[s*vgmstream->channels + ch_from] = buffer[s*vgmstream->channels + ch_to];
buffer[s*vgmstream->channels + ch_to] = temp;
}
}
}
/* channel bitmask to silence non-set channels (up to 32)
* can be used for 'crossfading subsongs' or layered channels, where a set of channels make a song section */
if (vgmstream->channel_mask) {
int ch,s;
for (s = 0; s < sample_count; s++) {
for (ch = 0; ch < vgmstream->channels; ch++) {
if ((vgmstream->channel_mask >> ch) & 1)
continue;
buffer[s*vgmstream->channels + ch] = 0;
}
}
}
#endif
#ifdef VGMSTREAM_MIXING
mix_vgmstream(buffer, sample_count, vgmstream);
#endif
}
/* Get the number of samples of a single frame (smallest self-contained sample group, 1/N channels) */
@ -2317,7 +2274,6 @@ void describe_vgmstream(VGMSTREAM * vgmstream, char * desc, int length) {
snprintf(temp,TEMPSIZE, "channels: %d\n", vgmstream->channels);
concatn(length,desc,temp);
#ifdef VGMSTREAM_MIXING
{
int output_channels = 0;
mixing_info(vgmstream, NULL, &output_channels);
@ -2329,7 +2285,6 @@ void describe_vgmstream(VGMSTREAM * vgmstream, char * desc, int length) {
concatn(length,desc,temp);
}
}
#endif
if (vgmstream->channel_layout) {
int cl = vgmstream->channel_layout;
@ -2682,9 +2637,7 @@ static void try_dual_file_stereo(VGMSTREAM * opened_vgmstream, STREAMFILE *strea
/* discard the second VGMSTREAM */
free(new_vgmstream);
#ifdef VGMSTREAM_MIXING
mixing_update_channel(opened_vgmstream); /* notify of new channel hacked-in */
#endif
}
fail:
@ -2838,6 +2791,12 @@ int vgmstream_open_stream(VGMSTREAM * vgmstream, STREAMFILE *streamFile, off_t s
int is_stereo_codec = 0;
if (vgmstream == NULL) {
VGM_LOG("VGMSTREAM: buggy code (null vgmstream)\n");
goto fail;
}
/* stream/offsets not needed, managed by layout */
if (vgmstream->layout_type == layout_segmented ||
vgmstream->layout_type == layout_layered)
@ -2877,6 +2836,12 @@ int vgmstream_open_stream(VGMSTREAM * vgmstream, STREAMFILE *streamFile, off_t s
is_stereo_codec = 1;
}
if (streamFile == NULL || start_offset < 0) {
VGM_LOG("VGMSTREAM: buggy code (null streamfile / wrong start_offset)\n");
goto fail;
}
get_streamfile_name(streamFile,filename,sizeof(filename));
/* open the file for reading by each channel */
{

View File

@ -854,11 +854,6 @@ typedef struct {
/* other config */
int allow_dual_stereo; /* search for dual stereo (file_L.ext + file_R.ext = single stereo file) */
#ifndef VGMSTREAM_MIXING
uint32_t channel_mask; /* to silence crossfading subsongs/layers */
int channel_mappings_on; /* channel mappings are active */
int channel_mappings[32]; /* swap channel "i" with "[i]" */
#endif
/* config requests, players must read and honor these values */
/* (ideally internally would work as a player, but for now player must do it manually) */
@ -903,9 +898,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) */
#ifdef VGMSTREAM_MIXING
void * mixing_data; /* state for mixing effects */
#endif
/* Optional data the codec needs for the whole stream. This is for codecs too
* different from vgmstream's structure to be reasonably shoehorned.

View File

@ -1054,15 +1054,9 @@ int winamp_Play(const in_char *fn) {
set_config_defaults(&config);
apply_config(vgmstream, &config);
#ifdef VGMSTREAM_MIXING
/* enable after all config but before outbuf (though ATM outbuf is not dynamic so no need to read input_channels) */
vgmstream_mixing_autodownmix(vgmstream, settings.downmix_channels);
vgmstream_mixing_enable(vgmstream, SAMPLE_BUFFER_SIZE, NULL /*&input_channels*/, &output_channels);
#else
output_channels = vgmstream->channels;
if (settings.downmix_channels > 0 && settings.downmix_channels < vgmstream->channels)
output_channels = settings.downmix_channels;
#endif
/* save original name */
@ -1200,10 +1194,9 @@ int winamp_InfoBox(const in_char *fn, HWND hwnd) {
set_config_defaults(&infoconfig);
apply_config(infostream, &infoconfig);
#ifdef VGMSTREAM_MIXING
vgmstream_mixing_autodownmix(infostream, settings.downmix_channels);
vgmstream_mixing_enable(infostream, 0, NULL, NULL);
#endif
describe_vgmstream(infostream,description,description_size);
@ -1255,10 +1248,9 @@ void winamp_GetFileInfo(const in_char *fn, in_char *title, int *length_in_ms) {
set_config_defaults(&infoconfig);
apply_config(infostream, &infoconfig);
#ifdef VGMSTREAM_MIXING
vgmstream_mixing_autodownmix(infostream, settings.downmix_channels);
//vgmstream_mixing_enable(infostream, SAMPLE_BUFFER_SIZE, NULL, NULL);
#endif
vgmstream_mixing_enable(infostream, 0, NULL, NULL);
if (title) {
get_title(title,GETFILEINFO_TITLE_LENGTH, fn, infostream);
@ -1352,13 +1344,8 @@ DWORD WINAPI __stdcall decode(void *arg) {
/* fade near the end */
if (vgmstream->loop_flag && fade_samples > 0 && !settings.loop_forever) {
int fade_channels;
int fade_channels = output_channels;
int samples_into_fade = decode_pos_samples - (stream_length_samples - fade_samples);
#ifdef VGMSTREAM_MIXING
fade_channels = output_channels;
#else
fade_channels = vgmstream->channels;
#endif
if (samples_into_fade + samples_to_do > 0) {
int j, k;
for (j = 0; j < samples_to_do; j++, samples_into_fade++) {
@ -1373,33 +1360,6 @@ DWORD WINAPI __stdcall decode(void *arg) {
}
}
#ifndef VGMSTREAM_MIXING
/* downmix enabled (useful when the stream's channels are too much for Winamp's output) */
if (settings.downmix_channels > 0 && settings.downmix_channels < vgmstream->channels) {
short temp_buffer[(576*2) * 2];
int s, ch;
for (s = 0; s < samples_to_do; s++) {
/* copy channels up to max */
for (ch = 0; ch < settings.downmix_channels; ch++) {
temp_buffer[s*settings.downmix_channels + ch] = sample_buffer[s*vgmstream->channels + ch];
}
/* then mix the rest */
for (ch = settings.downmix_channels; ch < vgmstream->channels; ch++) {
int downmix_ch = ch % settings.downmix_channels;
int new_sample = ((int)temp_buffer[s*settings.downmix_channels + downmix_ch] + (int)sample_buffer[s*vgmstream->channels + ch]);
new_sample = (int)(new_sample * 0.7); /* limit clipping without removing too much loudness... hopefully */
if (new_sample > 32767) new_sample = 32767;
else if (new_sample < -32768) new_sample = -32768;
temp_buffer[s*settings.downmix_channels + downmix_ch] = (short)new_sample;
}
}
/* copy back to global buffer... in case of multithreading stuff? */
memcpy(sample_buffer,temp_buffer, samples_to_do*settings.downmix_channels*sizeof(short));
}
#endif
/* output samples */
input_module.SAAddPCMData((char*)sample_buffer,output_channels,16,decode_pos_ms);
input_module.VSAAddPCMData((char*)sample_buffer,output_channels,16,decode_pos_ms);