mirror of
https://github.com/vgmstream/vgmstream.git
synced 2024-11-28 00:20:47 +01:00
Add subsongs and !tags.m3u support for Audacious
This commit is contained in:
parent
a76abbcf17
commit
bc80041925
74
README.md
74
README.md
@ -54,7 +54,7 @@ Converts playable files to wav. Typical usage would be:
|
||||
- `test.exe -o happy.wav happy.adx` to decode `happy.adx` to `happy.wav`.
|
||||
|
||||
If command-line isn't your thing you can also drag and drop files to the
|
||||
executable to decode them as (filename).wav
|
||||
executable to decode them as `(filename).wav`.
|
||||
|
||||
There are multiple options that alter how the file is converted, for example:
|
||||
- `test.exe -m -o file.wav file.adx`: print info but don't decode
|
||||
@ -83,27 +83,30 @@ For example `test.exe -s 2 -o ?04s_?n.wav file.fsb` could generate `0002_song1.w
|
||||
|
||||
|
||||
### in_vgmstream
|
||||
*Installation*: drop the ```in_vgmstream.dll``` in your Winamp plugins directory,
|
||||
*Installation*: drop the `in_vgmstream.dll` in your Winamp plugins directory,
|
||||
and follow the above instructions for installing the other files needed.
|
||||
|
||||
Once installed supported files should be playable.
|
||||
|
||||
### xmp-vgmstream
|
||||
*Installation*: drop the ```xmp-vgmstream.dll``` in your XMPlay plugins directory,
|
||||
*Installation*: drop the `xmp-vgmstream.dll` in your XMPlay plugins directory,
|
||||
and follow the above instructions for installing the other files needed.
|
||||
|
||||
Note that this has less features compared to in_vgmstream and has no configuration.
|
||||
Since XMPlay supports Winamp plugins you may also use ```in_vgmstream.dll``` instead.
|
||||
Since XMPlay supports Winamp plugins you may also use `in_vgmstream.dll` instead.
|
||||
|
||||
Because the XMPlay MP3 decoder incorrectly tries to play some vgmstream exts,
|
||||
you need to manually fix it by going to **options > plugins > input > vgmstream**
|
||||
and in the "priority filetypes" put: `ahx,asf,awc,ckd,fsb,genh,msf,p3d,rak,scd,txth,xvag`
|
||||
|
||||
XMPlay cannot support subsongs due to player limitations, try using *TXTP* instead
|
||||
(explained below).
|
||||
|
||||
### foo_input_vgmstream
|
||||
*Installation*: every file should be installed automatically by the `.fb2k-component`
|
||||
bundle.
|
||||
|
||||
A known quirk is that when loop options or tags change, playlist won't refresh
|
||||
A known quirk is that when loop options or tags change, playlist info won't refresh
|
||||
automatically. You need to manually refresh it by selecting songs and doing
|
||||
**shift + right click > Tagging > Reload info from file(s)**.
|
||||
|
||||
@ -149,6 +152,11 @@ not meant to be extracted (no simple separation). Some plugins are able to "unpa
|
||||
those files automatically into the playlist. For others without support, you can create
|
||||
multiple .txtp (explained below) to select one of the subsongs (like `bgm.sxd#10.txtp`).
|
||||
|
||||
You can use this python script to autogenerate one `.txtp` per subsong:
|
||||
https://github.com/losnoco/vgmstream/tree/master/cli/txtp_maker.py
|
||||
Put in the same dir as test.exe/vgmstream_cli, then to drag-and-drop files with subsongs
|
||||
to `txtp_maker.py`.
|
||||
|
||||
### Renamed files
|
||||
A few extensions that vgmstream supports clash with common ones. Since players
|
||||
like foobar or Winamp don't react well to that, they may be renamed to make
|
||||
@ -180,7 +188,7 @@ internal loop info, or apply subtle fixes, but is also limited in some ways
|
||||
may work as a last resort to make a file playable.
|
||||
|
||||
Some plugins have options that allow any extension (common or unknown) to be
|
||||
played, making renaming is unnecessary (may need to adjust plugin priority in
|
||||
played, making renaming unnecessary (may need to adjust plugin priority in
|
||||
player's options).
|
||||
|
||||
Also be aware that some plugins can tell the player they handle some extension,
|
||||
@ -269,8 +277,8 @@ complex and non-standard ways, like playing multiple small songs as a single
|
||||
one, or using some channels as a section of the song. For those cases we
|
||||
can use create a *TXTP* file.
|
||||
|
||||
**TXTP**: a text player configurator named `(name).txtp`. Text inside can
|
||||
contain a list of filenames to play as one (ex. `intro.vag(line)loop.vag`),
|
||||
**TXTP**: text files with player configuration, named `(name).txtp`. Text inside
|
||||
can contain a list of filenames to play as one (ex. `intro.vag(line)loop.vag`),
|
||||
list of separate channel files to join as a single multichannel file,
|
||||
subsong index (ex. `bgm.sxd#10`), per-file configurations like number of
|
||||
loops, remove unneeded channels, and many other features.
|
||||
@ -294,7 +302,7 @@ However other plugins may set themselves higher, stealing formats instead.
|
||||
If current Audacious version doesn't let to change plugin priority you may
|
||||
need to disable some plugins (requires restart) or set priority on compile
|
||||
time. Particularly, mpg123 plugin may steal formats that aren't even MP3,
|
||||
making impossible for vgmstream to play it properly.
|
||||
making impossible for vgmstream to play them properly.
|
||||
|
||||
### Channel issues
|
||||
Some games layer a huge number of channels, that are disabled or downmixed
|
||||
@ -303,7 +311,8 @@ foobar can only play up to 8 channels, and Winamp depends on your sound
|
||||
card). For those files you can set the "downmix" option in vgmstream, that
|
||||
can reduce the number of channels to a playable amount. Note that this type
|
||||
of downmixing is very generic, not meant to be used when converting to other
|
||||
formats.
|
||||
formats (channels are re-assigned and volumes modified in simplistic ways,
|
||||
since it can't guess how the file should be properly adjusted).
|
||||
|
||||
You can also choose which channels to play using *TXTP*. For example, create
|
||||
a file named `song.adx#C1,2.txtp` to play only channels 1 and 2 from `song.adx`.
|
||||
@ -327,10 +336,10 @@ filename1
|
||||
# %LOCAL_TAG text (applies to next track only)
|
||||
filename2
|
||||
```
|
||||
Accepted tags depend on the player (foobar: any; winamp: see ATF config),
|
||||
typically *ALBUM/ARTIST/TITLE/DISC/TRACK/COMPOSER/etc*, lower or uppercase,
|
||||
separated by one or multiple spaces. Repeated tags overwrite previous (ex.-
|
||||
may define *@COMPOSER* multiple times for "sections"). It only reads up to
|
||||
Accepted tags depend on the player (foobar: any; winamp: see ATF config, Audacious:
|
||||
few standard ones), typically *ALBUM/ARTIST/TITLE/DISC/TRACK/COMPOSER/etc*, lower
|
||||
or uppercase, separated by one or multiple spaces. Repeated tags overwrite previous
|
||||
(ex.- may define *@COMPOSER* multiple times for "sections"). It only reads up to
|
||||
current *filename* though, so any *@TAG* below would be ignored.
|
||||
|
||||
Playlist title formatting should follow player's config. ASCII or UTF-8 tags work.
|
||||
@ -359,6 +368,8 @@ filename1
|
||||
```
|
||||
As a side effect if text has @/% inside you also need them: `# @ALBUMARTIST@ Tom-H@ck`
|
||||
|
||||
For interoperability with other plugins, consider using only common tags without spaces.
|
||||
|
||||
### ReplayGain
|
||||
foobar2000/Winamp can apply the following replaygain tags (if ReplayGain is
|
||||
enabled in preferences):
|
||||
@ -414,18 +425,22 @@ If your player isn't picking tags make sure vgmstream is detecting the song
|
||||
(as other plugins can steal its extensions, see above), .m3u is properly
|
||||
named and that filenames inside match the song filename. For Winamp you need
|
||||
to make sure *options > titles > advanced title formatting* checkbox is set and
|
||||
the format defined. For foobar2000 don't forget you need to force refresh when
|
||||
tags change (for reasons outside vgmstream's control):
|
||||
**select songs > shift + right click > Tagging > Reload info from file(s)**.
|
||||
the format defined.
|
||||
|
||||
Currently there is no tool to aid in the creation of there m3u, but you can create
|
||||
a base m3u and edit as a text file.
|
||||
When tags change behavior varies depending on player:
|
||||
- *Winamp*: should refresh tags when file is played again.
|
||||
- *foobar2000*: needs to force refresh (for reasons outside vgmstream's control)
|
||||
- **select songs > shift + right click > Tagging > Reload info from file(s)**.
|
||||
- *Audacious*: files need to be readded to the playlist
|
||||
|
||||
Currently there is no tool to aid in the creation of these tags, but you can create
|
||||
a base .m3u and edit as a text file.
|
||||
|
||||
vgmstream's "m3u tagging" is meant to be simple to make and share (just a text
|
||||
file), easier to support in multiple players (rather than needing a custom plugin),
|
||||
having OST-like ordering in the M3U, and be flexible enough to have commands.
|
||||
If you are not satisfied with vgmstream's tagging format, foobar2000 has other
|
||||
plugins (with write support) that may be of use:
|
||||
allow OST-like ordering but also combinable with other .m3u, and be flexible enough
|
||||
to have commands. If you are not satisfied with vgmstream's tagging format,
|
||||
foobar2000 has other plugins (with write support) that may be of use:
|
||||
- m-TAGS: http://www.m-tags.org/
|
||||
- foo_external_tags: https://foobar.hyv.fi/?view=foo_external_tags
|
||||
|
||||
@ -484,7 +499,7 @@ are used in few games.
|
||||
- Nintendo AFC ADPCM
|
||||
- ITU-T G.721
|
||||
- CD-ROM XA ADPCM
|
||||
- Sony PSX ADPCM a.k.a VAG (standard, badflags, configurable, Pivotal)
|
||||
- Sony PSX ADPCM a.k.a VAG (standard, badflags, configurable, extended)
|
||||
- Sony HEVAG
|
||||
- Electronic Arts EA-XA (stereo, mono, Maxis)
|
||||
- Electronic Arts EA-XAS (v0, v1)
|
||||
@ -537,10 +552,17 @@ are used in few games.
|
||||
- FLAC
|
||||
- Others
|
||||
|
||||
Sometimes standard codecs come in non-standard layouts that aren't normally
|
||||
supported by other players (like multiple `.ogg` or `.mp3` files chunked and
|
||||
interleaved together in custom ways).
|
||||
|
||||
Some codecs are not fully correct compared to the games due to minor bugs, but
|
||||
in most cases it isn't audible, and general accuracy is high, with emphasis in
|
||||
proper support of encoder delay, accurate sample counts and seeking that other
|
||||
plugins may lack.
|
||||
|
||||
Note that vgmstream doesn't (can't) reproduce in-game music 1:1, as internal
|
||||
resampling, filters, volume, etc, are not replicated. Some codecs are not
|
||||
fully accurate compared to the games due to minor bugs, but in most cases
|
||||
it isn't audible.
|
||||
resampling, filters, volume, etc, are not replicated.
|
||||
|
||||
|
||||
## Supported file types
|
||||
|
@ -36,6 +36,8 @@ extern "C" {
|
||||
/*EXPORT*/ VgmstreamPlugin aud_plugin_instance;
|
||||
audacious_settings_t settings;
|
||||
|
||||
static const char* tagfile_name = "!tags.m3u";
|
||||
|
||||
/* Audacious will first send the file to a plugin based on this static extension list. If none
|
||||
* accepts it'll try again all plugins, ordered by priority, until one accepts the file. Problem is,
|
||||
* mpg123 plugin has higher priority and tendency to accept files that aren't even MP3. To fix this
|
||||
@ -56,6 +58,7 @@ const char *const VgmstreamPlugin::defaults[] = {
|
||||
"downmix_channels", "2",
|
||||
"exts_unknown_on", "FALSE",
|
||||
"exts_common_on", "FALSE",
|
||||
"tagfile_disable", "FALSE",
|
||||
NULL
|
||||
};
|
||||
|
||||
@ -81,10 +84,11 @@ const PreferencesWidget VgmstreamPlugin::widgets[] = {
|
||||
WidgetSpin(N_("Fade length:"), WidgetFloat(settings.fade_time), {0, 60, 0.1}),
|
||||
WidgetSpin(N_("Fade delay:"), WidgetFloat(settings.fade_delay), {0, 60, 0.1}),
|
||||
WidgetSpin(N_("Downmix:"), WidgetInt(settings.downmix_channels), {0, 8, 1}),
|
||||
WidgetCheck(N_("Enable unknown exts"), WidgetBool(settings.exts_unknown_on)),
|
||||
WidgetCheck(N_("Enable unknown exts:"), WidgetBool(settings.exts_unknown_on)),
|
||||
// Audacious 3.6 will only match one plugin so this option has no actual use
|
||||
// (ex. a fake .flac only gets to the FLAC plugin and never to vgmstream, even on error)
|
||||
//WidgetCheck(N_("Enable common exts"), WidgetBool(settings.exts_common_on)),
|
||||
WidgetCheck(N_("Disable tagfile:"), WidgetBool(settings.tagfile_disable)),
|
||||
};
|
||||
|
||||
void vgmstream_settings_load() {
|
||||
@ -143,18 +147,25 @@ void VgmstreamPlugin::cleanup() {
|
||||
vgmstream_settings_save();
|
||||
}
|
||||
|
||||
#if 0
|
||||
static int get_filename_subtune(const char* filename) {
|
||||
static int get_basename_subtune(const char* filename, char* buf, int buf_len, int* p_subtune) {
|
||||
int subtune;
|
||||
|
||||
int pos = strstr("?"))
|
||||
if (pos <= 0)
|
||||
return -1;
|
||||
if (sscanf(filename + pos, "%i", &subtune) != 1)
|
||||
return -1;
|
||||
return subtune;
|
||||
const char* pos = strrchr(filename, '?');
|
||||
if (!pos)
|
||||
return 0;
|
||||
if (sscanf(pos, "?%i", &subtune) != 1)
|
||||
return 0;
|
||||
|
||||
if (p_subtune)
|
||||
*p_subtune = subtune;
|
||||
|
||||
strncpy(buf, filename, buf_len);
|
||||
char* pos2 = strrchr(buf, '?');
|
||||
if (pos2) //removes '?'
|
||||
pos2[0] = '\0';
|
||||
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
static void apply_config(VGMSTREAM* vgmstream, audacious_settings_t* settings) {
|
||||
vgmstream_cfg_t vcfg = {0};
|
||||
@ -173,41 +184,41 @@ static void apply_config(VGMSTREAM* vgmstream, audacious_settings_t* settings) {
|
||||
static bool read_info(const char* filename, Tuple & tuple) {
|
||||
AUDINFO("read file=%s\n", filename);
|
||||
|
||||
#if 0
|
||||
//todo subsongs:
|
||||
// in theory just set FlagSubtunes in plugin.h, and set_subtunes below
|
||||
// Audacious will call "play" and this(?) with filename?N where N=subtune
|
||||
// and you just load subtune N, but I can't get it working
|
||||
int subtune;
|
||||
string basename;
|
||||
int subtune = get_filename_subtune(basename, &subtune);
|
||||
//must use basename to open streamfile
|
||||
#endif
|
||||
// Audacious first calls this as a regular file (use_subtune is 0). If file has subsongs,
|
||||
// you need to detect and call set_subtunes below and return. Then Audacious will call again
|
||||
// this and "play" with "filename?N" (where N=subtune, 1=first), that must be detected and handled
|
||||
// (see plugin.h)
|
||||
char basename[PATH_LIMIT]; //filename without '?'
|
||||
int subtune = 0;
|
||||
int use_subtune = get_basename_subtune(filename, basename, sizeof(basename), &subtune);
|
||||
|
||||
STREAMFILE* sf = open_vfs(filename);
|
||||
STREAMFILE* sf = open_vfs(use_subtune ? basename : filename);
|
||||
if (!sf) return false;
|
||||
|
||||
if (use_subtune)
|
||||
sf->stream_index = subtune;
|
||||
|
||||
VGMSTREAM* infostream = init_vgmstream_from_STREAMFILE(sf);
|
||||
if (!infostream) {
|
||||
close_streamfile(sf);
|
||||
return false;
|
||||
}
|
||||
|
||||
#if 0
|
||||
|
||||
int total_subtunes = infostream->num_streams;
|
||||
|
||||
//somehow max was changed to short in recent versions, though
|
||||
// some formats can exceed this
|
||||
// int was changed to short in some version, though vgmstream formats can exceed it
|
||||
if (total_subtunes > 32767)
|
||||
total_subtunes = 32767;
|
||||
if (infostream->num_streams > 1 && subtune <= 0) {
|
||||
// format has subsongs but Audacious didn't ask for subsong yet
|
||||
if (total_subtunes >= 1 && !use_subtune) {
|
||||
//set nullptr to leave subsong index linear (must add +1 to subtune)
|
||||
tuple.set_subtunes(total_subtunes, nullptr);
|
||||
return true; //must return?
|
||||
}
|
||||
|
||||
sf->stream_index = (subtune + 1);
|
||||
#endif
|
||||
close_streamfile(sf);
|
||||
close_vgmstream(infostream);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
apply_config(infostream, &settings);
|
||||
@ -229,13 +240,78 @@ static bool read_info(const char* filename, Tuple & tuple) {
|
||||
|
||||
//todo here we could call describe_vgmstream() and get substring to add tags and stuff
|
||||
tuple.set_str(Tuple::Codec, "vgmstream codec");
|
||||
//tuple.set_int(Tuple::Subtune, subtune);
|
||||
//tuple.set_int(Tuple::NumSubtunes, subtune); //done un set_subtunes
|
||||
if (use_subtune) {
|
||||
tuple.set_int(Tuple::Subtune, subtune);
|
||||
tuple.set_int(Tuple::NumSubtunes, infostream->num_streams);
|
||||
|
||||
char title[1024];
|
||||
vgmstream_get_title(title, sizeof(title), basename, infostream, NULL);
|
||||
tuple.set_str(Tuple::Title, title); //may be overwritten by tags
|
||||
}
|
||||
|
||||
|
||||
// this function is only called when files are added to playlist,
|
||||
// so to reload tags files need to readded
|
||||
if (!settings.tagfile_disable) {
|
||||
//todo improve string functions
|
||||
char tagfile_path[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 { /* ??? */
|
||||
strcpy(tagfile_path,tagfile_name);
|
||||
}
|
||||
|
||||
STREAMFILE* sf_tags = open_vfs(tagfile_path);
|
||||
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)) {
|
||||
// see tuple.h (ugly but other plugins do it like this)
|
||||
if (strcasecmp(tag_key, "ARTIST") == 0)
|
||||
tuple.set_str(Tuple::Artist, tag_val);
|
||||
else if (strcasecmp(tag_key, "ALBUMARTIST") == 0)
|
||||
tuple.set_str(Tuple::AlbumArtist, tag_val);
|
||||
else if (strcasecmp(tag_key, "TITLE") == 0)
|
||||
tuple.set_str(Tuple::Title, tag_val);
|
||||
else if (strcasecmp(tag_key, "ALBUM") == 0)
|
||||
tuple.set_str(Tuple::Album, tag_val);
|
||||
else if (strcasecmp(tag_key, "PERFORMER") == 0)
|
||||
tuple.set_str(Tuple::Performer, tag_val);
|
||||
else if (strcasecmp(tag_key, "COMPOSER") == 0)
|
||||
tuple.set_str(Tuple::Composer, tag_val);
|
||||
else if (strcasecmp(tag_key, "COMMENT") == 0)
|
||||
tuple.set_str(Tuple::Comment, tag_val);
|
||||
else if (strcasecmp(tag_key, "GENRE") == 0)
|
||||
tuple.set_str(Tuple::Genre, tag_val);
|
||||
else if (strcasecmp(tag_key, "TRACK") == 0)
|
||||
tuple.set_int(Tuple::Track, atoi(tag_val));
|
||||
else if (strcasecmp(tag_key, "YEAR") == 0)
|
||||
tuple.set_int(Tuple::Year, atoi (tag_val));
|
||||
#if defined(_AUD_PLUGIN_VERSION) && _AUD_PLUGIN_VERSION >= 48 // Audacious 3.8+
|
||||
else if (strcasecmp(tag_key, "REPLAYGAIN_TRACK_GAIN") == 0)
|
||||
tuple.set_gain(Tuple::TrackGain, Tuple::GainDivisor, tag_val);
|
||||
else if (strcasecmp(tag_key, "REPLAYGAIN_TRACK_PEAK") == 0)
|
||||
tuple.set_gain(Tuple::TrackPeak, Tuple::PeakDivisor, tag_val);
|
||||
else if (strcasecmp(tag_key, "REPLAYGAIN_ALBUM_GAIN") == 0)
|
||||
tuple.set_gain(Tuple::AlbumGain, Tuple::GainDivisor, tag_val);
|
||||
else if (strcasecmp(tag_key, "REPLAYGAIN_ALBUM_PEAK") == 0)
|
||||
tuple.set_gain(Tuple::AlbumPeak, Tuple::PeakDivisor, tag_val);
|
||||
#endif
|
||||
}
|
||||
|
||||
vgmstream_tags_close(tags);
|
||||
close_streamfile(sf_tags);
|
||||
}
|
||||
}
|
||||
|
||||
//todo tags (see tuple.h)
|
||||
//tuple.set_int (Tuple::Track, ...);
|
||||
//tuple.set_str (Tuple::Artist, ...);
|
||||
//tuple.set_str (Tuple::Album, ...);
|
||||
|
||||
close_streamfile(sf);
|
||||
close_vgmstream(infostream);
|
||||
@ -271,12 +347,20 @@ static void do_seek(VGMSTREAM* vgmstream, int seek_ms, int& current_sample_pos)
|
||||
bool VgmstreamPlugin::play(const char * filename, VFSFile & file) {
|
||||
AUDINFO("play file=%s\n", filename);
|
||||
|
||||
STREAMFILE* sf = open_vfs(filename);
|
||||
//handle subsongs (see read_info)
|
||||
char basename[PATH_LIMIT]; //filename without '?'
|
||||
int subtune = 0;
|
||||
int use_subtune = get_basename_subtune(filename, basename, sizeof(basename), &subtune);
|
||||
|
||||
STREAMFILE* sf = open_vfs(use_subtune ? basename : filename);
|
||||
if (!sf) {
|
||||
AUDERR("failed opening file %s\n", filename);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (use_subtune)
|
||||
sf->stream_index = subtune;
|
||||
|
||||
VGMSTREAM* vgmstream = init_vgmstream_from_STREAMFILE(sf);
|
||||
close_streamfile(sf);
|
||||
|
||||
|
@ -29,7 +29,7 @@ public:
|
||||
};
|
||||
|
||||
constexpr VgmstreamPlugin() : InputPlugin (info,
|
||||
InputInfo() //InputInfo(FlagSubtunes) // allow subsongs
|
||||
InputInfo(FlagSubtunes) // allow subsongs
|
||||
.with_priority(AUDACIOUS_VGMSTREAM_PRIORITY) // where 0=highest, 10=lowest (older) or 5 (newer)
|
||||
.with_exts(exts)) {} // priority exts (accepted exts are still validated at runtime)
|
||||
|
||||
@ -52,6 +52,7 @@ typedef struct {
|
||||
int downmix_channels;
|
||||
bool exts_unknown_on;
|
||||
bool exts_common_on;
|
||||
bool tagfile_disable;
|
||||
} audacious_settings_t;
|
||||
|
||||
extern audacious_settings_t settings;
|
||||
|
@ -3,7 +3,9 @@
|
||||
|
||||
#include <libaudcore/plugin.h>
|
||||
|
||||
extern "C" {
|
||||
#include "../src/vgmstream.h"
|
||||
}
|
||||
#include "plugin.h"
|
||||
#include "vfs.h"
|
||||
|
||||
@ -19,7 +21,7 @@ static STREAMFILE *open_vfs_by_VFSFILE(VFSFile *file, const char *path);
|
||||
static size_t read_vfs(VFS_STREAMFILE *streamfile, uint8_t *dest, off_t offset, size_t length) {
|
||||
size_t bytes_read;
|
||||
|
||||
if (!dest || length <= 0 || offset < 0)
|
||||
if (/*!streamfile->vfsFile ||*/ !dest || length <= 0 || offset < 0)
|
||||
return 0;
|
||||
|
||||
// if the offsets don't match, then we need to perform a seek
|
||||
@ -36,11 +38,14 @@ static size_t read_vfs(VFS_STREAMFILE *streamfile, uint8_t *dest, off_t offset,
|
||||
}
|
||||
|
||||
static void close_vfs(VFS_STREAMFILE *streamfile) {
|
||||
//if (streamfile->vfsFile)
|
||||
delete streamfile->vfsFile; //fcloses the internal file too
|
||||
free(streamfile);
|
||||
}
|
||||
|
||||
static size_t get_size_vfs(VFS_STREAMFILE *streamfile) {
|
||||
//if (!streamfile->vfsFile)
|
||||
// return 0;
|
||||
return streamfile->vfsFile->fsize();
|
||||
}
|
||||
|
||||
@ -100,5 +105,16 @@ STREAMFILE *open_vfs(const char *path) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#if 0 // files that don't exist seem blocked by probe.cc before reaching here
|
||||
bool infile_exists = vfsFile && *vfsFile;
|
||||
if (!infile_exists) {
|
||||
/* allow non-existing files in some cases */
|
||||
if (!vgmstream_is_virtual_filename(path)) {
|
||||
delete vfsFile;
|
||||
return NULL;
|
||||
}
|
||||
vfsFile = NULL;
|
||||
}
|
||||
#endif
|
||||
return open_vfs_by_VFSFILE(vfsFile, path);
|
||||
}
|
||||
|
@ -65,6 +65,7 @@ static void usage(const char* name, int is_full) {
|
||||
" -k N: seeks to N samples before decoding (for seek testing)\n"
|
||||
" -K N: seeks again to N samples before decoding (for seek testing)\n"
|
||||
" -t file: print tags found in file (for tag testing)\n"
|
||||
" -T: print title (for title testing)\n"
|
||||
" -D <max channels>: downmix to <max channels> (for plugin downmix testing)\n"
|
||||
" -O: decode but don't write to file (for performance testing)\n"
|
||||
);
|
||||
@ -100,6 +101,7 @@ typedef struct {
|
||||
int seek_samples1;
|
||||
int seek_samples2;
|
||||
int decode_only;
|
||||
int show_title;
|
||||
int downmix_channels;
|
||||
|
||||
/* not quite config but eh */
|
||||
@ -122,7 +124,7 @@ static int parse_config(cli_config* cfg, int argc, char** argv) {
|
||||
opterr = 0;
|
||||
|
||||
/* read config */
|
||||
while ((opt = getopt(argc, argv, "o:l:f:d:ipPcmxeLEFrgb2:s:t:k:K:hOvD:")) != -1) {
|
||||
while ((opt = getopt(argc, argv, "o:l:f:d:ipPcmxeLEFrgb2:s:t:Tk:K:hOvD:")) != -1) {
|
||||
switch (opt) {
|
||||
case 'o':
|
||||
cfg->outfilename = optarg;
|
||||
@ -185,6 +187,9 @@ static int parse_config(cli_config* cfg, int argc, char** argv) {
|
||||
case 't':
|
||||
cfg->tag_filename= optarg;
|
||||
break;
|
||||
case 'T':
|
||||
cfg->show_title = 1;
|
||||
break;
|
||||
case 'k':
|
||||
cfg->seek_samples1 = atoi(optarg);
|
||||
break;
|
||||
@ -351,6 +356,17 @@ static void print_tags(cli_config* cfg) {
|
||||
close_streamfile(sf_tags);
|
||||
}
|
||||
|
||||
static void print_title(VGMSTREAM* vgmstream, cli_config* cfg) {
|
||||
char title[1024];
|
||||
|
||||
if (!cfg->show_title)
|
||||
return;
|
||||
|
||||
vgmstream_get_title(title, sizeof(title), cfg->infilename, vgmstream, NULL);
|
||||
|
||||
printf("title: %s\n", title);
|
||||
}
|
||||
|
||||
static void clean_filename(char* dst, int clean_paths) {
|
||||
int i;
|
||||
for (i = 0; i < strlen(dst); i++) {
|
||||
@ -565,11 +581,10 @@ int main(int argc, char** argv) {
|
||||
}
|
||||
|
||||
|
||||
/* print file info (or batch commands, depending on config) */
|
||||
/* prints */
|
||||
print_info(vgmstream, &cfg);
|
||||
|
||||
/* print tags info */
|
||||
print_tags(&cfg);
|
||||
print_title(vgmstream, &cfg);
|
||||
|
||||
/* prints done */
|
||||
if (cfg.print_metaonly) {
|
||||
|
@ -63,6 +63,58 @@ int vgmstream_ctx_is_valid(const char* filename, vgmstream_ctx_valid_cfg *cfg) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
void vgmstream_get_title(char* buf, int buf_len, const char* filename, VGMSTREAM* vgmstream, vgmstream_title_t* cfg) {
|
||||
const char *pos;
|
||||
char* pos2;
|
||||
char temp[1024];
|
||||
|
||||
|
||||
/* name without path */
|
||||
pos = strrchr(filename, '\\');
|
||||
if (!pos)
|
||||
pos = strrchr(filename, '/');
|
||||
if (!pos)
|
||||
pos = filename;
|
||||
else
|
||||
pos++;
|
||||
strncpy(buf, pos, buf_len);
|
||||
|
||||
/* name without extension */
|
||||
pos2 = strrchr(buf, '.');
|
||||
if (pos2)
|
||||
pos2[0] = '\0';
|
||||
|
||||
{
|
||||
const char* stream_name = vgmstream->stream_name;
|
||||
int total_subsongs = vgmstream->num_streams;
|
||||
int target_subsong = vgmstream->stream_index;
|
||||
//int is_first = vgmstream->stream_index == 0;
|
||||
//int is_txtp = ; //todo don't show number/name for txtp but show for mini-txtp
|
||||
int show_name;
|
||||
|
||||
if (target_subsong == 0)
|
||||
target_subsong = 1;
|
||||
|
||||
/* show number if file has more than 1 subsong */
|
||||
if (total_subsongs > 1) {
|
||||
if (cfg && cfg->subsong_range)
|
||||
snprintf(temp, sizeof(temp), "%s#1~%i", buf, total_subsongs);
|
||||
else
|
||||
snprintf(temp, sizeof(temp), "%s#%i", buf, target_subsong);
|
||||
strncpy(buf, temp, buf_len);
|
||||
}
|
||||
|
||||
/* show name for some cases */
|
||||
show_name = (total_subsongs > 0 && (!cfg || !cfg->subsong_range)) ||
|
||||
(cfg && cfg->force_title);
|
||||
if (stream_name[0] != '\0' && show_name) {
|
||||
snprintf(temp, sizeof(temp), "%s (%s)", buf, stream_name);
|
||||
strncpy(buf, temp, buf_len);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void copy_time(int* dst_flag, int32_t* dst_time, double* dst_time_s, int* src_flag, int32_t* src_time, double* src_time_s) {
|
||||
if (!*src_flag)
|
||||
return;
|
||||
|
@ -68,6 +68,15 @@ int vgmstream_get_play_forever(VGMSTREAM* vgmstream);
|
||||
void vgmstream_set_play_forever(VGMSTREAM* vgmstream, int enabled);
|
||||
|
||||
|
||||
typedef struct {
|
||||
int force_title;
|
||||
int subsong_range;
|
||||
} vgmstream_title_t;
|
||||
|
||||
/* get a simple title for plugins */
|
||||
void vgmstream_get_title(char* buf, int buf_len, const char* filename, VGMSTREAM* vgmstream, vgmstream_title_t* cfg);
|
||||
|
||||
|
||||
|
||||
#if 0
|
||||
//possible future public/opaque API
|
||||
|
Loading…
Reference in New Issue
Block a user