Merge pull request #1564 from bnnm/api-misc4

api misc4
This commit is contained in:
bnnm 2024-07-25 20:26:39 +02:00 committed by GitHub
commit 808a3233a8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
58 changed files with 3349 additions and 3152 deletions

1
.gitattributes vendored
View File

@ -6,6 +6,7 @@
*.cpp text eol=lf
*.cc text eol=lf
*.h text eol=lf
*.sh text eol=lf
*.bat text eol=crlf

View File

@ -13,7 +13,7 @@ import glob, os, re
META_SRC = '../**/meta/*.c'
FORMAT_SRC = '../**/formats.c'
SORT_SRC = '../**/vgmstream.c'
SORT_SRC = '../**/vgmstream_init.c'
FORMAT_DOC = '../**/FORMATS.md'
DUMP_DOC = 'formats-info.md'
IS_SORT = True #sorts metas lines based on LIST_SRC

View File

@ -14,7 +14,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Fetch Git tags
run: |
@ -33,7 +33,7 @@ jobs:
echo "RELEASE=$(lsb_release -sr)" >> $GITHUB_ENV
- name: Cache celt
uses: actions/cache@v3
uses: actions/cache@v4
with:
path: |
${{runner.workspace}}/dependencies/celt-0061
@ -43,7 +43,7 @@ jobs:
key: linux-${{ env.RELEASE }}-celt-${{ hashFiles('cmake/dependencies/celt.cmake') }}
- name: Cache ffmpeg
uses: actions/cache@v3
uses: actions/cache@v4
with:
path: |
${{runner.workspace}}/build/dependencies/ffmpeg/bin/usr/local/include
@ -51,21 +51,21 @@ jobs:
key: linux-${{ env.RELEASE }}-ffmpeg-${{ hashFiles('cmake/dependencies/ffmpeg.cmake') }}
- name: Cache atrac9
uses: actions/cache@v3
uses: actions/cache@v4
with:
path: |
${{runner.workspace}}/build/dependencies/LibAtrac9/bin
key: linux-${{ env.RELEASE }}-atrac9-${{ hashFiles('cmake/dependencies/atrac9.cmake') }}
- name: Cache g719
uses: actions/cache@v3
uses: actions/cache@v4
with:
path: |
${{runner.workspace}}/build/dependencies/libg719_decode/libg719_decode.a
key: linux-${{ env.RELEASE }}-g719-${{ hashFiles('cmake/dependencies/g719.cmake') }}
- name: Cache mpg123
uses: actions/cache@v3
uses: actions/cache@v4
with:
path: |
${{runner.workspace}}/dependencies/mpg123
@ -73,14 +73,14 @@ jobs:
key: linux-${{ env.RELEASE }}-mpg123-${{ hashFiles('cmake/dependencies/mpg123.cmake') }}
- name: Cache speex
uses: actions/cache@v3
uses: actions/cache@v4
with:
path: |
${{runner.workspace}}/build/dependencies/speex/libspeex/.libs
key: linux-${{ env.RELEASE }}-speex-${{ hashFiles('cmake/dependencies/speex.cmake') }}
- name: Cache ogg
uses: actions/cache@v3
uses: actions/cache@v4
with:
path: |
${{runner.workspace}}/build/dependencies/ogg/libogg.a
@ -88,7 +88,7 @@ jobs:
key: linux-${{ env.RELEASE }}-ogg-${{ hashFiles('cmake/dependencies/ogg.cmake') }}
- name: Cache vorbis
uses: actions/cache@v3
uses: actions/cache@v4
with:
path: |
${{runner.workspace}}/build/dependencies/vorbis/lib/*.a
@ -111,7 +111,7 @@ jobs:
run: cmake --build . --config $BUILD_TYPE
- name: Upload CLI tools artifact
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
path: |
${{runner.workspace}}/build/cli/vgmstream-cli
@ -119,7 +119,7 @@ jobs:
name: vgmstream-cli
#- name: Upload Audacious plugin artifact
# uses: actions/upload-artifact@v3
# uses: actions/upload-artifact@v4
# with:
# path: ${{runner.workspace}}/build/audacious/vgmstream.so
# name: vgmstream-audacious

View File

@ -14,7 +14,7 @@ jobs:
runs-on: macos-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
with:
fetch-depth: 0
@ -22,7 +22,7 @@ jobs:
run: brew install autoconf automake cmake pkgconfig ffmpeg jansson libao libvorbis mpg123
- name: Cache celt
uses: actions/cache@v3
uses: actions/cache@v4
with:
path: |
${{ github.workspace }}/dependencies/celt-0061
@ -32,13 +32,13 @@ jobs:
key: mac-celt-${{ hashFiles('cmake/dependencies/celt.cmake') }}
- name: Cache atrac9
uses: actions/cache@v3
uses: actions/cache@v4
with:
path: ${{ github.workspace }}/build/dependencies/LibAtrac9/bin
key: mac-atrac9-${{ hashFiles('cmake/dependencies/atrac9.cmake') }}
- name: Cache g719
uses: actions/cache@v3
uses: actions/cache@v4
with:
path: ${{ github.workspace }}/build/dependencies/libg719_decode/libg719_decode.a
key: mac-g719-${{ hashFiles('cmake/dependencies/g719.cmake') }}
@ -50,7 +50,7 @@ jobs:
cmake --build build
- name: Upload CLI tools artifact
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
path: ${{ github.workspace }}/build/cli/vgmstream-cli
name: vgmstream-mac

View File

@ -16,7 +16,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Fetch Git tags
run: |
@ -32,7 +32,7 @@ jobs:
run: cmake -E make_directory ${{runner.workspace}}/embuild
- name: Cache celt
uses: actions/cache@v3
uses: actions/cache@v4
with:
path: |
${{runner.workspace}}/dependencies/celt-0061
@ -42,7 +42,7 @@ jobs:
key: wasm-celt-${{ hashFiles('cmake/dependencies/celt.cmake') }}
- name: Cache ffmpeg
uses: actions/cache@v3
uses: actions/cache@v4
with:
path: |
${{runner.workspace}}/embuild/dependencies/ffmpeg/bin/usr/local/include
@ -50,28 +50,28 @@ jobs:
key: wasm-ffmpeg-${{ hashFiles('cmake/dependencies/ffmpeg.cmake') }}
- name: Cache jansson
uses: actions/cache@v3
uses: actions/cache@v4
with:
path: |
${{runner.workspace}}/embuild/dependencies/jansson/src/.libs
key: wasm-jansson-${{ hashFiles('cmake/dependencies/jansson.cmake') }}
- name: Cache atrac9
uses: actions/cache@v3
uses: actions/cache@v4
with:
path: |
${{runner.workspace}}/embuild/dependencies/LibAtrac9/bin
key: wasm-atrac9-${{ hashFiles('cmake/dependencies/atrac9.cmake') }}
- name: Cache g719
uses: actions/cache@v3
uses: actions/cache@v4
with:
path: |
${{runner.workspace}}/embuild/dependencies/libg719_decode/libg719_decode.a
key: wasm-g719-${{ hashFiles('cmake/dependencies/g719.cmake') }}
- name: Cache mpg123
uses: actions/cache@v3
uses: actions/cache@v4
with:
path: |
${{runner.workspace}}/dependencies/mpg123
@ -79,14 +79,14 @@ jobs:
key: wasm-mpg123-${{ hashFiles('cmake/dependencies/mpg123.cmake') }}
- name: Cache speex
uses: actions/cache@v3
uses: actions/cache@v4
with:
path: |
${{runner.workspace}}/embuild/dependencies/speex/libspeex/.libs
key: wasm-speex-${{ hashFiles('cmake/dependencies/speex.cmake') }}
- name: Cache ogg
uses: actions/cache@v3
uses: actions/cache@v4
with:
path: |
${{runner.workspace}}/embuild/dependencies/ogg/libogg.a
@ -94,7 +94,7 @@ jobs:
key: wasm-ogg-${{ hashFiles('cmake/dependencies/ogg.cmake') }}
- name: Cache vorbis
uses: actions/cache@v3
uses: actions/cache@v4
with:
path: |
${{runner.workspace}}/embuild/dependencies/vorbis/lib/*.a
@ -111,7 +111,7 @@ jobs:
run: emmake make
- name: Upload WebAssembly artifact
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
path: |
${{runner.workspace}}/embuild/cli/vgmstream-cli.js

View File

@ -18,7 +18,7 @@ jobs:
# Steps represent a sequence of tasks that will be executed as part of the job
steps:
# Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Fetch Git tags
shell: cmd
@ -39,25 +39,25 @@ jobs:
# upload to "actions" tab artifacts (can only make a single .zip from a dir's files)
- name: Upload foobar2000 component artifact
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: foo_input_vgmstream.fb2k-component
path: ${{github.workspace}}\bin\artifacts\foobar2000
- name: Upload CLI tools 32-bit artifact
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: vgmstream-win
path: ${{github.workspace}}\bin\artifacts\cli-x32
- name: Upload CLI tools 64-bit artifact
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: vgmstream-win64
path: ${{github.workspace}}\bin\artifacts\cli-x64
- name: debug symbols artifact (all)
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: debug-symbols-pdb
path: ${{github.workspace}}\bin\artifacts\pdb

View File

@ -1,7 +1,7 @@
# CLI
add_executable(vgmstream_cli
vgmstream_cli.c wav_utils.c)
vgmstream_cli.c vgmstream_cli_utils.c wav_utils.c)
set_target_properties(vgmstream_cli PROPERTIES
PREFIX ""

View File

@ -22,6 +22,12 @@ ifeq ($(TARGET_OS),Windows_NT)
LIBAO_INC = -I$(LIBAO_IPATH)
LIBAO_LIB = -L$(LIBAO_LPATH) -lao
# strip needs full name
OUTPUT_CLI = vgmstream-cli.exe
OUTPUT_123 = vgmstream123.exe
OUTPUT_API = api_example.exe
else
#todo move to subfolders and remove
CFLAGS += -I../ext_includes
@ -41,7 +47,7 @@ export CFLAGS LDFLAGS
### targets
vgmstream_cli: libvgmstream.a $(TARGET_EXT_LIBS)
$(CC) $(CFLAGS) vgmstream_cli.c wav_utils.c $(LDFLAGS) -o $(OUTPUT_CLI)
$(CC) $(CFLAGS) vgmstream_cli.c vgmstream_cli_utils.c wav_utils.c $(LDFLAGS) -o $(OUTPUT_CLI)
$(STRIP) $(OUTPUT_CLI)
vgmstream123: libvgmstream.a $(TARGET_EXT_LIBS)
@ -50,7 +56,7 @@ vgmstream123: libvgmstream.a $(TARGET_EXT_LIBS)
api_example: libvgmstream.a $(TARGET_EXT_LIBS)
$(CC) $(CFLAGS) api_example.c $(LDFLAGS) -o $(OUTPUT_API)
$(STRIP) api_example
$(STRIP) $(OUTPUT_API)
libvgmstream.a:
$(MAKE) -C ../src $@

View File

@ -9,7 +9,7 @@ endif
AM_CFLAGS = -DVGMSTREAM_VERSION_AUTO -DVGM_LOG_OUTPUT -I$(top_builddir) -I$(top_srcdir) -I$(top_srcdir)/ext_includes/ $(AO_CFLAGS)
AM_MAKEFLAGS = -f Makefile.autotools
vgmstream_cli_SOURCES = vgmstream_cli.c wav_utils.c
vgmstream_cli_SOURCES = vgmstream_cli.c vgmstream_cli_utils.c wav_utils.c
vgmstream_cli_LDADD = ../src/libvgmstream.la
vgmstream123_SOURCES = vgmstream123.c wav_utils.c

View File

@ -22,7 +22,9 @@
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#include <strings.h>
#include <getopt.h>
#include <ao/ao.h>
@ -37,6 +39,8 @@
# include <termios.h>
#endif
#include "wav_utils.h"
#include "../src/vgmstream.h"
#include "../src/api.h"
@ -396,7 +400,7 @@ static int play_vgmstream(const char* filename, song_settings_t* cfg) {
render_vgmstream(buffer, to_do, vgmstream);
#if LITTLE_ENDIAN_OUTPUT
swap_samples_le(buffer, output_channels * to_do, 0);
wav_swap_samples_le(buffer, output_channels * to_do, 0);
#endif
if (verbose && !out_filename) {
@ -464,6 +468,7 @@ fail:
}
static int play_playlist(const char *filename, song_settings_t *default_par) {
#ifndef WIN32
int ret = 0;
FILE *f;
char *line = NULL;
@ -537,6 +542,9 @@ static int play_playlist(const char *filename, song_settings_t *default_par) {
fclose(f);
return ret;
#else
return -1;
#endif
}
static int play_compressed_file(const char *filename, song_settings_t *par, const char *expand_cmd) {
@ -684,7 +692,7 @@ static void add_driver_option(const char *key_value) {
}
static void usage(const char* progname, int is_help) {
static void print_usage(const char* progname, int is_help) {
song_settings_t default_par = DEFAULT_PARAMS;
const char* default_driver = "???";
@ -765,7 +773,7 @@ int main(int argc, char **argv) {
if (argc == 1) {
/* We were invoked with no arguments */
usage(argv[0], 0);
print_usage(argv[0], 0);
goto done;
}
@ -848,7 +856,7 @@ again_opts:
out_filename = optarg;
break;
case 'h':
usage(argv[0], 1);
print_usage(argv[0], 1);
goto done;
case 'P':
add_driver_option(optarg);

File diff suppressed because it is too large Load Diff

77
cli/vgmstream_cli.h Normal file
View File

@ -0,0 +1,77 @@
#ifndef _VGMSTREAM_CLI_H
#define _VGMSTREAM_CLI_H
#include "../src/api.h"
#include "../src/vgmstream.h"
typedef struct {
char** infilenames;
int infilenames_count;
const char* infilename;
const char* outfilename_config;
const char* outfilename;
int sample_buffer_size;
// playback config
double loop_count;
double fade_time;
double fade_delay;
bool ignore_fade;
bool ignore_loop;
bool force_loop;
bool really_force_loop;
bool play_forever;
bool play_sdtout;
bool play_wreckless;
// subsongs
int subsong_index;
int subsong_end;
// wav config
bool write_lwav;
bool write_original_wav;
// print flags
bool print_metaonly;
bool print_adxencd;
bool print_oggenc;
bool print_batchvar;
bool print_title;
#ifdef HAVE_JSON
bool print_metajson;
#endif
const char* tag_filename;
// debug stuff
bool decode_only;
bool test_reset;
bool validate_extensions;
int seek_samples1;
int seek_samples2;
int downmix_channels;
int stereo_track;
/* not quite config but eh */
int lwav_loop_start;
int lwav_loop_end;
} cli_config_t;
void replace_filename(char* dst, size_t dstsize, cli_config_t* cfg, VGMSTREAM* vgmstream);
void print_info(VGMSTREAM* vgmstream, cli_config_t* cfg);
void print_tags(cli_config_t* cfg);
void print_title(VGMSTREAM* vgmstream, cli_config_t* cfg);
#ifdef HAVE_JSON
void print_json_version(const char* vgmstream_version);
void print_json_info(VGMSTREAM* vgm, cli_config_t* cfg, const char* vgmstream_version);
#endif
#endif

View File

@ -104,10 +104,12 @@
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClInclude Include="vgmstream_cli.h" />
<ClInclude Include="wav_utils.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="vgmstream_cli.c" />
<ClCompile Include="vgmstream_cli_utils.c" />
<ClCompile Include="wav_utils.c" />
</ItemGroup>
<ItemGroup>

View File

@ -19,6 +19,9 @@
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="vgmstream_cli.h">
<Filter>Header Files</Filter>
</ClCompile>
<ClCompile Include="wav_utils.h">
<Filter>Header Files</Filter>
</ClCompile>
@ -27,6 +30,9 @@
<ClCompile Include="vgmstream_cli.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="vgmstream_cli_utils.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="wav_utils.c">
<Filter>Source Files</Filter>
</ClCompile>

296
cli/vgmstream_cli_utils.c Normal file
View File

@ -0,0 +1,296 @@
#include "vgmstream_cli.h"
#include "../src/api.h"
#include "../src/vgmstream.h"
//todo use <>?
#ifdef HAVE_JSON
#include "jansson/jansson.h"
#endif
static void clean_filename(char* dst, int clean_paths) {
for (int i = 0; i < strlen(dst); i++) {
char c = dst[i];
int is_badchar = (clean_paths && (c == '\\' || c == '/'))
|| c == '*' || c == '?' || c == ':' /*|| c == '|'*/ || c == '<' || c == '>';
if (is_badchar)
dst[i] = '_';
}
}
/* replaces a filename with "?n" (stream name), "?f" (infilename) or "?s" (subsong) wildcards
* ("?" was chosen since it's not a valid Windows filename char and hopefully nobody uses it on Linux) */
void replace_filename(char* dst, size_t dstsize, cli_config_t* cfg, VGMSTREAM* vgmstream) {
int subsong;
char stream_name[PATH_LIMIT];
char buf[PATH_LIMIT];
char tmp[PATH_LIMIT];
/* file has a "%" > temp replace for sprintf */
strcpy(buf, cfg->outfilename_config);
for (int i = 0; i < strlen(buf); i++) {
if (buf[i] == '%')
buf[i] = '|'; /* non-valid filename, not used in format */
}
/* init config */
subsong = vgmstream->stream_index;
if (subsong > vgmstream->num_streams || subsong != cfg->subsong_index) {
subsong = 0; /* for games without subsongs / bad config */
}
if (vgmstream->stream_name[0] != '\0') {
snprintf(stream_name, sizeof(stream_name), "%s", vgmstream->stream_name);
clean_filename(stream_name, 1); /* clean subsong name's subdirs */
}
else {
snprintf(stream_name, sizeof(stream_name), "%s", cfg->infilename);
clean_filename(stream_name, 0); /* don't clean user's subdirs */
}
/* do controlled replaces of each wildcard (in theory could appear N times) */
do {
char* pos = strchr(buf, '?');
if (!pos)
break;
/* use buf as format and copy formatted result to tmp (assuming sprintf's format must not overlap with dst) */
if (pos[1] == 'n') {
pos[0] = '%';
pos[1] = 's'; /* use %s */
snprintf(tmp, sizeof(tmp), buf, stream_name);
}
else if (pos[1] == 'f') {
pos[0] = '%';
pos[1] = 's'; /* use %s */
snprintf(tmp, sizeof(tmp), buf, cfg->infilename);
}
else if (pos[1] == 's') {
pos[0] = '%';
pos[1] = 'i'; /* use %i */
snprintf(tmp, sizeof(tmp), buf, subsong);
}
else if ((pos[1] == '0' && pos[2] >= '1' && pos[2] <= '9' && pos[3] == 's')) {
pos[0] = '%';
pos[3] = 'i'; /* use %0Ni */
snprintf(tmp, sizeof(tmp), buf, subsong);
}
else {
/* not recognized */
continue;
}
/* copy result to buf again, so it can be used as format in next replace
* (can be optimized with some pointer swapping but who cares about a few extra nanoseconds) */
strcpy(buf, tmp);
}
while (1);
/* replace % back */
for (int i = 0; i < strlen(buf); i++) {
if (buf[i] == '|')
buf[i] = '%';
}
snprintf(dst, dstsize, "%s", buf);
}
void print_info(VGMSTREAM* vgmstream, cli_config_t* cfg) {
int channels = vgmstream->channels;
if (!cfg->play_sdtout) {
if (cfg->print_adxencd) {
printf("adxencd");
if (!cfg->print_metaonly)
printf(" \"%s\"",cfg->outfilename);
if (vgmstream->loop_flag)
printf(" -lps%d -lpe%d", vgmstream->loop_start_sample, vgmstream->loop_end_sample);
printf("\n");
}
else if (cfg->print_oggenc) {
printf("oggenc");
if (!cfg->print_metaonly)
printf(" \"%s\"", cfg->outfilename);
if (vgmstream->loop_flag)
printf(" -c LOOPSTART=%d -c LOOPLENGTH=%d", vgmstream->loop_start_sample, vgmstream->loop_end_sample-vgmstream->loop_start_sample);
printf("\n");
}
else if (cfg->print_batchvar) {
if (!cfg->print_metaonly)
printf("set fname=\"%s\"\n", cfg->outfilename);
printf("set tsamp=%d\nset chan=%d\n", vgmstream->num_samples, channels);
if (vgmstream->loop_flag)
printf("set lstart=%d\nset lend=%d\nset loop=1\n", vgmstream->loop_start_sample, vgmstream->loop_end_sample);
else
printf("set loop=0\n");
}
else if (cfg->print_metaonly) {
printf("metadata for %s\n", cfg->infilename);
}
else {
printf("decoding %s\n", cfg->infilename);
}
}
if (!cfg->play_sdtout && !cfg->print_adxencd && !cfg->print_oggenc && !cfg->print_batchvar) {
char description[1024];
describe_vgmstream(vgmstream, description, 1024);
printf("%s", description);
}
}
void print_tags(cli_config_t* cfg) {
VGMSTREAM_TAGS* tags = NULL;
STREAMFILE* sf_tags = NULL;
const char *tag_key, *tag_val;
if (!cfg->tag_filename)
return;
sf_tags = open_stdio_streamfile(cfg->tag_filename);
if (!sf_tags) {
printf("tag file %s not found\n", cfg->tag_filename);
return;
}
printf("tags:\n");
tags = vgmstream_tags_init(&tag_key, &tag_val);
vgmstream_tags_reset(tags, cfg->infilename);
while (vgmstream_tags_next_tag(tags, sf_tags)) {
printf("- '%s'='%s'\n", tag_key, tag_val);
}
vgmstream_tags_close(tags);
close_streamfile(sf_tags);
}
void print_title(VGMSTREAM* vgmstream, cli_config_t* cfg) {
char title[1024];
vgmstream_title_t tcfg = {0};
if (!cfg->print_title)
return;
tcfg.force_title = 0;
tcfg.subsong_range = 0;
tcfg.remove_extension = 0;
vgmstream_get_title(title, sizeof(title), cfg->infilename, vgmstream, &tcfg);
printf("title: %s\n", title);
}
#ifdef HAVE_JSON
void print_json_version(const char* vgmstream_version) {
size_t extension_list_len;
size_t common_extension_list_len;
const char** extension_list;
const char** common_extension_list;
extension_list = vgmstream_get_formats(&extension_list_len);
common_extension_list = vgmstream_get_common_formats(&common_extension_list_len);
json_t* ext_list = json_array();
json_t* cext_list = json_array();
for (size_t i = 0; i < extension_list_len; ++i) {
json_t* ext = json_string(extension_list[i]);
json_array_append(ext_list, ext);
}
for (size_t i = 0; i < common_extension_list_len; ++i) {
json_t* cext = json_string(common_extension_list[i]);
json_array_append(cext_list, cext);
}
json_t* version_string = json_string(vgmstream_version);
json_t* final_object = json_object();
json_object_set(final_object, "version", version_string);
json_decref(version_string);
json_object_set(final_object, "extensions",
json_pack("{soso}",
"vgm", ext_list,
"common", cext_list));
json_dumpf(final_object, stdout, JSON_COMPACT);
}
void print_json_info(VGMSTREAM* vgm, cli_config_t* cfg, const char* vgmstream_version) {
json_t* version_string = json_string(vgmstream_version);
vgmstream_info info;
describe_vgmstream_info(vgm, &info);
json_t* mixing_info = NULL;
// The JSON pack format string is defined here: https://jansson.readthedocs.io/en/latest/apiref.html#building-values
if (info.mixing_info.input_channels > 0) {
mixing_info = json_pack("{sisi}",
"inputChannels", info.mixing_info.input_channels,
"outputChannels", info.mixing_info.output_channels);
}
json_t* loop_info = NULL;
if (info.loop_info.end > info.loop_info.start) {
loop_info = json_pack("{sisi}",
"start", info.loop_info.start,
"end", info.loop_info.end);
}
json_t* interleave_info = NULL;
if (info.interleave_info.last_block > info.interleave_info.first_block) {
interleave_info = json_pack("{sisi}",
"firstBlock", info.interleave_info.first_block,
"lastBlock", info.interleave_info.last_block
);
}
json_t* stream_info = json_pack("{sisssi}",
"index", info.stream_info.current,
"name", info.stream_info.name,
"total", info.stream_info.total
);
if (info.stream_info.name[0] == '\0') {
json_object_set(stream_info, "name", json_null());
}
json_t* final_object = json_pack(
"{sssisiso?siso?so?sisssssisssiso?}",
"version", version_string,
"sampleRate", info.sample_rate,
"channels", info.channels,
"mixingInfo", mixing_info,
"channelLayout", info.channel_layout,
"loopingInfo", loop_info,
"interleaveInfo", interleave_info,
"numberOfSamples", info.num_samples,
"encoding", info.encoding,
"layout", info.layout,
"frameSize", info.frame_size,
"metadataSource", info.metadata,
"bitrate", info.bitrate,
"streamInfo", stream_info
);
if (info.frame_size == 0) {
json_object_set(final_object, "frameSize", json_null());
}
if (info.channel_layout == 0) {
json_object_set(final_object, "channelLayout", json_null());
}
json_dumpf(final_object, stdout, JSON_COMPACT);
json_decref(final_object);
printf("\n");
}
#endif

View File

@ -104,27 +104,6 @@ size_t wav_make_header(uint8_t* buf, size_t buf_size, wav_header_t* wav) {
return header_size;
}
#if 0
void swap_samples_le(sample_t *buf, int count) {
/* Windows can't be BE... I think */
#if !defined(_WIN32)
#if !defined(__BYTE_ORDER__) || __BYTE_ORDER__ != __ORDER_LITTLE_ENDIAN__
for (int i = 0; i < count; i++) {
/* 16b sample in memory: aabb where aa=MSB, bb=LSB */
uint8_t b0 = buf[i] & 0xff;
uint8_t b1 = buf[i] >> 8;
uint8_t *p = (uint8_t*)&(buf[i]);
/* 16b sample in buffer: bbaa where bb=LSB, aa=MSB */
p[0] = b0;
p[1] = b1;
/* when endianness is LE, buffer has bbaa already so this function can be skipped */
}
#endif
#endif
}
#endif
static inline void swap_value(uint8_t* buf, int sample_size) {
for (int i = 0; i < sample_size / 2; i++) {
char temp = buf[i];
@ -134,7 +113,7 @@ static inline void swap_value(uint8_t* buf, int sample_size) {
}
/* when endianness is LE buffer is correct already and this function can be skipped */
void swap_samples_le(void* samples, int samples_len, int sample_size) {
void wav_swap_samples_le(void* samples, int samples_len, int sample_size) {
/* Windows can't be BE... I think */
#if !defined(_WIN32)
#if !defined(__BYTE_ORDER__) || __BYTE_ORDER__ != __ORDER_LITTLE_ENDIAN__

View File

@ -24,6 +24,6 @@ size_t wav_make_header(uint8_t* buf, size_t buf_size, wav_header_t* wav);
/* swap big endian samples to little endian. Does nothing if machine is already LE.
* Used when writting .WAV files, where samples in memory/buf may be BE while RIFF
* is expected to have LE samples. */
void swap_samples_le(void* samples, int samples_len, int sample_size);
void wav_swap_samples_le(void* samples, int samples_len, int sample_size);
#endif

View File

@ -10,16 +10,17 @@ There are no hard coding rules but for consistency one should follow the style u
- 4 spaces instead of tabs
- `\n` breaks (LF, Linux style), instead of `\r\n` (CRLF, Windows style)
- `underscore_and_lowercase_names` instead of `CamelCase`
- `/* C89 comments */` for general comments, `//C99 comments` for special comments (like disabling code but leaving it there for visibility)
- `/* C89 comments */` for general comments, `//C99 comments` for other comments
- brackets starting in the same line
- ex. `if (..) { LF ... LF }`
- line length ~100, more is ok for 'noise code' (uninteresting calcs or function defs)
- offsets/sizes in hex, counts/numbers in decimal
- test functions may return 1=ok, 0=ko for simplicity
- `free(ptr)` no need to NULL-check per standard, `close_stuff(ptr)` should follow when possible
- `lowercase_helper_structs_t`, `UPPERCASE_MAIN_STRUCTS`
- `lowercase_helper_structs_t`, `UPPERCASE_INTERNAL_STRUCTS`
- spaces in calcs/ifs/etc may be added as desired for clarity
- ex. `if (simple_check)` or `if ( complex_and_important_stuff(weird + weird) )`
- though generally you should split steps if readibility is impaired
- `goto` are used to abort and reach "fail" sections (typical C cleanup style), beware vars should be defined first
- pointer definitions should keep the `*` together for consistency
- ex. `VGMSTREAM* init_x() { ... }` `STREAMFILE* sf = ...`
@ -79,7 +80,8 @@ Some of the code can be inefficient or duplicated at places, but it isn't that m
./ext_includes/ external includes for compiling
./ext_libs/ external libs/DLLs for linking
./fb2k/ foobar2000 plugin
./src/ main vgmstream code
./src/ initial vgmstream code
./src/base/ core vgmstream features
./src/coding/ format data decoders
./src/layout/ format data demuxers
./src/meta/ format header parsers

View File

@ -830,9 +830,9 @@ different internally (encrypted, different versions, etc) and not always can be
- Sony MSF header [*MSF*]
- *msf*: `.msf .msa .at3 .mp3 .str .snd`
- Codecs: PCM16BE PCM16LE PSX ATRAC3 FFmpeg(various)
- **ps3_past.c**
- SNDP header [*PS3_PAST*]
- *ps3_past*: `.past`
- **sndp.c**
- Premium Agency SNDP header [*SNDP*]
- *sndp*: `.past`
- Codecs: PCM16LE
- **sgxd.c**
- Sony SGXD header [*SGXD*]
@ -1262,7 +1262,7 @@ different internally (encrypted, different versions, etc) and not always can be
- *smc_smh*: `.smc + .smh`
- Codecs: PSX
- **ppst.c**
- Parappa PPST header [*PPST*]
- epics PPST header [*PPST*]
- *ppst*: `.sng`
- Subfiles: *riff*
- **ubi_bao.c**
@ -2033,6 +2033,7 @@ are used in few games.
- Paradigm MC3 ADPCM
- Ocean DSA ADPCM
- lsf ADPCM
- Ongakukan ADPCM
- ITU-T G.721
- CompressWave (CWav) Huffman ADPCM
- Perceptual/transform-based

View File

@ -9,6 +9,8 @@
void decode_free(VGMSTREAM* vgmstream) {
if (!vgmstream->codec_data)
return;
#ifdef VGM_USE_VORBIS
if (vgmstream->coding_type == coding_OGG_VORBIS) {
@ -124,6 +126,9 @@ void decode_free(VGMSTREAM* vgmstream) {
void decode_seek(VGMSTREAM* vgmstream) {
if (!vgmstream->codec_data)
return;
if (vgmstream->coding_type == coding_CIRCUS_VQ) {
seek_circus_vq(vgmstream->codec_data, vgmstream->loop_current_sample);
}
@ -222,6 +227,8 @@ void decode_seek(VGMSTREAM* vgmstream) {
void decode_reset(VGMSTREAM* vgmstream) {
if (!vgmstream->codec_data)
return;
#ifdef VGM_USE_VORBIS
if (vgmstream->coding_type == coding_OGG_VORBIS) {
@ -1672,7 +1679,7 @@ int decode_do_loop(VGMSTREAM* vgmstream) {
vgmstream->loop_next_block_offset = vgmstream->next_block_offset;
//vgmstream->lstate = vgmstream->pstate; /* play state is applied over loops */
vgmstream->hit_loop = 1; /* info that loop is now ready to use */
vgmstream->hit_loop = true; /* info that loop is now ready to use */
}
return 0; /* not looped */

118
src/base/mixer.c Normal file
View File

@ -0,0 +1,118 @@
#include "../vgmstream.h"
#include "../util/channel_mappings.h"
#include "mixing.h"
#include "mixer_priv.h"
#include "mixer.h"
#include "sbuf.h"
#include <math.h>
#include <limits.h>
//TODO simplify
/**
* Mixer modifies decoded sample buffer before final output. This is implemented
* mostly with simplicity in mind rather than performance. Process:
* - detect if mixing applies at current moment or exit (mini performance optimization)
* - copy/upgrade buf to float mixbuf if needed
* - do mixing ops
* - copy/downgrade mixbuf to original buf if needed
*
* Mixing may add or remove channels. input_channels is the buf's original channels,
* and output_channels the resulting buf's channels. buf and mixbuf must be
* as big as max channels (mixing_channels).
*
* Mixing ops are added by a meta (ex. TXTP) or plugin through the API. Non-sensical
* mixes are ignored (to avoid rechecking every time).
*
* Currently, mixing must be manually enabled before starting to decode, because plugins
* need to setup bigger bufs when upmixing. (to be changed)
*
* segmented/layered layouts handle mixing on their own.
*/
mixer_t* mixer_init(int channels) {
mixer_t* mixer = calloc(1, sizeof(mixer_t));
if (!mixer) goto fail;
mixer->chain_size = VGMSTREAM_MAX_MIXING; /* fixed array for now */
mixer->mixing_channels = channels;
mixer->output_channels = channels;
mixer->input_channels = channels;
return mixer;
fail:
mixer_free(mixer);
return NULL;
}
void mixer_free(mixer_t* mixer) {
if (!mixer) return;
free(mixer->mixbuf);
free(mixer);
}
void mixer_update_channel(mixer_t* mixer) {
if (!mixer) return;
/* lame hack for dual stereo, but dual stereo is pretty hack-ish to begin with */
mixer->mixing_channels++;
mixer->output_channels++;
}
bool mixer_is_active(mixer_t* mixer) {
/* no support or not need to apply */
if (!mixer || !mixer->active || mixer->chain_count == 0)
return false;
return true;
}
void mixer_process(mixer_t* mixer, sample_t* outbuf, int32_t sample_count, int32_t current_pos) {
/* no support or not need to apply */
if (!mixer || !mixer->active || mixer->chain_count == 0)
return;
/* try to skip if no fades apply (set but does nothing yet) + only has fades
* (could be done in mix op but avoids upgrading bufs in some cases) */
mixer->current_subpos = 0;
if (mixer->has_fade) {
//;VGM_LOG("MIX: fade test %i, %i\n", data->has_non_fade, mixer_op_fade_is_active(data, current_pos, current_pos + sample_count));
if (!mixer->has_non_fade && !mixer_op_fade_is_active(mixer, current_pos, current_pos + sample_count))
return;
//;VGM_LOG("MIX: fade pos=%i\n", current_pos);
mixer->current_subpos = current_pos;
}
// upgrade buf for mixing (somehow using float buf rather than int32 is faster?)
sbuf_copy_s16_to_f32(mixer->mixbuf, outbuf, sample_count, mixer->input_channels);
/* apply mixing ops in order. Some ops change total channels they may change around:
* - 2ch w/ "1+2,1u" = ch1+ch2, ch1(add and push rest) = 3ch: ch1' ch1+ch2 ch2
* - 2ch w/ "1u" = downmix to 1ch (current_channels decreases once)
*/
mixer->current_channels = mixer->input_channels;
for (int m = 0; m < mixer->chain_count; m++) {
mix_op_t* mix = &mixer->chain[m];
//TODO: set callback
switch(mix->type) {
case MIX_SWAP: mixer_op_swap(mixer, sample_count, mix); break;
case MIX_ADD: mixer_op_add(mixer, sample_count, mix); break;
case MIX_VOLUME: mixer_op_volume(mixer, sample_count, mix); break;
case MIX_LIMIT: mixer_op_limit(mixer, sample_count, mix); break;
case MIX_UPMIX: mixer_op_upmix(mixer, sample_count, mix); break;
case MIX_DOWNMIX: mixer_op_downmix(mixer, sample_count, mix); break;
case MIX_KILLMIX: mixer_op_killmix(mixer, sample_count, mix); break;
case MIX_FADE: mixer_op_fade(mixer, sample_count, mix);
default:
break;
}
}
/* downgrade mix to original output */
sbuf_copy_f32_to_s16(outbuf, mixer->mixbuf, sample_count, mixer->output_channels);
}

16
src/base/mixer.h Normal file
View File

@ -0,0 +1,16 @@
#ifndef _MIXER_H_
#define _MIXER_H_
#include "../streamtypes.h"
typedef struct mixer_t mixer_t;
/* internal mixing pre-setup for vgmstream (doesn't imply usage).
* If init somehow fails next calls are ignored. */
mixer_t* mixer_init(int channels);
void mixer_free(mixer_t* mixer);
void mixer_update_channel(mixer_t* mixer);
void mixer_process(mixer_t* mixer, sample_t *outbuf, int32_t sample_count, int32_t current_pos);
bool mixer_is_active(mixer_t* mixer);
#endif

142
src/base/mixer_ops_common.c Normal file
View File

@ -0,0 +1,142 @@
#include "mixer_priv.h"
// TO-DO: some ops can be done with original PCM sbuf to avoid copying to the float sbuf
// when there are no actual float ops (ex. 'swap', if no ' volume' )
// Performance gain is probably fairly small, though.
void mixer_op_swap(mixer_t* mixer, int32_t sample_count, mix_op_t* op) {
float* sbuf = mixer->mixbuf;
for (int s = 0; s < sample_count; s++) {
float temp_f = sbuf[op->ch_dst];
sbuf[op->ch_dst] = sbuf[op->ch_src];
sbuf[op->ch_src] = temp_f;
sbuf += mixer->current_channels;
}
}
void mixer_op_add(mixer_t* mixer, int32_t sample_count, mix_op_t* op) {
float* sbuf = mixer->mixbuf;
/* could optimize when vol == 1 to avoid one multiplication but whatevs (not common) */
for (int s = 0; s < sample_count; s++) {
sbuf[op->ch_dst] = sbuf[op->ch_dst] + sbuf[op->ch_src] * op->vol;
sbuf += mixer->current_channels;
}
}
void mixer_op_volume(mixer_t* mixer, int32_t sample_count, mix_op_t* op) {
float* sbuf = mixer->mixbuf;
if (op->ch_dst < 0) {
/* "all channels", most common case */
for (int s = 0; s < sample_count * mixer->current_channels; s++) {
sbuf[s] = sbuf[s] * op->vol;
}
}
else {
for (int s = 0; s < sample_count; s++) {
sbuf[op->ch_dst] = sbuf[op->ch_dst] * op->vol;
sbuf += mixer->current_channels;
}
}
}
void mixer_op_limit(mixer_t* mixer, int32_t sample_count, mix_op_t* op) {
float* sbuf = mixer->mixbuf;
const float limiter_max = 32767.0f;
const float limiter_min = -32768.0f;
const float temp_max = limiter_max * op->vol;
const float temp_min = limiter_min * op->vol;
/* could optimize when vol == 1 to avoid one multiplication but whatevs (not common) */
for (int s = 0; s < sample_count; s++) {
if (op->ch_dst < 0) {
for (int ch = 0; ch < mixer->current_channels; ch++) {
if (sbuf[ch] > temp_max)
sbuf[ch] = temp_max;
else if (sbuf[ch] < temp_min)
sbuf[ch] = temp_min;
}
}
else {
if (sbuf[op->ch_dst] > temp_max)
sbuf[op->ch_dst] = temp_max;
else if (sbuf[op->ch_dst] < temp_min)
sbuf[op->ch_dst] = temp_min;
}
sbuf += mixer->current_channels;
}
}
void mixer_op_upmix(mixer_t* mixer, int32_t sample_count, mix_op_t* op) {
int max_channels = mixer->current_channels;
mixer->current_channels += 1;
float* sbuf_tmp = mixer->mixbuf + sample_count * mixer->current_channels;
float* sbuf = mixer->mixbuf + sample_count * max_channels;
/* copy 'backwards' as otherwise would overwrite samples before moving them forward */
for (int s = 0; s < sample_count; s++) {
sbuf_tmp -= mixer->current_channels;
sbuf -= max_channels;
int sbuf_ch = max_channels - 1;
for (int ch = mixer->current_channels - 1; ch >= 0; ch--) {
if (ch == op->ch_dst) {
sbuf_tmp[ch] = 0; /* inserted as silent */
}
else {
sbuf_tmp[ch] = sbuf[sbuf_ch]; /* 'pull' channels backward */
sbuf_ch--;
}
}
}
}
void mixer_op_downmix(mixer_t* mixer, int32_t sample_count, mix_op_t* op) {
int max_channels = mixer->current_channels;
mixer->current_channels -= 1;
float* sbuf = mixer->mixbuf;
float* sbuf_tmp = sbuf;
for (int s = 0; s < sample_count; s++) {
for (int ch = 0; ch < op->ch_dst; ch++) {
sbuf_tmp[ch] = sbuf[ch]; /* copy untouched channels */
}
for (int ch = op->ch_dst; ch < max_channels; ch++) {
sbuf_tmp[ch] = sbuf[ch + 1]; /* 'pull' dropped channels back */
}
sbuf_tmp += mixer->current_channels;
sbuf += max_channels;
}
}
void mixer_op_killmix(mixer_t* mixer, int32_t sample_count, mix_op_t* op) {
int max_channels = mixer->current_channels;
mixer->current_channels = op->ch_dst; /* clamp channels */
float* sbuf = mixer->mixbuf;
float* sbuf_tmp = sbuf;
for (int s = 0; s < sample_count; s++) {
for (int ch = 0; ch < mixer->current_channels; ch++) {
sbuf_tmp[ch] = sbuf[ch];
}
sbuf_tmp += mixer->current_channels;
sbuf += max_channels;
}
}

View File

@ -1,65 +1,9 @@
#ifndef _MIXING_FADE_H_
#define _MIXING_FADE_H_
#include "mixing_priv.h"
#include <math.h>
#include "mixer_priv.h"
#include <limits.h>
#include <math.h>
#define MIXING_PI 3.14159265358979323846f
static inline int is_fade_active(mixing_data *data, int32_t current_start, int32_t current_end) {
int i;
for (i = 0; i < data->mixing_count; i++) {
mix_command_data *mix = &data->mixing_chain[i];
int32_t fade_start, fade_end;
float vol_start = mix->vol_start;
if (mix->command != MIX_FADE)
continue;
/* check is current range falls within a fade
* (assuming fades were already optimized on add) */
if (mix->time_pre < 0 && vol_start == 1.0) {
fade_start = mix->time_start; /* ignore unused */
}
else {
fade_start = mix->time_pre < 0 ? 0 : mix->time_pre;
}
fade_end = mix->time_post < 0 ? INT_MAX : mix->time_post;
//;VGM_LOG("MIX: fade test, tp=%i, te=%i, cs=%i, ce=%i\n", mix->time_pre, mix->time_post, current_start, current_end);
if (current_start < fade_end && current_end > fade_start) {
//;VGM_LOG("MIX: fade active, cs=%i < fe=%i and ce=%i > fs=%i\n", current_start, fade_end, current_end, fade_start);
return 1;
}
}
return 0;
}
static inline int32_t get_current_pos(VGMSTREAM* vgmstream, int32_t sample_count) {
int32_t current_pos;
if (vgmstream->config_enabled) {
return vgmstream->pstate.play_position;
}
if (vgmstream->loop_flag && vgmstream->loop_count > 0) {
int loop_pre = vgmstream->loop_start_sample; /* samples before looping */
int loop_into = (vgmstream->current_sample - vgmstream->loop_start_sample); /* samples after loop */
int loop_samples = (vgmstream->loop_end_sample - vgmstream->loop_start_sample); /* looped section */
current_pos = loop_pre + (loop_samples * vgmstream->loop_count) + loop_into - sample_count;
}
else {
current_pos = (vgmstream->current_sample - sample_count);
}
return current_pos;
}
static inline float get_fade_gain_curve(char shape, float index) {
float gain;
@ -68,7 +12,7 @@ static inline float get_fade_gain_curve(char shape, float index) {
return index;
}
//todo optimizations: interleave calcs, maybe use cosf, powf, etc? (with extra defines)
//TODO optimizations: interleave calcs, maybe use cosf, powf, etc? (with extra defines)
/* (curve math mostly from SoX/FFmpeg) */
switch(shape) {
@ -79,6 +23,7 @@ static inline float get_fade_gain_curve(char shape, float index) {
//gain = pow(0.1f, (1.0f - index) * 2.5f);
gain = exp(-5.75646273248511f * (1.0f - index));
break;
case 'L': /* logarithmic (inverse of the above, maybe for crossfades) */
//gain = 1 - pow(0.1f, (index) * 2.5f);
gain = 1 - exp(-5.75646273248511f * (index));
@ -95,6 +40,7 @@ static inline float get_fade_gain_curve(char shape, float index) {
case 'p': /* parabola (maybe for crossfades) */
gain = 1.0f - sqrt(1.0f - index);
break;
case 'P': /* inverted parabola (maybe for fades) */
gain = (1.0f - (1.0f - index) * (1.0f - index));
break;
@ -108,28 +54,28 @@ static inline float get_fade_gain_curve(char shape, float index) {
return gain;
}
static int get_fade_gain(mix_command_data *mix, float *out_cur_vol, int32_t current_subpos) {
static bool get_fade_gain(mix_op_t* op, float* out_cur_vol, int32_t current_subpos) {
float cur_vol = 0.0f;
if ((current_subpos >= mix->time_pre || mix->time_pre < 0) && current_subpos < mix->time_start) {
cur_vol = mix->vol_start; /* before */
if ((current_subpos >= op->time_pre || op->time_pre < 0) && current_subpos < op->time_start) {
cur_vol = op->vol_start; /* before */
}
else if (current_subpos >= mix->time_end && (current_subpos < mix->time_post || mix->time_post < 0)) {
cur_vol = mix->vol_end; /* after */
else if (current_subpos >= op->time_end && (current_subpos < op->time_post || op->time_post < 0)) {
cur_vol = op->vol_end; /* after */
}
else if (current_subpos >= mix->time_start && current_subpos < mix->time_end) {
else if (current_subpos >= op->time_start && current_subpos < op->time_end) {
/* in between */
float range_vol, range_dur, range_idx, index, gain;
if (mix->vol_start < mix->vol_end) { /* fade in */
range_vol = mix->vol_end - mix->vol_start;
range_dur = mix->time_end - mix->time_start;
range_idx = current_subpos - mix->time_start;
if (op->vol_start < op->vol_end) { /* fade in */
range_vol = op->vol_end - op->vol_start;
range_dur = op->time_end - op->time_start;
range_idx = current_subpos - op->time_start;
index = range_idx / range_dur;
} else { /* fade out */
range_vol = mix->vol_end - mix->vol_start;
range_dur = mix->time_end - mix->time_start;
range_idx = mix->time_end - current_subpos;
range_vol = op->vol_end - op->vol_start;
range_dur = op->time_end - op->time_start;
range_idx = op->time_end - current_subpos;
index = range_idx / range_dur;
}
@ -149,23 +95,79 @@ static int get_fade_gain(mix_command_data *mix, float *out_cur_vol, int32_t curr
* curves are complementary (exponential fade-in ~= logarithmic fade-out); the following
* are described taking fade-in = normal.
*/
gain = get_fade_gain_curve(mix->shape, index);
gain = get_fade_gain_curve(op->shape, index);
if (mix->vol_start < mix->vol_end) { /* fade in */
cur_vol = mix->vol_start + range_vol * gain;
if (op->vol_start < op->vol_end) { /* fade in */
cur_vol = op->vol_start + range_vol * gain;
} else { /* fade out */
cur_vol = mix->vol_end - range_vol * gain; //mix->vol_start - range_vol * (1 - gain);
cur_vol = op->vol_end - range_vol * gain; //mix->vol_start - range_vol * (1 - gain);
}
}
else {
/* fade is outside reach */
goto fail;
return false;
}
*out_cur_vol = cur_vol;
return 1;
fail:
return 0;
return true;
}
#endif
void mixer_op_fade(mixer_t* mixer, int32_t sample_count, mix_op_t* mix) {
float* sbuf = mixer->mixbuf;
float new_gain = 0.0f;
int channels = mixer->current_channels;
int32_t current_subpos = mixer->current_subpos;
//TODO optimize for case 0?
for (int s = 0; s < sample_count; s++) {
bool fade_applies = get_fade_gain(mix, &new_gain, current_subpos);
if (!fade_applies) //TODO optimize?
continue;
if (mix->ch_dst < 0) {
for (int ch = 0; ch < channels; ch++) {
sbuf[ch] = sbuf[ch] * new_gain;
}
}
else {
sbuf[mix->ch_dst] = sbuf[mix->ch_dst] * new_gain;
}
sbuf += channels;
current_subpos++;
}
mixer->current_subpos = current_subpos;
}
bool mixer_op_fade_is_active(mixer_t* mixer, int32_t current_start, int32_t current_end) {
for (int i = 0; i < mixer->chain_count; i++) {
mix_op_t* mix = &mixer->chain[i];
int32_t fade_start, fade_end;
float vol_start = mix->vol_start;
if (mix->type != MIX_FADE)
continue;
/* check is current range falls within a fade
* (assuming fades were already optimized on add) */
if (mix->time_pre < 0 && vol_start == 1.0) {
fade_start = mix->time_start; /* ignore unused */
}
else {
fade_start = mix->time_pre < 0 ? 0 : mix->time_pre;
}
fade_end = mix->time_post < 0 ? INT_MAX : mix->time_post;
//;VGM_LOG("MIX: fade test, tp=%i, te=%i, cs=%i, ce=%i\n", mix->time_pre, mix->time_post, current_start, current_end);
if (current_start < fade_end && current_end > fade_start) {
//;VGM_LOG("MIX: fade active, cs=%i < fe=%i and ce=%i > fs=%i\n", current_start, fade_end, current_end, fade_start);
return true;
}
}
return false;
}

66
src/base/mixer_priv.h Normal file
View File

@ -0,0 +1,66 @@
#ifndef _MIXER_PRIV_H_
#define _MIXER_PRIV_H_
#include "../streamtypes.h"
#include "mixer.h"
#define VGMSTREAM_MAX_MIXING 512
typedef enum {
MIX_SWAP,
MIX_ADD,
MIX_VOLUME,
MIX_LIMIT,
MIX_UPMIX,
MIX_DOWNMIX,
MIX_KILLMIX,
MIX_FADE
} mix_type_t;
typedef struct {
mix_type_t type;
/* common */
int ch_dst;
int ch_src;
float vol;
/* fade envelope */
float vol_start; /* volume from pre to start */
float vol_end; /* volume from end to post */
char shape; /* curve type */
int32_t time_pre; /* position before time_start where vol_start applies (-1 = beginning) */
int32_t time_start; /* fade start position where vol changes from vol_start to vol_end */
int32_t time_end; /* fade end position where vol changes from vol_start to vol_end */
int32_t time_post; /* position after time_end where vol_end applies (-1 = end) */
} mix_op_t;
struct mixer_t {
int input_channels; /* starting channels before mixing */
int output_channels; /* resulting channels after mixing */
int mixing_channels; /* max channels needed to mix */
bool active; /* mixing working */
int chain_count; /* op number */
size_t chain_size; /* max ops */
mix_op_t chain[VGMSTREAM_MAX_MIXING]; /* effects to apply (could be alloc'ed but to simplify...) */
/* fades only apply at some points, other mixes are active */
bool has_non_fade;
bool has_fade;
float* mixbuf; /* internal mixing buffer */
int current_channels; /* state: channels may increase/decrease during ops */
int32_t current_subpos; /* state: current sample pos in the stream */
};
void mixer_op_swap(mixer_t* mixer, int32_t sample_count, mix_op_t* op);
void mixer_op_add(mixer_t* mixer, int32_t sample_count, mix_op_t* op);
void mixer_op_volume(mixer_t* mixer, int32_t sample_count, mix_op_t* op);
void mixer_op_limit(mixer_t* mixer, int32_t sample_count, mix_op_t* op);
void mixer_op_upmix(mixer_t* mixer, int32_t sample_count, mix_op_t* op);
void mixer_op_downmix(mixer_t* mixer, int32_t sample_count, mix_op_t* op);
void mixer_op_killmix(mixer_t* mixer, int32_t sample_count, mix_op_t* op);
void mixer_op_fade(mixer_t* mixer, int32_t sample_count, mix_op_t* op);
bool mixer_op_fade_is_active(mixer_t* mixer, int32_t current_start, int32_t current_end);
#endif

View File

@ -1,268 +1,71 @@
#include "../vgmstream.h"
#include "../util/channel_mappings.h"
#include "mixing.h"
#include "mixing_priv.h"
#include "mixing_fades.h"
#include "plugins.h"
#include "mixer.h"
#include "mixer_priv.h"
#include "sbuf.h"
#include <math.h>
#include <limits.h>
//TODO simplify
/**
* Mixing lets vgmstream modify the resulting sample buffer before final output.
* This can be implemented in a number of ways but it's done like it is considering
* overall simplicity in coding, usage and performance (main complexity is allowing
* down/upmixing). Code is mostly independent with some hooks in the main vgmstream
* code.
* Mixer modifies decoded sample buffer before final output. This is implemented
* mostly with simplicity in mind rather than performance. Process:
* - detect if mixing applies at current moment or exit (mini performance optimization)
* - copy/upgrade buf to float mixbuf if needed
* - do mixing ops
* - copy/downgrade mixbuf to original buf if needed
*
* Mixing may add or remove channels. input_channels is the buf's original channels,
* and output_channels the resulting buf's channels. buf and mixbuf must be
* as big as max channels (mixing_channels).
*
* Mixing ops are added by a meta (ex. TXTP) or plugin through the API. Non-sensical
* mixes are ignored (to avoid rechecking every time).
*
* Currently, mixing must be manually enabled before starting to decode, because plugins
* need to setup bigger bufs when upmixing. (to be changed)
*
* It works using two buffers:
* - outbuf: plugin's pcm16 buffer, at least input_channels*sample_count
* - mixbuf: internal's pcmfloat buffer, at least mixing_channels*sample_count
* outbuf starts with decoded samples of vgmstream->channel size. This unsures that
* if no mixing is done (most common case) we can skip copying samples between buffers.
* Resulting outbuf after mixing has samples for ->output_channels (plus garbage).
* - output_channels is the resulting total channels (that may be less/more/equal)
* - input_channels is normally ->channels or ->output_channels when it's higher
*
* First, a meta (ex. TXTP) or plugin may add mixing commands through the API,
* validated so non-sensical mixes are ignored (to ensure mixing code doesn't
* have to recheck every time). Then, before starting to decode mixing must be
* manually activated, because plugins need to be ready for possibly different
* input/output channels. API could be improved but this way we can avoid having
* to update all plugins, while allowing internal setup and layer/segment mixing
* (may change in the future for simpler usage).
*
* Then after decoding normally, vgmstream applies mixing internally:
* - detect if mixing is active and needs to be done at this point (some effects
* like fades only apply after certain time) and skip otherwise.
* - copy outbuf to mixbuf, as using a float buffer to increase accuracy (most ops
* apply float volumes) and slightly improve performance (avoids doing
* int16-to-float casts per mix, as it's not free)
* - apply all mixes on mixbuf
* - copy mixbuf to outbuf
* segmented/layered layouts handle mixing on their own.
*
* Mixing is tuned for most common case (no mix except fade-out at the end) and is
* fast enough but not super-optimized yet, there is some penalty the more effects
* are applied. Maybe could add extra sub-ops to avoid ifs and dumb values (volume=0.0
* could simply use a clear op), only use mixbuf if necessary (swap can be done without
* mixbuf if it goes first) or add function pointer indexes but isn't too important.
* Operations are applied once per "step" with 1 sample from all channels to simplify code
* (and maybe improve memory cache?), though maybe it should call one function per operation.
*/
/* ******************************************************************* */
static void sbuf_copy_f32_to_s16(int16_t* buf_s16, float* buf_f32, int samples, int channels) {
for (int s = 0; s < samples * channels; s++) {
/* when casting float to int, value is simply truncated:
* - (int)1.7 = 1, (int)-1.7 = -1
* alts for more accurate rounding could be:
* - (int)floor(f)
* - (int)(f < 0 ? f - 0.5f : f + 0.5f)
* - (((int) (f1 + 32768.5)) - 32768)
* - etc
* but since +-1 isn't really audible we'll just cast as it's the fastest
*/
buf_s16[s] = clamp16( (int32_t)buf_f32[s] );
static int32_t get_current_pos(VGMSTREAM* vgmstream, int32_t sample_count) {
int32_t current_pos;
if (vgmstream->config_enabled) {
return vgmstream->pstate.play_position;
}
if (vgmstream->loop_flag && vgmstream->loop_count > 0) {
int loop_pre = vgmstream->loop_start_sample; /* samples before looping */
int loop_into = (vgmstream->current_sample - vgmstream->loop_start_sample); /* samples after loop */
int loop_samples = (vgmstream->loop_end_sample - vgmstream->loop_start_sample); /* looped section */
current_pos = loop_pre + (loop_samples * vgmstream->loop_count) + loop_into - sample_count;
}
else {
current_pos = (vgmstream->current_sample - sample_count);
}
return current_pos;
}
void mix_vgmstream(sample_t *outbuf, int32_t sample_count, VGMSTREAM* vgmstream) {
mixing_data *data = vgmstream->mixing_data;
int ch, s, m, ok;
int32_t current_subpos = 0;
float temp_f, temp_min, temp_max, cur_vol = 0.0f;
float *temp_mixbuf;
sample_t *temp_outbuf;
const float limiter_max = 32767.0f;
const float limiter_min = -32768.0f;
/* no support or not need to apply */
if (!data || !data->mixing_on || data->mixing_count == 0)
if (!mixer_is_active(vgmstream->mixer))
return;
/* try to skip if no fades apply (set but does nothing yet) + only has fades */
if (data->has_fade) {
int32_t current_pos = get_current_pos(vgmstream, sample_count);
//;VGM_LOG("MIX: fade test %i, %i\n", data->has_non_fade, is_fade_active(data, current_pos, current_pos + sample_count));
if (!data->has_non_fade && !is_fade_active(data, current_pos, current_pos + sample_count))
return;
//;VGM_LOG("MIX: fade pos=%i\n", current_pos);
current_subpos = current_pos;
}
int32_t current_pos = get_current_pos(vgmstream, sample_count);
/* use advancing buffer pointers to simplify logic */
temp_mixbuf = data->mixbuf; /* you'd think using a int32 temp buf would be faster but somehow it's slower? */
temp_outbuf = outbuf;
/* mixing ops are designed to apply in order, all channels per 1 sample 'step'. Since some ops change
* total channels, channel number meaning varies as ops move them around, ex:
* - 4ch w/ "1-2,2+3" = ch1<>ch3, ch2(old ch1)+ch3 = 4ch: ch2 ch1+ch3 ch3 ch4
* - 4ch w/ "2+3,1-2" = ch2+ch3, ch1<>ch2(modified) = 4ch: ch2+ch3 ch1 ch3 ch4
* - 2ch w/ "1+2,1u" = ch1+ch2, ch1(add and push rest) = 3ch: ch1' ch1+ch2 ch2
* - 2ch w/ "1u,1+2" = ch1(add and push rest) = 3ch: ch1'+ch1 ch1 ch2
* - 2ch w/ "1-2,1d" = ch1<>ch2, ch1(drop and move ch2(old ch1) to ch1) = ch1
* - 2ch w/ "1d,1-2" = ch1(drop and pull rest), ch1(do nothing, ch2 doesn't exist now) = ch2
*/
/* apply mixes in order per channel */
for (s = 0; s < sample_count; s++) {
/* reset after new sample 'step'*/
float *stpbuf = temp_mixbuf;
int step_channels = vgmstream->channels;
for (ch = 0; ch < step_channels; ch++) {
stpbuf[ch] = temp_outbuf[ch]; /* copy current 'lane' */
}
for (m = 0; m < data->mixing_count; m++) {
mix_command_data *mix = &data->mixing_chain[m];
switch(mix->command) {
case MIX_SWAP:
temp_f = stpbuf[mix->ch_dst];
stpbuf[mix->ch_dst] = stpbuf[mix->ch_src];
stpbuf[mix->ch_src] = temp_f;
break;
case MIX_ADD:
stpbuf[mix->ch_dst] = stpbuf[mix->ch_dst] + stpbuf[mix->ch_src] * mix->vol;
break;
case MIX_ADD_COPY:
stpbuf[mix->ch_dst] = stpbuf[mix->ch_dst] + stpbuf[mix->ch_src];
break;
case MIX_VOLUME:
if (mix->ch_dst < 0) {
for (ch = 0; ch < step_channels; ch++) {
stpbuf[ch] = stpbuf[ch] * mix->vol;
}
}
else {
stpbuf[mix->ch_dst] = stpbuf[mix->ch_dst] * mix->vol;
}
break;
case MIX_LIMIT:
temp_max = limiter_max * mix->vol;
temp_min = limiter_min * mix->vol;
if (mix->ch_dst < 0) {
for (ch = 0; ch < step_channels; ch++) {
if (stpbuf[ch] > temp_max)
stpbuf[ch] = temp_max;
else if (stpbuf[ch] < temp_min)
stpbuf[ch] = temp_min;
}
}
else {
if (stpbuf[mix->ch_dst] > temp_max)
stpbuf[mix->ch_dst] = temp_max;
else if (stpbuf[mix->ch_dst] < temp_min)
stpbuf[mix->ch_dst] = temp_min;
}
break;
case MIX_UPMIX:
step_channels += 1;
for (ch = step_channels - 1; ch > mix->ch_dst; ch--) {
stpbuf[ch] = stpbuf[ch-1]; /* 'push' channels forward (or pull backwards) */
}
stpbuf[mix->ch_dst] = 0; /* inserted as silent */
break;
case MIX_DOWNMIX:
step_channels -= 1;
for (ch = mix->ch_dst; ch < step_channels; ch++) {
stpbuf[ch] = stpbuf[ch+1]; /* 'pull' channels back */
}
break;
case MIX_KILLMIX:
step_channels = mix->ch_dst; /* clamp channels */
break;
case MIX_FADE:
ok = get_fade_gain(mix, &cur_vol, current_subpos);
if (!ok) {
break; /* fade doesn't apply right now */
}
if (mix->ch_dst < 0) {
for (ch = 0; ch < step_channels; ch++) {
stpbuf[ch] = stpbuf[ch] * cur_vol;
}
}
else {
stpbuf[mix->ch_dst] = stpbuf[mix->ch_dst] * cur_vol;
}
break;
default:
break;
}
}
current_subpos++;
temp_mixbuf += step_channels;
temp_outbuf += vgmstream->channels;
}
/* copy resulting temp mix to output */
sbuf_copy_f32_to_s16(outbuf, data->mixbuf, sample_count, data->output_channels);
mixer_process(vgmstream->mixer, outbuf, sample_count, current_pos);
}
/* ******************************************************************* */
void mixing_init(VGMSTREAM* vgmstream) {
mixing_data *data = calloc(1, sizeof(mixing_data));
if (!data) goto fail;
data->mixing_size = VGMSTREAM_MAX_MIXING; /* fixed array for now */
data->mixing_channels = vgmstream->channels;
data->output_channels = vgmstream->channels;
vgmstream->mixing_data = data;
return;
fail:
free(data);
return;
}
void mixing_close(VGMSTREAM* vgmstream) {
mixing_data *data = NULL;
if (!vgmstream) return;
data = vgmstream->mixing_data;
if (!data) return;
free(data->mixbuf);
free(data);
}
void mixing_update_channel(VGMSTREAM* vgmstream) {
mixing_data *data = vgmstream->mixing_data;
if (!data) return;
/* lame hack for dual stereo, but dual stereo is pretty hack-ish to begin with */
data->mixing_channels++;
data->output_channels++;
}
/* ******************************************************************* */
static int fix_layered_channel_layout(VGMSTREAM* vgmstream) {
int i;
mixing_data* data = vgmstream->mixing_data;
mixer_t* mixer = vgmstream->mixer;
layered_layout_data* layout_data;
uint32_t prev_cl;
@ -272,7 +75,7 @@ static int fix_layered_channel_layout(VGMSTREAM* vgmstream) {
layout_data = vgmstream->layout_data;
/* mainly layer-v (in cases of layers-within-layers should cascade) */
if (data->output_channels != layout_data->layers[0]->channels)
if (mixer->output_channels != layout_data->layers[0]->channels)
return 0;
/* check all layers share layout (implicitly works as a channel check, if not 0) */
@ -280,7 +83,7 @@ static int fix_layered_channel_layout(VGMSTREAM* vgmstream) {
if (prev_cl == 0)
return 0;
for (i = 1; i < layout_data->layer_count; i++) {
for (int i = 1; i < layout_data->layer_count; i++) {
uint32_t layer_cl = layout_data->layers[i]->channel_layout;
if (prev_cl != layer_cl)
return 0;
@ -294,7 +97,7 @@ static int fix_layered_channel_layout(VGMSTREAM* vgmstream) {
/* channel layout + down/upmixing = ?, salvage what we can */
static void fix_channel_layout(VGMSTREAM* vgmstream) {
mixing_data* data = vgmstream->mixing_data;
mixer_t* mixer = vgmstream->mixer;
if (fix_layered_channel_layout(vgmstream))
goto done;
@ -302,7 +105,7 @@ static void fix_channel_layout(VGMSTREAM* vgmstream) {
/* segments should share channel layout automatically */
/* a bit wonky but eh... */
if (vgmstream->channel_layout && vgmstream->channels != data->output_channels) {
if (vgmstream->channel_layout && vgmstream->channels != mixer->output_channels) {
vgmstream->channel_layout = 0;
}
@ -310,22 +113,23 @@ done:
((VGMSTREAM*)vgmstream->start_vgmstream)->channel_layout = vgmstream->channel_layout;
}
void mixing_setup(VGMSTREAM* vgmstream, int32_t max_sample_count) {
mixing_data *data = vgmstream->mixing_data;
float *mixbuf_re = NULL;
if (!data) goto fail;
void mixing_setup(VGMSTREAM* vgmstream, int32_t max_sample_count) {
mixer_t* mixer = vgmstream->mixer;
if (!mixer)
return;
/* special value to not actually enable anything (used to query values) */
if (max_sample_count <= 0)
goto fail;
return;
/* create or alter internal buffer */
mixbuf_re = realloc(data->mixbuf, max_sample_count*data->mixing_channels*sizeof(float));
float* mixbuf_re = realloc(mixer->mixbuf, max_sample_count * mixer->mixing_channels * sizeof(float));
if (!mixbuf_re) goto fail;
data->mixbuf = mixbuf_re;
data->mixing_on = 1;
mixer->mixbuf = mixbuf_re;
mixer->active = true;
fix_channel_layout(vgmstream);
@ -340,14 +144,15 @@ fail:
}
void mixing_info(VGMSTREAM* vgmstream, int* p_input_channels, int* p_output_channels) {
mixing_data *data = vgmstream->mixing_data;
mixer_t* mixer = vgmstream->mixer;
int input_channels, output_channels;
if (!data) goto fail;
if (!mixer)
goto fail;
output_channels = data->output_channels;
if (data->output_channels > vgmstream->channels)
input_channels = data->output_channels;
output_channels = mixer->output_channels;
if (mixer->output_channels > vgmstream->channels)
input_channels = mixer->output_channels;
else
input_channels = vgmstream->channels;

View File

@ -7,19 +7,13 @@
* outbuf must big enough to hold output_channels*samples_to_do */
void mix_vgmstream(sample_t *outbuf, int32_t sample_count, VGMSTREAM* vgmstream);
/* internal mixing pre-setup for vgmstream (doesn't imply usage).
* If init somehow fails next calls are ignored. */
void mixing_init(VGMSTREAM* vgmstream);
void mixing_close(VGMSTREAM* vgmstream);
void mixing_update_channel(VGMSTREAM* vgmstream);
/* Call to let vgmstream apply mixing, which must handle input/output_channels.
* Once mixing is active any new mixes are ignored (to avoid the possibility
* of down/upmixing without querying input/output_channels). */
void mixing_setup(VGMSTREAM * vgmstream, int32_t max_sample_count);
void mixing_setup(VGMSTREAM* vgmstream, int32_t max_sample_count);
/* gets current mixing info */
void mixing_info(VGMSTREAM * vgmstream, int *input_channels, int *output_channels);
void mixing_info(VGMSTREAM* vgmstream, int *input_channels, int *output_channels);
/* adds mixes filtering and optimizing if needed */
void mixing_push_swap(VGMSTREAM* vgmstream, int ch_dst, int ch_src);
@ -39,4 +33,4 @@ void mixing_macro_crosslayer(VGMSTREAM* vgmstream, int max, char mode);
void mixing_macro_downmix(VGMSTREAM* vgmstream, int max /*, mapping_t output_mapping*/);
#endif /* _MIXING_H_ */
#endif

View File

@ -1,172 +1,170 @@
#include "../vgmstream.h"
#include "../util/channel_mappings.h"
#include "mixing.h"
#include "mixing_priv.h"
#include "mixer_priv.h"
#include <math.h>
#include <limits.h>
static int add_mixing(VGMSTREAM* vgmstream, mix_command_data *mix) {
mixing_data *data = vgmstream->mixing_data;
if (!data) return 0;
static bool add_mixing(VGMSTREAM* vgmstream, mix_op_t* op) {
mixer_t* mixer = vgmstream->mixer;
if (!mixer)
return false;
if (data->mixing_on) {
VGM_LOG("MIX: ignoring new mixes when mixing active\n");
return 0; /* to avoid down/upmixing after activation */
if (mixer->active) {
VGM_LOG("MIX: ignoring new ops when mixer is active\n");
return false; /* to avoid down/upmixing after activation */
}
if (data->mixing_count + 1 > data->mixing_size) {
if (mixer->chain_count + 1 > mixer->chain_size) {
VGM_LOG("MIX: too many mixes\n");
return 0;
return false;
}
data->mixing_chain[data->mixing_count] = *mix; /* memcpy */
data->mixing_count++;
mixer->chain[mixer->chain_count] = *op; /* memcpy */
mixer->chain_count++;
if (mix->command == MIX_FADE) {
data->has_fade = 1;
if (op->type == MIX_FADE) {
mixer->has_fade = true;
}
else {
data->has_non_fade = 1;
mixer->has_non_fade = true;
}
//;VGM_LOG("MIX: total %i\n", data->mixing_count);
return 1;
//;VGM_LOG("MIX: total %i\n", data->chain_count);
return true;
}
void mixing_push_swap(VGMSTREAM* vgmstream, int ch_dst, int ch_src) {
mixing_data *data = vgmstream->mixing_data;
mix_command_data mix = {0};
mixer_t* mixer = vgmstream->mixer;
mix_op_t op = {0};
if (ch_dst < 0 || ch_src < 0 || ch_dst == ch_src) return;
if (!data || ch_dst >= data->output_channels || ch_src >= data->output_channels) return;
mix.command = MIX_SWAP;
mix.ch_dst = ch_dst;
mix.ch_src = ch_src;
if (!mixer || ch_dst >= mixer->output_channels || ch_src >= mixer->output_channels) return;
op.type = MIX_SWAP;
op.ch_dst = ch_dst;
op.ch_src = ch_src;
add_mixing(vgmstream, &mix);
add_mixing(vgmstream, &op);
}
void mixing_push_add(VGMSTREAM* vgmstream, int ch_dst, int ch_src, double volume) {
mixing_data *data = vgmstream->mixing_data;
mix_command_data mix = {0};
if (!data) return;
mixer_t* mixer = vgmstream->mixer;
mix_op_t op = {0};
if (!mixer) return;
//if (volume < 0.0) return; /* negative volume inverts the waveform */
if (volume == 0.0) return; /* ch_src becomes silent and nothing is added */
if (ch_dst < 0 || ch_src < 0) return;
if (!data || ch_dst >= data->output_channels || ch_src >= data->output_channels) return;
if (!mixer || ch_dst >= mixer->output_channels || ch_src >= mixer->output_channels) return;
mix.command = (volume == 1.0) ? MIX_ADD_COPY : MIX_ADD;
mix.ch_dst = ch_dst;
mix.ch_src = ch_src;
mix.vol = volume;
op.type = MIX_ADD;
op.ch_dst = ch_dst;
op.ch_src = ch_src;
op.vol = volume;
//;VGM_LOG("MIX: add %i+%i*%f\n", ch_dst,ch_src,volume);
add_mixing(vgmstream, &mix);
add_mixing(vgmstream, &op);
}
void mixing_push_volume(VGMSTREAM* vgmstream, int ch_dst, double volume) {
mixing_data *data = vgmstream->mixing_data;
mix_command_data mix = {0};
mixer_t* mixer = vgmstream->mixer;
mix_op_t op = {0};
//if (ch_dst < 0) return; /* means all channels */
//if (volume < 0.0) return; /* negative volume inverts the waveform */
if (volume == 1.0) return; /* no change */
if (!data || ch_dst >= data->output_channels) return;
if (!mixer || ch_dst >= mixer->output_channels) return;
mix.command = MIX_VOLUME; //if (volume == 0.0) MIX_VOLUME0 /* could simplify */
mix.ch_dst = ch_dst;
mix.vol = volume;
op.type = MIX_VOLUME; //if (volume == 0.0) MIX_VOLUME0 /* could simplify */
op.ch_dst = ch_dst;
op.vol = volume;
//;VGM_LOG("MIX: volume %i*%f\n", ch_dst,volume);
add_mixing(vgmstream, &mix);
add_mixing(vgmstream, &op);
}
void mixing_push_limit(VGMSTREAM* vgmstream, int ch_dst, double volume) {
mixing_data *data = vgmstream->mixing_data;
mix_command_data mix = {0};
mixer_t* mixer = vgmstream->mixer;
mix_op_t op = {0};
//if (ch_dst < 0) return; /* means all channels */
if (volume < 0.0) return;
if (volume == 1.0) return; /* no actual difference */
if (!data || ch_dst >= data->output_channels) return;
if (!mixer || ch_dst >= mixer->output_channels) return;
//if (volume == 0.0) return; /* dumb but whatevs */
mix.command = MIX_LIMIT;
mix.ch_dst = ch_dst;
mix.vol = volume;
op.type = MIX_LIMIT;
op.ch_dst = ch_dst;
op.vol = volume;
add_mixing(vgmstream, &mix);
add_mixing(vgmstream, &op);
}
void mixing_push_upmix(VGMSTREAM* vgmstream, int ch_dst) {
mixing_data *data = vgmstream->mixing_data;
mix_command_data mix = {0};
mixer_t* mixer = vgmstream->mixer;
mix_op_t op = {0};
int ok;
if (ch_dst < 0) return;
if (!data || ch_dst > data->output_channels || data->output_channels +1 > VGMSTREAM_MAX_CHANNELS) return;
if (!mixer || ch_dst > mixer->output_channels || mixer->output_channels +1 > VGMSTREAM_MAX_CHANNELS) return;
/* dst can be == output_channels here, since we are inserting */
mix.command = MIX_UPMIX;
mix.ch_dst = ch_dst;
op.type = MIX_UPMIX;
op.ch_dst = ch_dst;
ok = add_mixing(vgmstream, &mix);
ok = add_mixing(vgmstream, &op);
if (ok) {
data->output_channels += 1;
if (data->mixing_channels < data->output_channels)
data->mixing_channels = data->output_channels;
mixer->output_channels += 1;
if (mixer->mixing_channels < mixer->output_channels)
mixer->mixing_channels = mixer->output_channels;
}
}
void mixing_push_downmix(VGMSTREAM* vgmstream, int ch_dst) {
mixing_data *data = vgmstream->mixing_data;
mix_command_data mix = {0};
mixer_t* mixer = vgmstream->mixer;
mix_op_t op = {0};
int ok;
if (ch_dst < 0) return;
if (!data || ch_dst >= data->output_channels || data->output_channels - 1 < 1) return;
if (!mixer || ch_dst >= mixer->output_channels || mixer->output_channels - 1 < 1) return;
mix.command = MIX_DOWNMIX;
mix.ch_dst = ch_dst;
op.type = MIX_DOWNMIX;
op.ch_dst = ch_dst;
ok = add_mixing(vgmstream, &mix);
ok = add_mixing(vgmstream, &op);
if (ok) {
data->output_channels -= 1;
mixer->output_channels -= 1;
}
}
void mixing_push_killmix(VGMSTREAM* vgmstream, int ch_dst) {
mixing_data *data = vgmstream->mixing_data;
mix_command_data mix = {0};
int ok;
mixer_t* mixer = vgmstream->mixer;
mix_op_t op = {0};
if (ch_dst <= 0) return; /* can't kill from first channel */
if (!data || ch_dst >= data->output_channels) return;
if (!mixer || ch_dst >= mixer->output_channels) return;
mix.command = MIX_KILLMIX;
mix.ch_dst = ch_dst;
op.type = MIX_KILLMIX;
op.ch_dst = ch_dst;
//;VGM_LOG("MIX: killmix %i\n", ch_dst);
ok = add_mixing(vgmstream, &mix);
bool ok = add_mixing(vgmstream, &op);
if (ok) {
data->output_channels = ch_dst; /* clamp channels */
mixer->output_channels = ch_dst; /* clamp channels */
}
}
static mix_command_data* get_last_fade(mixing_data *data, int target_channel) {
int i;
for (i = data->mixing_count; i > 0; i--) {
mix_command_data *mix = &data->mixing_chain[i-1];
if (mix->command != MIX_FADE)
static mix_op_t* get_last_fade(mixer_t* mixer, int target_channel) {
for (int i = mixer->chain_count; i > 0; i--) {
mix_op_t* op = &mixer->chain[i-1];
if (op->type != MIX_FADE)
continue;
if (mix->ch_dst == target_channel)
return mix;
if (op->ch_dst == target_channel)
return op;
}
return NULL;
@ -175,13 +173,13 @@ static mix_command_data* get_last_fade(mixing_data *data, int target_channel) {
void mixing_push_fade(VGMSTREAM* vgmstream, int ch_dst, double vol_start, double vol_end, char shape,
int32_t time_pre, int32_t time_start, int32_t time_end, int32_t time_post) {
mixing_data *data = vgmstream->mixing_data;
mix_command_data mix = {0};
mix_command_data *mix_prev;
mixer_t* mixer = vgmstream->mixer;
mix_op_t op = {0};
mix_op_t* op_prev;
//if (ch_dst < 0) return; /* means all channels */
if (!data || ch_dst >= data->output_channels) return;
if (!mixer || ch_dst >= mixer->output_channels) return;
if (time_pre > time_start || time_start > time_end || (time_post >= 0 && time_end > time_post)) return;
if (time_start < 0 || time_end < 0) return;
//if (time_pre < 0 || time_post < 0) return; /* special meaning of file start/end */
@ -192,15 +190,15 @@ void mixing_push_fade(VGMSTREAM* vgmstream, int ch_dst, double vol_start, double
if (shape == '(' || shape == ')')
shape = 'H';
mix.command = MIX_FADE;
mix.ch_dst = ch_dst;
mix.vol_start = vol_start;
mix.vol_end = vol_end;
mix.shape = shape;
mix.time_pre = time_pre;
mix.time_start = time_start;
mix.time_end = time_end;
mix.time_post = time_post;
op.type = MIX_FADE;
op.ch_dst = ch_dst;
op.vol_start = vol_start;
op.vol_end = vol_end;
op.shape = shape;
op.time_pre = time_pre;
op.time_start = time_start;
op.time_end = time_end;
op.time_post = time_post;
/* cancel fades and optimize a bit when using negative pre/post:
@ -216,33 +214,33 @@ void mixing_push_fade(VGMSTREAM* vgmstream, int ch_dst, double vol_start, double
* as they're uncommon and hard to optimize
* fades cancel fades of the same channel, and 'all channel' (-1) fades also cancel 'all channels'
*/
mix_prev = get_last_fade(data, mix.ch_dst);
if (mix_prev == NULL) {
op_prev = get_last_fade(mixer, op.ch_dst);
if (op_prev == NULL) {
if (vol_start == 1.0 && time_pre < 0)
time_pre = time_start; /* fade-out helds default volume before fade start can be clamped */
if (vol_end == 1.0 && time_post < 0)
time_post = time_end; /* fade-in helds default volume after fade end can be clamped */
}
else if (mix_prev->time_post < 0 || mix.time_pre < 0) {
else if (op_prev->time_post < 0 || op.time_pre < 0) {
int is_prev = 1;
/* test if prev is really cancelled by this */
if ((mix_prev->time_end > mix.time_start) ||
(mix_prev->time_post >= 0 && mix_prev->time_post > mix.time_start) ||
(mix.time_pre >= 0 && mix.time_pre < mix_prev->time_end))
if ((op_prev->time_end > op.time_start) ||
(op_prev->time_post >= 0 && op_prev->time_post > op.time_start) ||
(op.time_pre >= 0 && op.time_pre < op_prev->time_end))
is_prev = 0;
if (is_prev) {
/* change negative values to actual points */
if (mix_prev->time_post < 0 && mix.time_pre < 0) {
mix_prev->time_post = mix_prev->time_end;
mix.time_pre = mix_prev->time_post;
if (op_prev->time_post < 0 && op.time_pre < 0) {
op_prev->time_post = op_prev->time_end;
op.time_pre = op_prev->time_post;
}
if (mix_prev->time_post >= 0 && mix.time_pre < 0) {
mix.time_pre = mix_prev->time_post;
if (op_prev->time_post >= 0 && op.time_pre < 0) {
op.time_pre = op_prev->time_post;
}
else if (mix_prev->time_post < 0 && mix.time_pre >= 0) {
mix_prev->time_post = mix.time_pre;
else if (op_prev->time_post < 0 && op.time_pre >= 0) {
op_prev->time_post = op.time_pre;
}
/* else: both define start/ends, do nothing */
}
@ -250,5 +248,5 @@ void mixing_push_fade(VGMSTREAM* vgmstream, int ch_dst, double vol_start, double
}
//;VGM_LOG("MIX: fade %i^%f~%f=%c@%i~%i~%i~%i\n", ch_dst, vol_start, vol_end, shape, time_pre, time_start, time_end, time_post);
add_mixing(vgmstream, &mix);
add_mixing(vgmstream, &op);
}

View File

@ -1,7 +1,7 @@
#include "../vgmstream.h"
#include "../util/channel_mappings.h"
#include "mixing.h"
#include "mixing_priv.h"
#include "mixer_priv.h"
#include <math.h>
#include <limits.h>
@ -11,10 +11,8 @@
#define MIX_MACRO_BGM 'b'
void mixing_macro_volume(VGMSTREAM* vgmstream, double volume, uint32_t mask) {
mixing_data *data = vgmstream->mixing_data;
int ch;
if (!data)
mixer_t* mixer = vgmstream->mixer;
if (!mixer)
return;
if (mask == 0) {
@ -22,7 +20,7 @@ void mixing_macro_volume(VGMSTREAM* vgmstream, double volume, uint32_t mask) {
return;
}
for (ch = 0; ch < data->output_channels; ch++) {
for (int ch = 0; ch < mixer->output_channels; ch++) {
if (!((mask >> ch) & 1))
continue;
mixing_push_volume(vgmstream, ch, volume);
@ -30,10 +28,8 @@ void mixing_macro_volume(VGMSTREAM* vgmstream, double volume, uint32_t mask) {
}
void mixing_macro_track(VGMSTREAM* vgmstream, uint32_t mask) {
mixing_data *data = vgmstream->mixing_data;
int ch;
if (!data)
mixer_t* mixer = vgmstream->mixer;
if (!mixer)
return;
if (mask == 0) {
@ -41,7 +37,7 @@ void mixing_macro_track(VGMSTREAM* vgmstream, uint32_t mask) {
}
/* reverse remove all channels (easier this way as when removing channels numbers change) */
for (ch = data->output_channels - 1; ch >= 0; ch--) {
for (int ch = mixer->output_channels - 1; ch >= 0; ch--) {
if ((mask >> ch) & 1)
continue;
mixing_push_downmix(vgmstream, ch);
@ -51,16 +47,13 @@ void mixing_macro_track(VGMSTREAM* vgmstream, uint32_t mask) {
/* get highest channel count */
static int get_layered_max_channels(VGMSTREAM* vgmstream) {
int i, max;
layered_layout_data* data;
if (vgmstream->layout_type != layout_layered)
return 0;
data = vgmstream->layout_data;
layered_layout_data* data = vgmstream->layout_data;
max = 0;
for (i = 0; i < data->layer_count; i++) {
int max = 0;
for (int i = 0; i < data->layer_count; i++) {
int output_channels = 0;
mixing_info(data->layers[i], NULL, &output_channels);
@ -73,11 +66,6 @@ static int get_layered_max_channels(VGMSTREAM* vgmstream) {
}
static int is_layered_auto(VGMSTREAM* vgmstream, int max, char mode) {
int i;
mixing_data *data = vgmstream->mixing_data;
layered_layout_data* l_data;
if (vgmstream->layout_type != layout_layered)
return 0;
@ -86,15 +74,16 @@ static int is_layered_auto(VGMSTREAM* vgmstream, int max, char mode) {
return 0;
/* no channel down/upmixing (cannot guess output) */
for (i = 0; i < data->mixing_count; i++) {
mix_command_t mix = data->mixing_chain[i].command;
if (mix == MIX_UPMIX || mix == MIX_DOWNMIX || mix == MIX_KILLMIX) /*mix == MIX_SWAP || ??? */
mixer_t* mixer = vgmstream->mixer;
for (int i = 0; i < mixer->chain_count; i++) {
mix_type_t type = mixer->chain[i].type;
if (type == MIX_UPMIX || type == MIX_DOWNMIX || type == MIX_KILLMIX) /*type == MIX_SWAP || ??? */
return 0;
}
/* only previsible cases */
l_data = vgmstream->layout_data;
for (i = 0; i < l_data->layer_count; i++) {
layered_layout_data* l_data = vgmstream->layout_data;
for (int i = 0; i < l_data->layer_count; i++) {
int output_channels = 0;
mixing_info(l_data->layers[i], NULL, &output_channels);
@ -110,9 +99,8 @@ static int is_layered_auto(VGMSTREAM* vgmstream, int max, char mode) {
/* special layering, where channels are respected (so Ls only go to Ls), also more optimized */
static void mixing_macro_layer_auto(VGMSTREAM* vgmstream, int max, char mode) {
layered_layout_data* ldata = vgmstream->layout_data;
int i, ch;
int target_layer = 0, target_chs = 0, ch_max, target_ch = 0, target_silence = 0;
int ch_num;
int target_layer = 0, target_chs = 0, target_ch = 0, target_silence = 0;
int ch_num, ch_max;
/* With N layers like: (ch1 ch2) (ch1 ch2 ch3 ch4) (ch1 ch2), output is normally 2+4+2=8ch.
* We want to find highest layer (ch1..4) = 4ch, add other channels to it and drop them */
@ -120,7 +108,7 @@ static void mixing_macro_layer_auto(VGMSTREAM* vgmstream, int max, char mode) {
/* find target "main" channels (will be first most of the time) */
ch_num = 0;
ch_max = 0;
for (i = 0; i < ldata->layer_count; i++) {
for (int i = 0; i < ldata->layer_count; i++) {
int layer_chs = 0;
mixing_info(ldata->layers[i], NULL, &layer_chs);
@ -148,7 +136,7 @@ static void mixing_macro_layer_auto(VGMSTREAM* vgmstream, int max, char mode) {
/* add other channels to target (assumes standard channel mapping to simplify)
* most of the time all layers will have same number of channels though */
ch_num = 0;
for (i = 0; i < ldata->layer_count; i++) {
for (int i = 0; i < ldata->layer_count; i++) {
int layer_chs = 0;
if (target_layer == i) {
@ -165,7 +153,7 @@ static void mixing_macro_layer_auto(VGMSTREAM* vgmstream, int max, char mode) {
if (layer_chs == target_chs) {
/* 1:1 mapping */
for (ch = 0; ch < layer_chs; ch++) {
for (int ch = 0; ch < layer_chs; ch++) {
mixing_push_add(vgmstream, target_ch + ch, ch_num + ch, 1.0);
}
}
@ -184,7 +172,7 @@ static void mixing_macro_layer_auto(VGMSTREAM* vgmstream, int max, char mode) {
break;
default: /* less common */
//TODO add other mixes, depends on target_chs + mapping (ex. 4.0 to 5.0 != 5.1, 2.1 xiph to 5.1 != 5.1 xiph)
for (ch = 0; ch < layer_chs; ch++) {
for (int ch = 0; ch < layer_chs; ch++) {
mixing_push_add(vgmstream, target_ch + ch, ch_num + ch, 1.0);
}
break;
@ -196,13 +184,13 @@ static void mixing_macro_layer_auto(VGMSTREAM* vgmstream, int max, char mode) {
/* drop non-target channels */
ch_num = 0;
for (i = 0; i < ldata->layer_count; i++) {
for (int i = 0; i < ldata->layer_count; i++) {
if (i < target_layer) { /* least common, hopefully (slower to drop chs 1 by 1) */
int layer_chs = 0;
mixing_info(ldata->layers[i], NULL, &layer_chs);
for (ch = 0; ch < layer_chs; ch++) {
for (int ch = 0; ch < layer_chs; ch++) {
mixing_push_downmix(vgmstream, ch_num); //+ ch
}
@ -220,10 +208,10 @@ static void mixing_macro_layer_auto(VGMSTREAM* vgmstream, int max, char mode) {
void mixing_macro_layer(VGMSTREAM* vgmstream, int max, uint32_t mask, char mode) {
mixing_data *data = vgmstream->mixing_data;
mixer_t* mixer = vgmstream->mixer;
int current, ch, output_channels, selected_channels;
if (!data)
if (!mixer)
return;
if (is_layered_auto(vgmstream, max, mode)) {
@ -236,7 +224,7 @@ void mixing_macro_layer(VGMSTREAM* vgmstream, int max, uint32_t mask, char mode)
if (max == 0) /* auto calculate */
max = get_layered_max_channels(vgmstream);
if (max <= 0 || data->output_channels <= max)
if (max <= 0 || mixer->output_channels <= max)
return;
/* set all channels (non-existant channels will be ignored) */
@ -245,7 +233,7 @@ void mixing_macro_layer(VGMSTREAM* vgmstream, int max, uint32_t mask, char mode)
}
/* save before adding fake channels */
output_channels = data->output_channels;
output_channels = mixer->output_channels;
/* count possibly set channels */
selected_channels = 0;
@ -304,19 +292,19 @@ void mixing_macro_layer(VGMSTREAM* vgmstream, int max, uint32_t mask, char mode)
}
void mixing_macro_crosstrack(VGMSTREAM* vgmstream, int max) {
mixing_data *data = vgmstream->mixing_data;
mixer_t* mixer = vgmstream->mixer;
int current, ch, track, track_ch, track_num, output_channels;
int32_t change_pos, change_next, change_time;
if (!data)
if (!mixer)
return;
if (max <= 0 || data->output_channels <= max)
if (max <= 0 || mixer->output_channels <= max)
return;
if (!vgmstream->loop_flag) /* maybe force loop? */
return;
/* this probably only makes sense for even channels so upmix before if needed) */
output_channels = data->output_channels;
output_channels = mixer->output_channels;
if (output_channels % 2) {
mixing_push_upmix(vgmstream, output_channels);
output_channels += 1;
@ -368,19 +356,19 @@ void mixing_macro_crosstrack(VGMSTREAM* vgmstream, int max) {
}
void mixing_macro_crosslayer(VGMSTREAM* vgmstream, int max, char mode) {
mixing_data *data = vgmstream->mixing_data;
mixer_t* mixer = vgmstream->mixer;
int current, ch, layer, layer_ch, layer_num, loop, output_channels;
int32_t change_pos, change_time;
if (!data)
if (!mixer)
return;
if (max <= 0 || data->output_channels <= max)
if (max <= 0 || mixer->output_channels <= max)
return;
if (!vgmstream->loop_flag) /* maybe force loop? */
return;
/* this probably only makes sense for even channels so upmix before if needed) */
output_channels = data->output_channels;
output_channels = mixer->output_channels;
if (output_channels % 2) {
mixing_push_upmix(vgmstream, output_channels);
output_channels += 1;
@ -481,7 +469,7 @@ typedef enum {
} mixing_position_t;
void mixing_macro_downmix(VGMSTREAM* vgmstream, int max /*, mapping_t output_mapping*/) {
mixing_data *data = vgmstream->mixing_data;
mixer_t* mixer = vgmstream->mixer;
int ch, output_channels, mp_in, mp_out, ch_in, ch_out;
channel_mapping_t input_mapping, output_mapping;
const double vol_max = 1.0;
@ -490,15 +478,15 @@ void mixing_macro_downmix(VGMSTREAM* vgmstream, int max /*, mapping_t output_map
double matrix[16][16] = {{0}};
if (!data)
if (!mixer)
return;
if (max <= 1 || data->output_channels <= max || max >= 8)
if (max <= 1 || mixer->output_channels <= max || max >= 8)
return;
/* assume WAV defaults if not set */
input_mapping = vgmstream->channel_layout;
if (input_mapping == 0) {
switch(data->output_channels) {
switch(mixer->output_channels) {
case 1: input_mapping = mapping_MONO; break;
case 2: input_mapping = mapping_STEREO; break;
case 3: input_mapping = mapping_2POINT1; break;
@ -544,7 +532,7 @@ void mixing_macro_downmix(VGMSTREAM* vgmstream, int max /*, mapping_t output_map
}
/* save and make N fake channels at the beginning for easier calcs */
output_channels = data->output_channels;
output_channels = mixer->output_channels;
for (ch = 0; ch < max; ch++) {
mixing_push_upmix(vgmstream, 0);
}

View File

@ -1,52 +0,0 @@
#ifndef _MIXING_PRIV_H_
#define _MIXING_PRIV_H_
#include "../vgmstream.h"
#define VGMSTREAM_MAX_MIXING 512
/* mixing info */
typedef enum {
MIX_SWAP,
MIX_ADD,
MIX_ADD_COPY,
MIX_VOLUME,
MIX_LIMIT,
MIX_UPMIX,
MIX_DOWNMIX,
MIX_KILLMIX,
MIX_FADE
} mix_command_t;
typedef struct {
mix_command_t command;
/* common */
int ch_dst;
int ch_src;
float vol;
/* fade envelope */
float vol_start; /* volume from pre to start */
float vol_end; /* volume from end to post */
char shape; /* curve type */
int32_t time_pre; /* position before time_start where vol_start applies (-1 = beginning) */
int32_t time_start; /* fade start position where vol changes from vol_start to vol_end */
int32_t time_end; /* fade end position where vol changes from vol_start to vol_end */
int32_t time_post; /* position after time_end where vol_end applies (-1 = end) */
} mix_command_data;
typedef struct {
int mixing_channels; /* max channels needed to mix */
int output_channels; /* resulting channels after mixing */
int mixing_on; /* mixing allowed */
int mixing_count; /* mixing number */
size_t mixing_size; /* mixing max */
mix_command_data mixing_chain[VGMSTREAM_MAX_MIXING]; /* effects to apply (could be alloc'ed but to simplify...) */
float* mixbuf; /* internal mixing buffer */
/* fades only apply at some points, other mixes are active */
int has_non_fade;
int has_fade;
} mixing_data;
#endif

View File

@ -156,7 +156,7 @@ void vgmstream_get_title(char* buf, int buf_len, const char* filename, VGMSTREAM
/* MIXING: modifies vgmstream output */
/* ****************************************** */
void vgmstream_mixing_enable(VGMSTREAM* vgmstream, int32_t max_sample_count, int *input_channels, int *output_channels) {
void vgmstream_mixing_enable(VGMSTREAM* vgmstream, int32_t max_sample_count, int* input_channels, int* output_channels) {
mixing_setup(vgmstream, max_sample_count);
mixing_info(vgmstream, input_channels, output_channels);
@ -165,7 +165,7 @@ void vgmstream_mixing_enable(VGMSTREAM* vgmstream, int32_t max_sample_count, int
setup_vgmstream(vgmstream);
}
void vgmstream_mixing_autodownmix(VGMSTREAM *vgmstream, int max_channels) {
void vgmstream_mixing_autodownmix(VGMSTREAM* vgmstream, int max_channels) {
if (max_channels <= 0)
return;
@ -181,7 +181,7 @@ void vgmstream_mixing_autodownmix(VGMSTREAM *vgmstream, int max_channels) {
return;
}
void vgmstream_mixing_stereo_only(VGMSTREAM *vgmstream, int start) {
void vgmstream_mixing_stereo_only(VGMSTREAM* vgmstream, int start) {
if (start < 0)
return;
/* could check to avoid making mono files in edge cases but meh */

View File

@ -223,6 +223,8 @@ void setup_state_vgmstream(VGMSTREAM* vgmstream) {
/*****************************************************************************/
void render_free(VGMSTREAM* vgmstream) {
if (!vgmstream->layout_data)
return;
if (vgmstream->layout_type == layout_segmented) {
free_layout_segmented(vgmstream->layout_data);

28
src/base/sbuf.h Normal file
View File

@ -0,0 +1,28 @@
#ifndef _SBUF_H
#define _SBUF_H
#include "../streamtypes.h"
// TODO decide if using float 1.0 style or 32767 style (fuzzy PCM changes when doing that)
static inline void sbuf_copy_s16_to_f32(float* buf_f32, int16_t* buf_s16, int samples, int channels) {
for (int s = 0; s < samples * channels; s++) {
buf_f32[s] = buf_s16[s]; // / 32767.0f
}
}
static inline void sbuf_copy_f32_to_s16(int16_t* buf_s16, float* buf_f32, int samples, int channels) {
/* when casting float to int, value is simply truncated:
* - (int)1.7 = 1, (int)-1.7 = -1
* alts for more accurate rounding could be:
* - (int)floor(f)
* - (int)(f < 0 ? f - 0.5f : f + 0.5f)
* - (((int) (f1 + 32768.5)) - 32768)
* - etc
* but since +-1 isn't really audible we'll just cast as it's the fastest
*/
for (int s = 0; s < samples * channels; s++) {
buf_s16[s] = clamp16( buf_f32[s]); // * 32767.0f
}
}
#endif

View File

@ -13,7 +13,7 @@ void adx_next_key(VGMSTREAMCHANNEL* stream);
/* g721_decoder */
void decode_g721(VGMSTREAMCHANNEL* stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do);
void decode_g721(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do);
void g72x_init_state(struct g72x_state* state_ptr);
@ -126,14 +126,14 @@ size_t xa_bytes_to_samples(size_t bytes, int channels, int is_blocked, int is_fo
/* ea_xa_decoder */
void decode_ea_xa(VGMSTREAMCHANNEL* stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel, int is_stereo);
void decode_ea_xa_v2(VGMSTREAMCHANNEL* stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do);
void decode_maxis_xa(VGMSTREAMCHANNEL* stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel);
void decode_ea_xa(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel, int is_stereo);
void decode_ea_xa_v2(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do);
void decode_maxis_xa(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel);
int32_t ea_xa_bytes_to_samples(size_t bytes, int channels);
/* ea_xas_decoder */
void decode_ea_xas_v0(VGMSTREAMCHANNEL* stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do);
void decode_ea_xas_v0(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do);
void decode_ea_xas_v1(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel);
@ -145,7 +145,7 @@ void decode_cbd2_int(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspac
/* ws_decoder */
void decode_ws(VGMSTREAM* vgmstream, int channel, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do);
void decode_ws(VGMSTREAM* vgmstream, int channel, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do);
/* acm_decoder */
@ -197,11 +197,11 @@ void decode_nds_procyon(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channels
/* l5_555_decoder */
void decode_l5_555(VGMSTREAMCHANNEL* stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do);
void decode_l5_555(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do);
/* sassc_decoder */
void decode_sassc(VGMSTREAMCHANNEL* stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do);
void decode_sassc(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do);
/* lsf_decode */
@ -217,7 +217,7 @@ void decode_mta2(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing,
/* mc3_decoder */
void decode_mc3(VGMSTREAM* vgmstream, VGMSTREAMCHANNEL* stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel);
void decode_mc3(VGMSTREAM* vgmstream, VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel);
/* fadpcm_decoder */
@ -243,7 +243,7 @@ int32_t tantalus_bytes_to_samples(size_t bytes, int channels);
/* derf_decoder */
void decode_derf(VGMSTREAMCHANNEL* stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do);
void decode_derf(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do);
/* wady_decoder */
void decode_wady(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do);
@ -322,7 +322,7 @@ typedef struct ea_mt_codec_data ea_mt_codec_data;
ea_mt_codec_data* init_ea_mt(int channels, int pcm_blocks);
ea_mt_codec_data* init_ea_mt_loops(int channels, int pcm_blocks, int loop_sample, off_t* loop_offsets);
ea_mt_codec_data* init_ea_mt_cbx(int channels);
void decode_ea_mt(VGMSTREAM* vgmstream, sample * outbuf, int channelspacing, int32_t samples_to_do, int channel);
void decode_ea_mt(VGMSTREAM* vgmstream, sample_t* outbuf, int channelspacing, int32_t samples_to_do, int channel);
void reset_ea_mt(VGMSTREAM* vgmstream);
void flush_ea_mt(VGMSTREAM* vgmstream);
void seek_ea_mt(VGMSTREAM* vgmstream, int32_t num_sample);
@ -560,7 +560,7 @@ void free_g719(g719_codec_data* data, int channels);
#if defined(VGM_USE_MP4V2) && defined(VGM_USE_FDKAAC)
/* mp4_aac_decoder */
void decode_mp4_aac(mp4_aac_codec_data* data, sample * outbuf, int32_t samples_to_do, int channels);
void decode_mp4_aac(mp4_aac_codec_data* data, sample_t* outbuf, int32_t samples_to_do, int channels);
void reset_mp4_aac(VGMSTREAM* vgmstream);
void seek_mp4_aac(VGMSTREAM* vgmstream, int32_t num_sample);
void free_mp4_aac(mp4_aac_codec_data* data);

View File

@ -1218,7 +1218,7 @@ static const meta_info meta_info_list[] = {
{meta_NGC_NST_DSP, "Animaniacs NST header"},
{meta_BAF, "Bizarre Creations .baf header"},
{meta_MSF, "Sony MSF header"},
{meta_PS3_PAST, "SNDP header"},
{meta_SNDP, "Premium Agency SNDP header"},
{meta_SGXD, "Sony SGXD header"},
{meta_WII_RAS, "RAS header"},
{meta_SPM, "Square SPM header"},
@ -1316,7 +1316,7 @@ static const meta_info meta_info_list[] = {
{meta_MSB_MSH, "Sony MultiStream MSH+MSB header"},
{meta_TXTP, "TXTP generic header"},
{meta_SMC_SMH, "Genki SMC+SMH header"},
{meta_PPST, "Parappa PPST header"},
{meta_PPST, "epics PPST header"},
{meta_SPS_N1, "Nippon Ichi .SPS header"},
{meta_UBI_BAO, "Ubisoft BAO header"},
{meta_DSP_SWITCH_AUDIO, "UE4 Switch Audio header"},

View File

@ -90,14 +90,16 @@
<ClInclude Include="streamtypes.h" />
<ClInclude Include="util.h" />
<ClInclude Include="vgmstream.h" />
<ClInclude Include="vgmstream_init.h" />
<ClInclude Include="vgmstream_types.h" />
<ClInclude Include="base\api_internal.h" />
<ClInclude Include="base\decode.h" />
<ClInclude Include="base\mixer.h" />
<ClInclude Include="base\mixer_priv.h" />
<ClInclude Include="base\mixing.h" />
<ClInclude Include="base\mixing_fades.h" />
<ClInclude Include="base\mixing_priv.h" />
<ClInclude Include="base\plugins.h" />
<ClInclude Include="base\render.h" />
<ClInclude Include="base\sbuf.h" />
<ClInclude Include="coding\coding.h" />
<ClInclude Include="coding\coding_utils_samples.h" />
<ClInclude Include="coding\g72x_state.h" />
@ -212,6 +214,7 @@
<ClCompile Include="streamfile.c" />
<ClCompile Include="util.c" />
<ClCompile Include="vgmstream.c" />
<ClCompile Include="vgmstream_init.c" />
<ClCompile Include="base\api_decode_base.c" />
<ClCompile Include="base\api_decode_open.c" />
<ClCompile Include="base\api_decode_play.c" />
@ -221,6 +224,9 @@
<ClCompile Include="base\config.c" />
<ClCompile Include="base\decode.c" />
<ClCompile Include="base\info.c" />
<ClCompile Include="base\mixer.c" />
<ClCompile Include="base\mixer_ops_common.c" />
<ClCompile Include="base\mixer_ops_fade.c" />
<ClCompile Include="base\mixing.c" />
<ClCompile Include="base\mixing_commands.c" />
<ClCompile Include="base\mixing_macros.c" />
@ -627,7 +633,6 @@
<ClCompile Include="meta\ps2_vms.c" />
<ClCompile Include="meta\ps2_wmus.c" />
<ClCompile Include="meta\ps2_xa30.c" />
<ClCompile Include="meta\ps3_past.c" />
<ClCompile Include="meta\psb.c" />
<ClCompile Include="meta\psf.c" />
<ClCompile Include="meta\psnd.c" />
@ -680,6 +685,7 @@
<ClCompile Include="meta\smp.c" />
<ClCompile Include="meta\smpl.c" />
<ClCompile Include="meta\smv.c" />
<ClCompile Include="meta\sndp.c" />
<ClCompile Include="meta\snds.c" />
<ClCompile Include="meta\sndx.c" />
<ClCompile Include="meta\sndz.c" />

View File

@ -104,6 +104,9 @@
<ClInclude Include="vgmstream.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="vgmstream_init.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="vgmstream_types.h">
<Filter>Header Files</Filter>
</ClInclude>
@ -113,21 +116,24 @@
<ClInclude Include="base\decode.h">
<Filter>base\Header Files</Filter>
</ClInclude>
<ClInclude Include="base\mixer.h">
<Filter>base\Header Files</Filter>
</ClInclude>
<ClInclude Include="base\mixer_priv.h">
<Filter>base\Header Files</Filter>
</ClInclude>
<ClInclude Include="base\mixing.h">
<Filter>base\Header Files</Filter>
</ClInclude>
<ClInclude Include="base\mixing_fades.h">
<Filter>base\Header Files</Filter>
</ClInclude>
<ClInclude Include="base\mixing_priv.h">
<Filter>base\Header Files</Filter>
</ClInclude>
<ClInclude Include="base\plugins.h">
<Filter>base\Header Files</Filter>
</ClInclude>
<ClInclude Include="base\render.h">
<Filter>base\Header Files</Filter>
</ClInclude>
<ClInclude Include="base\sbuf.h">
<Filter>base\Header Files</Filter>
</ClInclude>
<ClInclude Include="coding\coding.h">
<Filter>coding\Header Files</Filter>
</ClInclude>
@ -466,6 +472,9 @@
<ClCompile Include="vgmstream.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="vgmstream_init.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="base\api_decode_base.c">
<Filter>base\Source Files</Filter>
</ClCompile>
@ -493,6 +502,15 @@
<ClCompile Include="base\info.c">
<Filter>base\Source Files</Filter>
</ClCompile>
<ClCompile Include="base\mixer.c">
<Filter>base\Source Files</Filter>
</ClCompile>
<ClCompile Include="base\mixer_ops_common.c">
<Filter>base\Source Files</Filter>
</ClCompile>
<ClCompile Include="base\mixer_ops_fade.c">
<Filter>base\Source Files</Filter>
</ClCompile>
<ClCompile Include="base\mixing.c">
<Filter>base\Source Files</Filter>
</ClCompile>
@ -1711,9 +1729,6 @@
<ClCompile Include="meta\ps2_xa30.c">
<Filter>meta\Source Files</Filter>
</ClCompile>
<ClCompile Include="meta\ps3_past.c">
<Filter>meta\Source Files</Filter>
</ClCompile>
<ClCompile Include="meta\psb.c">
<Filter>meta\Source Files</Filter>
</ClCompile>
@ -1870,6 +1885,9 @@
<ClCompile Include="meta\smv.c">
<Filter>meta\Source Files</Filter>
</ClCompile>
<ClCompile Include="meta\sndp.c">
<Filter>meta\Source Files</Filter>
</ClCompile>
<ClCompile Include="meta\snds.c">
<Filter>meta\Source Files</Filter>
</ClCompile>

View File

@ -1,118 +1,117 @@
#include "meta.h"
#include "../coding/coding.h"
#include "../layout/layout.h"
#include "9tav_streamfile.h"
/* 9TAV - from Metal Gear Solid 2/3 HD (Vita) */
VGMSTREAM * init_vgmstream_9tav(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
off_t start_offset;
int loop_flag, channel_count, sample_rate, track_count;
int32_t num_samples, loop_start, loop_end;
size_t track_size;
uint32_t config_data;
int i, is_padded;
layered_layout_data * data = NULL;
STREAMFILE* temp_streamFile = NULL;
/* checks */
/* .9tav: header id */
if (!check_extensions(streamFile, "9tav"))
goto fail;
if (read_32bitBE(0x00,streamFile) != 0x39544156) /* "9TAV" */
goto fail;
/* 0x04: always 0x09 */
channel_count = read_16bitLE(0x08,streamFile);
track_count = read_16bitLE(0x0a,streamFile); /* MGS3 uses multitracks */
sample_rate = read_32bitLE(0x0c,streamFile);
track_size = read_32bitLE(0x10,streamFile); /* without padding */
//data_size = read_32bitLE(0x14,streamFile); /* without padding */
num_samples = read_32bitLE(0x18,streamFile);
config_data = read_32bitBE(0x1c,streamFile);
if (read_32bitBE(0x20,streamFile) == 0x4D544146) { /* "MTAF" */
/* MGS3 has a MTAF header (data size and stuff don't match, probably for track info) */
loop_start = read_32bitLE(0x78, streamFile);
loop_end = read_32bitLE(0x7c, streamFile);
loop_flag = read_32bitLE(0x90, streamFile) & 1;
is_padded = 1; /* data also has padding and other oddities */
start_offset = 0x00;
}
else {
/* MGS2 doesn't */
loop_start = 0;
loop_end = 0;
loop_flag = 0;
is_padded = 0;
start_offset = 0x20;
}
/* init layout */
data = init_layout_layered(track_count);
if (!data) goto fail;
/* open each layer subfile */
for (i = 0; i < data->layer_count; i++) {
data->layers[i] = allocate_vgmstream(channel_count, loop_flag);
if (!data->layers[i]) goto fail;
data->layers[i]->meta_type = meta_9TAV;
data->layers[i]->sample_rate = sample_rate;
data->layers[i]->num_samples = num_samples;
data->layers[i]->loop_start_sample = loop_start;
data->layers[i]->loop_end_sample = loop_end;
#ifdef VGM_USE_ATRAC9
{
atrac9_config cfg = {0};
cfg.channels = channel_count;
cfg.config_data = config_data;
cfg.encoder_delay = atrac9_bytes_to_samples_cfg(track_size, cfg.config_data) - num_samples; /* seems ok */
if (cfg.encoder_delay > 4096) /* doesn't seem too normal */
cfg.encoder_delay = 0;
data->layers[i]->codec_data = init_atrac9(&cfg);
if (!data->layers[i]->codec_data) goto fail;
data->layers[i]->coding_type = coding_ATRAC9;
data->layers[i]->layout_type = layout_none;
}
#else
goto fail;
#endif
if (is_padded) {
temp_streamFile = setup_9tav_streamfile(streamFile, 0xFE4, track_size, i, track_count);
if (!temp_streamFile) goto fail;
}
if (!vgmstream_open_stream(data->layers[i],temp_streamFile == NULL ? streamFile : temp_streamFile,start_offset))
goto fail;
close_streamfile(temp_streamFile);
temp_streamFile = NULL;
}
/* setup layered VGMSTREAMs */
if (!setup_layout_layered(data))
goto fail;
/* build the layered VGMSTREAM */
vgmstream = allocate_layered_vgmstream(data);
if (!vgmstream) goto fail;
return vgmstream;
fail:
close_streamfile(temp_streamFile);
close_vgmstream(vgmstream);
if (!vgmstream)
free_layout_layered(data);
return NULL;
}
#include "meta.h"
#include "../coding/coding.h"
#include "../layout/layout.h"
#include "9tav_streamfile.h"
/* 9TAV - from Metal Gear Solid 2/3 HD (Vita) */
VGMSTREAM* init_vgmstream_9tav(STREAMFILE* sf) {
VGMSTREAM * vgmstream = NULL;
off_t start_offset;
int loop_flag, channels, sample_rate, track_count;
int32_t num_samples, loop_start, loop_end;
size_t track_size;
uint32_t config_data;
bool is_padded;
layered_layout_data* data = NULL;
STREAMFILE* temp_sf = NULL;
/* checks */
if (!is_id32be(0x00,sf, "9TAV"))
return NULL;
/* .9tav: header id */
if (!check_extensions(sf, "9tav"))
return NULL;
/* 0x04: always 0x09 (codec?) */
channels = read_u16le(0x08,sf);
track_count = read_u16le(0x0a,sf); /* MGS3 uses multitracks */
sample_rate = read_s32le(0x0c,sf);
track_size = read_u32le(0x10,sf); /* without padding */
//data_size = read_u32le(0x14,sf); /* without padding */
num_samples = read_s32le(0x18,sf);
config_data = read_u32be(0x1c,sf);
if (is_id32be(0x20,sf, "MTAF")) {
/* MGS3 has a MTAF header (data size and stuff don't match, probably for track info) */
loop_start = read_s32le(0x78, sf);
loop_end = read_s32le(0x7c, sf);
loop_flag = read_u32le(0x90, sf) & 1;
is_padded = true; /* data also has padding and other oddities */
start_offset = 0x00;
}
else {
/* MGS2 doesn't */
loop_start = 0;
loop_end = 0;
loop_flag = false;
is_padded = false;
start_offset = 0x20;
}
/* init layout */
data = init_layout_layered(track_count);
if (!data) goto fail;
/* open each layer subfile */
for (int i = 0; i < data->layer_count; i++) {
data->layers[i] = allocate_vgmstream(channels, loop_flag);
if (!data->layers[i]) goto fail;
data->layers[i]->meta_type = meta_9TAV;
data->layers[i]->sample_rate = sample_rate;
data->layers[i]->num_samples = num_samples;
data->layers[i]->loop_start_sample = loop_start;
data->layers[i]->loop_end_sample = loop_end;
#ifdef VGM_USE_ATRAC9
{
atrac9_config cfg = {0};
cfg.channels = channels;
cfg.config_data = config_data;
cfg.encoder_delay = atrac9_bytes_to_samples_cfg(track_size, cfg.config_data) - num_samples; /* seems ok */
if (cfg.encoder_delay > 4096) /* doesn't seem too normal */
cfg.encoder_delay = 0;
data->layers[i]->codec_data = init_atrac9(&cfg);
if (!data->layers[i]->codec_data) goto fail;
data->layers[i]->coding_type = coding_ATRAC9;
data->layers[i]->layout_type = layout_none;
}
#else
goto fail;
#endif
if (is_padded) {
temp_sf = setup_9tav_streamfile(sf, 0xFE4, track_size, i, track_count);
if (!temp_sf) goto fail;
}
if (!vgmstream_open_stream(data->layers[i],temp_sf == NULL ? sf : temp_sf,start_offset))
goto fail;
close_streamfile(temp_sf);
temp_sf = NULL;
}
/* setup layered VGMSTREAMs */
if (!setup_layout_layered(data))
goto fail;
/* build the layered VGMSTREAM */
vgmstream = allocate_layered_vgmstream(data);
if (!vgmstream) goto fail;
return vgmstream;
fail:
close_streamfile(temp_sf);
close_vgmstream(vgmstream);
if (!vgmstream)
free_layout_layered(data);
return NULL;
}

View File

@ -1,182 +1,182 @@
#ifndef _9TAV_STREAMFILE_H_
#define _9TAV_STREAMFILE_H_
#include "../streamfile.h"
typedef struct {
/* config */
off_t stream_offset;
size_t stream_size;
size_t track_size;
int track_number;
int track_count;
int skip_count;
int read_count;
size_t frame_size;
size_t interleave_count;
size_t interleave_last_count;
/* state */
off_t logical_offset; /* fake offset */
off_t physical_offset; /* actual offset */
size_t block_size; /* current size */
size_t skip_size; /* size from block start to reach data */
size_t data_size; /* usable size in a block */
size_t logical_size;
} ntav_io_data;
static size_t ntav_io_read(STREAMFILE *streamfile, uint8_t *dest, off_t offset, size_t length, ntav_io_data* data) {
size_t total_read = 0;
/* re-start when previous offset (can't map logical<>physical offsets) */
if (data->logical_offset < 0 || offset < data->logical_offset) {
data->physical_offset = data->stream_offset;
data->logical_offset = 0x00;
data->data_size = 0;
data->skip_size = 0;
data->read_count = 0;
data->skip_count = data->interleave_count * data->track_number;
//VGM_LOG("0 o=%lx, sc=%i\n", data->physical_offset, data->skip_count);
}
/* read blocks */
while (length > 0) {
//VGM_LOG("1 of=%lx, so=%lx, sz=%x, of2=%lx, log=%lx\n", data->physical_offset, data->stream_offset, data->stream_size, offset, data->logical_offset);
/* ignore EOF */
if (offset < 0 || data->physical_offset >= data->stream_offset + data->stream_size) {
//VGM_LOG("9 o=%lx, so=%lx, sz=%x, of2=%lx, log=%lx\n", data->physical_offset, data->stream_offset, data->stream_size, offset, data->logical_offset);
//VGM_LOG("eof\n");
break;
}
/* process new block */
if (data->data_size == 0) {
/* not very exact compared to real blocks but ok enough */
if (read_32bitLE(data->physical_offset, streamfile) == 0x00) {
data->block_size = 0x10;
//VGM_LOG("1 o=%lx, lo=%lx skip\n", data->physical_offset, data->logical_offset);
}
else {
data->block_size = data->frame_size;
//VGM_LOG("2 o=%lx, lo=%lx, skip=%i, read=%i\n", data->physical_offset, data->logical_offset, data->skip_count, data->read_count);
/* each track interleaves NTAV_INTERLEAVE frames, but can contain padding in between,
* so must read one by one up to max */
if (data->skip_count == 0 && data->read_count == 0) {
data->read_count = data->interleave_count;
}
if (data->skip_count) {
data->skip_count--;
}
if (data->read_count) {
data->data_size = data->block_size;
data->read_count--;
if (data->read_count == 0) {
if (data->logical_offset + data->interleave_count * data->frame_size > data->track_size)
data->skip_count = data->interleave_last_count * (data->track_count - 1);
else
data->skip_count = data->interleave_count * (data->track_count - 1);
}
}
}
}
/* move to next block */
if (data->data_size == 0 || offset >= data->logical_offset + data->data_size) {
data->physical_offset += data->block_size;
data->logical_offset += data->data_size;
data->data_size = 0;
continue;
}
/* read data */
{
size_t bytes_consumed, bytes_done, to_read;
bytes_consumed = offset - data->logical_offset;
to_read = data->data_size - bytes_consumed;
if (to_read > length)
to_read = length;
bytes_done = read_streamfile(dest, data->physical_offset + data->skip_size + bytes_consumed, to_read, streamfile);
total_read += bytes_done;
dest += bytes_done;
offset += bytes_done;
length -= bytes_done;
if (bytes_done != to_read || bytes_done == 0) {
break; /* error/EOF */
}
}
}
return total_read;
}
static size_t ntav_io_size(STREAMFILE *streamfile, ntav_io_data* data) {
uint8_t buf[1];
if (data->logical_size)
return data->logical_size;
/* force a fake read at max offset, to get max logical_offset (will be reset next read) */
ntav_io_read(streamfile, buf, 0x7FFFFFFF, 1, data);
data->logical_size = data->logical_offset;
return data->logical_size;
}
/* Handles deinterleaving of 9TAV blocked streams. Unlike other games using .sdt,
* KCEJ blocks have a data_size field and rest is padding. Even after that all blocks start
* with 0 (skipped) and there are padding blocks that start with LE 0xDEADBEEF.
* This streamfile handles 9tav extracted like regular sdt and remove padding manually. */
static STREAMFILE* setup_9tav_streamfile(STREAMFILE *streamFile, off_t stream_offset, size_t track_size, int track_number, int track_count) {
STREAMFILE *temp_streamFile = NULL, *new_streamFile = NULL;
ntav_io_data io_data = {0};
size_t io_data_size = sizeof(ntav_io_data);
size_t last_size;
io_data.stream_offset = stream_offset;
io_data.stream_size = get_streamfile_size(streamFile) - stream_offset;
io_data.track_size = track_size;
io_data.track_number = track_number;
io_data.track_count = track_count;
io_data.frame_size = 0x40;
io_data.interleave_count = 256;
last_size = track_size % (io_data.interleave_count * io_data.frame_size);
if (last_size)
io_data.interleave_last_count = last_size / io_data.frame_size;
io_data.logical_offset = -1; /* force state reset */
/* setup subfile */
new_streamFile = open_wrap_streamfile(streamFile);
if (!new_streamFile) goto fail;
temp_streamFile = new_streamFile;
new_streamFile = open_io_streamfile(new_streamFile, &io_data,io_data_size, ntav_io_read,ntav_io_size);
if (!new_streamFile) goto fail;
temp_streamFile = new_streamFile;
new_streamFile = open_buffer_streamfile(new_streamFile,0);
if (!new_streamFile) goto fail;
temp_streamFile = new_streamFile;
return temp_streamFile;
fail:
close_streamfile(temp_streamFile);
return NULL;
}
#endif /* _9TAV_STREAMFILE_H_ */
#ifndef _9TAV_STREAMFILE_H_
#define _9TAV_STREAMFILE_H_
#include "../streamfile.h"
typedef struct {
/* config */
off_t stream_offset;
size_t stream_size;
size_t track_size;
int track_number;
int track_count;
int skip_count;
int read_count;
size_t frame_size;
size_t interleave_count;
size_t interleave_last_count;
/* state */
off_t logical_offset; /* fake offset */
off_t physical_offset; /* actual offset */
size_t block_size; /* current size */
size_t skip_size; /* size from block start to reach data */
size_t data_size; /* usable size in a block */
size_t logical_size;
} ntav_io_data;
static size_t ntav_io_read(STREAMFILE *streamfile, uint8_t *dest, off_t offset, size_t length, ntav_io_data* data) {
size_t total_read = 0;
/* re-start when previous offset (can't map logical<>physical offsets) */
if (data->logical_offset < 0 || offset < data->logical_offset) {
data->physical_offset = data->stream_offset;
data->logical_offset = 0x00;
data->data_size = 0;
data->skip_size = 0;
data->read_count = 0;
data->skip_count = data->interleave_count * data->track_number;
//VGM_LOG("0 o=%lx, sc=%i\n", data->physical_offset, data->skip_count);
}
/* read blocks */
while (length > 0) {
//VGM_LOG("1 of=%lx, so=%lx, sz=%x, of2=%lx, log=%lx\n", data->physical_offset, data->stream_offset, data->stream_size, offset, data->logical_offset);
/* ignore EOF */
if (offset < 0 || data->physical_offset >= data->stream_offset + data->stream_size) {
//VGM_LOG("9 o=%lx, so=%lx, sz=%x, of2=%lx, log=%lx\n", data->physical_offset, data->stream_offset, data->stream_size, offset, data->logical_offset);
//VGM_LOG("eof\n");
break;
}
/* process new block */
if (data->data_size == 0) {
/* not very exact compared to real blocks but ok enough */
if (read_32bitLE(data->physical_offset, streamfile) == 0x00) {
data->block_size = 0x10;
//VGM_LOG("1 o=%lx, lo=%lx skip\n", data->physical_offset, data->logical_offset);
}
else {
data->block_size = data->frame_size;
//VGM_LOG("2 o=%lx, lo=%lx, skip=%i, read=%i\n", data->physical_offset, data->logical_offset, data->skip_count, data->read_count);
/* each track interleaves NTAV_INTERLEAVE frames, but can contain padding in between,
* so must read one by one up to max */
if (data->skip_count == 0 && data->read_count == 0) {
data->read_count = data->interleave_count;
}
if (data->skip_count) {
data->skip_count--;
}
if (data->read_count) {
data->data_size = data->block_size;
data->read_count--;
if (data->read_count == 0) {
if (data->logical_offset + data->interleave_count * data->frame_size > data->track_size)
data->skip_count = data->interleave_last_count * (data->track_count - 1);
else
data->skip_count = data->interleave_count * (data->track_count - 1);
}
}
}
}
/* move to next block */
if (data->data_size == 0 || offset >= data->logical_offset + data->data_size) {
data->physical_offset += data->block_size;
data->logical_offset += data->data_size;
data->data_size = 0;
continue;
}
/* read data */
{
size_t bytes_consumed, bytes_done, to_read;
bytes_consumed = offset - data->logical_offset;
to_read = data->data_size - bytes_consumed;
if (to_read > length)
to_read = length;
bytes_done = read_streamfile(dest, data->physical_offset + data->skip_size + bytes_consumed, to_read, streamfile);
total_read += bytes_done;
dest += bytes_done;
offset += bytes_done;
length -= bytes_done;
if (bytes_done != to_read || bytes_done == 0) {
break; /* error/EOF */
}
}
}
return total_read;
}
static size_t ntav_io_size(STREAMFILE *streamfile, ntav_io_data* data) {
uint8_t buf[1];
if (data->logical_size)
return data->logical_size;
/* force a fake read at max offset, to get max logical_offset (will be reset next read) */
ntav_io_read(streamfile, buf, 0x7FFFFFFF, 1, data);
data->logical_size = data->logical_offset;
return data->logical_size;
}
/* Handles deinterleaving of 9TAV blocked streams. Unlike other games using .sdt,
* KCEJ blocks have a data_size field and rest is padding. Even after that all blocks start
* with 0 (skipped) and there are padding blocks that start with LE 0xDEADBEEF.
* This streamfile handles 9tav extracted like regular sdt and remove padding manually. */
static STREAMFILE* setup_9tav_streamfile(STREAMFILE *streamFile, off_t stream_offset, size_t track_size, int track_number, int track_count) {
STREAMFILE *temp_streamFile = NULL, *new_streamFile = NULL;
ntav_io_data io_data = {0};
size_t io_data_size = sizeof(ntav_io_data);
size_t last_size;
io_data.stream_offset = stream_offset;
io_data.stream_size = get_streamfile_size(streamFile) - stream_offset;
io_data.track_size = track_size;
io_data.track_number = track_number;
io_data.track_count = track_count;
io_data.frame_size = 0x40;
io_data.interleave_count = 256;
last_size = track_size % (io_data.interleave_count * io_data.frame_size);
if (last_size)
io_data.interleave_last_count = last_size / io_data.frame_size;
io_data.logical_offset = -1; /* force state reset */
/* setup subfile */
new_streamFile = open_wrap_streamfile(streamFile);
if (!new_streamFile) goto fail;
temp_streamFile = new_streamFile;
new_streamFile = open_io_streamfile(new_streamFile, &io_data,io_data_size, ntav_io_read,ntav_io_size);
if (!new_streamFile) goto fail;
temp_streamFile = new_streamFile;
new_streamFile = open_buffer_streamfile(new_streamFile,0);
if (!new_streamFile) goto fail;
temp_streamFile = new_streamFile;
return temp_streamFile;
fail:
close_streamfile(temp_streamFile);
return NULL;
}
#endif /* _9TAV_STREAMFILE_H_ */

View File

@ -51,7 +51,7 @@ VGMSTREAM* init_vgmstream_acb(STREAMFILE* sf) {
}
}
//;VGM_LOG("acb: subfile offset=%lx + %x\n", subfile_offset, subfile_size);
//;VGM_LOG("acb: subfile offset=%x + %x\n", subfile_offset, subfile_size);
temp_sf = setup_subfile_streamfile(sf, subfile_offset,subfile_size, "awb");
if (!temp_sf) goto fail;
@ -538,7 +538,7 @@ static int load_acb_command_tlvs(acb_header* acb, STREAMFILE* sf, uint32_t Comma
tlv_type = read_u16be(Command_offset + pos + 0x00, sf); /* ReferenceItem */
tlv_index = read_u16be(Command_offset + pos + 0x02, sf);
//;VGM_LOG("acb: TLV at %x: type %x, index=%x\n", offset, tlv_type, tlv_index);
//;VGM_LOG("acb: TLV at %x: type %x, index=%x\n", Command_offset, tlv_type, tlv_index);
/* same as Synth's ReferenceItem type? */
switch(tlv_type) {
@ -561,7 +561,7 @@ static int load_acb_command_tlvs(acb_header* acb, STREAMFILE* sf, uint32_t Comma
case 2004: /* noteOnWithDuration */
/* same as the above plus extra field */
//;VGM_LOG("acb: TLV at %x: usable code %i?\n", offset-0x03, tlv_code);
//;VGM_LOG("acb: TLV at %x: usable code %i?\n", Command_offset-0x03, tlv_code);
break;
case 33: /* mute */
@ -576,7 +576,7 @@ static int load_acb_command_tlvs(acb_header* acb, STREAMFILE* sf, uint32_t Comma
case 7100: /* startAction */
case 7101: /* stopAction */
/* may be needed? */
//;VGM_LOG("acb: TLV at %x: check code %i?\n", offset-0x03, tlv_code);
//;VGM_LOG("acb: TLV at %x: check code %i?\n", Command_offset-0x03, tlv_code);
break;
case 0: /* no-op */
@ -1167,7 +1167,7 @@ static int load_acb_loops(acb_header* acb, VGMSTREAM* vgmstream) {
r = &acb->WaveformExtensionData[ExtensionIndex];
//;VGM_LOG("acb: WaveformExtensionData[%i]: LoopStart=%i, LoopEnd=%i\n", Index, r->LoopStart, r->LoopEnd);
//;VGM_LOG("acb: WaveformExtensionData[%i]: LoopStart=%i, LoopEnd=%i\n", ExtensionIndex, r->LoopStart, r->LoopEnd);
vgmstream_force_loop(vgmstream, 1, r->LoopStart, r->LoopEnd);

View File

@ -120,7 +120,7 @@ VGMSTREAM* init_vgmstream_awc(STREAMFILE* sf) {
vgmstream->layout_data = build_layered_awc(sf_body, &awc);
if (!vgmstream->layout_data) goto fail;
vgmstream->layout_type = layout_layered;
vgmstream->coding_type = coding_FFmpeg;
vgmstream->coding_type = coding_MPEG_custom;
}
else {
vgmstream->codec_data = init_mpeg_custom(sf_body, awc.stream_offset, &vgmstream->coding_type, vgmstream->channels, MPEG_STANDARD, NULL);

View File

@ -1,21 +1,21 @@
#ifndef _FSB5_STREAMFILE_H_
#define _FSB5_STREAMFILE_H_
#include "deblock_streamfile.h"
static STREAMFILE* setup_fsb5_streamfile(STREAMFILE* sf, off_t stream_start, size_t stream_size, int stream_count, int stream_number, size_t interleave) {
STREAMFILE* new_sf = NULL;
deblock_config_t cfg = {0};
cfg.stream_start = stream_start;
cfg.stream_size = stream_size;
cfg.chunk_size = interleave;
cfg.step_start = stream_number;
cfg.step_count = stream_count;
/* setup sf */
new_sf = open_wrap_streamfile(sf);
new_sf = open_io_deblock_streamfile_f(new_sf, &cfg);
return new_sf;
}
#endif /* _FSB5_STREAMFILE_H_ */
#ifndef _FSB5_STREAMFILE_H_
#define _FSB5_STREAMFILE_H_
#include "deblock_streamfile.h"
static STREAMFILE* setup_fsb5_streamfile(STREAMFILE* sf, off_t stream_start, size_t stream_size, int stream_count, int stream_number, size_t interleave) {
STREAMFILE* new_sf = NULL;
deblock_config_t cfg = {0};
cfg.stream_start = stream_start;
cfg.stream_size = stream_size;
cfg.chunk_size = interleave;
cfg.step_start = stream_number;
cfg.step_count = stream_count;
/* setup sf */
new_sf = open_wrap_streamfile(sf);
new_sf = open_io_deblock_streamfile_f(new_sf, &cfg);
return new_sf;
}
#endif /* _FSB5_STREAMFILE_H_ */

View File

@ -1,198 +1,198 @@
#ifndef _FSB_INTERLEAVE_STREAMFILE_H_
#define _FSB_INTERLEAVE_STREAMFILE_H_
#include "../streamfile.h"
typedef enum { FSB_INT_CELT } fsb_interleave_codec_t;
typedef struct {
/* state */
off_t logical_offset; /* offset that corresponds to physical_offset */
off_t physical_offset; /* actual file offset */
int skip_frames; /* frames to skip from other streams at points */
/* config */
fsb_interleave_codec_t codec;
int stream_count;
int stream_number;
size_t stream_size;
off_t start_offset; /* pointing to the stream's beginning */
size_t total_size; /* size of the resulting substream */
} fsb_interleave_io_data;
/* Reads skipping other streams */
static size_t fsb_interleave_io_read(STREAMFILE *streamfile, uint8_t *dest, off_t offset, size_t length, fsb_interleave_io_data* data) {
size_t total_read = 0;
/* ignore bad reads */
if (offset < 0 || offset > data->total_size) {
return total_read;
}
/* previous offset: re-start as we can't map logical<>physical offsets
* (kinda slow as it trashes buffers, but shouldn't happen often) */
if (offset < data->logical_offset) {
data->physical_offset = data->start_offset;
data->logical_offset = 0x00;
data->skip_frames = data->stream_number;
}
/* read doing one frame at a time */
while (length > 0) {
size_t to_read, bytes_read;
off_t intrablock_offset, intradata_offset;
uint32_t data_size;
if (offset >= data->total_size)
break;
/* get current data */
switch (data->codec) {
case FSB_INT_CELT:
data_size = 0x04+0x04+read_32bitLE(data->physical_offset+0x04,streamfile);
break;
default:
return 0;
}
/* skip frames from other streams */
if (data->skip_frames) {
data->physical_offset += data_size;
data->skip_frames--;
continue;
}
/* requested offset is outside current block, try next */
if (offset >= data->logical_offset + data_size) {
data->physical_offset += data_size;
data->logical_offset += data_size;
data->skip_frames = data->stream_count - 1;
continue;
}
/* reads could fall in the middle of the block */
intradata_offset = offset - data->logical_offset;
intrablock_offset = intradata_offset;
/* clamp reads up to this block's end */
to_read = (data_size - intradata_offset);
if (to_read > length)
to_read = length;
if (to_read == 0)
break; /* should never happen... */
/* finally read and move buffer/offsets */
bytes_read = read_streamfile(dest, data->physical_offset + intrablock_offset, to_read, streamfile);
total_read += bytes_read;
if (bytes_read != to_read)
break; /* couldn't read fully */
dest += bytes_read;
offset += bytes_read;
length -= bytes_read;
/* block fully read, go next */
if (intradata_offset + bytes_read == data_size) {
data->physical_offset += data_size;
data->logical_offset += data_size;
data->skip_frames = data->stream_count - 1;
}
}
return total_read;
}
static size_t fsb_interleave_io_size(STREAMFILE *streamfile, fsb_interleave_io_data* data) {
off_t physical_offset, max_physical_offset;
size_t total_size = 0;
int skip_frames = 0;
if (data->total_size)
return data->total_size;
physical_offset = data->start_offset;
max_physical_offset = physical_offset + data->stream_size;
skip_frames = data->stream_number;
/* get size of the underlying stream, skipping other streams
* (all streams should have the same frame count) */
while (physical_offset < max_physical_offset) {
size_t data_size;
uint32_t id;
switch(data->codec) {
case FSB_INT_CELT:
id = read_32bitBE(physical_offset+0x00,streamfile);
if (id != 0x17C30DF3) /* incorrect FSB CELT frame sync */
data_size = 0;
else
data_size = 0x04+0x04+read_32bitLE(physical_offset+0x04,streamfile);
break;
default:
return 0;
}
/* there may be padding at the end, so this doubles as EOF marker */
if (data_size == 0)
break;
/* skip frames from other streams */
if (skip_frames) {
physical_offset += data_size;
skip_frames--;
continue;
}
physical_offset += data_size;
total_size += data_size;
skip_frames = data->stream_count - 1;
}
data->total_size = total_size;
return data->total_size;
}
/* Prepares custom IO for multistreams, interleaves 1 packet per stream */
static STREAMFILE* setup_fsb_interleave_streamfile(STREAMFILE *streamFile, off_t start_offset, size_t stream_size, int stream_count, int stream_number, fsb_interleave_codec_t codec) {
STREAMFILE *temp_streamFile = NULL, *new_streamFile = NULL;
fsb_interleave_io_data io_data = {0};
size_t io_data_size = sizeof(fsb_interleave_io_data);
io_data.start_offset = start_offset;
io_data.physical_offset = start_offset;
io_data.skip_frames = stream_number; /* adjust since start_offset points to the first */
io_data.codec = codec;
io_data.stream_count = stream_count;
io_data.stream_number = stream_number;
io_data.stream_size = stream_size;
io_data.total_size = fsb_interleave_io_size(streamFile, &io_data); /* force init */
if (io_data.total_size == 0 || io_data.total_size > io_data.stream_size) {
VGM_LOG("FSB INTERLEAVE: wrong total_size %x vs %x\n", io_data.total_size,io_data.stream_size);
goto fail;
}
/* setup subfile */
new_streamFile = open_wrap_streamfile(streamFile);
if (!new_streamFile) goto fail;
temp_streamFile = new_streamFile;
new_streamFile = open_io_streamfile(temp_streamFile, &io_data,io_data_size, fsb_interleave_io_read,fsb_interleave_io_size);
if (!new_streamFile) goto fail;
temp_streamFile = new_streamFile;
new_streamFile = open_buffer_streamfile(new_streamFile,0);
if (!new_streamFile) goto fail;
temp_streamFile = new_streamFile;
return temp_streamFile;
fail:
close_streamfile(temp_streamFile);
return NULL;
}
#endif /* _FSB_INTERLEAVE_STREAMFILE_H_ */
#ifndef _FSB_INTERLEAVE_STREAMFILE_H_
#define _FSB_INTERLEAVE_STREAMFILE_H_
#include "../streamfile.h"
typedef enum { FSB_INT_CELT } fsb_interleave_codec_t;
typedef struct {
/* state */
off_t logical_offset; /* offset that corresponds to physical_offset */
off_t physical_offset; /* actual file offset */
int skip_frames; /* frames to skip from other streams at points */
/* config */
fsb_interleave_codec_t codec;
int stream_count;
int stream_number;
size_t stream_size;
off_t start_offset; /* pointing to the stream's beginning */
size_t total_size; /* size of the resulting substream */
} fsb_interleave_io_data;
/* Reads skipping other streams */
static size_t fsb_interleave_io_read(STREAMFILE *streamfile, uint8_t *dest, off_t offset, size_t length, fsb_interleave_io_data* data) {
size_t total_read = 0;
/* ignore bad reads */
if (offset < 0 || offset > data->total_size) {
return total_read;
}
/* previous offset: re-start as we can't map logical<>physical offsets
* (kinda slow as it trashes buffers, but shouldn't happen often) */
if (offset < data->logical_offset) {
data->physical_offset = data->start_offset;
data->logical_offset = 0x00;
data->skip_frames = data->stream_number;
}
/* read doing one frame at a time */
while (length > 0) {
size_t to_read, bytes_read;
off_t intrablock_offset, intradata_offset;
uint32_t data_size;
if (offset >= data->total_size)
break;
/* get current data */
switch (data->codec) {
case FSB_INT_CELT:
data_size = 0x04+0x04+read_32bitLE(data->physical_offset+0x04,streamfile);
break;
default:
return 0;
}
/* skip frames from other streams */
if (data->skip_frames) {
data->physical_offset += data_size;
data->skip_frames--;
continue;
}
/* requested offset is outside current block, try next */
if (offset >= data->logical_offset + data_size) {
data->physical_offset += data_size;
data->logical_offset += data_size;
data->skip_frames = data->stream_count - 1;
continue;
}
/* reads could fall in the middle of the block */
intradata_offset = offset - data->logical_offset;
intrablock_offset = intradata_offset;
/* clamp reads up to this block's end */
to_read = (data_size - intradata_offset);
if (to_read > length)
to_read = length;
if (to_read == 0)
break; /* should never happen... */
/* finally read and move buffer/offsets */
bytes_read = read_streamfile(dest, data->physical_offset + intrablock_offset, to_read, streamfile);
total_read += bytes_read;
if (bytes_read != to_read)
break; /* couldn't read fully */
dest += bytes_read;
offset += bytes_read;
length -= bytes_read;
/* block fully read, go next */
if (intradata_offset + bytes_read == data_size) {
data->physical_offset += data_size;
data->logical_offset += data_size;
data->skip_frames = data->stream_count - 1;
}
}
return total_read;
}
static size_t fsb_interleave_io_size(STREAMFILE *streamfile, fsb_interleave_io_data* data) {
off_t physical_offset, max_physical_offset;
size_t total_size = 0;
int skip_frames = 0;
if (data->total_size)
return data->total_size;
physical_offset = data->start_offset;
max_physical_offset = physical_offset + data->stream_size;
skip_frames = data->stream_number;
/* get size of the underlying stream, skipping other streams
* (all streams should have the same frame count) */
while (physical_offset < max_physical_offset) {
size_t data_size;
uint32_t id;
switch(data->codec) {
case FSB_INT_CELT:
id = read_32bitBE(physical_offset+0x00,streamfile);
if (id != 0x17C30DF3) /* incorrect FSB CELT frame sync */
data_size = 0;
else
data_size = 0x04+0x04+read_32bitLE(physical_offset+0x04,streamfile);
break;
default:
return 0;
}
/* there may be padding at the end, so this doubles as EOF marker */
if (data_size == 0)
break;
/* skip frames from other streams */
if (skip_frames) {
physical_offset += data_size;
skip_frames--;
continue;
}
physical_offset += data_size;
total_size += data_size;
skip_frames = data->stream_count - 1;
}
data->total_size = total_size;
return data->total_size;
}
/* Prepares custom IO for multistreams, interleaves 1 packet per stream */
static STREAMFILE* setup_fsb_interleave_streamfile(STREAMFILE *streamFile, off_t start_offset, size_t stream_size, int stream_count, int stream_number, fsb_interleave_codec_t codec) {
STREAMFILE *temp_streamFile = NULL, *new_streamFile = NULL;
fsb_interleave_io_data io_data = {0};
size_t io_data_size = sizeof(fsb_interleave_io_data);
io_data.start_offset = start_offset;
io_data.physical_offset = start_offset;
io_data.skip_frames = stream_number; /* adjust since start_offset points to the first */
io_data.codec = codec;
io_data.stream_count = stream_count;
io_data.stream_number = stream_number;
io_data.stream_size = stream_size;
io_data.total_size = fsb_interleave_io_size(streamFile, &io_data); /* force init */
if (io_data.total_size == 0 || io_data.total_size > io_data.stream_size) {
VGM_LOG("FSB INTERLEAVE: wrong total_size %x vs %x\n", io_data.total_size,io_data.stream_size);
goto fail;
}
/* setup subfile */
new_streamFile = open_wrap_streamfile(streamFile);
if (!new_streamFile) goto fail;
temp_streamFile = new_streamFile;
new_streamFile = open_io_streamfile(temp_streamFile, &io_data,io_data_size, fsb_interleave_io_read,fsb_interleave_io_size);
if (!new_streamFile) goto fail;
temp_streamFile = new_streamFile;
new_streamFile = open_buffer_streamfile(new_streamFile,0);
if (!new_streamFile) goto fail;
temp_streamFile = new_streamFile;
return temp_streamFile;
fail:
close_streamfile(temp_streamFile);
return NULL;
}
#endif /* _FSB_INTERLEAVE_STREAMFILE_H_ */

View File

@ -1,55 +1,54 @@
#include "meta.h"
#include "../coding/coding.h"
/* .gin - EA engine sounds [Need for Speed: Most Wanted (multi)] */
VGMSTREAM * init_vgmstream_gin(STREAMFILE *sf) {
VGMSTREAM *vgmstream = NULL;
off_t start_offset;
int loop_flag, channel_count, sample_rate, num_samples;
if (!check_extensions(sf, "gin"))
goto fail;
/* checks */
if (!is_id32be(0x00, sf, "Gnsu") && /* original */
!is_id32be(0x00, sf, "Octn")) /* later (2013+) games, looks same as "Gnsu" */
goto fail;
/* contains mapped values for engine RPM sounds but we'll just play the whole thing */
/* 0x04: size? "20\00\00"? */
/* 0x08/0c: min/max float RPM? */
/* 0x10: RPM up? (pitch/frequency) table size */
/* 0x14: RPM ??? table size */
/* always LE even on X360/PS3 */
num_samples = read_u32le(0x18, sf);
sample_rate = read_u32le(0x1c, sf);
start_offset = 0x20 +
(read_u32le(0x10, sf) + 1) * 0x04 +
(read_u32le(0x14, sf) + 1) * 0x04;
channel_count = 1;
loop_flag = 0;
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count, loop_flag);
if (!vgmstream) goto fail;
vgmstream->meta_type = meta_GIN;
vgmstream->sample_rate = sample_rate;
vgmstream->num_samples = num_samples;
vgmstream->coding_type = coding_EA_XAS_V0;
vgmstream->layout_type = layout_none;
/* calculate size for TMX */
vgmstream->stream_size = (align_size_to_block(num_samples, 32) / 32) * 0x13;
if (!vgmstream_open_stream(vgmstream, sf, start_offset))
goto fail;
return vgmstream;
fail:
close_vgmstream(vgmstream);
return NULL;
}
#include "meta.h"
#include "../coding/coding.h"
/* .gin - EA engine sounds [Need for Speed: Most Wanted (multi)] */
VGMSTREAM* init_vgmstream_gin(STREAMFILE* sf) {
VGMSTREAM* vgmstream = NULL;
off_t start_offset;
int loop_flag, channel_count, sample_rate, num_samples;
/* checks */
if (!is_id32be(0x00, sf, "Gnsu") && /* original */
!is_id32be(0x00, sf, "Octn")) /* later (2013+) games */
return NULL;
if (!check_extensions(sf, "gin"))
return NULL;
/* contains mapped values for engine RPM sounds but we'll just play the whole thing */
/* 0x04: size? "20\00\00"? */
/* 0x08/0c: min/max float RPM? */
/* 0x10: RPM up? (pitch/frequency) table size */
/* 0x14: RPM ??? table size */
/* always LE even on X360/PS3 */
num_samples = read_u32le(0x18, sf);
sample_rate = read_u32le(0x1c, sf);
start_offset = 0x20 +
(read_u32le(0x10, sf) + 1) * 0x04 +
(read_u32le(0x14, sf) + 1) * 0x04;
channel_count = 1;
loop_flag = 0;
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count, loop_flag);
if (!vgmstream) goto fail;
vgmstream->meta_type = meta_GIN;
vgmstream->sample_rate = sample_rate;
vgmstream->num_samples = num_samples;
vgmstream->coding_type = coding_EA_XAS_V0;
vgmstream->layout_type = layout_none;
/* calculate size for TMX */
vgmstream->stream_size = (align_size_to_block(num_samples, 32) / 32) * 0x13;
if (!vgmstream_open_stream(vgmstream, sf, start_offset))
goto fail;
return vgmstream;
fail:
close_vgmstream(vgmstream);
return NULL;
}

View File

@ -1,83 +1,82 @@
#include "meta.h"
#include "../coding/coding.h"
#include "kma9_streamfile.h"
/* KMA9 - Koei Tecmo's interleaved ATRAC9 [Nobunaga no Yabou - Souzou (Vita)] */
VGMSTREAM * init_vgmstream_kma9(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
STREAMFILE* temp_streamFile = NULL;
off_t start_offset;
size_t stream_size, interleave;
int loop_flag, channel_count;
int total_subsongs = 0, target_subsong = streamFile->stream_index;
/* checks */
if ( !check_extensions(streamFile,"km9") )
goto fail;
if (read_32bitBE(0x00,streamFile) != 0x4B4D4139) /* "KMA9" */
goto fail;
start_offset = read_32bitLE(0x04,streamFile);
channel_count = read_16bitLE(0x32,streamFile);
loop_flag = (read_32bitLE(0x28,streamFile) != 0);
total_subsongs = read_32bitLE(0x08,streamFile);
if (target_subsong == 0) target_subsong = 1;
if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail;
/* 0x0c: unknown */
interleave = read_32bitLE(0x10,streamFile); /* 1 superframe */
stream_size = read_32bitLE(0x14,streamFile); /* per subsong */
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count,loop_flag);
if (!vgmstream) goto fail;
vgmstream->sample_rate = read_32bitLE(0x34,streamFile);
vgmstream->num_samples = read_32bitLE(0x18,streamFile); /* without skip_samples? */
vgmstream->loop_start_sample = read_32bitLE(0x24,streamFile); /* with skip_samples? */
vgmstream->loop_end_sample = vgmstream->num_samples; /* 0x28 looks like end samples but isn't, no idea */
vgmstream->num_streams = total_subsongs;
vgmstream->stream_size = stream_size;
vgmstream->meta_type = meta_KMA9;
#ifdef VGM_USE_ATRAC9
{
atrac9_config cfg = {0};
cfg.channels = vgmstream->channels;
cfg.encoder_delay = read_32bitLE(0x20,streamFile);
cfg.config_data = read_32bitBE(0x5c,streamFile);
vgmstream->codec_data = init_atrac9(&cfg);
if (!vgmstream->codec_data) goto fail;
vgmstream->coding_type = coding_ATRAC9;
vgmstream->layout_type = layout_none;
if (loop_flag) { /* seems correct but must double check */
vgmstream->loop_start_sample -= cfg.encoder_delay;
//vgmstream->loop_end_sample -= cfg.encoder_delay;
}
/* KMA9 interleaves one ATRAC9 frame per subsong */
temp_streamFile = setup_kma9_streamfile(streamFile, start_offset, stream_size, interleave, (target_subsong-1), total_subsongs);
if (!temp_streamFile) goto fail;
start_offset = 0;
}
#else
goto fail;
#endif
/* open the file for reading */
if ( !vgmstream_open_stream(vgmstream, temp_streamFile, start_offset) )
goto fail;
close_streamfile(temp_streamFile);
return vgmstream;
fail:
close_streamfile(temp_streamFile);
close_vgmstream(vgmstream);
return NULL;
}
#include "meta.h"
#include "../coding/coding.h"
#include "kma9_streamfile.h"
/* KMA9 - Koei Tecmo games [Nobunaga no Yabou: Souzou (Vita)] */
VGMSTREAM* init_vgmstream_kma9(STREAMFILE* sf) {
VGMSTREAM* vgmstream = NULL;
STREAMFILE* temp_sf = NULL;
off_t start_offset;
size_t stream_size, interleave;
int loop_flag, channels;
int total_subsongs = 0, target_subsong = sf->stream_index;
/* checks */
if (!is_id32be(0x00,sf, "KMA9"))
return NULL;
if (!check_extensions(sf,"km9"))
return NULL;
start_offset = read_u32le(0x04,sf);
channels = read_u16le(0x32,sf);
loop_flag = (read_s32le(0x28,sf) != 0);
total_subsongs = read_s32le(0x08,sf);
if (target_subsong == 0) target_subsong = 1;
if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail;
/* 0x0c: unknown */
interleave = read_u32le(0x10,sf); /* 1 superframe */
stream_size = read_u32le(0x14,sf); /* per subsong */
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channels, loop_flag);
if (!vgmstream) goto fail;
vgmstream->sample_rate = read_s32le(0x34,sf);
vgmstream->num_samples = read_s32le(0x18,sf); /* without skip_samples? */
vgmstream->loop_start_sample = read_s32le(0x24,sf); /* with skip_samples? */
vgmstream->loop_end_sample = vgmstream->num_samples; /* 0x28 looks like end samples but isn't, no idea */
vgmstream->num_streams = total_subsongs;
vgmstream->stream_size = stream_size;
vgmstream->meta_type = meta_KMA9;
#ifdef VGM_USE_ATRAC9
{
atrac9_config cfg = {0};
cfg.channels = channels;
cfg.encoder_delay = read_s32le(0x20,sf);
cfg.config_data = read_u32be(0x5c,sf);
vgmstream->codec_data = init_atrac9(&cfg);
if (!vgmstream->codec_data) goto fail;
vgmstream->coding_type = coding_ATRAC9;
vgmstream->layout_type = layout_none;
if (loop_flag) { /* seems correct but must double check */
vgmstream->loop_start_sample -= cfg.encoder_delay;
//vgmstream->loop_end_sample -= cfg.encoder_delay;
}
/* KMA9 interleaves one ATRAC9 frame per subsong */
temp_sf = setup_kma9_streamfile(sf, start_offset, stream_size, interleave, (target_subsong-1), total_subsongs);
if (!temp_sf) goto fail;
start_offset = 0;
}
#else
goto fail;
#endif
if (!vgmstream_open_stream(vgmstream, temp_sf, start_offset))
goto fail;
close_streamfile(temp_sf);
return vgmstream;
fail:
close_streamfile(temp_sf);
close_vgmstream(vgmstream);
return NULL;
}

View File

@ -477,7 +477,7 @@ VGMSTREAM * init_vgmstream_baf(STREAMFILE* streamFile);
VGMSTREAM * init_vgmstream_msf(STREAMFILE* streamFile);
VGMSTREAM * init_vgmstream_ps3_past(STREAMFILE* streamFile);
VGMSTREAM* init_vgmstream_sndp(STREAMFILE* sf);
VGMSTREAM * init_vgmstream_sgxd(STREAMFILE* streamFile);

View File

@ -1,60 +1,60 @@
#include "meta.h"
#include "../coding/coding.h"
#include "ppst_streamfile.h"
/* PPST - ParaPpa STream (maybe), extracted from .img bigfile [Parappa the Rapper (PSP)] */
VGMSTREAM * init_vgmstream_ppst(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
int total_subsongs, target_subsong = streamFile->stream_index;
/* checks */
if (!check_extensions(streamFile, "sng"))
goto fail;
if (read_32bitBE(0x00,streamFile) != 0x50505354) /* "PPST" */
goto fail;
/* header has some control and comment fields then interleaved RIFF .at3 */
/* count subsongs (mainly 4, rarely 1) */
{
off_t offset = 0xa0;
total_subsongs = 0;
while (offset < 0x800) {
if (read_32bitLE(offset + 0x04, streamFile) == 0) /* subsong size */
break;
total_subsongs++;
offset += 0x08;
}
if (target_subsong == 0) target_subsong = 1;
if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail;
}
{
off_t start_offset = 0x800;
size_t interleave_size = 0x4000;
size_t stride_size = 0x4000*total_subsongs;
/* subsong header at 0xa0, 0x00(1): id, 0x01(3): blocks of interleave */
size_t stream_size = read_32bitLE(0xA0+0x08*(target_subsong-1)+0x04, streamFile);
STREAMFILE* temp_streamFile = setup_ppst_streamfile(streamFile, start_offset+interleave_size*(target_subsong-1), interleave_size, stride_size, stream_size);
if (!temp_streamFile) goto fail;
vgmstream = init_vgmstream_riff(temp_streamFile);
close_streamfile(temp_streamFile);
if (!vgmstream) goto fail;
vgmstream->num_streams = total_subsongs;
vgmstream->stream_size = stream_size;
vgmstream->meta_type = meta_PPST;
}
return vgmstream;
fail:
close_vgmstream(vgmstream);
return NULL;
}
#include "meta.h"
#include "../coding/coding.h"
#include "ppst_streamfile.h"
/* PPST - ParaPpa STream (maybe) [Parappa the Rapper (PSP)] */
VGMSTREAM* init_vgmstream_ppst(STREAMFILE* sf) {
VGMSTREAM* vgmstream = NULL;
int total_subsongs, target_subsong = sf->stream_index;
/* checks */
if (!is_id32be(0x00,sf, "PPST"))
return NULL;
if (!check_extensions(sf, "sng"))
return NULL;
/* header has some control and comment fields then interleaved RIFF .at3 */
/* count subsongs (mainly 4, rarely 1) */
{
off_t offset = 0xa0;
total_subsongs = 0;
while (offset < 0x800) {
if (read_u32le(offset + 0x04, sf) == 0) /* subsong size */
break;
total_subsongs++;
offset += 0x08;
}
if (target_subsong == 0) target_subsong = 1;
if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail;
}
{
off_t start_offset = 0x800;
size_t interleave_size = 0x4000;
size_t stride_size = 0x4000 * total_subsongs;
/* subsong header at 0xa0, 0x00(1): id, 0x01(3): blocks of interleave */
size_t stream_size = read_u32le(0xA0 + 0x08 * (target_subsong - 1) + 0x04, sf);
STREAMFILE* temp_sf = setup_ppst_streamfile(sf, start_offset + interleave_size * (target_subsong - 1), interleave_size, stride_size, stream_size);
if (!temp_sf) goto fail;
vgmstream = init_vgmstream_riff(temp_sf);
close_streamfile(temp_sf);
if (!vgmstream) goto fail;
vgmstream->num_streams = total_subsongs;
vgmstream->stream_size = stream_size;
vgmstream->meta_type = meta_PPST;
}
return vgmstream;
fail:
close_vgmstream(vgmstream);
return NULL;
}

View File

@ -1,101 +1,101 @@
#ifndef _PPST_STREAMFILE_H_
#define _PPST_STREAMFILE_H_
#include "../streamfile.h"
typedef struct {
off_t start_physical_offset; /* interleaved data start, for this substream */
size_t interleave_block_size; /* max size that can be read before encountering other substreams */
size_t stride_size; /* step size between interleave blocks (interleave*channels) */
size_t stream_size; /* final size of the deinterleaved substream */
} ppst_io_data;
/* Handles deinterleaving of complete files, skipping portions or other substreams. */
static size_t ppst_io_read(STREAMFILE *streamfile, uint8_t *dest, off_t offset, size_t length, ppst_io_data* data) {
size_t total_read = 0;
while (length > 0) {
size_t to_read;
size_t length_available;
off_t block_num;
off_t intrablock_offset;
off_t physical_offset;
if (offset >= data->stream_size)
return total_read;
block_num = offset / data->interleave_block_size;
intrablock_offset = offset % data->interleave_block_size;
physical_offset = data->start_physical_offset + block_num*data->stride_size + intrablock_offset;
length_available = data->interleave_block_size - intrablock_offset;
if (length_available > data->stream_size - offset)
length_available = data->stream_size - offset;
if (length < length_available) {
to_read = length;
}
else {
to_read = length_available;
}
if (to_read > 0) {
size_t bytes_read;
bytes_read = read_streamfile(dest, physical_offset, to_read, streamfile);
total_read += bytes_read;
if (bytes_read != to_read) {
return total_read;
}
dest += bytes_read;
offset += bytes_read;
length -= bytes_read;
}
}
return total_read;
}
static size_t ppst_io_size(STREAMFILE *streamfile, ppst_io_data* data) {
return data->stream_size;
}
static STREAMFILE* setup_ppst_streamfile(STREAMFILE *streamFile, off_t start_offset, size_t interleave_block_size, size_t stride_size, size_t stream_size) {
STREAMFILE *temp_streamFile = NULL, *new_streamFile = NULL;
ppst_io_data io_data = {0};
size_t io_data_size = sizeof(ppst_io_data);
io_data.start_physical_offset = start_offset;
io_data.interleave_block_size = interleave_block_size;
io_data.stride_size = stride_size;
io_data.stream_size = stream_size;
/* setup subfile */
new_streamFile = open_wrap_streamfile(streamFile);
if (!new_streamFile) goto fail;
temp_streamFile = new_streamFile;
new_streamFile = open_io_streamfile(temp_streamFile, &io_data,io_data_size, ppst_io_read,ppst_io_size);
if (!new_streamFile) goto fail;
temp_streamFile = new_streamFile;
new_streamFile = open_buffer_streamfile(new_streamFile,0);
if (!new_streamFile) goto fail;
temp_streamFile = new_streamFile;
new_streamFile = open_fakename_streamfile(temp_streamFile, NULL,"at3");
if (!new_streamFile) goto fail;
temp_streamFile = new_streamFile;
return temp_streamFile;
fail:
close_streamfile(temp_streamFile);
return NULL;
}
#endif /* _SCD_STREAMFILE_H_ */
#ifndef _PPST_STREAMFILE_H_
#define _PPST_STREAMFILE_H_
#include "../streamfile.h"
typedef struct {
off_t start_physical_offset; /* interleaved data start, for this substream */
size_t interleave_block_size; /* max size that can be read before encountering other substreams */
size_t stride_size; /* step size between interleave blocks (interleave*channels) */
size_t stream_size; /* final size of the deinterleaved substream */
} ppst_io_data;
/* Handles deinterleaving of complete files, skipping portions or other substreams. */
static size_t ppst_io_read(STREAMFILE *streamfile, uint8_t *dest, off_t offset, size_t length, ppst_io_data* data) {
size_t total_read = 0;
while (length > 0) {
size_t to_read;
size_t length_available;
off_t block_num;
off_t intrablock_offset;
off_t physical_offset;
if (offset >= data->stream_size)
return total_read;
block_num = offset / data->interleave_block_size;
intrablock_offset = offset % data->interleave_block_size;
physical_offset = data->start_physical_offset + block_num*data->stride_size + intrablock_offset;
length_available = data->interleave_block_size - intrablock_offset;
if (length_available > data->stream_size - offset)
length_available = data->stream_size - offset;
if (length < length_available) {
to_read = length;
}
else {
to_read = length_available;
}
if (to_read > 0) {
size_t bytes_read;
bytes_read = read_streamfile(dest, physical_offset, to_read, streamfile);
total_read += bytes_read;
if (bytes_read != to_read) {
return total_read;
}
dest += bytes_read;
offset += bytes_read;
length -= bytes_read;
}
}
return total_read;
}
static size_t ppst_io_size(STREAMFILE *streamfile, ppst_io_data* data) {
return data->stream_size;
}
static STREAMFILE* setup_ppst_streamfile(STREAMFILE *streamFile, off_t start_offset, size_t interleave_block_size, size_t stride_size, size_t stream_size) {
STREAMFILE *temp_streamFile = NULL, *new_streamFile = NULL;
ppst_io_data io_data = {0};
size_t io_data_size = sizeof(ppst_io_data);
io_data.start_physical_offset = start_offset;
io_data.interleave_block_size = interleave_block_size;
io_data.stride_size = stride_size;
io_data.stream_size = stream_size;
/* setup subfile */
new_streamFile = open_wrap_streamfile(streamFile);
if (!new_streamFile) goto fail;
temp_streamFile = new_streamFile;
new_streamFile = open_io_streamfile(temp_streamFile, &io_data,io_data_size, ppst_io_read,ppst_io_size);
if (!new_streamFile) goto fail;
temp_streamFile = new_streamFile;
new_streamFile = open_buffer_streamfile(new_streamFile,0);
if (!new_streamFile) goto fail;
temp_streamFile = new_streamFile;
new_streamFile = open_fakename_streamfile(temp_streamFile, NULL,"at3");
if (!new_streamFile) goto fail;
temp_streamFile = new_streamFile;
return temp_streamFile;
fail:
close_streamfile(temp_streamFile);
return NULL;
}
#endif /* _SCD_STREAMFILE_H_ */

View File

@ -1,73 +0,0 @@
#include "meta.h"
#include "../util.h"
/* .PAST (Bakugan Battle Brawlers */
VGMSTREAM * init_vgmstream_ps3_past(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
char filename[PATH_LIMIT];
off_t start_offset;
int loop_flag;
int channel_count;
/* check extension, case insensitive */
streamFile->get_name(streamFile,filename,sizeof(filename));
if (strcasecmp("past",filename_extension(filename))) goto fail;
/* check header */
if (read_32bitBE(0x0,streamFile) != 0x534E4450) /* SNDP */
goto fail;
loop_flag = (read_32bitBE(0x1C,streamFile)!=0);
channel_count = (uint16_t)read_16bitBE(0xC,streamFile);
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count,loop_flag);
if (!vgmstream) goto fail;
/* fill in the vital statistics */
start_offset = 0x30;
vgmstream->channels = channel_count;
vgmstream->sample_rate = read_32bitBE(0x10,streamFile);
vgmstream->coding_type = coding_PCM16LE;
vgmstream->num_samples = (read_32bitBE(0x14,streamFile))/2/channel_count;
if (loop_flag) {
vgmstream->loop_start_sample = read_32bitBE(0x18,streamFile)/2/channel_count;
vgmstream->loop_end_sample = read_32bitBE(0x1C,streamFile)/2/channel_count;
}
if (channel_count == 1)
{
vgmstream->layout_type = layout_none;
}
else
{
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = 0x2;
}
vgmstream->meta_type = meta_PS3_PAST;
/* open the file for reading */
{
int i;
STREAMFILE * file;
file = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE);
if (!file) goto fail;
for (i=0;i<channel_count;i++) {
vgmstream->ch[i].streamfile = file;
vgmstream->ch[i].channel_start_offset=
vgmstream->ch[i].offset=start_offset+
vgmstream->interleave_block_size*i;
}
}
return vgmstream;
fail:
/* clean up anything we may have opened */
if (vgmstream) close_vgmstream(vgmstream);
return NULL;
}

View File

@ -30,7 +30,7 @@ VGMSTREAM* init_vgmstream_rstm_rockstar(STREAMFILE* sf) {
//loop_flag = (read_s32le(0x24,sf) > 0);
loop_flag = loop_end != stream_size;
/* build the VGMSTREAM */
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channels, loop_flag);
if (!vgmstream) goto fail;

40
src/meta/sndp.c Normal file
View File

@ -0,0 +1,40 @@
#include "meta.h"
#include "../util.h"
#include "../coding/coding.h"
/* SNDP - from from Premium Agency games [Bakugan Battle Brawlers (PS3)] */
VGMSTREAM* init_vgmstream_sndp(STREAMFILE* sf) {
VGMSTREAM* vgmstream = NULL;
/* checks */
if (!is_id32be(0x00,sf, "SNDP"))
return NULL;
if (!check_extensions(sf,"past"))
return NULL;
bool loop_flag = (read_u32be(0x1c,sf) !=0);
int channels = read_u16be(0x0c,sf);
off_t start_offset = 0x30;
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channels, loop_flag);
if (!vgmstream) goto fail;
vgmstream->sample_rate = read_u32be(0x10,sf);
vgmstream->num_samples = pcm16_bytes_to_samples(read_u32be(0x14,sf), channels);
vgmstream->loop_start_sample = pcm16_bytes_to_samples(read_u32be(0x18,sf), channels);
vgmstream->loop_end_sample = pcm16_bytes_to_samples(read_u32be(0x1C,sf), channels);
vgmstream->coding_type = coding_PCM16LE;
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = 0x02;
vgmstream->meta_type = meta_SNDP;
if (!vgmstream_open_stream(vgmstream, sf, start_offset))
goto fail;
return vgmstream;
fail:
close_vgmstream(vgmstream);
return NULL;
}

View File

@ -1,163 +1,163 @@
#ifndef _XVAG_STREAMFILE_H_
#define _XVAG_STREAMFILE_H_
#include "../streamfile.h"
typedef struct {
/* config */
int stream_number;
int stream_count;
size_t interleave_size;
size_t frame_size;
off_t stream_offset;
//size_t stream_size;
/* state */
off_t logical_offset; /* offset that corresponds to physical_offset */
off_t physical_offset; /* actual file offset */
size_t skip_size; /* size to skip from a block start to reach data start */
size_t data_size; /* logical size of the block */
size_t logical_size;
} xvag_io_data;
static size_t xvag_io_read(STREAMFILE *streamfile, uint8_t *dest, off_t offset, size_t length, xvag_io_data* data) {
size_t total_read = 0;
/* ignore bad reads */
if (offset < 0 || offset > data->logical_size) {
return 0;
}
/* previous offset: re-start as we can't map logical<>physical offsets
* (kinda slow as it trashes buffers, but shouldn't happen often) */
if (offset < data->logical_offset) {
data->logical_offset = 0x00;
data->physical_offset = data->stream_offset;
data->data_size = 0;
}
/* read blocks, one at a time */
while (length > 0) {
/* ignore EOF */
if (data->logical_offset >= data->logical_size) {
break;
}
/* process new block */
if (data->data_size == 0) {
data->skip_size = data->interleave_size * data->stream_number;
data->data_size = data->interleave_size;
/* some ATRAC9 XVAG have padding+RIFF at start [The Last of Us (PS4), Farpoint (PS4)] */
if (data->logical_offset == 0 && read_32bitBE(data->physical_offset+data->skip_size,streamfile) == 0) {
data->skip_size += data->frame_size;
data->data_size -= data->frame_size;
}
}
/* move to next block */
if (offset >= data->logical_offset + data->data_size) {
data->physical_offset += data->interleave_size*data->stream_count;
data->logical_offset += data->data_size;
data->data_size = 0;
continue;
}
/* read data */
{
size_t bytes_consumed, bytes_done, to_read;
bytes_consumed = offset - data->logical_offset;
to_read = data->data_size - bytes_consumed;
if (to_read > length)
to_read = length;
bytes_done = read_streamfile(dest, data->physical_offset + data->skip_size + bytes_consumed, to_read, streamfile);
offset += bytes_done;
total_read += bytes_done;
length -= bytes_done;
dest += bytes_done;
if (bytes_done != to_read || bytes_done == 0) {
break; /* error/EOF */
}
}
}
return total_read;
}
static size_t xvag_io_size(STREAMFILE *streamfile, xvag_io_data* data) {
off_t physical_offset = data->stream_offset;
off_t max_physical_offset = get_streamfile_size(streamfile);
size_t logical_size = 0;
if (data->logical_size)
return data->logical_size;
/* get size of the logical stream */
while (physical_offset < max_physical_offset) {
size_t skip_size = data->interleave_size * data->stream_number;
size_t data_size = data->interleave_size;
/* some ATRAC9 XVAG have padding+RIFF at start [The Last of Us (PS4), Farpoint (PS4)] */
if (logical_size == 0 && read_32bitBE(physical_offset+skip_size,streamfile) == 0) {
skip_size += data->frame_size;
data_size -= data->frame_size;
}
logical_size += data_size;
physical_offset += data->interleave_size*data->stream_count;
}
if (logical_size > max_physical_offset)
return 0;
data->logical_size = logical_size;
return data->logical_size;
}
/* Prepares custom IO for XVAG, which interleaves many superframes per subsong/layer.
* May have start padding, even with only one subsong. All layers share config_data too. */
static STREAMFILE* setup_xvag_streamfile(STREAMFILE *streamFile, off_t stream_offset, size_t interleave_size, size_t frame_size, int stream_number, int stream_count) {
STREAMFILE *temp_streamFile = NULL, *new_streamFile = NULL;
xvag_io_data io_data = {0};
size_t io_data_size = sizeof(xvag_io_data);
io_data.stream_number = stream_number;
io_data.stream_count = stream_count;
io_data.stream_offset = stream_offset;
//io_data.stream_size = stream_size;
io_data.interleave_size = interleave_size;
io_data.frame_size = frame_size;
io_data.physical_offset = stream_offset;
io_data.logical_size = xvag_io_size(streamFile, &io_data); /* force init */
if (io_data.logical_size == 0) {
VGM_LOG("XVAG: wrong logical size\n");
goto fail;
}
/* setup subfile */
new_streamFile = open_wrap_streamfile(streamFile);
if (!new_streamFile) goto fail;
temp_streamFile = new_streamFile;
new_streamFile = open_io_streamfile(temp_streamFile, &io_data,io_data_size, xvag_io_read,xvag_io_size);
if (!new_streamFile) goto fail;
temp_streamFile = new_streamFile;
return temp_streamFile;
fail:
close_streamfile(temp_streamFile);
return NULL;
}
#endif /* _XVAG_STREAMFILE_H_ */
#ifndef _XVAG_STREAMFILE_H_
#define _XVAG_STREAMFILE_H_
#include "../streamfile.h"
typedef struct {
/* config */
int stream_number;
int stream_count;
size_t interleave_size;
size_t frame_size;
off_t stream_offset;
//size_t stream_size;
/* state */
off_t logical_offset; /* offset that corresponds to physical_offset */
off_t physical_offset; /* actual file offset */
size_t skip_size; /* size to skip from a block start to reach data start */
size_t data_size; /* logical size of the block */
size_t logical_size;
} xvag_io_data;
static size_t xvag_io_read(STREAMFILE *streamfile, uint8_t *dest, off_t offset, size_t length, xvag_io_data* data) {
size_t total_read = 0;
/* ignore bad reads */
if (offset < 0 || offset > data->logical_size) {
return 0;
}
/* previous offset: re-start as we can't map logical<>physical offsets
* (kinda slow as it trashes buffers, but shouldn't happen often) */
if (offset < data->logical_offset) {
data->logical_offset = 0x00;
data->physical_offset = data->stream_offset;
data->data_size = 0;
}
/* read blocks, one at a time */
while (length > 0) {
/* ignore EOF */
if (data->logical_offset >= data->logical_size) {
break;
}
/* process new block */
if (data->data_size == 0) {
data->skip_size = data->interleave_size * data->stream_number;
data->data_size = data->interleave_size;
/* some ATRAC9 XVAG have padding+RIFF at start [The Last of Us (PS4), Farpoint (PS4)] */
if (data->logical_offset == 0 && read_32bitBE(data->physical_offset+data->skip_size,streamfile) == 0) {
data->skip_size += data->frame_size;
data->data_size -= data->frame_size;
}
}
/* move to next block */
if (offset >= data->logical_offset + data->data_size) {
data->physical_offset += data->interleave_size*data->stream_count;
data->logical_offset += data->data_size;
data->data_size = 0;
continue;
}
/* read data */
{
size_t bytes_consumed, bytes_done, to_read;
bytes_consumed = offset - data->logical_offset;
to_read = data->data_size - bytes_consumed;
if (to_read > length)
to_read = length;
bytes_done = read_streamfile(dest, data->physical_offset + data->skip_size + bytes_consumed, to_read, streamfile);
offset += bytes_done;
total_read += bytes_done;
length -= bytes_done;
dest += bytes_done;
if (bytes_done != to_read || bytes_done == 0) {
break; /* error/EOF */
}
}
}
return total_read;
}
static size_t xvag_io_size(STREAMFILE *streamfile, xvag_io_data* data) {
off_t physical_offset = data->stream_offset;
off_t max_physical_offset = get_streamfile_size(streamfile);
size_t logical_size = 0;
if (data->logical_size)
return data->logical_size;
/* get size of the logical stream */
while (physical_offset < max_physical_offset) {
size_t skip_size = data->interleave_size * data->stream_number;
size_t data_size = data->interleave_size;
/* some ATRAC9 XVAG have padding+RIFF at start [The Last of Us (PS4), Farpoint (PS4)] */
if (logical_size == 0 && read_32bitBE(physical_offset+skip_size,streamfile) == 0) {
skip_size += data->frame_size;
data_size -= data->frame_size;
}
logical_size += data_size;
physical_offset += data->interleave_size*data->stream_count;
}
if (logical_size > max_physical_offset)
return 0;
data->logical_size = logical_size;
return data->logical_size;
}
/* Prepares custom IO for XVAG, which interleaves many superframes per subsong/layer.
* May have start padding, even with only one subsong. All layers share config_data too. */
static STREAMFILE* setup_xvag_streamfile(STREAMFILE *streamFile, off_t stream_offset, size_t interleave_size, size_t frame_size, int stream_number, int stream_count) {
STREAMFILE *temp_streamFile = NULL, *new_streamFile = NULL;
xvag_io_data io_data = {0};
size_t io_data_size = sizeof(xvag_io_data);
io_data.stream_number = stream_number;
io_data.stream_count = stream_count;
io_data.stream_offset = stream_offset;
//io_data.stream_size = stream_size;
io_data.interleave_size = interleave_size;
io_data.frame_size = frame_size;
io_data.physical_offset = stream_offset;
io_data.logical_size = xvag_io_size(streamFile, &io_data); /* force init */
if (io_data.logical_size == 0) {
VGM_LOG("XVAG: wrong logical size\n");
goto fail;
}
/* setup subfile */
new_streamFile = open_wrap_streamfile(streamFile);
if (!new_streamFile) goto fail;
temp_streamFile = new_streamFile;
new_streamFile = open_io_streamfile(temp_streamFile, &io_data,io_data_size, xvag_io_read,xvag_io_size);
if (!new_streamFile) goto fail;
temp_streamFile = new_streamFile;
return temp_streamFile;
fail:
close_streamfile(temp_streamFile);
return NULL;
}
#endif /* _XVAG_STREAMFILE_H_ */

View File

@ -1,58 +1,58 @@
#ifndef _ZSND_STREAMFILE_H_
#define _ZSND_STREAMFILE_H_
#include "../streamfile.h"
typedef struct {
off_t max_offset;
} zsnd_io_data;
static size_t zsnd_io_read(STREAMFILE *streamfile, uint8_t *dest, off_t offset, size_t length, zsnd_io_data* data) {
size_t bytes_read, bytes_to_do;
int i;
/* clamp reads */
bytes_to_do = length;
if (offset > data->max_offset)
offset = data->max_offset;
if (offset + length > data->max_offset)
bytes_to_do = data->max_offset - offset;
bytes_read = streamfile->read(streamfile, dest, offset, bytes_to_do);
/* pretend we got data after max_offset */
if (bytes_read < length) {
for (i = bytes_read; i < length; i++) {
dest[i] = 0;
}
bytes_read = length;
}
return bytes_read;
}
/* ZSND removes last interleave data from the file if blank, but codecs still need to read after it.
* This data could be from next stream or from EOF, so return blank data instead. */
static STREAMFILE* setup_zsnd_streamfile(STREAMFILE *streamFile, off_t start_offset, size_t stream_size) {
STREAMFILE *temp_streamFile = NULL, *new_streamFile = NULL;
zsnd_io_data io_data = {0};
size_t io_data_size = sizeof(zsnd_io_data);
io_data.max_offset = start_offset + stream_size;
/* setup custom streamfile */
new_streamFile = open_wrap_streamfile(streamFile);
if (!new_streamFile) goto fail;
temp_streamFile = new_streamFile;
new_streamFile = open_io_streamfile(temp_streamFile, &io_data,io_data_size, zsnd_io_read,NULL);
if (!new_streamFile) goto fail;
temp_streamFile = new_streamFile;
return temp_streamFile;
fail:
close_streamfile(temp_streamFile);
return NULL;
}
#endif /* _ZSND_STREAMFILE_H_ */
#ifndef _ZSND_STREAMFILE_H_
#define _ZSND_STREAMFILE_H_
#include "../streamfile.h"
typedef struct {
off_t max_offset;
} zsnd_io_data;
static size_t zsnd_io_read(STREAMFILE *streamfile, uint8_t *dest, off_t offset, size_t length, zsnd_io_data* data) {
size_t bytes_read, bytes_to_do;
int i;
/* clamp reads */
bytes_to_do = length;
if (offset > data->max_offset)
offset = data->max_offset;
if (offset + length > data->max_offset)
bytes_to_do = data->max_offset - offset;
bytes_read = streamfile->read(streamfile, dest, offset, bytes_to_do);
/* pretend we got data after max_offset */
if (bytes_read < length) {
for (i = bytes_read; i < length; i++) {
dest[i] = 0;
}
bytes_read = length;
}
return bytes_read;
}
/* ZSND removes last interleave data from the file if blank, but codecs still need to read after it.
* This data could be from next stream or from EOF, so return blank data instead. */
static STREAMFILE* setup_zsnd_streamfile(STREAMFILE *streamFile, off_t start_offset, size_t stream_size) {
STREAMFILE *temp_streamFile = NULL, *new_streamFile = NULL;
zsnd_io_data io_data = {0};
size_t io_data_size = sizeof(zsnd_io_data);
io_data.max_offset = start_offset + stream_size;
/* setup custom streamfile */
new_streamFile = open_wrap_streamfile(streamFile);
if (!new_streamFile) goto fail;
temp_streamFile = new_streamFile;
new_streamFile = open_io_streamfile(temp_streamFile, &io_data,io_data_size, zsnd_io_read,NULL);
if (!new_streamFile) goto fail;
temp_streamFile = new_streamFile;
return temp_streamFile;
fail:
close_streamfile(temp_streamFile);
return NULL;
}
#endif /* _ZSND_STREAMFILE_H_ */

File diff suppressed because it is too large Load Diff

View File

@ -164,7 +164,7 @@ typedef struct {
layout_t layout_type; /* type of layout */
meta_t meta_type; /* type of metadata */
/* loopin config */
/* loop config */
int loop_flag; /* is this stream looped? */
int32_t loop_start_sample; /* first sample of the loop (included in the loop) */
int32_t loop_end_sample; /* last sample of the loop (not included in the loop) */
@ -186,7 +186,7 @@ typedef struct {
uint32_t channel_layout; /* order: FL FR FC LFE BL BR FLC FRC BC SL SR etc (WAVEFORMATEX flags where FL=lowest bit set) */
/* other config */
int allow_dual_stereo; /* search for dual stereo (file_L.ext + file_R.ext = single stereo file) */
bool allow_dual_stereo; /* search for dual stereo (file_L.ext + file_R.ext = single stereo file) */
/* layout/block state */
@ -205,7 +205,7 @@ typedef struct {
size_t loop_block_size; /* saved from current_block_size */
int32_t loop_block_samples; /* saved from current_block_samples */
off_t loop_next_block_offset; /* saved from next_block_offset */
int hit_loop; /* save config when loop is hit, but first time only */
bool hit_loop; /* save config when loop is hit, but first time only */
/* decoder config/state */
@ -221,7 +221,7 @@ typedef struct {
VGMSTREAMCHANNEL* loop_ch; /* shallow copy of channels as they were at the loop point (for loops) */
void* start_vgmstream; /* shallow copy of the VGMSTREAM as it was at the beginning of the stream (for resets) */
void* mixing_data; /* state for mixing effects */
void* mixer; /* state for mixing effects */
/* Optional data the codec needs for the whole stream. This is for codecs too
* different from vgmstream's structure to be reasonably shoehorned.
@ -233,7 +233,7 @@ typedef struct {
/* play config/state */
int config_enabled; /* config can be used */
bool config_enabled; /* config can be used */
play_config_t config; /* player config (applied over decoding) */
play_state_t pstate; /* player state (applied over decoding) */
int loop_count; /* counter of complete loops (1=looped once) */

600
src/vgmstream_init.c Normal file
View File

@ -0,0 +1,600 @@
#include "vgmstream_init.h"
//typedef VGMSTREAM* (*init_vgmstream_t)(STREAMFILE*);
/* list of metadata parser functions that will recognize files, used on init */
init_vgmstream_t init_vgmstream_functions[] = {
init_vgmstream_adx,
init_vgmstream_brstm,
init_vgmstream_brwav,
init_vgmstream_bfwav,
init_vgmstream_bcwav,
init_vgmstream_brwar,
init_vgmstream_nds_strm,
init_vgmstream_afc,
init_vgmstream_ast,
init_vgmstream_halpst,
init_vgmstream_rs03,
init_vgmstream_ngc_dsp_std,
init_vgmstream_ngc_dsp_std_le,
init_vgmstream_ngc_mdsp_std,
init_vgmstream_csmp,
init_vgmstream_rfrm,
init_vgmstream_cstr,
init_vgmstream_gcsw,
init_vgmstream_ads,
init_vgmstream_npsf,
init_vgmstream_xa,
init_vgmstream_rxws,
init_vgmstream_ngc_dsp_stm,
init_vgmstream_exst,
init_vgmstream_svag_kcet,
init_vgmstream_ngc_mpdsp,
init_vgmstream_ngc_dsp_std_int,
init_vgmstream_vag,
init_vgmstream_vag_aaap,
init_vgmstream_vag_footer,
init_vgmstream_vag_evolution_games,
init_vgmstream_ild,
init_vgmstream_ngc_str,
init_vgmstream_ea_schl,
init_vgmstream_caf,
init_vgmstream_vpk,
init_vgmstream_genh,
init_vgmstream_ogg_vorbis,
init_vgmstream_sfl_ogg,
init_vgmstream_sadb,
init_vgmstream_ps2_bmdx,
init_vgmstream_wsi,
init_vgmstream_aifc,
init_vgmstream_str_snds,
init_vgmstream_ws_aud,
init_vgmstream_ahx,
init_vgmstream_iivb,
init_vgmstream_svs,
init_vgmstream_riff,
init_vgmstream_rifx,
init_vgmstream_nwa,
init_vgmstream_ea_1snh,
init_vgmstream_ea_eacs,
init_vgmstream_xss,
init_vgmstream_sl3,
init_vgmstream_hgc1,
init_vgmstream_aus,
init_vgmstream_rws,
init_vgmstream_fsb,
init_vgmstream_fsb5,
init_vgmstream_rwax,
init_vgmstream_xwb,
init_vgmstream_ps2_xa30,
init_vgmstream_musc,
init_vgmstream_musx,
init_vgmstream_filp,
init_vgmstream_ikm,
init_vgmstream_ster,
init_vgmstream_bg00,
init_vgmstream_sat_dvi,
init_vgmstream_dc_kcey,
init_vgmstream_rstm_rockstar,
init_vgmstream_acm,
init_vgmstream_mus_acm,
init_vgmstream_vig_kces,
init_vgmstream_hxd,
init_vgmstream_vsv,
init_vgmstream_ps2_pcm,
init_vgmstream_ps2_rkv,
init_vgmstream_lp_ap_lep,
init_vgmstream_sdt,
init_vgmstream_aix,
init_vgmstream_wvs_xbox,
init_vgmstream_wvs_ngc,
init_vgmstream_str_sega,
init_vgmstream_str_sega_custom,
init_vgmstream_dec,
init_vgmstream_vs,
init_vgmstream_xmu,
init_vgmstream_xvas,
init_vgmstream_sat_sap,
init_vgmstream_dc_idvi,
init_vgmstream_ps2_rnd,
init_vgmstream_idsp_tt,
init_vgmstream_kraw,
init_vgmstream_omu,
init_vgmstream_xa2_acclaim,
init_vgmstream_idsp_nl,
init_vgmstream_idsp_ie,
init_vgmstream_ymf,
init_vgmstream_sadl,
init_vgmstream_fag,
init_vgmstream_mic,
init_vgmstream_ngc_pdt_split,
init_vgmstream_ngc_pdt,
init_vgmstream_mus_krome,
init_vgmstream_spsd,
init_vgmstream_rsd,
init_vgmstream_bgw,
init_vgmstream_spw,
init_vgmstream_ps2_ass,
init_vgmstream_ubi_jade,
init_vgmstream_ubi_jade_container,
init_vgmstream_seg,
init_vgmstream_nds_strm_ffta2,
init_vgmstream_knon,
init_vgmstream_gca,
init_vgmstream_spt_spd,
init_vgmstream_ish_isd,
init_vgmstream_gsnd,
init_vgmstream_ydsp,
init_vgmstream_ssm,
init_vgmstream_ps2_joe,
init_vgmstream_vgs,
init_vgmstream_dcs_wav,
init_vgmstream_mul,
init_vgmstream_thp,
init_vgmstream_sts,
init_vgmstream_p2bt_move_visa,
init_vgmstream_gbts,
init_vgmstream_wii_sng,
init_vgmstream_ngc_dsp_iadp,
init_vgmstream_aax,
init_vgmstream_utf_dsp,
init_vgmstream_ngc_ffcc_str,
init_vgmstream_sat_baka,
init_vgmstream_swav,
init_vgmstream_vsf,
init_vgmstream_nds_rrds,
init_vgmstream_ps2_vsf_tta,
init_vgmstream_ads_midway,
init_vgmstream_ps2_mcg,
init_vgmstream_zsd,
init_vgmstream_vgs_ps,
init_vgmstream_redspark,
init_vgmstream_wii_wsd,
init_vgmstream_dsp_ndp,
init_vgmstream_ps2_sps,
init_vgmstream_nds_hwas,
init_vgmstream_ngc_lps,
init_vgmstream_ps2_snd,
init_vgmstream_naomi_adpcm,
init_vgmstream_sd9,
init_vgmstream_2dx9,
init_vgmstream_dsp_ygo,
init_vgmstream_ps2_vgv,
init_vgmstream_gcub,
init_vgmstream_maxis_xa,
init_vgmstream_ngc_sck_dsp,
init_vgmstream_apple_caff,
init_vgmstream_pc_mxst,
init_vgmstream_sab,
init_vgmstream_bns,
init_vgmstream_wii_was,
init_vgmstream_pona_3do,
init_vgmstream_pona_psx,
init_vgmstream_xbox_hlwav,
init_vgmstream_myspd,
init_vgmstream_his,
init_vgmstream_ast_mmv,
init_vgmstream_ast_mv,
init_vgmstream_dmsg,
init_vgmstream_ngc_dsp_aaap,
init_vgmstream_ngc_dsp_konami,
init_vgmstream_wb,
init_vgmstream_bnsf,
init_vgmstream_ps2_gcm,
init_vgmstream_smpl,
init_vgmstream_msa,
init_vgmstream_voi,
init_vgmstream_ngc_rkv,
init_vgmstream_dsp_ddsp,
init_vgmstream_p3d,
init_vgmstream_ngc_dsp_mpds,
init_vgmstream_dsp_str_ig,
init_vgmstream_ea_swvr,
init_vgmstream_dsp_xiii,
init_vgmstream_dsp_cabelas,
init_vgmstream_lpcm_shade,
init_vgmstream_ps2_vms,
init_vgmstream_xau,
init_vgmstream_bar,
init_vgmstream_dsp_dspw,
init_vgmstream_jstm,
init_vgmstream_xvag,
init_vgmstream_cps,
init_vgmstream_sqex_scd,
init_vgmstream_ngc_nst_dsp,
init_vgmstream_baf,
init_vgmstream_msf,
init_vgmstream_sndp,
init_vgmstream_sgxd,
init_vgmstream_wii_ras,
init_vgmstream_spm,
init_vgmstream_ps2_iab,
init_vgmstream_vs_str,
init_vgmstream_lsf_n1nj4n,
init_vgmstream_xwav_new,
init_vgmstream_xwav_old,
init_vgmstream_hyperscan_kvag,
init_vgmstream_psnd,
init_vgmstream_adp_wildfire,
init_vgmstream_adp_qd,
init_vgmstream_eb_sfx,
init_vgmstream_eb_sf0,
init_vgmstream_mtaf,
init_vgmstream_alp,
init_vgmstream_wpd,
init_vgmstream_mn_str,
init_vgmstream_mss,
init_vgmstream_ps2_hsf,
init_vgmstream_ivag,
init_vgmstream_2pfs,
init_vgmstream_xnb,
init_vgmstream_ubi_ckd,
init_vgmstream_ps2_vbk,
init_vgmstream_otm,
init_vgmstream_bcstm,
init_vgmstream_idsp_namco,
init_vgmstream_kt_g1l,
init_vgmstream_kt_wiibgm,
init_vgmstream_bfstm,
init_vgmstream_mca,
#if 0
init_vgmstream_mp4_aac,
#endif
#if defined(VGM_USE_MP4V2) && defined(VGM_USE_FDKAAC)
init_vgmstream_akb_mp4,
#endif
init_vgmstream_ktss,
init_vgmstream_hca,
init_vgmstream_svag_snk,
init_vgmstream_ps2_vds_vdm,
init_vgmstream_cxs,
init_vgmstream_adx_monster,
init_vgmstream_akb,
init_vgmstream_akb2,
#ifdef VGM_USE_FFMPEG
init_vgmstream_mp4_aac_ffmpeg,
#endif
init_vgmstream_bik,
init_vgmstream_astb,
init_vgmstream_wwise,
init_vgmstream_ubi_raki,
init_vgmstream_pasx,
init_vgmstream_xma,
init_vgmstream_sndx,
init_vgmstream_ogl,
init_vgmstream_mc3,
init_vgmstream_ghs,
init_vgmstream_aac_triace,
init_vgmstream_va3,
init_vgmstream_mta2,
init_vgmstream_mta2_container,
init_vgmstream_xa_xa30,
init_vgmstream_xa_04sw,
init_vgmstream_ea_bnk,
init_vgmstream_ea_abk_schl,
init_vgmstream_ea_amb_schl,
init_vgmstream_ea_hdr_dat,
init_vgmstream_ea_hdr_dat_v2,
init_vgmstream_ea_map_mus,
init_vgmstream_ea_mpf_mus_schl,
init_vgmstream_ea_msb_mus_schl,
init_vgmstream_ea_schl_fixed,
init_vgmstream_sk_aud,
init_vgmstream_stma,
init_vgmstream_ea_snu,
init_vgmstream_awc,
init_vgmstream_opus_std,
init_vgmstream_opus_n1,
init_vgmstream_opus_capcom,
init_vgmstream_opus_nop,
init_vgmstream_opus_shinen,
init_vgmstream_opus_nus3,
init_vgmstream_opus_sps_n1,
init_vgmstream_pc_ast,
init_vgmstream_naac,
init_vgmstream_ubi_sb,
init_vgmstream_ubi_sm,
init_vgmstream_ubi_bnm,
init_vgmstream_ubi_bnm_ps2,
init_vgmstream_ubi_dat,
init_vgmstream_ubi_blk,
init_vgmstream_ezw,
init_vgmstream_vxn,
init_vgmstream_ea_snr_sns,
init_vgmstream_ea_sps,
init_vgmstream_ea_abk_eaac,
init_vgmstream_ea_amb_eaac,
init_vgmstream_ea_hdr_sth_dat,
init_vgmstream_ea_mpf_mus_eaac,
init_vgmstream_ea_msb_mus_eaac,
init_vgmstream_ea_tmx,
init_vgmstream_ea_sbr,
init_vgmstream_ea_sbr_harmony,
init_vgmstream_vid1,
init_vgmstream_flx,
init_vgmstream_mogg,
init_vgmstream_kma9,
init_vgmstream_xwc,
init_vgmstream_atsl,
init_vgmstream_sps_n1,
init_vgmstream_apa3,
init_vgmstream_sqex_sead,
init_vgmstream_waf,
init_vgmstream_wave,
init_vgmstream_wave_segmented,
init_vgmstream_smv,
init_vgmstream_nxap,
init_vgmstream_ea_wve_au00,
init_vgmstream_ea_wve_ad10,
init_vgmstream_sthd,
init_vgmstream_pcm_sre,
init_vgmstream_dsp_mcadpcm,
init_vgmstream_ubi_lyn,
init_vgmstream_ubi_lyn_container,
init_vgmstream_msb_msh,
init_vgmstream_txtp,
init_vgmstream_smc_smh,
init_vgmstream_ppst,
init_vgmstream_sps_n1_segmented,
init_vgmstream_ubi_bao_pk,
init_vgmstream_ubi_bao_atomic,
init_vgmstream_dsp_switch_audio,
init_vgmstream_sadf,
init_vgmstream_h4m,
init_vgmstream_ads_container,
init_vgmstream_asf,
init_vgmstream_xmd,
init_vgmstream_cks,
init_vgmstream_ckb,
init_vgmstream_wv6,
init_vgmstream_str_wav,
init_vgmstream_wavebatch,
init_vgmstream_hd3_bd3,
init_vgmstream_bnk_sony,
init_vgmstream_nus3bank,
init_vgmstream_sscf,
init_vgmstream_dsp_sps_n1,
init_vgmstream_dsp_itl_ch,
init_vgmstream_a2m,
init_vgmstream_ahv,
init_vgmstream_msv,
init_vgmstream_sdf,
init_vgmstream_svg,
init_vgmstream_vai,
init_vgmstream_aif_asobo,
init_vgmstream_ao,
init_vgmstream_apc,
init_vgmstream_wv2,
init_vgmstream_xau_konami,
init_vgmstream_derf,
init_vgmstream_utk,
init_vgmstream_nxa1,
init_vgmstream_adpcm_capcom,
init_vgmstream_ue4opus,
init_vgmstream_xwma,
init_vgmstream_xopus,
init_vgmstream_vs_square,
init_vgmstream_msf_banpresto_wmsf,
init_vgmstream_msf_banpresto_2msf,
init_vgmstream_nwav,
init_vgmstream_xpcm,
init_vgmstream_msf_tamasoft,
init_vgmstream_xps_dat,
init_vgmstream_xps,
init_vgmstream_zsnd,
init_vgmstream_opus_opusx,
init_vgmstream_dsp_adpy,
init_vgmstream_dsp_adpx,
init_vgmstream_ogg_opus,
init_vgmstream_nus3audio,
init_vgmstream_imc,
init_vgmstream_imc_container,
init_vgmstream_smp,
init_vgmstream_gin,
init_vgmstream_dsf,
init_vgmstream_208,
init_vgmstream_dsp_lucasarts_ds2,
init_vgmstream_ffdl,
init_vgmstream_mus_vc,
init_vgmstream_strm_abylight,
init_vgmstream_sfh,
init_vgmstream_ea_schl_video,
init_vgmstream_msf_konami,
init_vgmstream_xwma_konami,
init_vgmstream_9tav,
init_vgmstream_fsb5_fev_bank,
init_vgmstream_bwav,
init_vgmstream_opus_prototype,
init_vgmstream_awb,
init_vgmstream_acb,
init_vgmstream_rad,
init_vgmstream_smk,
init_vgmstream_mzrt_v0,
init_vgmstream_xavs,
init_vgmstream_psf_single,
init_vgmstream_psf_segmented,
init_vgmstream_dsp_itl,
init_vgmstream_sch,
init_vgmstream_ima,
init_vgmstream_nub,
init_vgmstream_nub_wav,
init_vgmstream_nub_vag,
init_vgmstream_nub_at3,
init_vgmstream_nub_xma,
init_vgmstream_nub_idsp,
init_vgmstream_nub_is14,
init_vgmstream_xwv_valve,
init_vgmstream_ubi_hx,
init_vgmstream_bmp_konami,
init_vgmstream_opus_opusnx,
init_vgmstream_opus_sqex,
init_vgmstream_isb,
init_vgmstream_xssb,
init_vgmstream_xma_ue3,
init_vgmstream_csb,
init_vgmstream_fwse,
init_vgmstream_fda,
init_vgmstream_kwb,
init_vgmstream_lrmd,
init_vgmstream_bkhd,
init_vgmstream_bkhd_fx,
init_vgmstream_diva,
init_vgmstream_imuse,
init_vgmstream_ktsr,
init_vgmstream_asrs,
init_vgmstream_mups,
init_vgmstream_kat,
init_vgmstream_pcm_success,
init_vgmstream_ktsc,
init_vgmstream_adp_konami,
init_vgmstream_zwv,
init_vgmstream_dsb,
init_vgmstream_bsf,
init_vgmstream_sdrh_new,
init_vgmstream_sdrh_old,
init_vgmstream_wady,
init_vgmstream_dsp_sqex,
init_vgmstream_dsp_wiivoice,
init_vgmstream_xws,
init_vgmstream_cpk,
init_vgmstream_opus_nsopus,
init_vgmstream_sbk,
init_vgmstream_dsp_wiiadpcm,
init_vgmstream_dsp_cwac,
init_vgmstream_ifs,
init_vgmstream_acx,
init_vgmstream_compresswave,
init_vgmstream_ktac,
init_vgmstream_mzrt_v1,
init_vgmstream_bsnf,
init_vgmstream_tac,
init_vgmstream_idsp_tose,
init_vgmstream_dsp_kwa,
init_vgmstream_ogv_3rdeye,
init_vgmstream_sspr,
init_vgmstream_piff_tpcm,
init_vgmstream_wxd_wxh,
init_vgmstream_bnk_relic,
init_vgmstream_xsh_xsd_xss,
init_vgmstream_psb,
init_vgmstream_lopu_fb,
init_vgmstream_lpcm_fb,
init_vgmstream_wbk,
init_vgmstream_wbk_nslb,
init_vgmstream_dsp_apex,
init_vgmstream_ubi_ckd_cwav,
init_vgmstream_sspf,
init_vgmstream_opus_rsnd,
init_vgmstream_s3v,
init_vgmstream_esf,
init_vgmstream_adm3,
init_vgmstream_tt_ad,
init_vgmstream_bw_mp3_riff,
init_vgmstream_bw_riff_mp3,
init_vgmstream_sndz,
init_vgmstream_vab,
init_vgmstream_bigrp,
init_vgmstream_sscf_encrypted,
init_vgmstream_s_p_sth,
init_vgmstream_utf_ahx,
init_vgmstream_ego_dic,
init_vgmstream_awd,
init_vgmstream_rws_809,
init_vgmstream_pwb,
init_vgmstream_squeakstream,
init_vgmstream_squeaksample,
init_vgmstream_snds,
init_vgmstream_adm2,
init_vgmstream_nxof,
init_vgmstream_gwb_gwd,
init_vgmstream_s_pack,
init_vgmstream_cbx,
init_vgmstream_vas_rockstar,
init_vgmstream_ea_sbk,
init_vgmstream_dsp_asura,
init_vgmstream_dsp_asura_ds2,
init_vgmstream_dsp_asura_ttss,
init_vgmstream_dsp_asura_sfx,
init_vgmstream_adp_ongakukan,
/* lower priority metas (no clean header identity, somewhat ambiguous, or need extension/companion file to identify) */
init_vgmstream_agsc,
init_vgmstream_scd_pcm,
init_vgmstream_vas_kceo,
init_vgmstream_vas_kceo_container,
init_vgmstream_ps2_wmus,
init_vgmstream_mib_mih,
init_vgmstream_mjb_mjh,
init_vgmstream_mic_koei,
init_vgmstream_seb,
init_vgmstream_tgc,
init_vgmstream_rage_aud,
init_vgmstream_asd_naxat,
/* need companion files */
init_vgmstream_pos,
init_vgmstream_sli_loops,
/* lowest priority metas (should go after all metas, and TXTH should go before raw formats) */
init_vgmstream_txth, /* proper parsers should supersede TXTH, once added */
init_vgmstream_dtk, /* semi-raw GC streamed files */
init_vgmstream_mpeg, /* semi-raw MP3 */
init_vgmstream_btsnd, /* semi-headerless */
init_vgmstream_fsb_encrypted,
init_vgmstream_nus3bank_encrypted,
init_vgmstream_encrypted, /* encrypted stuff */
init_vgmstream_raw_rsf, /* raw GC streamed files */
init_vgmstream_raw_int, /* .int raw PCM */
init_vgmstream_ps_headerless, /* tries to detect a bunch of PS-ADPCM formats */
init_vgmstream_raw_snds, /* .snds raw SNDS IMA */
init_vgmstream_raw_wavm, /* .wavm raw xbox */
init_vgmstream_raw_pcm, /* .raw raw PCM */
init_vgmstream_raw_s14_sss, /* .s14/sss raw siren14 */
init_vgmstream_exakt_sc, /* .sc raw PCM */
init_vgmstream_zwdsp, /* fake format */
init_vgmstream_ps2_adm, /* weird non-constant PSX blocks */
init_vgmstream_rwsd, /* crap, to be removed */
#ifdef VGM_USE_FFMPEG
init_vgmstream_ffmpeg, /* may play anything incorrectly, since FFmpeg doesn't check extensions */
#endif
};
#define LOCAL_ARRAY_LENGTH(array) (sizeof(array) / sizeof(array[0]))
static const int init_vgmstream_count = LOCAL_ARRAY_LENGTH(init_vgmstream_functions);
VGMSTREAM* detect_vgmstream_format(STREAMFILE* sf) {
if (!sf)
return NULL;
/* try a series of formats, see which works */
for (int i = 0; i < init_vgmstream_count; i++) {
init_vgmstream_t init_vgmstream_function = init_vgmstream_functions[i];
/* call init function and see if valid VGMSTREAM was returned */
VGMSTREAM* vgmstream = init_vgmstream_function(sf);
if (!vgmstream)
continue;
int format_id = i + 1;
/* validate + setup vgmstream */
if (!prepare_vgmstream(vgmstream, sf, format_id)) {
/* keep trying if wasn't valid, as simpler formats may return a vgmstream by mistake */
close_vgmstream(vgmstream);
continue;
}
return vgmstream;
}
/* not supported */
return NULL;
}
init_vgmstream_t get_vgmstream_format_init(int format_id) {
// ID is expected to be from 1...N, to distinguish from 0 = not set
if (format_id <= 0 || format_id > init_vgmstream_count)
return NULL;
return init_vgmstream_functions[format_id - 1];
}

11
src/vgmstream_init.h Normal file
View File

@ -0,0 +1,11 @@
#ifndef _DETECTION_H_
#define _DETECTION_H_
#include "meta/meta.h"
#include "vgmstream.h"
bool prepare_vgmstream(VGMSTREAM* vgmstream, STREAMFILE* sf, int format_id);
VGMSTREAM* detect_vgmstream_format(STREAMFILE* sf);
init_vgmstream_t get_vgmstream_format_init(int format_id);
#endif

View File

@ -486,7 +486,7 @@ typedef enum {
meta_XVAG, /* Ratchet & Clank Future: Quest for Booty (PS3) */
meta_CPS,
meta_MSF,
meta_PS3_PAST, /* Bakugan Battle Brawlers (PS3) */
meta_SNDP,
meta_SGXD, /* Sony: Folklore, Genji, Tokyo Jungle (PS3), Brave Story, Kurohyo (PSP) */
meta_WII_RAS, /* Donkey Kong Country Returns (Wii) */
meta_SPM,
@ -583,7 +583,7 @@ typedef enum {
meta_MSB_MSH, /* sfx companion of MIH+MIB */
meta_TXTP,
meta_SMC_SMH, /* Wangan Midnight (System 246) */
meta_PPST, /* PPST [Parappa the Rapper (PSP)] */
meta_PPST,
meta_SPS_N1,
meta_UBI_BAO,
meta_DSP_SWITCH_AUDIO, /* Gal Gun 2 (Switch) */