From df0755104b04118a7dc45826d3fd740ef4d12839 Mon Sep 17 00:00:00 2001 From: bnnm Date: Wed, 29 Jul 2020 19:41:17 +0200 Subject: [PATCH] Use internal plugin settings and seeking in foobar --- fb2k/foo_vgmstream.cpp | 250 ++++++++++------------------------------- fb2k/foo_vgmstream.h | 39 ++----- 2 files changed, 72 insertions(+), 217 deletions(-) diff --git a/fb2k/foo_vgmstream.cpp b/fb2k/foo_vgmstream.cpp index f9cb93e1..b74c1625 100644 --- a/fb2k/foo_vgmstream.cpp +++ b/fb2k/foo_vgmstream.cpp @@ -38,6 +38,9 @@ extern "C" { "https://github.com/kode54/vgmstream/\n" \ "https://sourceforge.net/projects/vgmstream/ (original)" +#define PLUGIN_FILENAME "foo_input_vgmstream.dll" + + // called every time a file is added to the playlist (to get info) or when playing input_vgmstream::input_vgmstream() { vgmstream = NULL; @@ -49,13 +52,11 @@ input_vgmstream::input_vgmstream() { paused = 0; decode_pos_ms = 0; decode_pos_samples = 0; - stream_length_samples = 0; - fade_samples = 0; - seek_pos_samples = 0; + length_samples = 0; - fade_seconds = 10.0f; - fade_delay_seconds = 0.0f; - loop_count = 2.0f; + fade_seconds = 10.0; + fade_delay_seconds = 0.0; + loop_count = 2.0; loop_forever = false; ignore_loop = 0; disable_subsongs = false; @@ -74,7 +75,7 @@ input_vgmstream::~input_vgmstream() { } // called first when a new file is accepted, before playing it -void input_vgmstream::open(service_ptr_t p_filehint, const char * p_path,t_input_open_reason p_reason,abort_callback & p_abort) { +void input_vgmstream::open(service_ptr_t p_filehint, const char * p_path, t_input_open_reason p_reason, abort_callback & p_abort) { if (!p_path) { // shouldn't be possible throw exception_io_data(); @@ -180,14 +181,14 @@ void input_vgmstream::get_info(t_uint32 p_subsong, file_info & p_info, abort_cal strcpy(tagfile_path,tagfile_name); } - STREAMFILE *tagFile = open_foo_streamfile(tagfile_path, &p_abort, NULL); - if (tagFile != NULL) { - VGMSTREAM_TAGS *tags; + STREAMFILE* sf_tags = open_foo_streamfile(tagfile_path, &p_abort, NULL); + if (sf_tags != NULL) { + VGMSTREAM_TAGS* tags; const char *tag_key, *tag_val; tags = vgmstream_tags_init(&tag_key, &tag_val); vgmstream_tags_reset(tags, filename); - while (vgmstream_tags_next_tag(tags, tagFile)) { + while (vgmstream_tags_next_tag(tags, sf_tags)) { if (replaygain_info::g_is_meta_replaygain(tag_key)) { p_info.info_set_replaygain(tag_key, tag_val); /* there is info_set_replaygain_auto too but no doc */ @@ -200,17 +201,17 @@ void input_vgmstream::get_info(t_uint32 p_subsong, file_info & p_info, abort_cal } } vgmstream_tags_close(tags); - close_streamfile(tagFile); + close_streamfile(sf_tags); } } /* set technical info (details tab in file properties) */ - p_info.info_set("vgmstream_version",PLUGIN_VERSION); + p_info.info_set("vgmstream_version", PLUGIN_VERSION); p_info.info_set_int("samplerate", samplerate); p_info.info_set_int("channels", channels); - p_info.info_set_int("bitspersample",16); + p_info.info_set_int("bitspersample", 16); /* not quite accurate but some people are confused by "lossless" * (could set lossless if PCM, but then again PCMFloat or PCM8 are converted/"lossy" in vgmstream) */ p_info.info_set("encoding","lossy/lossless"); @@ -259,7 +260,7 @@ void input_vgmstream::decode_initialize(t_uint32 p_subsong, unsigned p_flags, ab }; // called when audio buffer needs to be filled -bool input_vgmstream::decode_run(audio_chunk & p_chunk,abort_callback & p_abort) { +bool input_vgmstream::decode_run(audio_chunk & p_chunk, abort_callback & p_abort) { if (!decoding) return false; if (!vgmstream) return false; @@ -268,111 +269,48 @@ bool input_vgmstream::decode_run(audio_chunk & p_chunk,abort_callback & p_abort) 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; + bool play_forever = vgmstream_get_play_forever(vgmstream); + if (decode_pos_samples + max_buffer_samples > length_samples && !play_forever) + samples_to_do = length_samples - decode_pos_samples; else samples_to_do = max_buffer_samples; - if (samples_to_do /*< DECODE_SIZE*/ == 0) { + if (samples_to_do == 0) { /*< DECODE_SIZE*/ decoding = false; return false; /* EOF, didn't decode samples in this call */ } - - render_vgmstream(sample_buffer,samples_to_do,vgmstream); - - /* fade! */ - if (vgmstream->loop_flag && fade_samples > 0 && !loop_okay) { - int fade_channels = output_channels; - int samples_into_fade = decode_pos_samples - (stream_length_samples - fade_samples); - if (samples_into_fade + samples_to_do > 0) { - int j,k; - for (j=0;j 0) { - double fadedness = (double)(fade_samples-samples_into_fade)/fade_samples; - for (k = 0; k < fade_channels; k++) { - sample_buffer[j*fade_channels+k] = - (short)(sample_buffer[j*fade_channels+k]*fadedness); - } - } - } - } - } + render_vgmstream(sample_buffer, samples_to_do, vgmstream); 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])); + + 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); - decode_pos_samples+=samples_to_do; - decode_pos_ms=decode_pos_samples*1000LL/vgmstream->sample_rate; + decode_pos_samples += samples_to_do; + decode_pos_ms = decode_pos_samples * 1000LL / vgmstream->sample_rate; return true; /* decoded in this call (sample_buffer or less) */ } } // called when seeking -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 = SAMPLE_BUFFER_SIZE; - bool loop_okay = config.song_play_forever && vgmstream->loop_flag && !config.song_ignore_loop && !force_ignore_loop; +void input_vgmstream::decode_seek(double p_seconds, abort_callback & p_abort) { + int32_t seek_sample = (int)audio_math::time_to_samples(p_seconds, vgmstream->sample_rate); + bool play_forever = vgmstream_get_play_forever(vgmstream); // 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) - seek_pos_samples = stream_length_samples; + // (p_seconds can't go over seek bar with infinite looping on, though) + if (seek_sample > length_samples) + seek_sample = length_samples; - int corrected_pos_samples = seek_pos_samples; - int loop_length = (vgmstream->loop_end_sample - vgmstream->loop_start_sample); - int loop_count = 0; - - // optimize seeks withing loops - if (vgmstream->loop_flag && loop_length > 0 && seek_pos_samples >= vgmstream->loop_end_sample) { - corrected_pos_samples -= vgmstream->loop_start_sample; - loop_count = 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 (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) - corrected_pos_samples = stream_length_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; - - decode_pos_samples += seek_samples; - render_vgmstream(sample_buffer, seek_samples, vgmstream); - } - - // seek may have been clamped to skip unneeded loops, adjust as some internals need this value - vgmstream->loop_count = loop_count; //todo make seek_vgmstream, not ok if seeking in fade section with ignore_fade - - // remove seek loop correction from counter so file ends correctly - decode_pos_samples = seek_pos_samples; + seek_vgmstream(vgmstream, seek_sample); + decode_pos_samples = seek_sample; decode_pos_ms = decode_pos_samples * 1000LL / vgmstream->sample_rate; - - decoding = loop_okay || decode_pos_samples < stream_length_samples; + decoding = play_forever || decode_pos_samples < length_samples; } bool input_vgmstream::decode_can_seek() {return true;} @@ -383,7 +321,7 @@ void input_vgmstream::decode_on_idle(abort_callback & p_abort) {/*m_file->on_idl void input_vgmstream::retag_set_info(t_uint32 p_subsong, const file_info & p_info, abort_callback & p_abort) { /*throw exception_io_data();*/ } void input_vgmstream::retag_commit(abort_callback & p_abort) { /*throw exception_io_data();*/ } -bool input_vgmstream::g_is_our_content_type(const char * p_content_type) {return false;} +bool input_vgmstream::g_is_our_content_type(const char * p_content_type) { return false; } // called to check if file can be processed by the plugin bool input_vgmstream::g_is_our_path(const char * p_path, const char * p_extension) { @@ -395,14 +333,14 @@ bool input_vgmstream::g_is_our_path(const char * p_path, const char * p_extensio } // internal util to create a VGMSTREAM -VGMSTREAM * input_vgmstream::init_vgmstream_foo(t_uint32 p_subsong, const char * const filename, abort_callback & p_abort) { - VGMSTREAM *vgmstream = NULL; +VGMSTREAM* input_vgmstream::init_vgmstream_foo(t_uint32 p_subsong, const char * const filename, abort_callback & p_abort) { + VGMSTREAM* vgmstream = NULL; - STREAMFILE *streamFile = open_foo_streamfile(filename, &p_abort, &stats); - if (streamFile) { - streamFile->stream_index = p_subsong; - vgmstream = init_vgmstream_from_STREAMFILE(streamFile); - close_streamfile(streamFile); + STREAMFILE* sf = open_foo_streamfile(filename, &p_abort, &stats); + if (sf) { + sf->stream_index = p_subsong; + vgmstream = init_vgmstream_from_STREAMFILE(sf); + close_streamfile(sf); } return vgmstream; } @@ -431,8 +369,7 @@ void input_vgmstream::setup_vgmstream(abort_callback & p_abort) { if (subsong == 0) subsong = 1; - set_config_defaults(&config); - apply_config(vgmstream, &config); + apply_config(vgmstream); /* 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); @@ -441,15 +378,13 @@ void input_vgmstream::setup_vgmstream(abort_callback & p_abort) { decode_pos_ms = 0; decode_pos_samples = 0; paused = 0; - stream_length_samples = get_vgmstream_play_samples(config.song_loop_count,config.song_fade_time,config.song_fade_delay,vgmstream); - fade_samples = (int)(config.song_fade_time * vgmstream->sample_rate); + length_samples = vgmstream_get_samples(vgmstream); } // internal util to get info void input_vgmstream::get_subsong_info(t_uint32 p_subsong, pfc::string_base & title, int *length_in_ms, int *total_samples, int *loop_flag, int *loop_start, int *loop_end, int *sample_rate, int *channels, int *bitrate, pfc::string_base & description, abort_callback & p_abort) { - VGMSTREAM * infostream = NULL; + VGMSTREAM* infostream = NULL; bool is_infostream = false; - foobar_song_config infoconfig; char temp[1024]; int info_channels; @@ -461,16 +396,16 @@ void input_vgmstream::get_subsong_info(t_uint32 p_subsong, pfc::string_base & ti if (!infostream) { throw exception_io_data(); } - set_config_defaults(&infoconfig); - apply_config(infostream,&infoconfig); + is_infostream = true; + apply_config(infostream); + vgmstream_mixing_autodownmix(infostream, downmix_channels); vgmstream_mixing_enable(infostream, 0, NULL /*&input_channels*/, &info_channels); } else { // vgmstream ready as get_info is valid after open() with any reason infostream = vgmstream; - infoconfig = config; info_channels = output_channels; } @@ -486,7 +421,7 @@ void input_vgmstream::get_subsong_info(t_uint32 p_subsong, pfc::string_base & ti *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); + int num_samples = vgmstream_get_samples(infostream); *length_in_ms = num_samples*1000LL / infostream->sample_rate; char temp[1024]; @@ -546,80 +481,17 @@ bool input_vgmstream::get_description_tag(pfc::string_base & temp, pfc::string_b return false; } -void input_vgmstream::set_config_defaults(foobar_song_config *current) { - current->song_play_forever = loop_forever; - current->song_loop_count = loop_count; - current->song_fade_time = fade_seconds; - current->song_fade_delay = fade_delay_seconds; - current->song_ignore_fade = 0; - current->song_force_loop = 0; - current->song_really_force_loop = 0; - current->song_ignore_loop = ignore_loop; -} +void input_vgmstream::apply_config(VGMSTREAM* vgmstream) { + vgmstream_cfg_t vcfg = {0}; -void input_vgmstream::apply_config(VGMSTREAM* vgmstream, foobar_song_config* cfg) { + vcfg.allow_play_forever = 1; + vcfg.play_forever = loop_forever; + vcfg.loop_times = loop_count; + vcfg.fade_period = fade_seconds; + vcfg.fade_delay = fade_delay_seconds; + vcfg.ignore_loop = ignore_loop; - /* honor suggested config (order matters, and config mixes with/overwrites player defaults) */ - if (vgmstream->config.play_forever) { - cfg->song_play_forever = 1; - cfg->song_ignore_loop = 0; - } - if (vgmstream->config.loop_count_set) { - cfg->song_loop_count = vgmstream->config.loop_count; - cfg->song_play_forever = 0; - cfg->song_ignore_loop = 0; - } - if (vgmstream->config.fade_delay_set) { - cfg->song_fade_delay = vgmstream->config.fade_delay; - } - if (vgmstream->config.fade_time_set) { - cfg->song_fade_time = vgmstream->config.fade_time; - } - if (vgmstream->config.ignore_fade) { - cfg->song_ignore_fade = 1; - } - - if (vgmstream->config.force_loop) { - cfg->song_ignore_loop = 0; - cfg->song_force_loop = 1; - cfg->song_really_force_loop = 0; - } - if (vgmstream->config.really_force_loop) { - cfg->song_ignore_loop = 0; - cfg->song_force_loop = 0; - cfg->song_really_force_loop = 1; - } - if (vgmstream->config.ignore_loop) { - cfg->song_ignore_loop = 1; - cfg->song_force_loop = 0; - cfg->song_really_force_loop = 0; - } - - - /* apply config */ - if (cfg->song_force_loop && !vgmstream->loop_flag) { - vgmstream_force_loop(vgmstream, 1, 0,vgmstream->num_samples); - } - if (cfg->song_really_force_loop) { - vgmstream_force_loop(vgmstream, 1, 0,vgmstream->num_samples); - } - if (cfg->song_ignore_loop) { - vgmstream_force_loop(vgmstream, 0, 0,0); - } - - /* remove non-compatible options */ - if (!vgmstream->loop_flag) { - cfg->song_play_forever = 0; - } - if (cfg->song_play_forever) { - cfg->song_ignore_fade = 0; - } - - /* loop N times, but also play stream end instead of fading out */ - if (cfg->song_loop_count > 0 && cfg->song_ignore_fade) { - vgmstream_set_loop_target(vgmstream, (int)cfg->song_loop_count); - cfg->song_fade_time = 0; - } + vgmstream_apply_config(vgmstream, &vcfg); } GUID input_vgmstream::g_get_guid() { @@ -632,7 +504,7 @@ const char * input_vgmstream::g_get_name() { } GUID input_vgmstream::g_get_preferences_guid() { - static const GUID guid = { 0x2b5d0302, 0x165b, 0x409c,{ 0x94, 0x74, 0x2c, 0x8c, 0x2c, 0xd7, 0x6a, 0x25 } };; + static const GUID guid = { 0x2b5d0302, 0x165b, 0x409c,{ 0x94, 0x74, 0x2c, 0x8c, 0x2c, 0xd7, 0x6a, 0x25 } }; return guid; } @@ -644,5 +516,5 @@ bool input_vgmstream::g_is_low_merit() { // foobar plugin defs static input_factory_t g_input_vgmstream_factory; -DECLARE_COMPONENT_VERSION(APP_NAME,PLUGIN_VERSION,PLUGIN_DESCRIPTION); -VALIDATE_COMPONENT_FILENAME("foo_input_vgmstream.dll"); +DECLARE_COMPONENT_VERSION(APP_NAME, PLUGIN_VERSION, PLUGIN_DESCRIPTION); +VALIDATE_COMPONENT_FILENAME(PLUGIN_FILENAME); diff --git a/fb2k/foo_vgmstream.h b/fb2k/foo_vgmstream.h index a6e6ab7e..aa4c44ce 100644 --- a/fb2k/foo_vgmstream.h +++ b/fb2k/foo_vgmstream.h @@ -3,44 +3,32 @@ #define SAMPLE_BUFFER_SIZE 1024 -/* current song settings */ -typedef struct { - int song_play_forever; - double song_loop_count; - double song_fade_time; - double song_fade_delay; - int song_ignore_loop; - int song_force_loop; - int song_really_force_loop; - int song_ignore_fade; -} foobar_song_config; - class input_vgmstream : public input_stubs { public: input_vgmstream(); ~input_vgmstream(); - void open(service_ptr_t p_filehint,const char * p_path,t_input_open_reason p_reason,abort_callback & p_abort); + void open(service_ptr_t p_filehint, const char * p_path, t_input_open_reason p_reason, abort_callback & p_abort); unsigned get_subsong_count(); t_uint32 get_subsong(unsigned p_index); - void get_info(t_uint32 p_subsong,file_info & p_info,abort_callback & p_abort); + void get_info(t_uint32 p_subsong, file_info & p_info, abort_callback & p_abort); t_filestats get_file_stats(abort_callback & p_abort); - void decode_initialize(t_uint32 p_subsong,unsigned p_flags,abort_callback & p_abort); - bool decode_run(audio_chunk & p_chunk,abort_callback & p_abort); - void decode_seek(double p_seconds,abort_callback & p_abort); + void decode_initialize(t_uint32 p_subsong, unsigned p_flags, abort_callback & p_abort); + bool decode_run(audio_chunk & p_chunk, abort_callback & p_abort); + void decode_seek(double p_seconds, abort_callback & p_abort); bool decode_can_seek(); bool decode_get_dynamic_info(file_info & p_out, double & p_timestamp_delta); bool decode_get_dynamic_info_track(file_info & p_out, double & p_timestamp_delta); void decode_on_idle(abort_callback & p_abort); - void retag_set_info(t_uint32 p_subsong,const file_info & p_info,abort_callback & p_abort); + void retag_set_info(t_uint32 p_subsong, const file_info & p_info, abort_callback & p_abort); void retag_commit(abort_callback & p_abort); static bool g_is_our_content_type(const char * p_content_type); - static bool g_is_our_path(const char * p_path,const char * p_extension); + static bool g_is_our_path(const char * p_path, const char * p_extension); static GUID g_get_guid(); static const char * g_get_name(); @@ -53,7 +41,7 @@ class input_vgmstream : public input_stubs { t_filestats stats; /* state */ - VGMSTREAM * vgmstream; + VGMSTREAM* vgmstream; t_uint32 subsong; bool direct_subsong; int output_channels; @@ -62,9 +50,7 @@ class input_vgmstream : public input_stubs { int paused; int decode_pos_ms; int decode_pos_samples; - int stream_length_samples; - int fade_samples; - int seek_pos_samples; + int length_samples; short sample_buffer[SAMPLE_BUFFER_SIZE * VGMSTREAM_MAX_CHANNELS]; /* settings */ @@ -75,6 +61,7 @@ class input_vgmstream : public input_stubs { bool force_ignore_loop; int ignore_loop; bool disable_subsongs; + int downmix_channels; bool tagfile_disable; pfc::string8 tagfile_name; @@ -82,17 +69,13 @@ class input_vgmstream : public input_stubs { //bool exts_common_on; //bool exts_unknown_on; - /* song config */ - foobar_song_config config; - /* helpers */ VGMSTREAM * init_vgmstream_foo(t_uint32 p_subsong, const char * const filename, abort_callback & p_abort); void setup_vgmstream(abort_callback & p_abort); void load_settings(); void get_subsong_info(t_uint32 p_subsong, pfc::string_base & title, int *length_in_ms, int *total_samples, int *loop_flag, int *loop_start, int *loop_end, int *sample_rate, int *channels, int *bitrate, pfc::string_base & description, abort_callback & p_abort); bool get_description_tag(pfc::string_base & temp, pfc::string_base const& description, const char *tag, char delimiter = '\n'); - void set_config_defaults(foobar_song_config *current); - void apply_config(VGMSTREAM * vgmstream, foobar_song_config *current); + void apply_config(VGMSTREAM * vgmstream); static void g_load_cfg(int *accept_unknown, int *accept_common); };