Update mixing code

This commit is contained in:
bnnm 2019-03-09 20:50:58 +01:00
parent d8ffd7f0d5
commit a6e777d120
5 changed files with 326 additions and 104 deletions

View File

@ -465,7 +465,7 @@ int main(int argc, char ** argv) {
#ifdef VGMSTREAM_MIXING
/* enable after all config but before outbuf */
vgmstream_enable_mixing(vgmstream, BUFFER_SAMPLES, &input_channels, &channels);
vgmstream_mixing_enable(vgmstream, BUFFER_SAMPLES, &input_channels, &channels);
#endif
buf = malloc(BUFFER_SAMPLES * sizeof(sample_t) * input_channels);

View File

@ -1,9 +1,61 @@
#include "meta.h"
#include "../coding/coding.h"
#include "../layout/layout.h"
#ifdef VGMSTREAM_MIXING
#include "../mixing.h"
#endif
#define TXTP_LINE_MAX 1024
#define TXTP_MIXING_MAX 128
#ifdef VGMSTREAM_MIXING
/* mixing info */
typedef enum {
MIX_SWAP,
MIX_ADD,
MIX_ADD_VOLUME,
MIX_VOLUME,
MIX_LIMIT,
MIX_DOWNMIX,
MIX_KILLMIX,
MIX_UPMIX,
MIX_FADE,
MACRO_VOLUME,
MACRO_TRACK,
MACRO_LAYER,
MACRO_CROSSTRACK,
MACRO_CROSSLAYER,
} txtp_mix_t;
typedef struct {
txtp_mix_t command;
/* common */
int ch_dst;
int ch_src;
double vol;
/* fade envelope */
double vol_start;
double vol_end;
char shape;
int32_t sample_pre;
int32_t sample_start;
int32_t sample_end;
int32_t sample_post;
double time_pre;
double time_start;
double time_end;
double time_post;
/* macros */
int max;
uint32_t mask;
int overlap;
} txtp_mix_data;
#endif
typedef struct {
@ -16,7 +68,7 @@ typedef struct {
#endif
#ifdef VGMSTREAM_MIXING
int mixing_count;
mix_config_data mixing[VGMSTREAM_MAX_MIXING];
txtp_mix_data mixing[TXTP_MIXING_MAX];
#endif
double config_loop_count;
@ -55,7 +107,7 @@ static txtp_header* parse_txtp(STREAMFILE* streamFile);
static void clean_txtp(txtp_header* txtp);
static void apply_config(VGMSTREAM *vgmstream, txtp_entry *current);
#ifdef VGMSTREAM_MIXING
void add_mixing(txtp_entry* cfg, mix_config_data* mix, mix_command_t command);
void add_mixing(txtp_entry* cfg, txtp_mix_data* mix, txtp_mix_t command);
#endif
/* TXTP - an artificial playlist-like format to play files with segments/layers/config */
@ -232,10 +284,10 @@ static void apply_config(VGMSTREAM *vgmstream, txtp_entry *current) {
if (current->loop_install) {
if (current->loop_start_second > 0 || current->loop_end_second > 0) {
current->loop_start_sample = current->loop_start_second * (double)vgmstream->sample_rate;
current->loop_end_sample = current->loop_end_second * (double)vgmstream->sample_rate;
current->loop_start_sample = current->loop_start_second * vgmstream->sample_rate;
current->loop_end_sample = current->loop_end_second * vgmstream->sample_rate;
if (current->loop_end_sample > vgmstream->num_samples &&
current->loop_end_sample - vgmstream->num_samples <= 0.1 * (double)vgmstream->sample_rate)
current->loop_end_sample - vgmstream->num_samples <= 0.1 * vgmstream->sample_rate)
current->loop_end_sample = vgmstream->num_samples; /* allow some rounding leeway */
}
@ -252,7 +304,7 @@ static void apply_config(VGMSTREAM *vgmstream, txtp_entry *current) {
int ch;
for (ch = 0; ch < vgmstream->channels; ch++) {
if (!((current->channel_mask >> ch) & 1)) {
mix_config_data mix = {0};
txtp_mix_data mix = {0};
mix.ch_dst = ch;
mix.vol = 0.0f;
add_mixing(current, &mix, MIX_VOLUME);
@ -262,10 +314,47 @@ static void apply_config(VGMSTREAM *vgmstream, txtp_entry *current) {
/* copy mixing list (should be done last as some mixes depend on config) */
if (current->mixing_count > 0) {
int i;
int m;
for (i = 0; i < current->mixing_count; i++) {
vgmstream_add_mixing(vgmstream, current->mixing[i]);
for (m = 0; m < current->mixing_count; m++) {
txtp_mix_data mix = current->mixing[m];
switch(mix.command) {
/* base mixes */
case MIX_SWAP: mixing_push_swap(vgmstream, mix.ch_dst, mix.ch_src); break;
case MIX_ADD: mixing_push_add(vgmstream, mix.ch_dst, mix.ch_src, 1.0); break;
case MIX_ADD_VOLUME: mixing_push_add(vgmstream, mix.ch_dst, mix.ch_src, mix.vol); break;
case MIX_VOLUME: mixing_push_volume(vgmstream, mix.ch_dst, mix.vol); break;
case MIX_LIMIT: mixing_push_limit(vgmstream, mix.ch_dst, mix.vol); break;
case MIX_UPMIX: mixing_push_upmix(vgmstream, mix.ch_dst); break;
case MIX_DOWNMIX: mixing_push_downmix(vgmstream, mix.ch_dst); break;
case MIX_KILLMIX: mixing_push_killmix(vgmstream, mix.ch_dst); break;
case MIX_FADE:
/* Convert from time to samples now that sample rate is final.
* Samples and time values may be mixed though, so it's done for every
* value (if one is 0 the other will be too, though) */
if (mix.time_pre > 0.0) mix.sample_pre = mix.time_pre * vgmstream->sample_rate;
if (mix.time_start > 0.0) mix.sample_start = mix.time_start * vgmstream->sample_rate;
if (mix.time_end > 0.0) mix.sample_end = mix.time_end * vgmstream->sample_rate;
if (mix.time_post > 0.0) mix.sample_post = mix.time_post * vgmstream->sample_rate;
/* convert special meaning too */
if (mix.time_pre < 0.0) mix.sample_pre = -1;
if (mix.time_post < 0.0) mix.sample_post = -1;
mixing_push_fade(vgmstream, mix.ch_dst, mix.vol_start, mix.vol_end, mix.shape,
mix.sample_pre, mix.sample_start, mix.sample_end, mix.sample_post);
break;
/* macro mixes */
case MACRO_VOLUME: mixing_macro_volume(vgmstream, mix.vol, mix.mask); break;
case MACRO_TRACK: mixing_macro_track(vgmstream, mix.mask); break;
case MACRO_LAYER: mixing_macro_layer(vgmstream, mix.max, mix.mask, mix.overlap); break;
case MACRO_CROSSTRACK: mixing_macro_crosstrack(vgmstream, mix.max); break;
case MACRO_CROSSLAYER: mixing_macro_crosslayer(vgmstream, mix.max); break;
default:
break;
}
}
}
#endif
@ -360,10 +449,10 @@ static int get_time(const char * config, double *value_f, int32_t *value_i) {
/* assume format is samples: N */
m = sscanf(config, " %i%n", &temp_i1,&n);
if (m == 1) {
if (temp_i1 < 0)
return 0;
/* allow negative samples for special meanings */
//if (temp_i1 < 0)
// return 0;
//*is_time_i = 1;
*value_i = temp_i1;
return n;
}
@ -431,31 +520,107 @@ static int get_mask(const char * config, uint32_t *value) {
#ifdef VGMSTREAM_MIXING
static int get_fade(const char * config, mix_config_data *mix, int *out_n) {
int n, m;
static int get_fade(const char * config, txtp_mix_data *mix, int *out_n) {
int n, m, tn = 0;
char type, separator;
//todo add { } shortcuts / time / etc
m = sscanf(config, " %d %c%n", &mix->ch_dst, &type, &n);
if (n == 0 || m != 2) goto fail;
config += n;
tn += n;
m = sscanf(config, " %d ^ %f ~ %f = %c @ %f ~ %f + %f ~ %f%n",
&mix->ch_dst,
&mix->vol_start, &mix->vol_end, &mix->shape,
&mix->time_pre, &mix->time_start, &mix->time_end, &mix->time_post,
&n);
if (type == '^') {
/* full definition */
m = sscanf(config, " %lf ~ %lf = %c @%n", &mix->vol_start, &mix->vol_end, &mix->shape, &n);
if (n == 0 || m != 3) goto fail;
config += n;
tn += n;
VGM_LOG("curve m=%i, n=%i\n", m,n);
if (m == 8 && n != 0) {
mix->time_end += mix->time_start;
*out_n = n;
return 1;
n = get_time(config, &mix->time_pre, &mix->sample_pre);
if (n == 0) goto fail;
config += n;
tn += n;
m = sscanf(config, " %c%n", &separator, &n);
if (n == 0 || m != 1 || separator != '~') goto fail;
config += n;
tn += n;
n = get_time(config, &mix->time_start, &mix->sample_start);
if (n == 0) goto fail;
config += n;
tn += n;
m = sscanf(config, " %c%n", &separator, &n);
if (n == 0 || m != 1 || separator != '+') goto fail;
config += n;
tn += n;
n = get_time(config, &mix->time_end, &mix->sample_end);
if (n == 0) goto fail;
config += n;
tn += n;
m = sscanf(config, " %c%n", &separator, &n);
if (n == 0 || m != 1 || separator != '~') goto fail;
config += n;
tn += n;
n = get_time(config, &mix->time_post, &mix->sample_post);
if (n == 0) goto fail;
config += n;
tn += n;
}
else {
/* simplified definition */
if (type == '{' || type == '(') {
mix->vol_start = 0.0;
mix->vol_end = 1.0;
}
else if (type == '}' || type == ')') {
mix->vol_start = 1.0;
mix->vol_end = 0.0;
}
else {
goto fail;
}
mix->shape = type; /* internally converted */
mix->time_pre = -1.0;
mix->sample_pre = -1;
n = get_time(config, &mix->time_start, &mix->sample_start);
if (n == 0) goto fail;
config += n;
tn += n;
m = sscanf(config, " %c%n", &separator, &n);
if (n == 0 || m != 1 || separator != '+') goto fail;
config += n;
tn += n;
n = get_time(config, &mix->time_end, &mix->sample_end);
if (n == 0) goto fail;
config += n;
tn += n;
mix->time_post = -1.0;
mix->sample_post = -1;
}
mix->time_end = mix->time_start + mix->time_end; /* defined as length */
*out_n = tn;
return 1;
fail:
return 0;
}
#endif
#ifdef VGMSTREAM_MIXING
void add_mixing(txtp_entry* cfg, mix_config_data* mix, mix_command_t command) {
if (cfg->mixing_count + 1 > VGMSTREAM_MAX_MIXING) {
void add_mixing(txtp_entry* cfg, txtp_mix_data* mix, txtp_mix_t command) {
if (cfg->mixing_count + 1 > TXTP_MIXING_MAX) {
VGM_LOG("TXTP: too many mixes\n");
return;
}
@ -465,6 +630,7 @@ void add_mixing(txtp_entry* cfg, mix_config_data* mix, mix_command_t command) {
mix->ch_dst--;
mix->ch_src--;
mix->command = command;
cfg->mixing[cfg->mixing_count] = *mix; /* memcpy'ed */
cfg->mixing_count++;
}
@ -516,7 +682,7 @@ static void add_config(txtp_entry* current, txtp_entry* cfg, const char* filenam
}
static int add_filename(txtp_header * txtp, char *filename, int is_default) {
int i, n, nc, mc;
int i, n, nc, nm, mc;
txtp_entry cfg = {0};
size_t range_start, range_end;
char command[TXTP_LINE_MAX] = {0};
@ -596,10 +762,11 @@ static int add_filename(txtp_header * txtp, char *filename, int is_default) {
char cmd;
while (config[0] != '\0') {
mix_config_data mix = {0};
txtp_mix_data mix = {0};
//;VGM_LOG("TXTP: subcommand='%s'\n", config);
//todo use strchr instead?
if (sscanf(config, " %c%n", &cmd, &n) == 1 && n != 0 && cmd == ',') {
config += n;
continue;
@ -612,8 +779,8 @@ static int add_filename(txtp_header * txtp, char *filename, int is_default) {
continue;
}
if ((sscanf(config, " %d + %d * %f%n", &mix.ch_dst, &mix.ch_src, &mix.vol, &n) == 3 && n != 0) ||
(sscanf(config, " %d + %d x %f%n", &mix.ch_dst, &mix.ch_src, &mix.vol, &n) == 3 && n != 0)) {
if ((sscanf(config, " %d + %d * %lf%n", &mix.ch_dst, &mix.ch_src, &mix.vol, &n) == 3 && n != 0) ||
(sscanf(config, " %d + %d x %lf%n", &mix.ch_dst, &mix.ch_src, &mix.vol, &n) == 3 && n != 0)) {
//;VGM_LOG("TXTP: mix %i+%i*%f\n", mix.ch_dst, mix.ch_src, mix.vol);
add_mixing(&cfg, &mix, MIX_ADD_VOLUME); /* N+M*V: mixes M*volume to N */
config += n;
@ -627,15 +794,15 @@ static int add_filename(txtp_header * txtp, char *filename, int is_default) {
continue;
}
if ((sscanf(config, " %d * %f%n", &mix.ch_dst, &mix.vol, &n) == 2 && n != 0) ||
(sscanf(config, " %d x %f%n", &mix.ch_dst, &mix.vol, &n) == 2 && n != 0)) {
if ((sscanf(config, " %d * %lf%n", &mix.ch_dst, &mix.vol, &n) == 2 && n != 0) ||
(sscanf(config, " %d x %lf%n", &mix.ch_dst, &mix.vol, &n) == 2 && n != 0)) {
//;VGM_LOG("TXTP: mix %i*%f\n", mix.ch_dst, mix.vol);
add_mixing(&cfg, &mix, MIX_VOLUME); /* N*V: changes volume of N */
config += n;
continue;
}
if ((sscanf(config, " %d = %f%n", &mix.ch_dst, &mix.vol, &n) == 2 && n != 0)) {
if ((sscanf(config, " %d = %lf%n", &mix.ch_dst, &mix.vol, &n) == 2 && n != 0)) {
//;VGM_LOG("TXTP: mix %i=%f\n", mix.ch_dst, mix.vol);
add_mixing(&cfg, &mix, MIX_LIMIT); /* N=V: limits volume of N */
config += n;
@ -644,7 +811,7 @@ static int add_filename(txtp_header * txtp, char *filename, int is_default) {
if (sscanf(config, " %d%c%n", &mix.ch_dst, &cmd, &n) == 2 && n != 0 && cmd == 'D') {
//;VGM_LOG("TXTP: mix %iD\n", mix.ch_dst);
add_mixing(&cfg, &mix, MIX_DOWNMIX_REST); /* ND: downmix N and all following channels */
add_mixing(&cfg, &mix, MIX_KILLMIX); /* ND: downmix N and all following channels */
config += n;
continue;
}
@ -744,6 +911,58 @@ static int add_filename(txtp_header * txtp, char *filename, int is_default) {
//;VGM_LOG("TXTP: loop_install %i (max=%i): %i %i / %f %f\n", cfg.loop_install, cfg.loop_end_max,
// cfg.loop_start_sample, cfg.loop_end_sample, cfg.loop_start_second, cfg.loop_end_second);
}
#ifdef VGMSTREAM_MIXING
//todo cleanup
else if (strcmp(command,"@volume") == 0) {
txtp_mix_data mix = {0};
nm = get_double(config, &mix.vol);
config += nm;
if (nm == 0) continue;
nm = get_mask(config, &mix.mask);
config += nm;
add_mixing(&cfg, &mix, MACRO_VOLUME);
}
else if (strcmp(command,"@track") == 0) {
txtp_mix_data mix = {0};
nm = get_mask(config, &mix.mask);
config += nm;
if (nm == 0) continue;
add_mixing(&cfg, &mix, MACRO_TRACK);
}
else if (strcmp(command,"@layer") == 0 || strcmp(command,"@overlap") == 0) {
txtp_mix_data mix = {0};
nm = get_int(config, &mix.max);
config += nm;
if (nm == 0) continue;
nm = get_mask(config, &mix.mask);
config += nm;
mix.overlap = (strcmp(command,"@overlap") == 0);
add_mixing(&cfg, &mix, MACRO_LAYER);
}
else if (strcmp(command,"@crosslayer") == 0 || strcmp(command,"@crosstrack") == 0) {
txtp_mix_data mix = {0};
txtp_mix_t type;
if (strcmp(command,"@crosstrack") == 0)
type = MACRO_CROSSTRACK;
else
type = MACRO_CROSSLAYER;
nm = get_int(config, &mix.max);
config += nm;
if (nm == 0) continue;
add_mixing(&cfg, &mix, type);
}
#endif
else if (config[nc] == ' ') {
//;VGM_LOG("TXTP: comment\n");
break; /* comment, ignore rest */

View File

@ -6,6 +6,47 @@
#include "streamfile.h"
#if 0
/* ****************************************** */
/* PLAYER: simplifies plugin code */
/* ****************************************** */
/* opaque player state */
typedef struct VGMSTREAM_PLAYER VGMSTREAM_PLAYER;
typedef struct {
//...
} VGMSTREAM_PLAYER_INFO;
VGMSTREAM_PLAYER* vgmstream_player_init(...);
VGMSTREAM_PLAYER* vgmstream_player_check_file(...);
VGMSTREAM_PLAYER* vgmstream_player_set_file(...);
VGMSTREAM_PLAYER* vgmstream_player_get_config(...);
VGMSTREAM_PLAYER* vgmstream_player_set_config(...);
VGMSTREAM_PLAYER* vgmstream_player_get_buffer(...);
VGMSTREAM_PLAYER* vgmstream_player_get_info(...);
VGMSTREAM_PLAYER* vgmstream_player_play(...);
VGMSTREAM_PLAYER* vgmstream_player_seek(...);
VGMSTREAM_PLAYER* vgmstream_player_close(...);
#endif
/* ****************************************** */
/* TAGS: loads key=val tags from a file */
/* ****************************************** */
/* opaque tag state */
typedef struct VGMSTREAM_TAGS VGMSTREAM_TAGS;
@ -26,10 +67,22 @@ int vgmstream_tags_next_tag(VGMSTREAM_TAGS* tags, STREAMFILE* tagfile);
/* Closes tag file */
void vgmstream_tags_close(VGMSTREAM_TAGS* tags);
#ifdef VGMSTREAM_MIXING
/* ****************************************** */
/* MIXING: modifies vgmstream output */
/* ****************************************** */
/* Enables mixing effects, with max outbuf samples as a hint. Once active, plugin
* must use returned input_channels to create outbuf and output_channels to output audio. */
void vgmstream_enable_mixing(VGMSTREAM* vgmstream, int32_t max_sample_count, int *input_channels, int *output_channels);
* must use returned input_channels to create outbuf and output_channels to output audio.
* Needs to be enabled last after adding effects. */
void vgmstream_mixing_enable(VGMSTREAM* vgmstream, int32_t max_sample_count, int *input_channels, int *output_channels);
/* sets a fadeout */
//void vgmstream_mixing_fadeout(VGMSTREAM *vgmstream, float start_second, float duration_seconds);
/* sets downmixing if needed */
//void vgmstream_mixing_downmix(VGMSTREAM *vgmstream, int max_channels)
#endif
#endif /* _PLUGINS_H_ */

View File

@ -9,6 +9,9 @@
#include "meta/meta.h"
#include "layout/layout.h"
#include "coding/coding.h"
#ifdef VGMSTREAM_MIXING
#include "mixing.h"
#endif
static void try_dual_file_stereo(VGMSTREAM * opened_vgmstream, STREAMFILE *streamFile, VGMSTREAM* (*init_vgmstream_function)(STREAMFILE*));
@ -570,15 +573,6 @@ static VGMSTREAM * init_vgmstream_internal(STREAMFILE *streamFile) {
void setup_vgmstream(VGMSTREAM * vgmstream) {
#ifdef VGMSTREAM_MIXING
/* fill default config to simplify external code (mixing off will always happen
* initially, and if they contain values it means mixing must be enabled) */
if (!vgmstream->mixing_on || vgmstream->input_channels <= 0)
vgmstream->input_channels = vgmstream->channels;
if (!vgmstream->mixing_on || vgmstream->output_channels <= 0)
vgmstream->output_channels = vgmstream->channels;
#endif
/* save start things so we can restart when seeking */
memcpy(vgmstream->start_ch, vgmstream->ch, sizeof(VGMSTREAMCHANNEL)*vgmstream->channels);
memcpy(vgmstream->start_vgmstream, vgmstream, sizeof(VGMSTREAM));
@ -756,8 +750,7 @@ VGMSTREAM * allocate_vgmstream(int channel_count, int loop_flag) {
vgmstream->loop_flag = loop_flag;
#ifdef VGMSTREAM_MIXING
/* fixed arrays, for now */
vgmstream->mixing_size = VGMSTREAM_MAX_MIXING;
mixing_init(vgmstream); /* pre-init */
#endif
//vgmstream->stream_name_size = STREAM_NAME_SIZE;
return vgmstream;
@ -767,6 +760,7 @@ fail:
free(vgmstream->start_ch);
free(vgmstream->loop_ch);
free(vgmstream->start_vgmstream);
mixing_close(vgmstream);
}
free(vgmstream);
return NULL;
@ -905,7 +899,9 @@ void close_vgmstream(VGMSTREAM * vgmstream) {
}
}
}
#ifdef VGMSTREAM_MIXING
mixing_close(vgmstream);
#endif
free(vgmstream->ch);
free(vgmstream->start_ch);
free(vgmstream->loop_ch);

View File

@ -5,15 +5,12 @@
#ifndef _VGMSTREAM_H
#define _VGMSTREAM_H
/* reasonable maxs */
/* reasonable limits */
enum { PATH_LIMIT = 32768 };
enum { STREAM_NAME_SIZE = 255 };
enum { VGMSTREAM_MAX_CHANNELS = 64 };
enum { VGMSTREAM_MIN_SAMPLE_RATE = 300 }; /* 300 is Wwise min */
enum { VGMSTREAM_MAX_SAMPLE_RATE = 96000 };
#ifdef VGMSTREAM_MIXING
enum { VGMSTREAM_MAX_MIXING = 64 };
#endif
#include "streamfile.h"
@ -773,37 +770,6 @@ typedef enum {
mapping_7POINT1_surround = speaker_FL | speaker_FR | speaker_FC | speaker_LFE | speaker_BL | speaker_BR | speaker_SL | speaker_SR,
} mapping_t;
#ifdef VGMSTREAM_MIXING
/* mixing info */
typedef enum {
MIX_SWAP,
MIX_ADD,
MIX_ADD_VOLUME,
MIX_VOLUME,
MIX_LIMIT,
MIX_DOWNMIX,
MIX_DOWNMIX_REST,
MIX_UPMIX,
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 */
float time_pre; /* position before curve where vol_str applies (-1 = beginning) */
float time_start; /* curve start position where vol changes from src to dst */
float time_end; /* curve end position where vol changes from src to dst */
float time_post; /* position after curve where vol_dst applies (-1 = end) */
} mix_config_data;
#endif
/* info for a single vgmstream channel */
typedef struct {
@ -888,15 +854,7 @@ typedef struct {
int channel_mappings_on; /* channel mappings are active */
int channel_mappings[32]; /* swap channel "i" with "[i]" */
#endif
#ifdef VGMSTREAM_MIXING
/* may be ignored if plugin doesn't support it, but fields will be always set to simplify plugin's code */
int input_channels; /* starting channels before mixing (outbuf must be this big) */
int output_channels; /* resulting channels after mixing */
int mixing_on; /* mixing allowed */
int mixing_count; /* mixing number */
size_t mixing_size; /* mixing max */
mix_config_data mixing_chain[VGMSTREAM_MAX_MIXING]; /* effects to apply (could be alloc'ed but to simplify...) */
#endif
/* config requests, players must read and honor these values */
/* (ideally internally would work as a player, but for now player must do it manually) */
double config_loop_count;
@ -940,13 +898,18 @@ 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) */
/* Data the codec needs for the whole stream. This is for codecs too
#ifdef VGMSTREAM_MIXING
void * mixing_data; /* state for mixing effects */
#endif
/* Optional data the codec needs for the whole stream. This is for codecs too
* different from vgmstream's structure to be reasonably shoehorned.
* Note also that support must be added for resetting, looping and
* closing for every codec that uses this, as it will not be handled. */
void * codec_data;
/* Same, for special layouts. layout_data + codec_data may exist at the same time. */
void * layout_data;
} VGMSTREAM;
#ifdef VGM_USE_VORBIS
@ -1381,15 +1344,6 @@ VGMSTREAM * allocate_vgmstream(int channel_count, int looped);
/* Prepare the VGMSTREAM's initial state once parsed and ready, but before playing. */
void setup_vgmstream(VGMSTREAM * vgmstream);
#ifdef VGMSTREAM_MIXING
/* Applies mixing commands to the vgmstream to the sample buffer.
* Mixing must be enabled and outbuf must be big enough for output_channels*samples_to_do big. */
void mix_vgmstream(sample_t *outbuf, int32_t sample_count, VGMSTREAM* vgmstream);
/* Add a new internal mix. Always use this as it validates mixes. */
void vgmstream_add_mixing(VGMSTREAM* vgmstream, mix_config_data mix);
#endif
/* Get the number of samples of a single frame (smallest self-contained sample group, 1/N channels) */
int get_vgmstream_samples_per_frame(VGMSTREAM * vgmstream);
/* Get the number of bytes of a single frame (smallest self-contained byte group, 1/N channels) */