mirror of
https://github.com/vgmstream/vgmstream.git
synced 2025-02-20 04:21:11 +01:00
api: tweaks
This commit is contained in:
parent
be705d3b90
commit
388fce7c9f
@ -1,5 +1,4 @@
|
||||
#include "../src/libvgmstream.h"
|
||||
#if LIBVGMSTREAM_ENABLE
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <assert.h>
|
||||
@ -59,7 +58,7 @@ static int api_example(const char* infile) {
|
||||
libvgmstream_options_t opt = {
|
||||
.libsf = get_streamfile(infile)
|
||||
};
|
||||
err = libvgmstream_open_song(lib, &opt);
|
||||
err = libvgmstream_open_stream(lib, &opt);
|
||||
// external SF is not needed after _open
|
||||
libstreamfile_close(opt.libsf);
|
||||
|
||||
@ -137,7 +136,7 @@ static int api_example(const char* infile) {
|
||||
printf("\n");
|
||||
|
||||
// close current streamfile before opening new ones, optional
|
||||
//libvgmstream_close_song(lib);
|
||||
//libvgmstream_close_stream(lib);
|
||||
|
||||
// process done
|
||||
libvgmstream_free(lib);
|
||||
@ -403,4 +402,3 @@ int main(int argc, char** argv) {
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
@ -1,6 +1,5 @@
|
||||
#include "api_internal.h"
|
||||
#include "mixing.h"
|
||||
#if LIBVGMSTREAM_ENABLE
|
||||
|
||||
#define INTERNAL_BUF_SAMPLES 1024
|
||||
|
||||
@ -104,5 +103,3 @@ int api_get_sample_size(libvgmstream_sample_t sample_type) {
|
||||
return 0x02;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -1,7 +1,6 @@
|
||||
#include "api_internal.h"
|
||||
#include "sbuf.h"
|
||||
#include "mixing.h"
|
||||
#if LIBVGMSTREAM_ENABLE
|
||||
|
||||
|
||||
static void load_vgmstream(libvgmstream_priv_t* priv, libvgmstream_options_t* opt) {
|
||||
@ -106,14 +105,14 @@ static void update_format_info(libvgmstream_priv_t* priv) {
|
||||
}
|
||||
}
|
||||
|
||||
LIBVGMSTREAM_API int libvgmstream_open_song(libvgmstream_t* lib, libvgmstream_options_t* opt) {
|
||||
LIBVGMSTREAM_API int libvgmstream_open_stream(libvgmstream_t* lib, libvgmstream_options_t* opt) {
|
||||
if (!lib ||!lib->priv)
|
||||
return LIBVGMSTREAM_ERROR_GENERIC;
|
||||
if (!opt || !opt->libsf || opt->subsong_index < 0)
|
||||
return LIBVGMSTREAM_ERROR_GENERIC;
|
||||
|
||||
// close loaded song if any + reset
|
||||
libvgmstream_close_song(lib);
|
||||
libvgmstream_close_stream(lib);
|
||||
|
||||
libvgmstream_priv_t* priv = lib->priv;
|
||||
|
||||
@ -131,7 +130,7 @@ LIBVGMSTREAM_API int libvgmstream_open_song(libvgmstream_t* lib, libvgmstream_op
|
||||
}
|
||||
|
||||
|
||||
LIBVGMSTREAM_API void libvgmstream_close_song(libvgmstream_t* lib) {
|
||||
LIBVGMSTREAM_API void libvgmstream_close_stream(libvgmstream_t* lib) {
|
||||
if (!lib || !lib->priv)
|
||||
return;
|
||||
|
||||
@ -142,5 +141,3 @@ LIBVGMSTREAM_API void libvgmstream_close_song(libvgmstream_t* lib) {
|
||||
|
||||
libvgmstream_priv_reset(priv, true);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -2,8 +2,6 @@
|
||||
#include "mixing.h"
|
||||
#include "render.h"
|
||||
|
||||
#if LIBVGMSTREAM_ENABLE
|
||||
|
||||
|
||||
static bool reset_buf(libvgmstream_priv_t* priv) {
|
||||
// state reset
|
||||
@ -155,5 +153,3 @@ LIBVGMSTREAM_API void libvgmstream_reset(libvgmstream_t* lib) {
|
||||
}
|
||||
libvgmstream_priv_reset(priv, false);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -1,5 +1,4 @@
|
||||
#include "api_internal.h"
|
||||
#if LIBVGMSTREAM_ENABLE
|
||||
|
||||
|
||||
static int get_internal_log_level(libvgmstream_loglevel_t level) {
|
||||
@ -60,7 +59,7 @@ LIBVGMSTREAM_API bool libvgmstream_is_valid(const char* filename, libvgmstream_v
|
||||
|
||||
vgmstream_ctx_valid_cfg icfg = {
|
||||
.is_extension = cfg->is_extension,
|
||||
.skip_standard = cfg->skip_default,
|
||||
.skip_standard = cfg->skip_standard,
|
||||
.reject_extensionless = cfg->reject_extensionless,
|
||||
.accept_unknown = cfg->accept_unknown,
|
||||
.accept_common = cfg->accept_common
|
||||
@ -91,5 +90,3 @@ LIBVGMSTREAM_API int libvgmstream_get_title(libvgmstream_t* lib, libvgmstream_ti
|
||||
LIBVGMSTREAM_API bool libvgmstream_is_virtual_filename(const char* filename) {
|
||||
return vgmstream_is_virtual_filename(filename);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -5,7 +5,6 @@
|
||||
#include "../vgmstream.h"
|
||||
#include "plugins.h"
|
||||
|
||||
#if LIBVGMSTREAM_ENABLE
|
||||
|
||||
#define LIBVGMSTREAM_OK 0
|
||||
#define LIBVGMSTREAM_ERROR_GENERIC -1
|
||||
@ -63,4 +62,3 @@ int api_get_sample_size(libvgmstream_sample_t sample_type);
|
||||
STREAMFILE* open_api_streamfile(libstreamfile_t* libsf);
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
@ -1,5 +1,4 @@
|
||||
#include "api_internal.h"
|
||||
#if LIBVGMSTREAM_ENABLE
|
||||
|
||||
static libstreamfile_t* libstreamfile_from_streamfile(STREAMFILE* sf);
|
||||
|
||||
@ -8,75 +7,75 @@ static libstreamfile_t* libstreamfile_from_streamfile(STREAMFILE* sf);
|
||||
typedef struct {
|
||||
int64_t offset;
|
||||
int64_t size;
|
||||
STREAMFILE* inner_sf;
|
||||
STREAMFILE* sf;
|
||||
char name[PATH_LIMIT];
|
||||
} libsf_data_t;
|
||||
} libsf_priv_t;
|
||||
|
||||
static int libsf_read(void* user_data, uint8_t* dst, int dst_size) {
|
||||
libsf_data_t* data = user_data;
|
||||
if (!data || !dst)
|
||||
libsf_priv_t* priv = user_data;
|
||||
if (!priv || !dst)
|
||||
return 0;
|
||||
|
||||
int bytes = data->inner_sf->read(data->inner_sf, dst, data->offset, dst_size);
|
||||
data->offset += bytes;
|
||||
int bytes = priv->sf->read(priv->sf, dst, priv->offset, dst_size);
|
||||
priv->offset += bytes;
|
||||
|
||||
return bytes;
|
||||
}
|
||||
|
||||
static int64_t libsf_seek(void* user_data, int64_t offset, int whence) {
|
||||
libsf_data_t* data = user_data;
|
||||
if (!data)
|
||||
libsf_priv_t* priv = user_data;
|
||||
if (!priv)
|
||||
return -1;
|
||||
|
||||
switch (whence) {
|
||||
case LIBSTREAMFILE_SEEK_SET: /* absolute */
|
||||
break;
|
||||
case LIBSTREAMFILE_SEEK_CUR: /* relative to current */
|
||||
offset += data->offset;
|
||||
offset += priv->offset;
|
||||
break;
|
||||
case LIBSTREAMFILE_SEEK_END: /* relative to file end (should be negative) */
|
||||
offset += data->size;
|
||||
offset += priv->size;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
/* clamp offset like fseek */
|
||||
if (offset > data->size)
|
||||
offset = data->size;
|
||||
if (offset > priv->size)
|
||||
offset = priv->size;
|
||||
else if (offset < 0)
|
||||
offset = 0;
|
||||
|
||||
/* main seek */
|
||||
data->offset = offset;
|
||||
priv->offset = offset;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int64_t libsf_get_size(void* user_data) {
|
||||
libsf_data_t* data = user_data;
|
||||
if (!data)
|
||||
libsf_priv_t* priv = user_data;
|
||||
if (!priv)
|
||||
return 0;
|
||||
return data->size;
|
||||
return priv->size;
|
||||
}
|
||||
|
||||
static const char* libsf_get_name(void* user_data) {
|
||||
libsf_data_t* data = user_data;
|
||||
if (!data)
|
||||
libsf_priv_t* priv = user_data;
|
||||
if (!priv)
|
||||
return NULL;
|
||||
|
||||
if (data->name[0] == '\0') {
|
||||
data->inner_sf->get_name(data->inner_sf, data->name, sizeof(data->name));
|
||||
if (priv->name[0] == '\0') {
|
||||
priv->sf->get_name(priv->sf, priv->name, sizeof(priv->name));
|
||||
}
|
||||
|
||||
return data->name;
|
||||
return priv->name;
|
||||
}
|
||||
|
||||
static libstreamfile_t* libsf_open(void* user_data, const char* filename) {
|
||||
libsf_data_t* data = user_data;
|
||||
if (!data || !data->inner_sf || !filename)
|
||||
libsf_priv_t* priv = user_data;
|
||||
if (!priv || !priv->sf || !filename)
|
||||
return NULL;
|
||||
|
||||
STREAMFILE* sf = data->inner_sf->open(data->inner_sf, filename, 0);
|
||||
STREAMFILE* sf = priv->sf->open(priv->sf, filename, 0);
|
||||
if (!sf)
|
||||
return NULL;
|
||||
|
||||
@ -93,11 +92,11 @@ static void libsf_close(libstreamfile_t* libsf) {
|
||||
if (!libsf)
|
||||
return;
|
||||
|
||||
libsf_data_t* data = libsf->user_data;
|
||||
if (data && data->inner_sf) {
|
||||
data->inner_sf->close(data->inner_sf);
|
||||
libsf_priv_t* priv = libsf->user_data;
|
||||
if (priv && priv->sf) {
|
||||
priv->sf->close(priv->sf);
|
||||
}
|
||||
free(data);
|
||||
free(priv);
|
||||
free(libsf);
|
||||
}
|
||||
|
||||
@ -106,7 +105,7 @@ static libstreamfile_t* libstreamfile_from_streamfile(STREAMFILE* sf) {
|
||||
return NULL;
|
||||
|
||||
libstreamfile_t* libsf = NULL;
|
||||
libsf_data_t* data = NULL;
|
||||
libsf_priv_t* priv = NULL;
|
||||
|
||||
libsf = calloc(1, sizeof(libstreamfile_t));
|
||||
if (!libsf) goto fail;
|
||||
@ -118,12 +117,12 @@ static libstreamfile_t* libstreamfile_from_streamfile(STREAMFILE* sf) {
|
||||
libsf->open = libsf_open;
|
||||
libsf->close = libsf_close;
|
||||
|
||||
libsf->user_data = calloc(1, sizeof(libsf_data_t));
|
||||
libsf->user_data = calloc(1, sizeof(libsf_priv_t));
|
||||
if (!libsf->user_data) goto fail;
|
||||
|
||||
data = libsf->user_data;
|
||||
data->inner_sf = sf;
|
||||
data->size = get_streamfile_size(sf);
|
||||
priv = libsf->user_data;
|
||||
priv->sf = sf;
|
||||
priv->size = get_streamfile_size(sf);
|
||||
|
||||
return libsf;
|
||||
fail:
|
||||
@ -145,4 +144,18 @@ LIBVGMSTREAM_API libstreamfile_t* libstreamfile_open_from_stdio(const char* file
|
||||
|
||||
return libsf;
|
||||
}
|
||||
#endif
|
||||
|
||||
LIBVGMSTREAM_API libstreamfile_t* libstreamfile_open_from_file(void* file_, const char* filename) {
|
||||
FILE* file = file_;
|
||||
STREAMFILE* sf = open_stdio_streamfile_by_file(file, filename);
|
||||
if (!sf)
|
||||
return NULL;
|
||||
|
||||
libstreamfile_t* libsf = libstreamfile_from_streamfile(sf);
|
||||
if (!libsf) {
|
||||
close_streamfile(sf);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return libsf;
|
||||
}
|
||||
|
195
src/base/api_libsf_cache.c
Normal file
195
src/base/api_libsf_cache.c
Normal file
@ -0,0 +1,195 @@
|
||||
#include "api_internal.h"
|
||||
|
||||
/* libstreamfile_t for external use, that caches some external libsf */
|
||||
|
||||
/* value can be adjusted freely but 8k is a good enough compromise. */
|
||||
#define CACHE_DEFAULT_BUFFER_SIZE 0x8000
|
||||
|
||||
typedef struct {
|
||||
libstreamfile_t* libsf;
|
||||
|
||||
int64_t offset; /* last read offset (info) */
|
||||
int64_t buf_offset; /* current buffer data start */
|
||||
uint8_t* buf; /* data buffer */
|
||||
size_t buf_size; /* max buffer size */
|
||||
size_t valid_size; /* current buffer size */
|
||||
size_t file_size; /* buffered file size */
|
||||
|
||||
char name[PATH_LIMIT];
|
||||
} cache_priv_t;
|
||||
|
||||
static int cache_read(void* user_data, uint8_t* dst, int dst_size) {
|
||||
cache_priv_t* priv = user_data;
|
||||
size_t read_total = 0;
|
||||
if (!dst || dst_size <= 0)
|
||||
return 0;
|
||||
|
||||
/* is the part of the requested length in the buffer? */
|
||||
if (priv->offset >= priv->buf_offset && priv->offset < priv->buf_offset + priv->valid_size) {
|
||||
size_t buf_limit;
|
||||
int buf_into = (int)(priv->offset - priv->buf_offset);
|
||||
|
||||
buf_limit = priv->valid_size - buf_into;
|
||||
if (buf_limit > dst_size)
|
||||
buf_limit = dst_size;
|
||||
|
||||
memcpy(dst, priv->buf + buf_into, buf_limit);
|
||||
read_total += buf_limit;
|
||||
dst_size -= buf_limit;
|
||||
priv->offset += buf_limit;
|
||||
dst += buf_limit;
|
||||
}
|
||||
|
||||
|
||||
/* read the rest of the requested length */
|
||||
while (dst_size > 0) {
|
||||
size_t buf_limit;
|
||||
|
||||
/* ignore requests at EOF */
|
||||
if (priv->offset >= priv->file_size) {
|
||||
//offset = priv->file_size; /* seems fseek doesn't clamp offset */
|
||||
//VGM_ASSERT_ONCE(offset > sf->file_size, "STDIO: reading over file_size 0x%x @ 0x%lx + 0x%x\n", sf->file_size, offset, length);
|
||||
break;
|
||||
}
|
||||
|
||||
/* position to new offset */
|
||||
priv->libsf->seek(priv, priv->offset, 0);
|
||||
|
||||
/* fill the buffer (offset now is beyond buf_offset) */
|
||||
priv->buf_offset = priv->offset;
|
||||
priv->valid_size = priv->libsf->read(priv, priv->buf, priv->buf_size);
|
||||
|
||||
/* decide how much must be read this time */
|
||||
if (dst_size > priv->buf_size)
|
||||
buf_limit = priv->buf_size;
|
||||
else
|
||||
buf_limit = dst_size;
|
||||
|
||||
/* give up on partial reads (EOF) */
|
||||
if (priv->valid_size < buf_limit) {
|
||||
memcpy(dst, priv->buf, priv->valid_size);
|
||||
priv->offset += priv->valid_size;
|
||||
read_total += priv->valid_size;
|
||||
break;
|
||||
}
|
||||
|
||||
/* use the new buffer */
|
||||
memcpy(dst, priv->buf, buf_limit);
|
||||
priv->offset += buf_limit;
|
||||
read_total += buf_limit;
|
||||
dst_size -= buf_limit;
|
||||
dst += buf_limit;
|
||||
}
|
||||
|
||||
return read_total;
|
||||
}
|
||||
|
||||
static int64_t cache_seek(void* user_data, int64_t offset, int whence) {
|
||||
cache_priv_t* priv = user_data;
|
||||
|
||||
switch (whence) {
|
||||
case LIBSTREAMFILE_SEEK_SET: /* absolute */
|
||||
break;
|
||||
case LIBSTREAMFILE_SEEK_CUR: /* relative to current */
|
||||
offset += priv->offset;
|
||||
break;
|
||||
case LIBSTREAMFILE_SEEK_END: /* relative to file end (should be negative) */
|
||||
offset += priv->file_size;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
/* clamp offset like fseek */
|
||||
if (offset > priv->file_size)
|
||||
offset = priv->file_size;
|
||||
else if (offset < 0)
|
||||
offset = 0;
|
||||
|
||||
/* main seek */
|
||||
priv->offset = offset;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int64_t cache_get_size(void* user_data) {
|
||||
cache_priv_t* priv = user_data;
|
||||
return priv->file_size;
|
||||
}
|
||||
|
||||
static const char* cache_get_name(void* user_data) {
|
||||
cache_priv_t* priv = user_data;
|
||||
return priv->name;
|
||||
}
|
||||
|
||||
static libstreamfile_t* cache_open(void* user_data, const char* filename) {
|
||||
cache_priv_t* priv = user_data;
|
||||
if (!priv || !priv->libsf || !filename)
|
||||
return NULL;
|
||||
|
||||
libstreamfile_t* inner_libsf = priv->libsf->open(priv->libsf->user_data, filename);
|
||||
if (!inner_libsf)
|
||||
return NULL;
|
||||
|
||||
libstreamfile_t* libsf = libstreamfile_open_buffered(inner_libsf, priv->buf_size);
|
||||
if (!libsf) {
|
||||
libstreamfile_close(inner_libsf);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return libsf;
|
||||
}
|
||||
|
||||
static void cache_close(libstreamfile_t* libsf) {
|
||||
if (!libsf)
|
||||
return;
|
||||
|
||||
cache_priv_t* priv = libsf->user_data;
|
||||
if (priv && priv->libsf) {
|
||||
libstreamfile_close(priv->libsf);
|
||||
}
|
||||
if (priv) {
|
||||
free(priv->buf);
|
||||
}
|
||||
free(priv);
|
||||
free(libsf);
|
||||
}
|
||||
|
||||
|
||||
LIBVGMSTREAM_API libstreamfile_t* libstreamfile_open_buffered(libstreamfile_t* ext_libsf, int buf_size) {
|
||||
if (!ext_libsf)
|
||||
return NULL;
|
||||
if (!buf_size)
|
||||
buf_size = CACHE_DEFAULT_BUFFER_SIZE;
|
||||
|
||||
libstreamfile_t* libsf = NULL;
|
||||
cache_priv_t* priv = NULL;
|
||||
|
||||
libsf = calloc(1, sizeof(libstreamfile_t));
|
||||
if (!libsf) goto fail;
|
||||
|
||||
libsf->read = cache_read;
|
||||
libsf->seek = cache_seek;
|
||||
libsf->get_size = cache_get_size;
|
||||
libsf->get_name = cache_get_name;
|
||||
libsf->open = cache_open;
|
||||
libsf->close = cache_close;
|
||||
|
||||
libsf->user_data = calloc(1, sizeof(cache_priv_t));
|
||||
if (!libsf->user_data) goto fail;
|
||||
|
||||
priv = libsf->user_data;
|
||||
priv->libsf = ext_libsf;
|
||||
priv->buf_size = buf_size;
|
||||
priv->buf = calloc(buf_size, sizeof(uint8_t));
|
||||
if (!priv->buf) goto fail;
|
||||
|
||||
priv->file_size = priv->libsf->get_size(priv->libsf->user_data);
|
||||
|
||||
snprintf(priv->name, sizeof(priv->name), "%s", priv->libsf->get_name(priv->libsf->user_data));
|
||||
priv->name[sizeof(priv->name) - 1] = '\0';
|
||||
|
||||
return libsf;
|
||||
fail:
|
||||
cache_close(libsf);
|
||||
return NULL;
|
||||
}
|
@ -1,5 +1,4 @@
|
||||
#include "api_internal.h"
|
||||
#if LIBVGMSTREAM_ENABLE
|
||||
|
||||
typedef struct {
|
||||
VGMSTREAM_TAGS* vtags;
|
||||
@ -65,5 +64,3 @@ LIBVGMSTREAM_API void libvgmstream_tags_free(libvgmstream_tags_t* tags) {
|
||||
free(tags->priv);
|
||||
free(tags);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -1,5 +1,4 @@
|
||||
#include "api_internal.h"
|
||||
#if LIBVGMSTREAM_ENABLE
|
||||
/* STREAMFILE for internal use, that bridges calls to external libstreamfile_t */
|
||||
|
||||
|
||||
@ -90,5 +89,3 @@ static STREAMFILE* open_api_streamfile_internal(libstreamfile_t* libsf, bool ext
|
||||
STREAMFILE* open_api_streamfile(libstreamfile_t* libsf) {
|
||||
return open_api_streamfile_internal(libsf, true);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -1,18 +1,16 @@
|
||||
#ifndef _LIBVGMSTREAM_H_
|
||||
#define _LIBVGMSTREAM_H_
|
||||
|
||||
#define LIBVGMSTREAM_ENABLE 1
|
||||
#if LIBVGMSTREAM_ENABLE
|
||||
|
||||
/* libvgmstream: vgmstream's public API
|
||||
*
|
||||
* Basic usage (also see api_example.c):
|
||||
* - libvgmstream_init(...) // create context
|
||||
* - libvgmstream_setup(...) // setup config (if needed)
|
||||
* - libvgmstream_open_song(...) // open format
|
||||
* - libvgmstream_render(...) // main decode
|
||||
* - output samples + repeat libvgmstream_render until stream is done
|
||||
* - libvgmstream_free(...) // cleanup
|
||||
* - libvgmstream_init(...) // create context
|
||||
* - libvgmstream_setup(...) // setup config (if needed)
|
||||
* - libvgmstream_open_stream(...) // open format
|
||||
* - libvgmstream_render(...) // main decode
|
||||
* - output samples + repeat libvgmstream_render until 'done' is set
|
||||
* - libvgmstream_free(...) // cleanup
|
||||
*
|
||||
* By default vgmstream behaves like a decoder (returns samples until stream end), but you can configure
|
||||
* it to loop N times or even downmix. In other words, it also behaves a bit like a player.
|
||||
@ -56,9 +54,9 @@
|
||||
* - only refers to the API itself, changes related to formats/etc don't alter this
|
||||
* - vgmstream's features are mostly stable, but this API may be tweaked from time to time
|
||||
*/
|
||||
#define LIBVGMSTREAM_API_VERSION_MAJOR 1 // breaking API/ABI changes
|
||||
#define LIBVGMSTREAM_API_VERSION_MINOR 0 // compatible API/ABI changes
|
||||
#define LIBVGMSTREAM_API_VERSION_PATCH 0 // fixes
|
||||
#define LIBVGMSTREAM_API_VERSION_MAJOR 0x01 // breaking API/ABI changes
|
||||
#define LIBVGMSTREAM_API_VERSION_MINOR 0x00 // compatible API/ABI changes
|
||||
#define LIBVGMSTREAM_API_VERSION_PATCH 0x00 // fixes
|
||||
|
||||
/* Current API version, for dynamic checks. returns hex value: 0xMMmmpppp = MM-major, mm-minor, pppp-patch
|
||||
* - use when loading vgmstream as a dynamic library to ensure API/ABI compatibility
|
||||
@ -75,10 +73,10 @@ LIBVGMSTREAM_API uint32_t libvgmstream_get_version(void);
|
||||
|
||||
/* interleaved samples: buf[0]=ch0, buf[1]=ch1, buf[2]=ch0, buf[3]=ch0, ... */
|
||||
typedef enum {
|
||||
LIBVGMSTREAM_SAMPLE_PCM16 = 0x01,
|
||||
LIBVGMSTREAM_SAMPLE_PCM24 = 0x02,
|
||||
LIBVGMSTREAM_SAMPLE_PCM32 = 0x03,
|
||||
LIBVGMSTREAM_SAMPLE_FLOAT = 0x04,
|
||||
LIBVGMSTREAM_SAMPLE_PCM16 = 1,
|
||||
LIBVGMSTREAM_SAMPLE_PCM24 = 2,
|
||||
LIBVGMSTREAM_SAMPLE_PCM32 = 3,
|
||||
LIBVGMSTREAM_SAMPLE_FLOAT = 4,
|
||||
} libvgmstream_sample_t;
|
||||
|
||||
/* current song info, may be copied around (values are info-only) */
|
||||
@ -86,12 +84,11 @@ typedef struct {
|
||||
/* main (always set) */
|
||||
int channels; // output channels
|
||||
int sample_rate; // output sample rate
|
||||
|
||||
libvgmstream_sample_t sample_type; // output buffer's sample type
|
||||
int sample_size; // derived from sample_type (pcm16=0x02, float=0x04, etc)
|
||||
|
||||
/* extra info (may be 0 if not known or not relevant) */
|
||||
uint32_t channel_layout; // standard WAVE bitflags
|
||||
uint32_t channel_layout; // standard WAVE bitflags, 0 if unset or non-standard
|
||||
|
||||
int subsong_index; // 0 = none, N = loaded subsong N (1=first)
|
||||
int subsong_count; // 0 = format has no concept of subsongs, N = has N subsongs
|
||||
@ -110,6 +107,7 @@ typedef struct {
|
||||
bool loop_flag; // if file loops
|
||||
// ** false + defined loops means looping was forcefully disabled
|
||||
// ** true + undefined loops means the file loops in a way not representable by loop points
|
||||
//bool rough_samples; // signal cases where loop points or sample count can't exactly reflect actual behavior
|
||||
|
||||
bool play_forever; // if file loops forever based on current config (meaning _play never stops)
|
||||
int64_t play_samples; // totals after all calculations (after applying loop/fade/etc config)
|
||||
@ -117,32 +115,29 @@ typedef struct {
|
||||
// ** if play_forever is set this is still provided for reference based on non-forever config
|
||||
|
||||
int stream_bitrate; // average bitrate of the subsong (slightly bloated vs codec_bitrate; incorrect in rare cases)
|
||||
//int codec_bitrate; // average bitrate of the codec data
|
||||
// ** not possible / slow to calculate in most cases
|
||||
//int codec_bitrate; // average bitrate of the codec data [not possible / slow to calculate in most cases]
|
||||
|
||||
/* descriptions */
|
||||
char codec_name[128]; //
|
||||
char layout_name[128]; //
|
||||
char meta_name[128]; // (not internal "tag" metadata)
|
||||
char stream_name[256]; // some internal name or representation, not always useful
|
||||
// ** these are a bit big for a struct, but the typical use case of vgsmtream is opening a file > immediately
|
||||
char codec_name[128]; // represention of main decoder name
|
||||
char layout_name[128]; // represention of how data is laid out
|
||||
char meta_name[128]; // represention of format's name (not internal "tag" metadata)
|
||||
char stream_name[256]; // stream's internal name or representation (not an internal filename not always useful)
|
||||
// ** these are a bit big for a struct, but the typical use case of vgmstream is opening a file > immediately
|
||||
// query description and since libvgmstream returns its own copy it shouldn't be too much of a problem
|
||||
// ** (may be separated later)
|
||||
|
||||
/* misc */
|
||||
//bool rough_samples; // signal cases where loop points or sample count can't exactly reflect actual behavior
|
||||
|
||||
int format_id; // when reopening subfiles or similar formats without checking other all possible formats
|
||||
// ** this value WILL change without warning between vgmstream versions/commits
|
||||
// ** this value WILL change without warning between vgmstream versions/commits, but usually only add
|
||||
|
||||
} libvgmstream_format_t;
|
||||
|
||||
/* current decoder state */
|
||||
typedef struct {
|
||||
void* buf; // current decoded buf (valid after _decode until next call; may change between calls)
|
||||
int buf_samples; // current buffer samples (0 is possible in some cases)
|
||||
int buf_bytes; // current buffer bytes (channels * sample_size * samples)
|
||||
|
||||
bool done; // when stream is done based on config
|
||||
bool done; // when stream is done, based on config
|
||||
// ** note that with play_forever this flag is never set
|
||||
} libvgmstream_decoder_t;
|
||||
|
||||
@ -183,7 +178,7 @@ typedef struct {
|
||||
double fade_time; // fade period after target loops
|
||||
double fade_delay; // fade delay after target loops
|
||||
|
||||
int auto_downmix_channels; // downmixing if vgmstream's channels are higher than value
|
||||
int auto_downmix_channels; // downmix if vgmstream's channels are higher than value
|
||||
// ** for players that can only handle N channels
|
||||
// ** this type of downmixing is very simplistic and not recommended
|
||||
|
||||
@ -218,11 +213,11 @@ typedef struct {
|
||||
* - returns < 0 on error (file not recognised, invalid subsong index, etc)
|
||||
* - will close currently loaded song if needed
|
||||
*/
|
||||
LIBVGMSTREAM_API int libvgmstream_open_song(libvgmstream_t* lib, libvgmstream_options_t* open_options);
|
||||
LIBVGMSTREAM_API int libvgmstream_open_stream(libvgmstream_t* lib, libvgmstream_options_t* open_options);
|
||||
|
||||
/* Closes current song; may still use libvgmstream to open other songs
|
||||
*/
|
||||
LIBVGMSTREAM_API void libvgmstream_close_song(libvgmstream_t* lib);
|
||||
LIBVGMSTREAM_API void libvgmstream_close_stream(libvgmstream_t* lib);
|
||||
|
||||
|
||||
/* Decodes next batch of samples
|
||||
@ -289,14 +284,14 @@ LIBVGMSTREAM_API const char** libvgmstream_get_common_extensions(size_t* size);
|
||||
|
||||
|
||||
typedef struct {
|
||||
bool is_extension; /* set if filename is just an extension */
|
||||
bool skip_default; /* set if shouldn't check default formats */
|
||||
bool reject_extensionless; /* set if player can't play extensionless files */
|
||||
bool accept_unknown; /* set to allow any extension (for txth) */
|
||||
bool accept_common; /* set to allow known-but-common extension (when player has plugin priority) */
|
||||
bool is_extension; /* set if filename is just an extension (otherwise may be seen as 'extensionless') */
|
||||
bool skip_standard; /* disable extension check vs default formats */
|
||||
bool reject_extensionless; /* enable if player can't play extensionless files */
|
||||
bool accept_unknown; /* enable to allow any extension even if not known by vgmstream (for .txth) */
|
||||
bool accept_common; /* enable to allow known-but-common extension (when player has plugin priority) */
|
||||
} libvgmstream_valid_t;
|
||||
|
||||
/* Returns if vgmstream can parse a filename by extension, to reject some files earlier
|
||||
/* Returns if vgmstream can parse a filename by extension, to reject some files earlier.
|
||||
* - doesn't check file contents (that's only done on _open)
|
||||
* - config may be NULL
|
||||
* - mainly for plugins that want to fail early; libvgmstream doesn't use this
|
||||
@ -363,4 +358,3 @@ LIBVGMSTREAM_API void libvgmstream_tags_free(libvgmstream_tags_t* tags);
|
||||
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
@ -1,13 +1,15 @@
|
||||
#ifndef _LIBVGMSTREAM_STREAMFILE_H_
|
||||
#define _LIBVGMSTREAM_STREAMFILE_H_
|
||||
#include "libvgmstream.h"
|
||||
#if LIBVGMSTREAM_ENABLE
|
||||
|
||||
/* vgmstream's IO API, defined as a "streamfile" (SF).
|
||||
/* vgmstream's IO API, defined as a "streamfile" ('SF').
|
||||
*
|
||||
* vgmstream roughly assumes there is an underlying filesystem (as usual in games): seeking + reading from arbitrary offsets,
|
||||
* opening companion files, filename tests, etc. If your case is too different you may still create a partial streamfile: returning
|
||||
* a fake filename, only handling "open" that reopens itself (same filename), etc. Simpler formats will probably work just fine.
|
||||
* vgmstream mostly assumes there is an underlying filesystem (as usual in games), plus given video game formats are
|
||||
* often ill-defined it needs extra ops to handle edge cases: seeking + reading from arbitrary offsets, opening companion
|
||||
* files, filename/size tests, etc.
|
||||
*
|
||||
* If your case is too different you may still create a partial streamfile: returning a fake filename, only handling "open"
|
||||
* that reopens itself (same filename), etc. Simpler formats should work fine.
|
||||
*/
|
||||
|
||||
|
||||
@ -19,7 +21,7 @@ enum {
|
||||
//LIBSTREAMFILE_SEEK_GET_SIZE = 5,
|
||||
};
|
||||
|
||||
// maybe "libvgmstream_streamfile_t" but it was getting unwieldly
|
||||
// should be "libvgmstream_streamfile_t" but it was getting unwieldly
|
||||
typedef struct libstreamfile_t {
|
||||
//uint32_t flags; // info flags for vgmstream
|
||||
void* user_data; // any internal structure
|
||||
@ -30,11 +32,11 @@ typedef struct libstreamfile_t {
|
||||
int (*read)(void* user_data, uint8_t* dst, int dst_size);
|
||||
|
||||
/* seek to offset
|
||||
* - note that vgmstream needs to seek + read fairly often (to be optimized later)
|
||||
* - note that vgmstream needs to seek + read fairly often (to be optimized someday)
|
||||
*/
|
||||
int64_t (*seek)(void* user_data, int64_t offset, int whence);
|
||||
|
||||
/* get max offset (typically for checks or calculations)
|
||||
/* get max offset (typically for checks or sample calculations)
|
||||
*/
|
||||
int64_t (*get_size)(void* user_data);
|
||||
|
||||
@ -47,7 +49,7 @@ typedef struct libstreamfile_t {
|
||||
*/
|
||||
struct libstreamfile_t* (*open)(void* user_data, const char* filename);
|
||||
|
||||
/* free current SF (needed for copied streamfiles) */
|
||||
/* free current SF */
|
||||
void (*close)(struct libstreamfile_t* libsf);
|
||||
|
||||
} libstreamfile_t;
|
||||
@ -60,8 +62,13 @@ static inline void libstreamfile_close(libstreamfile_t* libsf) {
|
||||
libsf->close(libsf);
|
||||
}
|
||||
|
||||
|
||||
/* base libstreamfile using STDIO (cached) */
|
||||
LIBVGMSTREAM_API libstreamfile_t* libstreamfile_open_from_stdio(const char* filename);
|
||||
|
||||
#endif
|
||||
/* base libstreamfile using a FILE (cached); the filename is needed as metadata */
|
||||
LIBVGMSTREAM_API libstreamfile_t* libstreamfile_open_from_file(void* file, const char* filename);
|
||||
|
||||
/* cached streamfile (recommended to wrap your external libsf since vgmstream needs to seek a lot) */
|
||||
LIBVGMSTREAM_API libstreamfile_t* libstreamfile_open_buffered(libstreamfile_t* ext_libsf, int buf_size);
|
||||
|
||||
#endif
|
||||
|
Loading…
x
Reference in New Issue
Block a user