mirror of
https://github.com/vgmstream/vgmstream.git
synced 2025-02-17 11:18:31 +01:00
Enable TXTP mixing
This commit is contained in:
parent
248ec056e3
commit
8739631792
@ -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.");
|
||||
|
245
doc/TXTP.md
245
doc/TXTP.md
@ -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
|
||||
```
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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 */
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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_ */
|
||||
|
@ -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 */
|
||||
{
|
||||
|
@ -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.
|
||||
|
@ -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);
|
||||
|
Loading…
x
Reference in New Issue
Block a user