Tweak tags API

This commit is contained in:
bnnm 2019-03-04 22:50:05 +01:00
parent 186d0c8ebf
commit 92c2c80f5b
5 changed files with 129 additions and 87 deletions

View File

@ -418,7 +418,8 @@ int main(int argc, char ** argv) {
/* print tags info */
if (cfg.tag_filename) {
VGMSTREAM_TAGS tag;
VGMSTREAM_TAGS *tags;
const char *tag_key, *tag_val;
STREAMFILE *tagFile = open_stdio_streamfile(cfg.tag_filename);
if (!tagFile) {
@ -427,11 +428,13 @@ int main(int argc, char ** argv) {
}
printf("tags:\n");
vgmstream_tags_reset(&tag, cfg.infilename);
while ( vgmstream_tags_next_tag(&tag, tagFile)) {
printf("- '%s'='%s'\n", tag.key, tag.val);
}
tags = vgmstream_tags_init(&tag_key, &tag_val);
vgmstream_tags_reset(tags, cfg.infilename);
while ( vgmstream_tags_next_tag(tags, tagFile)) {
printf("- '%s'='%s'\n", tag_key, tag_val);
}
vgmstream_tags_close(tags);
close_streamfile(tagFile);
}
@ -462,12 +465,7 @@ int main(int argc, char ** argv) {
#ifdef VGMSTREAM_MIXING
/* enable after all config but before outbuf */
{
vgmstream_enable_mixing(vgmstream, BUFFER_SAMPLES);
channels = vgmstream->output_channels;
input_channels = vgmstream->input_channels;
}
vgmstream_enable_mixing(vgmstream, BUFFER_SAMPLES, &input_channels, &channels);
#endif
buf = malloc(BUFFER_SAMPLES * sizeof(sample_t) * input_channels);

View File

@ -170,18 +170,21 @@ void input_vgmstream::get_info(t_uint32 p_subsong, file_info & p_info, abort_cal
STREAMFILE *tagFile = open_foo_streamfile(tagfile_path, &p_abort, &stats);
if (tagFile != NULL) {
VGMSTREAM_TAGS tag;
vgmstream_tags_reset(&tag, filename);
while (vgmstream_tags_next_tag(&tag, tagFile)) {
if (replaygain_info::g_is_meta_replaygain(tag.key)) {
p_info.info_set_replaygain(tag.key,tag.val);
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)) {
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 {
p_info.meta_set(tag.key,tag.val);
p_info.meta_set(tag_key,tag_val);
}
}
vgmstream_tags_close(tags);
close_streamfile(tagFile);
}
}

View File

@ -1,6 +1,29 @@
#include "vgmstream.h"
#include "plugins.h"
#define VGMSTREAM_TAGS_LINE_MAX 2048
/* opaque tag state */
struct VGMSTREAM_TAGS {
/* extracted output */
char key[VGMSTREAM_TAGS_LINE_MAX];
char val[VGMSTREAM_TAGS_LINE_MAX];
/* file to find tags for */
char targetname[VGMSTREAM_TAGS_LINE_MAX];
/* tag section for filename (see comments below) */
int section_found;
off_t section_start;
off_t section_end;
off_t offset;
/* commands */
int autotrack_on;
int autotrack_written;
int track_count;
};
static void tags_clean(VGMSTREAM_TAGS* tag) {
int i;
@ -14,62 +37,80 @@ static void tags_clean(VGMSTREAM_TAGS* tag) {
}
}
VGMSTREAM_TAGS* vgmstream_tags_init(const char* *tag_key, const char* *tag_val) {
VGMSTREAM_TAGS* tags = malloc(sizeof(VGMSTREAM_TAGS));
if (!tags) goto fail;
*tag_key = tags->key;
*tag_val = tags->val;
return tags;
fail:
return NULL;
}
void vgmstream_tags_close(VGMSTREAM_TAGS *tags) {
free(tags);
}
/* Tags are divided in two: "global" @TAGS and "file" %TAGS for target filename. To extract both
* we find the filename's tag "section": (other_filename) ..(#tag section).. (target_filename).
* When a new "other_filename" is found that offset is marked as section_start, and when target_filename
* is found it's marked as section_end. Then we can begin extracting tags within that section, until
* all tags are exhausted. Global tags are extracted while searching, so they always go first, and
* also meaning any tags after the section is found are ignored. */
int vgmstream_tags_next_tag(VGMSTREAM_TAGS* tag, STREAMFILE* tagfile) {
int vgmstream_tags_next_tag(VGMSTREAM_TAGS* tags, STREAMFILE* tagfile) {
off_t file_size = get_streamfile_size(tagfile);
char currentname[TAG_LINE_MAX] = {0};
char line[TAG_LINE_MAX] = {0};
char currentname[VGMSTREAM_TAGS_LINE_MAX] = {0};
char line[VGMSTREAM_TAGS_LINE_MAX] = {0};
int ok, bytes_read, line_done;
if (!tags)
return 0;
/* prepare file start and skip BOM if needed */
if (tag->offset == 0) {
if (tags->offset == 0) {
if ((uint16_t)read_16bitLE(0x00, tagfile) == 0xFFFE ||
(uint16_t)read_16bitLE(0x00, tagfile) == 0xFEFF) {
tag->offset = 0x02;
if (tag->section_start == 0)
tag->section_start = 0x02;
tags->offset = 0x02;
if (tags->section_start == 0)
tags->section_start = 0x02;
}
else if (((uint32_t)read_32bitBE(0x00, tagfile) & 0xFFFFFF00) == 0xEFBBBF00) {
tag->offset = 0x03;
if (tag->section_start == 0)
tag->section_start = 0x03;
tags->offset = 0x03;
if (tags->section_start == 0)
tags->section_start = 0x03;
}
}
/* read lines */
while (tag->offset <= file_size) {
while (tags->offset <= file_size) {
/* no more tags to extract */
if (tag->section_found && tag->offset >= tag->section_end) {
if (tags->section_found && tags->offset >= tags->section_end) {
/* write extra tags after all regular tags */
if (tag->autotrack_on && !tag->autotrack_written) {
sprintf(tag->key, "%s", "TRACK");
sprintf(tag->val, "%i", tag->track_count);
tag->autotrack_written = 1;
if (tags->autotrack_on && !tags->autotrack_written) {
sprintf(tags->key, "%s", "TRACK");
sprintf(tags->val, "%i", tags->track_count);
tags->autotrack_written = 1;
return 1;
}
goto fail;
}
bytes_read = get_streamfile_text_line(TAG_LINE_MAX,line, tag->offset,tagfile, &line_done);
bytes_read = get_streamfile_text_line(VGMSTREAM_TAGS_LINE_MAX,line, tags->offset,tagfile, &line_done);
if (!line_done || bytes_read == 0) goto fail;
tag->offset += bytes_read;
tags->offset += bytes_read;
if (tag->section_found) {
if (tags->section_found) {
/* find possible file tag */
ok = sscanf(line, "# %%%[^ \t] %[^\r\n] ", tag->key,tag->val);
ok = sscanf(line, "# %%%[^ \t] %[^\r\n] ", tags->key,tags->val);
if (ok == 2) {
tags_clean(tag);
tags_clean(tags);
return 1;
}
}
@ -77,19 +118,19 @@ int vgmstream_tags_next_tag(VGMSTREAM_TAGS* tag, STREAMFILE* tagfile) {
if (line[0] == '#') {
/* find possible global command */
ok = sscanf(line, "# $%[^ \t] %[^\r\n]", tag->key,tag->val);
ok = sscanf(line, "# $%[^ \t] %[^\r\n]", tags->key,tags->val);
if (ok == 1 || ok == 2) {
if (strcasecmp(tag->key,"AUTOTRACK") == 0) {
tag->autotrack_on = 1;
if (strcasecmp(tags->key,"AUTOTRACK") == 0) {
tags->autotrack_on = 1;
}
continue; /* not an actual tag */
}
/* find possible global tag */
ok = sscanf(line, "# @%[^ \t] %[^\r\n]", tag->key,tag->val);
ok = sscanf(line, "# @%[^ \t] %[^\r\n]", tags->key,tags->val);
if (ok == 2) {
tags_clean(tag);
tags_clean(tags);
return 1;
}
@ -99,18 +140,18 @@ int vgmstream_tags_next_tag(VGMSTREAM_TAGS* tag, STREAMFILE* tagfile) {
/* find possible filename and section start/end */
ok = sscanf(line, " %[^\r\n] ", currentname);
if (ok == 1) {
if (strcasecmp(tag->targetname,currentname) == 0) { /* looks ok even for UTF-8 */
if (strcasecmp(tags->targetname,currentname) == 0) { /* looks ok even for UTF-8 */
/* section ok, start would be set before this (or be 0) */
tag->section_end = tag->offset;
tag->section_found = 1;
tag->offset = tag->section_start;
tags->section_end = tags->offset;
tags->section_found = 1;
tags->offset = tags->section_start;
}
else {
/* mark new possible section */
tag->section_start = tag->offset;
tags->section_start = tags->offset;
}
tag->track_count++; /* new track found (target filename or not) */
tags->track_count++; /* new track found (target filename or not) */
continue;
}
@ -121,21 +162,24 @@ int vgmstream_tags_next_tag(VGMSTREAM_TAGS* tag, STREAMFILE* tagfile) {
/* may reach here if read up to file_size but no section was found */
fail:
tag->key[0] = '\0';
tag->val[0] = '\0';
tags->key[0] = '\0';
tags->val[0] = '\0';
return 0;
}
void vgmstream_tags_reset(VGMSTREAM_TAGS* tag, const char* target_filename) {
void vgmstream_tags_reset(VGMSTREAM_TAGS* tags, const char* target_filename) {
const char *path;
memset(tag, 0, sizeof(VGMSTREAM_TAGS));
if (!tags)
return;
memset(tags, 0, sizeof(VGMSTREAM_TAGS));
/* get base name */
//todo Windows CMD accepts both \\ and /, better way to handle this?
/* Windows CMD accepts both \\ and /, and maybe plugin uses either */
path = strrchr(target_filename,'\\');
if (!path)
path = strrchr(target_filename,'/');
@ -144,8 +188,8 @@ void vgmstream_tags_reset(VGMSTREAM_TAGS* tag, const char* target_filename) {
//todo validate sizes and copy sensible max
if (path) {
strcpy(tag->targetname, path);
strcpy(tags->targetname, path);
} else {
strcpy(tag->targetname, target_filename);
strcpy(tags->targetname, target_filename);
}
}

View File

@ -5,37 +5,31 @@
#define _PLUGINS_H_
#include "streamfile.h"
#define TAG_LINE_MAX 2048
//todo improve API and make opaque
//typedef struct VGMSTREAM_TAGS VGMSTREAM_TAGS;
typedef struct {
/* extracted output */
char key[TAG_LINE_MAX];
char val[TAG_LINE_MAX];
/* opaque tag state */
typedef struct VGMSTREAM_TAGS VGMSTREAM_TAGS;
/* file to find tags for */
char targetname[TAG_LINE_MAX];
/* tag section for filename (see comments below) */
int section_found;
off_t section_start;
off_t section_end;
off_t offset;
/* commands */
int autotrack_on;
int autotrack_written;
int track_count;
} VGMSTREAM_TAGS;
/* Initializes TAGS and returns pointers to extracted strings (always valid but change
* on every vgmstream_tags_next_tag call). Next functions are safe to call even if this fails (validate NULL).
* ex.: const char *tag_key, *tag_val; tags=vgmstream_tags_init(&tag_key, &tag_val); */
VGMSTREAM_TAGS* vgmstream_tags_init(const char* *tag_key, const char* *tag_val);
/* Resets tagfile to restart reading from the beginning for a new filename.
* Must be called first before extracting tags. */
void vgmstream_tags_reset(VGMSTREAM_TAGS* tags, const char* target_filename);
/* Extracts next valid tag in tagfile to *tag. Returns 0 if no more tags are found (meant to be
* called repeatedly until 0). Key/values are trimmed and values can be in UTF-8. */
int vgmstream_tags_next_tag(VGMSTREAM_TAGS* tag, STREAMFILE* tagfile);
int vgmstream_tags_next_tag(VGMSTREAM_TAGS* tags, STREAMFILE* tagfile);
/* resets tagfile to restart reading from the beginning for a new filename */
void vgmstream_tags_reset(VGMSTREAM_TAGS* tag, const char* target_filename);
/* Closes tag file */
void vgmstream_tags_close(VGMSTREAM_TAGS* tags);
#ifdef VGMSTREAM_MIXING
/* Enables mixing effects, with max outbuf samples as a hint. Once active, plugin
* must use returned input_channels to create outbuf and output_channels to output audio. */
void vgmstream_enable_mixing(VGMSTREAM* vgmstream, int32_t max_sample_count, int *input_channels, int *output_channels);
#endif
#endif /* _PLUGINS_H_ */

View File

@ -1509,11 +1509,13 @@ static void load_tagfile_info(in_char* filename) {
/* load all tags from tagfile */
tagFile = open_winamp_streamfile_by_ipath(tagfile_path_i);
if (tagFile != NULL) {
VGMSTREAM_TAGS tag;
VGMSTREAM_TAGS *tags;
const char *tag_key, *tag_val;
int i;
vgmstream_tags_reset(&tag, filename_utf8);
while (vgmstream_tags_next_tag(&tag, tagFile)) {
tags = vgmstream_tags_init(&tag_key, &tag_val);
vgmstream_tags_reset(tags, filename_utf8);
while (vgmstream_tags_next_tag(tags, tagFile)) {
int repeated_tag = 0;
int current_tag = last_tags.tag_count;
if (current_tag >= WINAMP_TAGS_ENTRY_MAX)
@ -1521,7 +1523,7 @@ static void load_tagfile_info(in_char* filename) {
/* should overwrite repeated tags as global tags may appear multiple times */
for (i = 0; i < current_tag; i++) {
if (strcmp(last_tags.keys[i], tag.key) == 0) {
if (strcmp(last_tags.keys[i], tag_key) == 0) {
current_tag = i;
repeated_tag = 1;
break;
@ -1529,13 +1531,14 @@ static void load_tagfile_info(in_char* filename) {
}
last_tags.keys[current_tag][0] = '\0';
strncat(last_tags.keys[current_tag], tag.key, WINAMP_TAGS_ENTRY_SIZE);
strncat(last_tags.keys[current_tag], tag_key, WINAMP_TAGS_ENTRY_SIZE);
last_tags.vals[current_tag][0] = '\0';
strncat(last_tags.vals[current_tag], tag.val, WINAMP_TAGS_ENTRY_SIZE);
strncat(last_tags.vals[current_tag], tag_val, WINAMP_TAGS_ENTRY_SIZE);
if (!repeated_tag)
last_tags.tag_count++;
}
vgmstream_tags_close(tags);
close_streamfile(tagFile);
last_tags.loaded = 1;
}