Add mixing code for foobar and fix loop counts during seek

This commit is contained in:
bnnm 2019-03-23 22:50:15 +01:00
parent da4b31c0d3
commit d3fc30d408
2 changed files with 73 additions and 34 deletions

View File

@ -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;

View File

@ -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;