mirror of
https://github.com/vgmstream/vgmstream.git
synced 2024-11-12 01:30:49 +01:00
Add mixing code for foobar and fix loop counts during seek
This commit is contained in:
parent
da4b31c0d3
commit
d3fc30d408
@ -43,6 +43,7 @@ input_vgmstream::input_vgmstream() {
|
||||
vgmstream = NULL;
|
||||
subsong = 0; // 0 = not set, will be properly changed on first setup_vgmstream
|
||||
direct_subsong = false;
|
||||
output_channels = 0;
|
||||
|
||||
decoding = false;
|
||||
paused = 0;
|
||||
@ -60,7 +61,7 @@ input_vgmstream::input_vgmstream() {
|
||||
disable_subsongs = false;
|
||||
downmix_channels = 0;
|
||||
tagfile_disable = false;
|
||||
tagfile_name = "!tags.m3u"; //todo make configurable
|
||||
tagfile_name = "!tags.m3u";
|
||||
override_title = false;
|
||||
|
||||
load_settings();
|
||||
@ -221,6 +222,9 @@ void input_vgmstream::get_info(t_uint32 p_subsong, file_info & p_info, abort_cal
|
||||
if (get_description_tag(temp,description,"stream name: ")) p_info.info_set("stream_name",temp);
|
||||
|
||||
if (get_description_tag(temp,description,"channel mask: ")) p_info.info_set("channel_mask",temp);
|
||||
if (get_description_tag(temp,description,"output channels: ")) p_info.info_set("output_channels",temp);
|
||||
if (get_description_tag(temp,description,"input channels: ")) p_info.info_set("input_channels",temp);
|
||||
|
||||
}
|
||||
|
||||
t_filestats input_vgmstream::get_file_stats(abort_callback & p_abort) {
|
||||
@ -244,16 +248,16 @@ bool input_vgmstream::decode_run(audio_chunk & p_chunk,abort_callback & p_abort)
|
||||
if (!decoding) return false;
|
||||
if (!vgmstream) return false;
|
||||
|
||||
int max_buffer_samples = sizeof(sample_buffer)/sizeof(sample_buffer[0])/vgmstream->channels;
|
||||
int max_buffer_samples = SAMPLE_BUFFER_SIZE;
|
||||
int samples_to_do = max_buffer_samples;
|
||||
t_size bytes;
|
||||
|
||||
{
|
||||
bool loop_okay = config.song_play_forever && vgmstream->loop_flag && !config.song_ignore_loop && !force_ignore_loop;
|
||||
if (decode_pos_samples+max_buffer_samples>stream_length_samples && !loop_okay)
|
||||
samples_to_do=stream_length_samples-decode_pos_samples;
|
||||
if (decode_pos_samples + max_buffer_samples > stream_length_samples && !loop_okay)
|
||||
samples_to_do = stream_length_samples - decode_pos_samples;
|
||||
else
|
||||
samples_to_do=max_buffer_samples;
|
||||
samples_to_do = max_buffer_samples;
|
||||
|
||||
if (samples_to_do /*< DECODE_SIZE*/ == 0) {
|
||||
decoding = false;
|
||||
@ -265,24 +269,37 @@ 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 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++) {
|
||||
if (samples_into_fade > 0) {
|
||||
double fadedness = (double)(fade_samples-samples_into_fade)/fade_samples;
|
||||
for (k=0;k<vgmstream->channels;k++) {
|
||||
sample_buffer[j*vgmstream->channels+k] =
|
||||
(short)(sample_buffer[j*vgmstream->channels+k]*fadedness);
|
||||
for (k = 0; k < fade_channels; k++) {
|
||||
sample_buffer[j*fade_channels+k] =
|
||||
(short)(sample_buffer[j*fade_channels+k]*fadedness);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#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];
|
||||
short temp_buffer[SAMPLE_BUFFER_SIZE * VGMSTREAM_MAX_CHANNELS];
|
||||
int s, ch;
|
||||
|
||||
for (s = 0; s < samples_to_do; s++) {
|
||||
@ -315,7 +332,7 @@ bool input_vgmstream::decode_run(audio_chunk & p_chunk,abort_callback & p_abort)
|
||||
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;
|
||||
@ -326,57 +343,62 @@ bool input_vgmstream::decode_run(audio_chunk & p_chunk,abort_callback & p_abort)
|
||||
|
||||
void input_vgmstream::decode_seek(double p_seconds,abort_callback & p_abort) {
|
||||
seek_pos_samples = (int) audio_math::time_to_samples(p_seconds, vgmstream->sample_rate);
|
||||
int max_buffer_samples = sizeof(sample_buffer)/sizeof(sample_buffer[0])/vgmstream->channels;
|
||||
int max_buffer_samples = SAMPLE_BUFFER_SIZE;
|
||||
bool loop_okay = config.song_play_forever && vgmstream->loop_flag && !config.song_ignore_loop && !force_ignore_loop;
|
||||
int loop_skips = 0;
|
||||
|
||||
// possible when disabling looping without refreshing foobar's cached song length
|
||||
// (with infinite looping on p_seconds can't go over seek bar though)
|
||||
if(seek_pos_samples > stream_length_samples)
|
||||
if (seek_pos_samples > stream_length_samples)
|
||||
seek_pos_samples = stream_length_samples;
|
||||
|
||||
int corrected_pos_samples = seek_pos_samples;
|
||||
|
||||
// adjust for correct position within loop
|
||||
if(vgmstream->loop_flag && (vgmstream->loop_end_sample - vgmstream->loop_start_sample) && seek_pos_samples >= vgmstream->loop_end_sample) {
|
||||
// optimize seeks withing loops
|
||||
if (vgmstream->loop_flag && (vgmstream->loop_end_sample - vgmstream->loop_start_sample) && seek_pos_samples >= vgmstream->loop_end_sample) {
|
||||
int loop_length = (vgmstream->loop_end_sample - vgmstream->loop_start_sample);
|
||||
|
||||
corrected_pos_samples -= vgmstream->loop_start_sample;
|
||||
corrected_pos_samples %= (vgmstream->loop_end_sample - vgmstream->loop_start_sample);
|
||||
loop_skips = corrected_pos_samples / loop_length;
|
||||
corrected_pos_samples %= loop_length;
|
||||
corrected_pos_samples += vgmstream->loop_start_sample;
|
||||
}
|
||||
|
||||
// Allow for delta seeks forward, by up to the total length of the stream, if the delta is less than the corrected offset
|
||||
if(decode_pos_samples > corrected_pos_samples && decode_pos_samples <= seek_pos_samples &&
|
||||
(seek_pos_samples - decode_pos_samples) < stream_length_samples) {
|
||||
if (decode_pos_samples > corrected_pos_samples && decode_pos_samples <= seek_pos_samples &&
|
||||
(seek_pos_samples - decode_pos_samples) < stream_length_samples) {
|
||||
if (corrected_pos_samples > (seek_pos_samples - decode_pos_samples))
|
||||
corrected_pos_samples = seek_pos_samples;
|
||||
}
|
||||
|
||||
// Reset of backwards seek
|
||||
else if(corrected_pos_samples < decode_pos_samples) {
|
||||
reset_vgmstream(vgmstream);
|
||||
apply_config(vgmstream, &config); /* config is undone by reset */
|
||||
|
||||
decode_pos_samples = 0;
|
||||
}
|
||||
|
||||
// seeking overrun = bad
|
||||
if(corrected_pos_samples > stream_length_samples)
|
||||
if (corrected_pos_samples > stream_length_samples)
|
||||
corrected_pos_samples = stream_length_samples;
|
||||
|
||||
while(decode_pos_samples<corrected_pos_samples) {
|
||||
while(decode_pos_samples < corrected_pos_samples) {
|
||||
int seek_samples = max_buffer_samples;
|
||||
if((decode_pos_samples+max_buffer_samples>=stream_length_samples) && !loop_okay)
|
||||
seek_samples=stream_length_samples-seek_pos_samples;
|
||||
if(decode_pos_samples+max_buffer_samples>seek_pos_samples)
|
||||
seek_samples=seek_pos_samples-decode_pos_samples;
|
||||
if((decode_pos_samples + max_buffer_samples >= stream_length_samples) && !loop_okay)
|
||||
seek_samples = stream_length_samples - seek_pos_samples;
|
||||
if(decode_pos_samples + max_buffer_samples > seek_pos_samples)
|
||||
seek_samples = seek_pos_samples - decode_pos_samples;
|
||||
|
||||
decode_pos_samples+=seek_samples;
|
||||
render_vgmstream(sample_buffer,seek_samples,vgmstream);
|
||||
decode_pos_samples += seek_samples;
|
||||
render_vgmstream(sample_buffer, seek_samples, vgmstream);
|
||||
}
|
||||
|
||||
// remove seek loop correction from counter so file ends correctly
|
||||
decode_pos_samples=seek_pos_samples;
|
||||
// seek may have been clamped to skip unneeded loops, adjust as some internals need this value
|
||||
vgmstream->loop_count += loop_skips; //todo evil, make seek_vgmstream
|
||||
|
||||
decode_pos_ms=decode_pos_samples*1000LL/vgmstream->sample_rate;
|
||||
// remove seek loop correction from counter so file ends correctly
|
||||
decode_pos_samples = seek_pos_samples;
|
||||
|
||||
decode_pos_ms = decode_pos_samples * 1000LL / vgmstream->sample_rate;
|
||||
|
||||
decoding = loop_okay || decode_pos_samples < stream_length_samples;
|
||||
}
|
||||
@ -449,6 +471,13 @@ 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;
|
||||
@ -461,6 +490,7 @@ void input_vgmstream::get_subsong_info(t_uint32 p_subsong, pfc::string_base & ti
|
||||
bool is_infostream = false;
|
||||
foobar_song_config infoconfig;
|
||||
char temp[1024];
|
||||
int info_channels;
|
||||
|
||||
// reuse current vgmstream if not querying a new subsong
|
||||
// if it's a direct subsong then subsong may be N while p_subsong 1
|
||||
@ -473,26 +503,34 @@ 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;
|
||||
infoconfig = config;
|
||||
info_channels = output_channels;
|
||||
}
|
||||
|
||||
|
||||
if (length_in_ms) {
|
||||
*length_in_ms = -1000;
|
||||
if (infostream) {
|
||||
int num_samples = get_vgmstream_play_samples(infoconfig.song_loop_count,infoconfig.song_fade_time,infoconfig.song_fade_delay,infostream);
|
||||
*length_in_ms = num_samples*1000LL / infostream->sample_rate;
|
||||
*channels = info_channels;
|
||||
*sample_rate = infostream->sample_rate;
|
||||
*channels = infostream->channels;
|
||||
*total_samples = infostream->num_samples;
|
||||
*bitrate = get_vgmstream_average_bitrate(infostream);
|
||||
*loop_flag = infostream->loop_flag;
|
||||
*loop_start = infostream->loop_start_sample;
|
||||
*loop_end = infostream->loop_end_sample;
|
||||
|
||||
int num_samples = get_vgmstream_play_samples(infoconfig.song_loop_count,infoconfig.song_fade_time,infoconfig.song_fade_delay,infostream);
|
||||
*length_in_ms = num_samples*1000LL / infostream->sample_rate;
|
||||
|
||||
char temp[1024];
|
||||
describe_vgmstream(infostream, temp, 1024);
|
||||
description = temp;
|
||||
|
@ -55,6 +55,7 @@ class input_vgmstream : public input_stubs {
|
||||
VGMSTREAM * vgmstream;
|
||||
t_uint32 subsong;
|
||||
bool direct_subsong;
|
||||
int output_channels;
|
||||
|
||||
bool decoding;
|
||||
int paused;
|
||||
@ -63,7 +64,7 @@ class input_vgmstream : public input_stubs {
|
||||
int stream_length_samples;
|
||||
int fade_samples;
|
||||
int seek_pos_samples;
|
||||
short sample_buffer[SAMPLE_BUFFER_SIZE];
|
||||
short sample_buffer[SAMPLE_BUFFER_SIZE * VGMSTREAM_MAX_CHANNELS];
|
||||
|
||||
/* settings */
|
||||
double fade_seconds;
|
||||
|
Loading…
Reference in New Issue
Block a user