mirror of
https://github.com/vgmstream/vgmstream.git
synced 2024-11-23 22:41:05 +01:00
cleanup: foobar misc + tweak file info
This commit is contained in:
parent
373329adab
commit
0cb37f0dc7
@ -1,28 +1,32 @@
|
|||||||
#ifndef _FOO_FILETYPES_H_
|
#ifndef _FOO_FILETYPES_H_
|
||||||
#define _FOO_FILETYPES_H_
|
#define _FOO_FILETYPES_H_
|
||||||
|
|
||||||
class input_file_type_v2_impl_vgmstream : public input_file_type_v2 {
|
class input_file_type_v2_impl_vgmstream : public input_file_type_v2 {
|
||||||
public:
|
public:
|
||||||
input_file_type_v2_impl_vgmstream() {
|
input_file_type_v2_impl_vgmstream() {
|
||||||
ext_list = vgmstream_get_formats(&ext_list_len);
|
ext_list = vgmstream_get_formats(&ext_list_len);
|
||||||
}
|
}
|
||||||
unsigned get_count() { return ext_list_len; }
|
|
||||||
bool is_associatable(unsigned idx) { return true; }
|
unsigned get_count() { return ext_list_len; }
|
||||||
void get_format_name(unsigned idx, pfc::string_base & out, bool isPlural) {
|
|
||||||
out.reset();
|
bool is_associatable(unsigned idx) { return true; }
|
||||||
pfc::stringToUpperAppend(out, ext_list[idx], pfc::strlen_utf8(ext_list[idx]));
|
|
||||||
out += " Audio File";
|
void get_format_name(unsigned idx, pfc::string_base & out, bool isPlural) {
|
||||||
if (isPlural) out += "s";
|
out.reset();
|
||||||
}
|
pfc::stringToUpperAppend(out, ext_list[idx], pfc::strlen_utf8(ext_list[idx]));
|
||||||
void get_extensions(unsigned idx, pfc::string_base & out) {
|
out += " Audio File";
|
||||||
out = ext_list[idx];
|
if (isPlural) out += "s";
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
void get_extensions(unsigned idx, pfc::string_base & out) {
|
||||||
const char ** ext_list;
|
out = ext_list[idx];
|
||||||
size_t ext_list_len;
|
}
|
||||||
};
|
|
||||||
|
private:
|
||||||
namespace { static service_factory_single_t<input_file_type_v2_impl_vgmstream> g_filetypes; }
|
const char ** ext_list;
|
||||||
|
size_t ext_list_len;
|
||||||
#endif /*_FOO_FILETYPES_H_ */
|
};
|
||||||
|
|
||||||
|
namespace { static service_factory_single_t<input_file_type_v2_impl_vgmstream> g_filetypes; }
|
||||||
|
|
||||||
|
#endif /*_FOO_FILETYPES_H_ */
|
||||||
|
@ -5,10 +5,6 @@
|
|||||||
#include <io.h>
|
#include <io.h>
|
||||||
|
|
||||||
#include "foo_prefs.h"
|
#include "foo_prefs.h"
|
||||||
extern "C" {
|
|
||||||
#include "../src/vgmstream.h"
|
|
||||||
#include "../src/util.h"
|
|
||||||
}
|
|
||||||
#include "foo_vgmstream.h"
|
#include "foo_vgmstream.h"
|
||||||
|
|
||||||
|
|
||||||
@ -27,13 +23,13 @@ static cfg_bool cfg_ExtsCommonOn ({0x405af423,0x5037,0x4eae,{0xa6,0xe3,0x
|
|||||||
// Needs to be here in rder to access the static config
|
// Needs to be here in rder to access the static config
|
||||||
void input_vgmstream::load_settings() {
|
void input_vgmstream::load_settings() {
|
||||||
// no verification needed here, as it is done below
|
// no verification needed here, as it is done below
|
||||||
sscanf(cfg_FadeLength.get_ptr(),"%lf",&fade_seconds);
|
sscanf(cfg_FadeLength.get_ptr(), "%lf", &fade_seconds);
|
||||||
sscanf(cfg_LoopCount.get_ptr(),"%lf",&loop_count);
|
sscanf(cfg_LoopCount.get_ptr(), "%lf", &loop_count);
|
||||||
sscanf(cfg_FadeDelay.get_ptr(),"%lf",&fade_delay_seconds);
|
sscanf(cfg_FadeDelay.get_ptr(), "%lf", &fade_delay_seconds);
|
||||||
loop_forever = cfg_LoopForever;
|
loop_forever = cfg_LoopForever;
|
||||||
ignore_loop = cfg_IgnoreLoop;
|
ignore_loop = cfg_IgnoreLoop;
|
||||||
disable_subsongs = cfg_DisableSubsongs;
|
disable_subsongs = cfg_DisableSubsongs;
|
||||||
sscanf(cfg_DownmixChannels.get_ptr(),"%d",&downmix_channels);
|
sscanf(cfg_DownmixChannels.get_ptr(), "%d", &downmix_channels);
|
||||||
tagfile_disable = cfg_TagfileDisable;
|
tagfile_disable = cfg_TagfileDisable;
|
||||||
override_title = cfg_OverrideTitle;
|
override_title = cfg_OverrideTitle;
|
||||||
//exts_unknown_on = cfg_ExtsUnknownOn;
|
//exts_unknown_on = cfg_ExtsUnknownOn;
|
||||||
@ -43,13 +39,13 @@ void input_vgmstream::load_settings() {
|
|||||||
if (loop_count <= 0)
|
if (loop_count <= 0)
|
||||||
loop_count = 1;
|
loop_count = 1;
|
||||||
}
|
}
|
||||||
void input_vgmstream::g_load_cfg(int *accept_unknown, int *accept_common) {
|
void input_vgmstream::g_load_cfg(int* accept_unknown, int* accept_common) {
|
||||||
//todo improve
|
//TODO improve
|
||||||
*accept_unknown = cfg_ExtsUnknownOn ? 1 : 0;
|
*accept_unknown = cfg_ExtsUnknownOn;
|
||||||
*accept_common = cfg_ExtsCommonOn ? 1 : 0;
|
*accept_common = cfg_ExtsCommonOn;
|
||||||
}
|
}
|
||||||
|
|
||||||
const char * vgmstream_prefs::get_name() {
|
const char* vgmstream_prefs::get_name() {
|
||||||
return input_vgmstream::g_get_name();
|
return input_vgmstream::g_get_name();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -61,50 +57,55 @@ GUID vgmstream_prefs::get_parent_guid() {
|
|||||||
return guid_input;
|
return guid_input;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static UINT get_check(bool value) {
|
||||||
|
return value ? BST_CHECKED : BST_UNCHECKED;
|
||||||
|
}
|
||||||
|
|
||||||
BOOL vgmstreamPreferences::OnInitDialog(CWindow, LPARAM) {
|
BOOL vgmstreamPreferences::OnInitDialog(CWindow, LPARAM) {
|
||||||
CheckDlgButton(IDC_LOOP_FOREVER, cfg_LoopForever?BST_CHECKED:BST_UNCHECKED);
|
CheckDlgButton(IDC_LOOP_FOREVER, get_check(cfg_LoopForever));
|
||||||
CheckDlgButton(IDC_IGNORE_LOOP, cfg_IgnoreLoop?BST_CHECKED:BST_UNCHECKED);
|
CheckDlgButton(IDC_IGNORE_LOOP, get_check(cfg_IgnoreLoop));
|
||||||
CheckDlgButton(IDC_LOOP_NORMALLY, (!cfg_IgnoreLoop && !cfg_LoopForever)?BST_CHECKED:BST_UNCHECKED);
|
CheckDlgButton(IDC_LOOP_NORMALLY, get_check(!cfg_IgnoreLoop && !cfg_LoopForever));
|
||||||
|
|
||||||
uSetDlgItemText(m_hWnd, IDC_LOOP_COUNT, cfg_LoopCount);
|
uSetDlgItemText(m_hWnd, IDC_LOOP_COUNT, cfg_LoopCount);
|
||||||
uSetDlgItemText(m_hWnd, IDC_FADE_SECONDS, cfg_FadeLength);
|
uSetDlgItemText(m_hWnd, IDC_FADE_SECONDS, cfg_FadeLength);
|
||||||
uSetDlgItemText(m_hWnd, IDC_FADE_DELAY_SECONDS, cfg_FadeDelay);
|
uSetDlgItemText(m_hWnd, IDC_FADE_DELAY_SECONDS, cfg_FadeDelay);
|
||||||
|
|
||||||
CheckDlgButton(IDC_DISABLE_SUBSONGS, cfg_DisableSubsongs?BST_CHECKED:BST_UNCHECKED);
|
CheckDlgButton(IDC_DISABLE_SUBSONGS, get_check(cfg_DisableSubsongs));
|
||||||
|
|
||||||
uSetDlgItemText(m_hWnd, IDC_DOWNMIX_CHANNELS, cfg_DownmixChannels);
|
uSetDlgItemText(m_hWnd, IDC_DOWNMIX_CHANNELS, cfg_DownmixChannels);
|
||||||
|
|
||||||
CheckDlgButton(IDC_TAGFILE_DISABLE, cfg_TagfileDisable?BST_CHECKED:BST_UNCHECKED);
|
CheckDlgButton(IDC_TAGFILE_DISABLE, get_check(cfg_TagfileDisable));
|
||||||
CheckDlgButton(IDC_OVERRIDE_TITLE, cfg_OverrideTitle?BST_CHECKED:BST_UNCHECKED);
|
CheckDlgButton(IDC_OVERRIDE_TITLE, get_check(cfg_OverrideTitle));
|
||||||
CheckDlgButton(IDC_EXTS_UNKNOWN_ON, cfg_ExtsUnknownOn?BST_CHECKED:BST_UNCHECKED);
|
CheckDlgButton(IDC_EXTS_UNKNOWN_ON, get_check(cfg_ExtsUnknownOn));
|
||||||
CheckDlgButton(IDC_EXTS_COMMON_ON, cfg_ExtsCommonOn?BST_CHECKED:BST_UNCHECKED);
|
CheckDlgButton(IDC_EXTS_COMMON_ON, get_check(cfg_ExtsCommonOn));
|
||||||
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
t_uint32 vgmstreamPreferences::get_state() {
|
t_uint32 vgmstreamPreferences::get_state() {
|
||||||
t_uint32 state = preferences_state::resettable;
|
t_uint32 state = preferences_state::resettable;
|
||||||
if (HasChanged()) state |= preferences_state::changed;
|
if (HasChanged())
|
||||||
|
state |= preferences_state::changed;
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
||||||
void vgmstreamPreferences::reset() {
|
void vgmstreamPreferences::reset() {
|
||||||
CheckDlgButton(IDC_LOOP_FOREVER, DEFAULT_LOOP_FOREVER?BST_CHECKED:BST_UNCHECKED);
|
CheckDlgButton(IDC_LOOP_FOREVER, get_check(DEFAULT_LOOP_FOREVER));
|
||||||
CheckDlgButton(IDC_IGNORE_LOOP, DEFAULT_IGNORE_LOOP?BST_CHECKED:BST_UNCHECKED);
|
CheckDlgButton(IDC_IGNORE_LOOP, get_check(DEFAULT_IGNORE_LOOP));
|
||||||
CheckDlgButton(IDC_LOOP_NORMALLY, (!DEFAULT_IGNORE_LOOP && !DEFAULT_LOOP_FOREVER)?BST_CHECKED:BST_UNCHECKED);
|
CheckDlgButton(IDC_LOOP_NORMALLY, get_check(!DEFAULT_IGNORE_LOOP && !DEFAULT_LOOP_FOREVER));
|
||||||
|
|
||||||
uSetDlgItemText(m_hWnd, IDC_LOOP_COUNT, DEFAULT_LOOP_COUNT);
|
uSetDlgItemText(m_hWnd, IDC_LOOP_COUNT, DEFAULT_LOOP_COUNT);
|
||||||
uSetDlgItemText(m_hWnd, IDC_FADE_SECONDS, DEFAULT_FADE_SECONDS);
|
uSetDlgItemText(m_hWnd, IDC_FADE_SECONDS, DEFAULT_FADE_SECONDS);
|
||||||
uSetDlgItemText(m_hWnd, IDC_FADE_DELAY_SECONDS, DEFAULT_FADE_DELAY_SECONDS);
|
uSetDlgItemText(m_hWnd, IDC_FADE_DELAY_SECONDS, DEFAULT_FADE_DELAY_SECONDS);
|
||||||
|
|
||||||
CheckDlgButton(IDC_DISABLE_SUBSONGS, DEFAULT_DISABLE_SUBSONGS?BST_CHECKED:BST_UNCHECKED);
|
CheckDlgButton(IDC_DISABLE_SUBSONGS, get_check(DEFAULT_DISABLE_SUBSONGS));
|
||||||
|
|
||||||
uSetDlgItemText(m_hWnd, IDC_DOWNMIX_CHANNELS, DEFAULT_DOWNMIX_CHANNELS);
|
uSetDlgItemText(m_hWnd, IDC_DOWNMIX_CHANNELS, DEFAULT_DOWNMIX_CHANNELS);
|
||||||
|
|
||||||
CheckDlgButton(IDC_TAGFILE_DISABLE, DEFAULT_TAGFILE_DISABLE?BST_CHECKED:BST_UNCHECKED);
|
CheckDlgButton(IDC_TAGFILE_DISABLE, get_check(DEFAULT_TAGFILE_DISABLE));
|
||||||
CheckDlgButton(IDC_OVERRIDE_TITLE, DEFAULT_OVERRIDE_TITLE?BST_CHECKED:BST_UNCHECKED);
|
CheckDlgButton(IDC_OVERRIDE_TITLE, get_check(DEFAULT_OVERRIDE_TITLE));
|
||||||
CheckDlgButton(IDC_EXTS_UNKNOWN_ON, DEFAULT_EXTS_UNKNOWN_ON?BST_CHECKED:BST_UNCHECKED);
|
CheckDlgButton(IDC_EXTS_UNKNOWN_ON, get_check(DEFAULT_EXTS_UNKNOWN_ON));
|
||||||
CheckDlgButton(IDC_EXTS_COMMON_ON, DEFAULT_EXTS_COMMON_ON?BST_CHECKED:BST_UNCHECKED);
|
CheckDlgButton(IDC_EXTS_COMMON_ON, get_check(DEFAULT_EXTS_COMMON_ON));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -125,9 +126,8 @@ void vgmstreamPreferences::apply() {
|
|||||||
|
|
||||||
pfc::string buf;
|
pfc::string buf;
|
||||||
buf = uGetDlgItemText(m_hWnd, IDC_FADE_SECONDS);
|
buf = uGetDlgItemText(m_hWnd, IDC_FADE_SECONDS);
|
||||||
if (sscanf(buf.get_ptr(),"%lf%n",&temp_fade_seconds,&consumed)<1
|
if (sscanf(buf.get_ptr(), "%lf%n", &temp_fade_seconds, &consumed) < 1
|
||||||
|| consumed!=strlen(buf.get_ptr()) ||
|
|| consumed != strlen(buf.get_ptr()) || temp_fade_seconds < 0) {
|
||||||
temp_fade_seconds<0) {
|
|
||||||
uMessageBox(m_hWnd,
|
uMessageBox(m_hWnd,
|
||||||
"Invalid value for Fade Length\n"
|
"Invalid value for Fade Length\n"
|
||||||
"Must be a number greater than or equal to zero",
|
"Must be a number greater than or equal to zero",
|
||||||
@ -136,9 +136,8 @@ void vgmstreamPreferences::apply() {
|
|||||||
} else cfg_FadeLength = buf.get_ptr();
|
} else cfg_FadeLength = buf.get_ptr();
|
||||||
|
|
||||||
buf = uGetDlgItemText(m_hWnd, IDC_LOOP_COUNT);
|
buf = uGetDlgItemText(m_hWnd, IDC_LOOP_COUNT);
|
||||||
if (sscanf(buf.get_ptr(),"%lf%n",&temp_loop_count,&consumed)<1
|
if (sscanf(buf.get_ptr(), "%lf%n", &temp_loop_count, &consumed) < 1
|
||||||
|| consumed!=strlen(buf.get_ptr()) ||
|
|| consumed != strlen(buf.get_ptr()) || temp_loop_count < 0) {
|
||||||
temp_loop_count<0) {
|
|
||||||
uMessageBox(m_hWnd,
|
uMessageBox(m_hWnd,
|
||||||
"Invalid value for Loop Count\n"
|
"Invalid value for Loop Count\n"
|
||||||
"Must be a number greater than or equal to zero",
|
"Must be a number greater than or equal to zero",
|
||||||
@ -147,24 +146,22 @@ void vgmstreamPreferences::apply() {
|
|||||||
} else cfg_LoopCount = buf.get_ptr();
|
} else cfg_LoopCount = buf.get_ptr();
|
||||||
|
|
||||||
buf = uGetDlgItemText(m_hWnd, IDC_FADE_DELAY_SECONDS);
|
buf = uGetDlgItemText(m_hWnd, IDC_FADE_DELAY_SECONDS);
|
||||||
if (sscanf(buf.get_ptr(),"%lf%n",&temp_fade_delay_seconds,&consumed)<1
|
if (sscanf(buf.get_ptr(), "%lf%n", &temp_fade_delay_seconds, &consumed) < 1
|
||||||
|| consumed!=strlen(buf.get_ptr()) ||
|
|| consumed != strlen(buf.get_ptr()) || temp_fade_delay_seconds < 0) {
|
||||||
temp_fade_delay_seconds<0) {
|
|
||||||
uMessageBox(m_hWnd,
|
uMessageBox(m_hWnd,
|
||||||
"Invalid value for Fade Delay\n"
|
"Invalid value for Fade Delay\n"
|
||||||
"Must be a number",
|
"Must be a number",
|
||||||
"Error",MB_OK|MB_ICONERROR);
|
"Error", MB_OK|MB_ICONERROR);
|
||||||
return;
|
return;
|
||||||
} else cfg_FadeDelay = buf.get_ptr();
|
} else cfg_FadeDelay = buf.get_ptr();
|
||||||
|
|
||||||
buf = uGetDlgItemText(m_hWnd, IDC_DOWNMIX_CHANNELS);
|
buf = uGetDlgItemText(m_hWnd, IDC_DOWNMIX_CHANNELS);
|
||||||
if (sscanf(buf.get_ptr(),"%d%n",&temp_downmix_channels,&consumed)<1
|
if (sscanf(buf.get_ptr(), "%d%n", &temp_downmix_channels, &consumed) < 1
|
||||||
|| consumed!=strlen(buf.get_ptr()) ||
|
|| consumed != strlen(buf.get_ptr()) || temp_downmix_channels < 0) {
|
||||||
temp_downmix_channels<0) {
|
|
||||||
uMessageBox(m_hWnd,
|
uMessageBox(m_hWnd,
|
||||||
"Invalid value for Downmix Channels\n"
|
"Invalid value for Downmix Channels\n"
|
||||||
"Must be a number greater than or equal to zero",
|
"Must be a number greater than or equal to zero",
|
||||||
"Error",MB_OK|MB_ICONERROR);
|
"Error", MB_OK|MB_ICONERROR);
|
||||||
return;
|
return;
|
||||||
} else cfg_DownmixChannels = buf.get_ptr();
|
} else cfg_DownmixChannels = buf.get_ptr();
|
||||||
|
|
||||||
|
@ -115,12 +115,15 @@ static size_t foo_read(FOO_STREAMFILE* sf, uint8_t* dst, offv_t offset, size_t l
|
|||||||
sf->offset = offset; /* last fread offset */
|
sf->offset = offset; /* last fread offset */
|
||||||
return read_total;
|
return read_total;
|
||||||
}
|
}
|
||||||
|
|
||||||
static size_t foo_get_size(FOO_STREAMFILE* sf) {
|
static size_t foo_get_size(FOO_STREAMFILE* sf) {
|
||||||
return sf->file_size;
|
return sf->file_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
static offv_t foo_get_offset(FOO_STREAMFILE* sf) {
|
static offv_t foo_get_offset(FOO_STREAMFILE* sf) {
|
||||||
return sf->offset;
|
return sf->offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void foo_get_name(FOO_STREAMFILE* sf, char* name, size_t name_size) {
|
static void foo_get_name(FOO_STREAMFILE* sf, char* name, size_t name_size) {
|
||||||
int copy_size = sf->name_len + 1;
|
int copy_size = sf->name_len + 1;
|
||||||
if (copy_size > name_size)
|
if (copy_size > name_size)
|
||||||
@ -138,6 +141,7 @@ static void foo_get_name(FOO_STREAMFILE* sf, char* name, size_t name_size) {
|
|||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
static void foo_close(FOO_STREAMFILE* sf) {
|
static void foo_close(FOO_STREAMFILE* sf) {
|
||||||
sf->m_file.release(); //release alloc'ed ptr
|
sf->m_file.release(); //release alloc'ed ptr
|
||||||
free(sf->name);
|
free(sf->name);
|
||||||
@ -247,11 +251,11 @@ static STREAMFILE* open_foo_streamfile_buffer_by_file(service_ptr_t<file> m_file
|
|||||||
if (strncmp(filename, "unpack", 6) == 0) {
|
if (strncmp(filename, "unpack", 6) == 0) {
|
||||||
const char* archfile_ptr = strrchr(this_sf->name, '|');
|
const char* archfile_ptr = strrchr(this_sf->name, '|');
|
||||||
if (archfile_ptr)
|
if (archfile_ptr)
|
||||||
this_sf->archfile_end = (intptr_t)archfile_ptr + 1 - (intptr_t)this_sf->name; // after "|""
|
this_sf->archfile_end = (int)((intptr_t)archfile_ptr + 1 - (intptr_t)this_sf->name); // after "|""
|
||||||
|
|
||||||
const char* archpath_ptr = strrchr(this_sf->name, '\\');
|
const char* archpath_ptr = strrchr(this_sf->name, '\\');
|
||||||
if (archpath_ptr)
|
if (archpath_ptr)
|
||||||
this_sf->archpath_end = (intptr_t)archpath_ptr + 1 - (intptr_t)this_sf->name; // after "\\"
|
this_sf->archpath_end = (int)((intptr_t)archpath_ptr + 1 - (intptr_t)this_sf->name); // after "\\"
|
||||||
|
|
||||||
if (this_sf->archpath_end <= 0 || this_sf->archfile_end <= 0 || this_sf->archpath_end > this_sf->archfile_end ||
|
if (this_sf->archpath_end <= 0 || this_sf->archfile_end <= 0 || this_sf->archpath_end > this_sf->archfile_end ||
|
||||||
this_sf->archfile_end > this_sf->name_len || this_sf->archfile_end >= PATH_LIMIT) {
|
this_sf->archfile_end > this_sf->name_len || this_sf->archfile_end >= PATH_LIMIT) {
|
||||||
|
@ -59,7 +59,7 @@ input_vgmstream::input_vgmstream() {
|
|||||||
fade_delay_seconds = 0.0;
|
fade_delay_seconds = 0.0;
|
||||||
loop_count = 2.0;
|
loop_count = 2.0;
|
||||||
loop_forever = false;
|
loop_forever = false;
|
||||||
ignore_loop = 0;
|
ignore_loop = false;
|
||||||
disable_subsongs = false;
|
disable_subsongs = false;
|
||||||
downmix_channels = 0;
|
downmix_channels = 0;
|
||||||
tagfile_disable = false;
|
tagfile_disable = false;
|
||||||
@ -80,53 +80,48 @@ input_vgmstream::~input_vgmstream() {
|
|||||||
// called first when a new file is accepted, before playing it
|
// called first when a new file is accepted, before playing it
|
||||||
void input_vgmstream::open(service_ptr_t<file> p_filehint, const char * p_path, t_input_open_reason p_reason, abort_callback & p_abort) {
|
void input_vgmstream::open(service_ptr_t<file> p_filehint, const char * p_path, t_input_open_reason p_reason, abort_callback & p_abort) {
|
||||||
|
|
||||||
if (!p_path) { // shouldn't be possible
|
if (!p_path) // shouldn't be possible
|
||||||
throw exception_io_data();
|
throw exception_io_data();
|
||||||
return; //???
|
|
||||||
}
|
|
||||||
|
|
||||||
filename = p_path;
|
filename = p_path;
|
||||||
|
|
||||||
// allow non-existing files in some cases
|
// allow non-existing files in some cases
|
||||||
bool infile_virtual = !filesystem::g_exists(p_path, p_abort)
|
bool infile_virtual = !filesystem::g_exists(p_path, p_abort) && vgmstream_is_virtual_filename(filename);
|
||||||
&& vgmstream_is_virtual_filename(filename) == 1;
|
|
||||||
|
|
||||||
// don't try to open virtual files as it'll fail
|
// don't try to open virtual files as it'll fail
|
||||||
// (doesn't seem to have any adverse effect, except maybe no stats)
|
// (doesn't seem to have any adverse effect, except maybe no stats)
|
||||||
// setup_vgmstream also makes further checks before file is finally opened
|
// setup_vgmstream also makes further checks before file is finally opened
|
||||||
if (!infile_virtual) {
|
if (!infile_virtual) {
|
||||||
// keep file stats around (timestamp, filesize)
|
// keep file stats around (timestamp, filesize)
|
||||||
if ( p_filehint.is_empty() )
|
if ( p_filehint.is_empty() ) {
|
||||||
input_open_file_helper( p_filehint, filename, p_reason, p_abort );
|
input_open_file_helper( p_filehint, filename, p_reason, p_abort );
|
||||||
|
}
|
||||||
stats = p_filehint->get_stats( p_abort );
|
stats = p_filehint->get_stats( p_abort );
|
||||||
|
|
||||||
uint32_t flags = stats2_legacy; //foobar2000_io.stats2_xxx, not sure about the implications
|
uint32_t flags = stats2_legacy; //foobar2000_io.stats2_xxx, not sure about implications
|
||||||
stats2 = p_filehint->get_stats2_(flags, p_abort); // ???
|
stats2 = p_filehint->get_stats2_(flags, p_abort); // ???
|
||||||
}
|
}
|
||||||
|
|
||||||
switch(p_reason) {
|
switch(p_reason) {
|
||||||
case input_open_decode: // prepare to retrieve info and decode
|
case input_open_decode: // prepare to retrieve info and decode
|
||||||
case input_open_info_read: // prepare to retrieve info
|
case input_open_info_read: // prepare to retrieve info
|
||||||
setup_vgmstream(p_abort); // must init vgmstream to get subsongs
|
// init vgmstream to get subsongs
|
||||||
|
setup_vgmstream(p_abort);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case input_open_info_write: // prepare to retrieve info and tag
|
case input_open_info_write: // prepare to retrieve info and tag
|
||||||
|
default:
|
||||||
throw exception_io_data();
|
throw exception_io_data();
|
||||||
break;
|
|
||||||
|
|
||||||
default: // nothing else should be possible
|
|
||||||
throw exception_io_data();
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// called after opening file (possibly per subsong too)
|
// called after opening file (possibly per subsong too)
|
||||||
unsigned input_vgmstream::get_subsong_count() {
|
unsigned input_vgmstream::get_subsong_count() {
|
||||||
// if the plugin uses input_factory_t template and returns > 1 here when adding a song to the playlist,
|
|
||||||
// foobar will automagically "unpack" it by calling decode_initialize/get_info with all subsong indexes.
|
|
||||||
// There is no need to add any playlist code, only properly handle the subsong index.
|
|
||||||
if (disable_subsongs)
|
if (disable_subsongs)
|
||||||
return 1;
|
return 1;
|
||||||
|
// If the plugin uses input_factory_t template and returns > 1 here when adding a song to the playlist,
|
||||||
|
// foobar will automagically "unpack" it by calling decode_initialize/get_info with all subsong indexes.
|
||||||
|
// There is no need to add any playlist code, only properly handle the subsong index.
|
||||||
|
|
||||||
// vgmstream ready as method is valid after open() with any reason
|
// vgmstream ready as method is valid after open() with any reason
|
||||||
int subsong_count = vgmstream->num_streams;
|
int subsong_count = vgmstream->num_streams;
|
||||||
@ -142,117 +137,155 @@ unsigned input_vgmstream::get_subsong_count() {
|
|||||||
|
|
||||||
// called after get_subsong_count to play subsong N (even when count is 1)
|
// called after get_subsong_count to play subsong N (even when count is 1)
|
||||||
t_uint32 input_vgmstream::get_subsong(unsigned p_index) {
|
t_uint32 input_vgmstream::get_subsong(unsigned p_index) {
|
||||||
return p_index + 1; // translates index (0..N < subsong_count) for vgmstream: 1=first
|
// translates index (0..N < subsong_count) for vgmstream: 1=first
|
||||||
|
return p_index + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// called before playing to get info
|
// called before playing to get info
|
||||||
void input_vgmstream::get_info(t_uint32 p_subsong, file_info & p_info, abort_callback & p_abort) {
|
void input_vgmstream::get_info(t_uint32 p_subsong, file_info& p_info, abort_callback& p_abort) {
|
||||||
int length_in_ms=0, channels = 0, samplerate = 0;
|
vgmstream_info_t v_info = {}; // init default (not {0} since it has classes)
|
||||||
int total_samples = -1;
|
|
||||||
int bitrate = 0;
|
|
||||||
int loop_flag = -1, loop_start = -1, loop_end = -1;
|
|
||||||
pfc::string8 description;
|
|
||||||
pfc::string8_fast temp;
|
|
||||||
|
|
||||||
get_subsong_info(p_subsong, temp, &length_in_ms, &total_samples, &loop_flag, &loop_start, &loop_end, &samplerate, &channels, &bitrate, description, p_abort);
|
query_subsong_info(p_subsong, v_info, p_abort);
|
||||||
|
|
||||||
|
// export tags ('metadata' tab in file properties)
|
||||||
|
put_info_tags(p_info, v_info);
|
||||||
|
put_into_tagfile(p_info, p_abort);
|
||||||
|
|
||||||
/* set tag info (metadata tab in file properties) */
|
// set technical info ('details' tab in file properties)
|
||||||
|
put_info_details(p_info, v_info);
|
||||||
|
}
|
||||||
|
|
||||||
/* Shows a custom subsong title by default with subsong name, to simplify for average users.
|
void input_vgmstream::put_info_tags(file_info& p_info, vgmstream_info_t& v_info) {
|
||||||
* This can be overriden and extended using the exported STREAM_x below and foobar's formatting.
|
|
||||||
* foobar defaults to filename minus extension if there is no meta "title" value. */
|
|
||||||
if (!override_title) {
|
if (!override_title) {
|
||||||
p_info.meta_set("TITLE",temp);
|
/* Shows a default (sub)song title with stream name.
|
||||||
}
|
* This can be overriden and extended using the exported STREAM_x below and foobar's formatting.
|
||||||
if (get_description_tag(temp,description,"stream count: ")) p_info.meta_set("stream_count",temp);
|
* foobar defaults to filename minus extension if there is no meta "title" value. */
|
||||||
if (get_description_tag(temp,description,"stream index: ")) p_info.meta_set("stream_index",temp);
|
p_info.meta_set("TITLE", v_info.title);
|
||||||
if (get_description_tag(temp,description,"stream name: ")) p_info.meta_set("stream_name",temp);
|
|
||||||
if (loop_end) {
|
|
||||||
p_info.meta_set("loop_start", pfc::format_int(loop_start));
|
|
||||||
p_info.meta_set("loop_end", pfc::format_int(loop_end));
|
|
||||||
// has extra text info
|
|
||||||
//if (get_description_tag(temp,description,"loop start: ")) p_info.meta_set("loop_start",temp);
|
|
||||||
//if (get_description_tag(temp,description,"loop end: ")) p_info.meta_set("loop_end",temp);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* get external file tags */
|
if (!v_info.stream_name.is_empty()) {
|
||||||
//todo optimize and don't parse tags again for this session (not sure how), seems foobar
|
p_info.meta_set("stream_name", v_info.stream_name);
|
||||||
// calls get_info on every play even if the file hasn't changes, and won't refresh "meta"
|
}
|
||||||
// unless forced or closing playlist+exe
|
|
||||||
if (!tagfile_disable) {
|
|
||||||
//todo use foobar's fancy-but-arcane string functions
|
|
||||||
char tagfile_path[PATH_LIMIT];
|
|
||||||
strcpy(tagfile_path, filename);
|
|
||||||
|
|
||||||
char *path = strrchr(tagfile_path,'\\');
|
if (v_info.subsong_count > 1) {
|
||||||
if (path!=NULL) {
|
p_info.meta_set("stream_count", pfc::format_int(v_info.subsong_count));
|
||||||
path[1] = '\0'; /* includes "\", remove after that from tagfile_path */
|
p_info.meta_set("stream_index", pfc::format_int(v_info.subsong_index == 0 ? 1 : v_info.subsong_index));
|
||||||
strcat(tagfile_path,tagfile_name);
|
}
|
||||||
|
|
||||||
|
if (v_info.loop_end > 0) {
|
||||||
|
p_info.meta_set("loop_start", pfc::format_int(v_info.loop_start));
|
||||||
|
p_info.meta_set("loop_end", pfc::format_int(v_info.loop_end));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void input_vgmstream::put_into_tagfile(file_info& p_info, abort_callback& p_abort) {
|
||||||
|
if (tagfile_disable)
|
||||||
|
return;
|
||||||
|
//TODO: optimize and don't parse tags again for this session (not sure how).
|
||||||
|
// Seems foobar calls get_info on every play even if the file hasn't changed,
|
||||||
|
// and won't refresh "meta" unless forced or closing playlist + exe.
|
||||||
|
|
||||||
|
//TODO: use foobar's fancy-but-arcane string functions
|
||||||
|
char tagfile_path[FOO_PATH_LIMIT];
|
||||||
|
strcpy(tagfile_path, filename);
|
||||||
|
|
||||||
|
char* path = strrchr(tagfile_path, '\\');
|
||||||
|
if (path != NULL) {
|
||||||
|
path[1] = '\0'; // includes "\", remove after that from tagfile_path
|
||||||
|
strcat(tagfile_path, tagfile_name);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// possible?
|
||||||
|
strcpy(tagfile_path, tagfile_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
STREAMFILE* sf_tags = open_foo_streamfile(tagfile_path, &p_abort, NULL);
|
||||||
|
if (sf_tags == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
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, sf_tags)) {
|
||||||
|
if (replaygain_info::g_is_meta_replaygain(tag_key)) {
|
||||||
|
p_info.info_set_replaygain(tag_key, tag_val);
|
||||||
|
// there is set_replaygain_auto/set_replaygain_ex too but no doc
|
||||||
}
|
}
|
||||||
else { /* ??? */
|
else if (stricmp_utf8("ALBUMARTIST", tag_key) == 0) {
|
||||||
strcpy(tagfile_path,tagfile_name);
|
// normalize tag for foobar as otherwise can't understand it (though it's recognized in .ogg)
|
||||||
|
p_info.meta_set("ALBUM ARTIST", tag_val);
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
STREAMFILE* sf_tags = open_foo_streamfile(tagfile_path, &p_abort, NULL);
|
p_info.meta_set(tag_key, tag_val);
|
||||||
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, 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 */
|
|
||||||
}
|
|
||||||
else if (stricmp_utf8("ALBUMARTIST", tag_key) == 0)
|
|
||||||
/* normalize as foobar won't handle (though it's accepted in .ogg) */
|
|
||||||
p_info.meta_set("ALBUM ARTIST", tag_val);
|
|
||||||
else {
|
|
||||||
p_info.meta_set(tag_key, tag_val);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
vgmstream_tags_close(tags);
|
|
||||||
close_streamfile(sf_tags);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
vgmstream_tags_close(tags);
|
||||||
|
close_streamfile(sf_tags);
|
||||||
|
}
|
||||||
|
|
||||||
/* set technical info (details tab in file properties) */
|
// include main info, note that order doesn't matter (foobar sorts by fixed order + name)
|
||||||
|
void input_vgmstream::put_info_details(file_info& p_info, vgmstream_info_t& v_info) {
|
||||||
|
p_info.info_set("vgmstream_version", PLUGIN_VERSION); //to make clearer vgmsrteam is actually opening the file
|
||||||
|
|
||||||
p_info.info_set("vgmstream_version", PLUGIN_VERSION);
|
// not quite accurate but some people are confused by "lossless"
|
||||||
p_info.info_set_int("samplerate", samplerate);
|
// (could set lossless if PCM, but then again in vgm may be converted/"lossy" vs original source)
|
||||||
p_info.info_set_int("channels", channels);
|
p_info.info_set("encoding", "lossy/lossless");
|
||||||
p_info.info_set_int("bitspersample", 16);
|
|
||||||
/* not quite accurate but some people are confused by "lossless"
|
p_info.info_set_int("channels", v_info.channels);
|
||||||
* (could set lossless if PCM, but then again PCMFloat or PCM8 are converted/"lossy" in vgmstream) */
|
p_info.info_set_int("samplerate", v_info.sample_rate);
|
||||||
p_info.info_set("encoding","lossy/lossless");
|
p_info.info_set_int("bitspersample", v_info.bits_per_sample);
|
||||||
p_info.info_set_bitrate(bitrate / 1000);
|
p_info.info_set_bitrate(v_info.bitrate / 1000);
|
||||||
if (total_samples > 0)
|
|
||||||
p_info.info_set_int("stream_total_samples", total_samples);
|
if (v_info.input_channels > 0 && v_info.channels != v_info.input_channels) {
|
||||||
if (loop_start >= 0 && loop_end > loop_start) {
|
p_info.info_set_int("input_channels", v_info.input_channels);
|
||||||
if (!loop_flag) p_info.info_set("looping", "disabled");
|
//p_info.info_set_int("output_channels", v_info.output_channels);
|
||||||
p_info.info_set_int("loop_start", loop_start);
|
|
||||||
p_info.info_set_int("loop_end", loop_end);
|
|
||||||
}
|
}
|
||||||
p_info.set_length(((double)length_in_ms)/1000);
|
|
||||||
|
|
||||||
if (get_description_tag(temp,description,"encoding: ")) p_info.info_set("codec",temp);
|
p_info.set_length(v_info.play_length_s);
|
||||||
if (get_description_tag(temp,description,"layout: ")) p_info.info_set("layout",temp);
|
|
||||||
if (get_description_tag(temp,description,"interleave: ",' ')) p_info.info_set("interleave",temp);
|
|
||||||
if (get_description_tag(temp,description,"interleave last block:",' ')) p_info.info_set("interleave_last_block",temp);
|
|
||||||
|
|
||||||
if (get_description_tag(temp,description,"block size: ")) p_info.info_set("block_size",temp);
|
if (v_info.stream_samples > 0) { // ?
|
||||||
if (get_description_tag(temp,description,"metadata from: ")) p_info.info_set("metadata_source",temp);
|
p_info.info_set_int("samples", v_info.stream_samples);
|
||||||
if (get_description_tag(temp,description,"stream count: ")) p_info.info_set("stream_count",temp);
|
}
|
||||||
if (get_description_tag(temp,description,"stream index: ")) p_info.info_set("stream_index",temp);
|
|
||||||
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 (v_info.loop_end > 0) {
|
||||||
if (get_description_tag(temp,description,"output channels: ")) p_info.info_set("output_channels",temp);
|
p_info.info_set_int("loop_start", v_info.loop_start);
|
||||||
if (get_description_tag(temp,description,"input channels: ")) p_info.info_set("input_channels",temp);
|
p_info.info_set_int("loop_end", v_info.loop_end);
|
||||||
|
if (!v_info.loop_flag)
|
||||||
|
p_info.info_set("looping", "disabled");
|
||||||
|
}
|
||||||
|
|
||||||
|
p_info.info_set("codec", v_info.codec_name);
|
||||||
|
p_info.info_set("layout", v_info.layout_name);
|
||||||
|
p_info.info_set("metadata", v_info.meta_name);
|
||||||
|
|
||||||
|
if (!v_info.stream_name.is_empty()) {
|
||||||
|
p_info.info_set("stream_name", v_info.stream_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (v_info.subsong_count > 1) {
|
||||||
|
p_info.info_set_int("stream_count", v_info.subsong_count);
|
||||||
|
p_info.info_set_int("stream_index", v_info.subsong_index);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!v_info.channel_mask.is_empty()) {
|
||||||
|
p_info.info_set("channel_mask", v_info.channel_mask);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
// for >2ch foobar writes info like "Channels - 8: FL FR FC LFE BL BR FCL FCR", which may be undesirable?
|
||||||
|
if (v_info.channel_layout > 0) {
|
||||||
|
// there is info_set_channels_ex in newer SDKs too
|
||||||
|
p_info.info_set_wfx_chanMask(v_info.channel_layout);
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
//if (v_info.interleave > 0) p_info.info_set("interleave", v_info.interleave);
|
||||||
|
//if (v_info.interleave_last > 0) p_info.info_set("interleave_last_block", v_info.interleave_last);
|
||||||
|
//if (v_info.block_size > 0) p_info.info_set("block_size", v_info.block_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
t_filestats input_vgmstream::get_file_stats(abort_callback & p_abort) {
|
t_filestats input_vgmstream::get_file_stats(abort_callback & p_abort) {
|
||||||
@ -283,8 +316,10 @@ void input_vgmstream::decode_initialize(t_uint32 p_subsong, unsigned p_flags, ab
|
|||||||
|
|
||||||
// called when audio buffer needs to be filled
|
// 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 (!decoding)
|
||||||
if (!vgmstream) return false;
|
return false;
|
||||||
|
if (!vgmstream)
|
||||||
|
return false;
|
||||||
|
|
||||||
int max_buffer_samples = SAMPLE_BUFFER_SIZE;
|
int max_buffer_samples = SAMPLE_BUFFER_SIZE;
|
||||||
int samples_to_do = max_buffer_samples;
|
int samples_to_do = max_buffer_samples;
|
||||||
@ -319,44 +354,46 @@ bool input_vgmstream::decode_run(audio_chunk & p_chunk, abort_callback & p_abort
|
|||||||
}
|
}
|
||||||
|
|
||||||
// called when seeking
|
// called when seeking
|
||||||
void input_vgmstream::decode_seek(double p_seconds, abort_callback & p_abort) {
|
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);
|
int64_t seek_sample = (int64_t)audio_math::time_to_samples(p_seconds, vgmstream->sample_rate);
|
||||||
bool play_forever = vgmstream_get_play_forever(vgmstream);
|
bool play_forever = vgmstream_get_play_forever(vgmstream);
|
||||||
|
|
||||||
|
// TODO: check play position after seek_sample and let seek clamp
|
||||||
// possible when disabling looping without refreshing foobar's cached song length
|
// possible when disabling looping without refreshing foobar's cached song length
|
||||||
// (p_seconds can't go over seek bar with infinite looping on, though)
|
// (p_seconds can't go over seek bar with infinite looping on, though)
|
||||||
if (seek_sample > length_samples)
|
if (seek_sample > length_samples)
|
||||||
seek_sample = length_samples;
|
seek_sample = length_samples;
|
||||||
|
|
||||||
seek_vgmstream(vgmstream, seek_sample);
|
seek_vgmstream(vgmstream, (int32_t)seek_sample);
|
||||||
|
|
||||||
decode_pos_samples = seek_sample;
|
decode_pos_samples = seek_sample;
|
||||||
decode_pos_ms = decode_pos_samples * 1000LL / vgmstream->sample_rate;
|
decode_pos_ms = decode_pos_samples * 1000LL / vgmstream->sample_rate;
|
||||||
decoding = play_forever || decode_pos_samples < length_samples;
|
decoding = play_forever || decode_pos_samples < length_samples;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool input_vgmstream::decode_can_seek() {return true;}
|
bool input_vgmstream::decode_can_seek() { return true; }
|
||||||
bool input_vgmstream::decode_get_dynamic_info(file_info & p_out, double & p_timestamp_delta) { return false; }
|
bool input_vgmstream::decode_get_dynamic_info(file_info& p_out, double& p_timestamp_delta) { return false; }
|
||||||
bool input_vgmstream::decode_get_dynamic_info_track(file_info & p_out, double & p_timestamp_delta) {return false;}
|
bool input_vgmstream::decode_get_dynamic_info_track(file_info& p_out, double& p_timestamp_delta) { return false; }
|
||||||
void input_vgmstream::decode_on_idle(abort_callback & p_abort) {/*m_file->on_idle(p_abort);*/}
|
void input_vgmstream::decode_on_idle(abort_callback& p_abort) { /*m_file->on_idle(p_abort);*/ }
|
||||||
|
|
||||||
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_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();*/ }
|
void input_vgmstream::retag_commit(abort_callback& p_abort) { /*throw exception_io_data();*/ }
|
||||||
void input_vgmstream::remove_tags(abort_callback & p_abort) { /*throw exception_io_data();*/ }
|
void input_vgmstream::remove_tags(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
|
// 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) {
|
bool input_vgmstream::g_is_our_path(const char* p_path, const char* p_extension) {
|
||||||
vgmstream_ctx_valid_cfg cfg = {0};
|
vgmstream_ctx_valid_cfg cfg = {0};
|
||||||
|
|
||||||
cfg.is_extension = 1;
|
cfg.is_extension = true;
|
||||||
input_vgmstream::g_load_cfg(&cfg.accept_unknown, &cfg.accept_common);
|
input_vgmstream::g_load_cfg(&cfg.accept_unknown, &cfg.accept_common);
|
||||||
return vgmstream_ctx_is_valid(p_extension, &cfg) > 0 ? true : false;
|
return vgmstream_ctx_is_valid(p_extension, &cfg) > 0 ? true : false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// internal util to create a VGMSTREAM
|
// 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* input_vgmstream::init_vgmstream_foo(t_uint32 p_subsong, const char* const filename, abort_callback& p_abort) {
|
||||||
VGMSTREAM* vgmstream = NULL;
|
VGMSTREAM* vgmstream = NULL;
|
||||||
|
|
||||||
/* Workaround for a foobar bug (mainly for complex TXTP):
|
/* Workaround for a foobar bug (mainly for complex TXTP):
|
||||||
@ -387,10 +424,8 @@ void input_vgmstream::setup_vgmstream(abort_callback & p_abort) {
|
|||||||
|
|
||||||
// subsong and filename are always defined before this
|
// subsong and filename are always defined before this
|
||||||
vgmstream = init_vgmstream_foo(subsong, filename, p_abort);
|
vgmstream = init_vgmstream_foo(subsong, filename, p_abort);
|
||||||
if (!vgmstream) {
|
if (!vgmstream)
|
||||||
throw exception_io_data();
|
throw exception_io_data();
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// default subsong is 0, meaning first init (vgmstream should open first stream, but not set stream_index).
|
// default subsong is 0, meaning first init (vgmstream should open first stream, but not set stream_index).
|
||||||
// if the stream_index is already set, then the subsong was opened directly by some means (txtp, playlist, etc).
|
// if the stream_index is already set, then the subsong was opened directly by some means (txtp, playlist, etc).
|
||||||
@ -414,92 +449,10 @@ void input_vgmstream::setup_vgmstream(abort_callback & p_abort) {
|
|||||||
length_samples = vgmstream_get_samples(vgmstream);
|
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;
|
|
||||||
bool is_infostream = false;
|
|
||||||
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
|
|
||||||
// there is no need to recreate the infostream, there is only one subsong used
|
|
||||||
if (subsong != p_subsong && !direct_subsong) {
|
|
||||||
infostream = init_vgmstream_foo(p_subsong, filename, p_abort);
|
|
||||||
if (!infostream) {
|
|
||||||
throw exception_io_data();
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
info_channels = output_channels;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
if (length_in_ms) {
|
|
||||||
*length_in_ms = -1000;
|
|
||||||
if (infostream) {
|
|
||||||
*channels = info_channels;
|
|
||||||
*sample_rate = infostream->sample_rate;
|
|
||||||
*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 = vgmstream_get_samples(infostream);
|
|
||||||
*length_in_ms = num_samples*1000LL / infostream->sample_rate;
|
|
||||||
|
|
||||||
describe_vgmstream(infostream, temp, sizeof(temp));
|
|
||||||
description = temp;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* infostream gets added with index 0 (other) or 1 (current) */
|
|
||||||
if (infostream && title) {
|
|
||||||
vgmstream_title_t tcfg = {0};
|
|
||||||
tcfg.remove_extension = 1;
|
|
||||||
tcfg.remove_archive = 1;
|
|
||||||
|
|
||||||
const char* filename_str = filename;
|
|
||||||
vgmstream_get_title(temp, sizeof(temp), filename_str, infostream, &tcfg);
|
|
||||||
title = temp;
|
|
||||||
}
|
|
||||||
|
|
||||||
// and only close if was querying a new subsong
|
|
||||||
if (is_infostream) {
|
|
||||||
close_vgmstream(infostream);
|
|
||||||
infostream = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool input_vgmstream::get_description_tag(pfc::string_base & temp, pfc::string_base const& description, const char *tag, char delimiter) {
|
|
||||||
// extract a "tag" from the description string
|
|
||||||
t_size pos = description.find_first(tag);
|
|
||||||
t_size eos;
|
|
||||||
if (pos != pfc::infinite_size) {
|
|
||||||
pos += strlen(tag);
|
|
||||||
eos = description.find_first(delimiter, pos);
|
|
||||||
if (eos == pfc::infinite_size) eos = description.length();
|
|
||||||
temp.set_string(description + pos, eos - pos);
|
|
||||||
//console::formatter() << "tag=" << tag << ", delim=" << delimiter << "temp=" << temp << ", pos=" << pos << "" << eos;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void input_vgmstream::apply_config(VGMSTREAM* vgmstream) {
|
void input_vgmstream::apply_config(VGMSTREAM* vgmstream) {
|
||||||
vgmstream_cfg_t vcfg = {0};
|
vgmstream_cfg_t vcfg = {0};
|
||||||
|
|
||||||
vcfg.allow_play_forever = 1;
|
vcfg.allow_play_forever = true;
|
||||||
vcfg.play_forever = loop_forever;
|
vcfg.play_forever = loop_forever;
|
||||||
vcfg.loop_count = loop_count;
|
vcfg.loop_count = loop_count;
|
||||||
vcfg.fade_time = fade_seconds;
|
vcfg.fade_time = fade_seconds;
|
||||||
@ -509,23 +462,132 @@ void input_vgmstream::apply_config(VGMSTREAM* vgmstream) {
|
|||||||
vgmstream_apply_config(vgmstream, &vcfg);
|
vgmstream_apply_config(vgmstream, &vcfg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// internal util to get info
|
||||||
|
void input_vgmstream::query_subsong_info(t_uint32 p_subsong, vgmstream_info_t& v_info, abort_callback& p_abort) {
|
||||||
|
VGMSTREAM* infostream = NULL;
|
||||||
|
bool is_infostream = false;
|
||||||
|
int info_channels;
|
||||||
|
char temp[1024];
|
||||||
|
|
||||||
|
// Reuse current vgmstream if not querying a new subsong.
|
||||||
|
// If it's a direct subsong then subsong may be N while p_subsong = 1
|
||||||
|
// so there is no need to recreate the infostream, only one subsong is used.
|
||||||
|
if (subsong != p_subsong && !direct_subsong) {
|
||||||
|
infostream = init_vgmstream_foo(p_subsong, filename, p_abort);
|
||||||
|
if (!infostream)
|
||||||
|
throw exception_io_data();
|
||||||
|
|
||||||
|
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;
|
||||||
|
info_channels = output_channels;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!infostream)
|
||||||
|
throw exception_io_data();
|
||||||
|
|
||||||
|
/* basic info */
|
||||||
|
{
|
||||||
|
v_info.channels = info_channels;
|
||||||
|
v_info.input_channels = infostream->channels;
|
||||||
|
v_info.sample_rate = infostream->sample_rate;
|
||||||
|
v_info.stream_samples = infostream->num_samples;
|
||||||
|
v_info.bitrate = get_vgmstream_average_bitrate(infostream);
|
||||||
|
v_info.loop_flag = infostream->loop_flag;
|
||||||
|
v_info.loop_start = infostream->loop_start_sample;
|
||||||
|
v_info.loop_end = infostream->loop_end_sample;
|
||||||
|
|
||||||
|
v_info.subsong_count = infostream->num_streams;
|
||||||
|
v_info.subsong_index = infostream->stream_index;
|
||||||
|
if (v_info.subsong_index == 0)
|
||||||
|
v_info.subsong_index = 1;
|
||||||
|
|
||||||
|
v_info.channel_layout = infostream->channel_layout;
|
||||||
|
|
||||||
|
int64_t play_duration = vgmstream_get_samples(infostream);
|
||||||
|
v_info.play_length_s = (double)play_duration / (double)infostream->sample_rate;
|
||||||
|
|
||||||
|
v_info.bits_per_sample = sizeof(short) * 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
// formatted info
|
||||||
|
{
|
||||||
|
pfc::string8 description;
|
||||||
|
|
||||||
|
describe_vgmstream(infostream, temp, sizeof(temp));
|
||||||
|
description = temp;
|
||||||
|
|
||||||
|
query_description_tag(v_info.codec_name, description, "encoding: ");
|
||||||
|
query_description_tag(v_info.layout_name, description, "layout: ");
|
||||||
|
query_description_tag(v_info.meta_name, description, "metadata from: ");
|
||||||
|
query_description_tag(v_info.stream_name, description, "stream name: ");
|
||||||
|
query_description_tag(v_info.channel_mask, description, "channel mask: ");
|
||||||
|
|
||||||
|
//query_description_tag(temp, description,"interleave: ",' ');
|
||||||
|
//query_description_tag(temp, description,"interleave last block:",' ');
|
||||||
|
//query_description_tag(temp, description,"block size: ");
|
||||||
|
}
|
||||||
|
|
||||||
|
// infostream gets added with index 0 (other) or 1 (current)
|
||||||
|
{
|
||||||
|
vgmstream_title_t tcfg = {0};
|
||||||
|
tcfg.remove_extension = true;
|
||||||
|
tcfg.remove_archive = true;
|
||||||
|
|
||||||
|
const char* filename_str = filename;
|
||||||
|
vgmstream_get_title(temp, sizeof(temp), filename_str, infostream, &tcfg);
|
||||||
|
v_info.title = temp;
|
||||||
|
}
|
||||||
|
|
||||||
|
// and only close if was querying a new subsong
|
||||||
|
if (is_infostream) {
|
||||||
|
close_vgmstream(infostream);
|
||||||
|
infostream = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// extract a "tag" from the description string
|
||||||
|
bool input_vgmstream::query_description_tag(pfc::string_base& tag_value, pfc::string_base const& description, const char* tag_key, char delimiter) {
|
||||||
|
|
||||||
|
t_size pos = description.find_first(tag_key);
|
||||||
|
if (pos == pfc::infinite_size)
|
||||||
|
return false;
|
||||||
|
pos += strlen(tag_key);
|
||||||
|
|
||||||
|
t_size eos = description.find_first(delimiter, pos);
|
||||||
|
if (eos == pfc::infinite_size)
|
||||||
|
eos = description.length();
|
||||||
|
|
||||||
|
tag_value.set_string(description + pos, eos - pos);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// checks priority (foobar 1.4+)
|
||||||
|
bool input_vgmstream::g_is_low_merit() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// foobar recognizes plugin with this (meaning, different GUID = different plugin)
|
||||||
GUID input_vgmstream::g_get_guid() {
|
GUID input_vgmstream::g_get_guid() {
|
||||||
static const GUID guid = { 0x9e7263c7, 0x4cdd, 0x482c,{ 0x9a, 0xec, 0x5e, 0x71, 0x28, 0xcb, 0xc3, 0x4 } };
|
static const GUID guid = { 0x9e7263c7, 0x4cdd, 0x482c,{ 0x9a, 0xec, 0x5e, 0x71, 0x28, 0xcb, 0xc3, 0x4 } };
|
||||||
return guid;
|
return guid;
|
||||||
}
|
}
|
||||||
|
|
||||||
const char * input_vgmstream::g_get_name() {
|
|
||||||
return "vgmstream";
|
|
||||||
}
|
|
||||||
|
|
||||||
GUID input_vgmstream::g_get_preferences_guid() {
|
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;
|
return guid;
|
||||||
}
|
}
|
||||||
|
|
||||||
// checks priority (foobar 1.4+)
|
const char* input_vgmstream::g_get_name() {
|
||||||
bool input_vgmstream::g_is_low_merit() {
|
return "vgmstream";
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// foobar plugin defs
|
// foobar plugin defs
|
||||||
|
@ -1,12 +1,41 @@
|
|||||||
#ifndef _FOO_VGMSTREAM_
|
#ifndef _FOO_VGMSTREAM_
|
||||||
#define _FOO_VGMSTREAM_
|
#define _FOO_VGMSTREAM_
|
||||||
|
|
||||||
#define SAMPLE_BUFFER_SIZE 1024
|
#define SAMPLE_BUFFER_SIZE 1024
|
||||||
|
#define FOO_PATH_LIMIT 4096 /* see vgmstream_limits.h*/
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#include "../src/vgmstream.h"
|
#include "../src/vgmstream.h"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
pfc::string8_fast title;
|
||||||
|
pfc::string8_fast stream_name;
|
||||||
|
pfc::string8_fast layout_name;
|
||||||
|
pfc::string8_fast codec_name;
|
||||||
|
pfc::string8_fast meta_name;
|
||||||
|
pfc::string8_fast channel_mask;
|
||||||
|
|
||||||
|
int channels;
|
||||||
|
int sample_rate;
|
||||||
|
|
||||||
|
int input_channels;
|
||||||
|
int subsong_count;
|
||||||
|
int subsong_index;
|
||||||
|
|
||||||
|
int bits_per_sample;
|
||||||
|
|
||||||
|
int64_t stream_samples;
|
||||||
|
int64_t loop_start;
|
||||||
|
int64_t loop_end;
|
||||||
|
bool loop_flag;
|
||||||
|
|
||||||
|
int bitrate;
|
||||||
|
uint32_t channel_layout;
|
||||||
|
|
||||||
|
double play_length_s;
|
||||||
|
|
||||||
|
} vgmstream_info_t;
|
||||||
|
|
||||||
class input_vgmstream : public input_stubs {
|
class input_vgmstream : public input_stubs {
|
||||||
public:
|
public:
|
||||||
@ -65,7 +94,7 @@ class input_vgmstream : public input_stubs {
|
|||||||
double fade_delay_seconds;
|
double fade_delay_seconds;
|
||||||
double loop_count;
|
double loop_count;
|
||||||
bool loop_forever;
|
bool loop_forever;
|
||||||
int ignore_loop;
|
bool ignore_loop;
|
||||||
bool disable_subsongs;
|
bool disable_subsongs;
|
||||||
|
|
||||||
int downmix_channels;
|
int downmix_channels;
|
||||||
@ -76,13 +105,19 @@ class input_vgmstream : public input_stubs {
|
|||||||
//bool exts_unknown_on;
|
//bool exts_unknown_on;
|
||||||
|
|
||||||
/* helpers */
|
/* helpers */
|
||||||
|
void load_settings();
|
||||||
|
|
||||||
|
void put_info_tags(file_info& p_info, vgmstream_info_t& v_info);
|
||||||
|
void put_into_tagfile(file_info& p_info, abort_callback& p_abort);
|
||||||
|
void put_info_details(file_info& p_info, vgmstream_info_t& v_info);
|
||||||
|
|
||||||
VGMSTREAM* init_vgmstream_foo(t_uint32 p_subsong, const char* const filename, abort_callback& p_abort);
|
VGMSTREAM* init_vgmstream_foo(t_uint32 p_subsong, const char* const filename, abort_callback& p_abort);
|
||||||
void setup_vgmstream(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 apply_config(VGMSTREAM* vgmstream);
|
void apply_config(VGMSTREAM* vgmstream);
|
||||||
|
|
||||||
|
void query_subsong_info(t_uint32 p_subsong, vgmstream_info_t& v_info, abort_callback& p_abort);
|
||||||
|
bool query_description_tag(pfc::string_base& temp, pfc::string_base const& description, const char* tag, char delimiter = '\n');
|
||||||
|
|
||||||
static void g_load_cfg(int* accept_unknown, int* accept_common);
|
static void g_load_cfg(int* accept_unknown, int* accept_common);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user