mirror of
https://github.com/vgmstream/vgmstream.git
synced 2025-01-19 00:04:04 +01:00
Ongakukan ADPCM code improvements
significant improvements include: - char flags in ongakukan_adp_lib.c have all, with one exception, been made to bools. -- ATM, sample_mode will remain a char. -- several other variables have been removed. they did not affect decoding in any significant way. -- init_ongakukan_adpcm now accepts at least one bool as an argument for a total of four arguments. -- many function from lib renamed to follow external lib function naming standard. - simplified ongakukan_adp_decoder.c -- decode_ongakukan_adp in particular benefited from this code simpification; loop is now re-done so all samples are copied to outbuf when they're made available. -- as a consequence of this, at least one var was removed entirely, while others have been redefined into different purposes; samples_filled is now a bool var that flags whether or not all available samples were decoded. -- init_ongakukan_adp has four arguments as well, and accepts at least one bool as an argument of its own. -- streamfile is now close there as well instead of in the external decoder lib. - adp_ongakukan.c saw much simpified code as well; meta checks have been relaxed significantly, which meant the removal of several var and at least three vars repurposed into bools. -- supposed_size is introduced as an entirely new var; it now calculates the supposed file size of an ADP file with several 32-bit fields from the first byte as a base. -- consequently, file_size checks are now made against supposed_size. - as a consequence of all of the above, riff.c has now lost an additional RIFF size check; initially if RIFF chunk reported a big-enough size than RIFF file realistically has, it would be enough for vgmstream to stop working with said file.
This commit is contained in:
parent
2eef6b4535
commit
edde33489b
@ -298,7 +298,7 @@ void free_imuse(imuse_codec_data* data);
|
||||
typedef struct ongakukan_adp_data ongakukan_adp_data;
|
||||
|
||||
ongakukan_adp_data* init_ongakukan_adp(STREAMFILE* sf, int32_t data_offset, int32_t data_size,
|
||||
char sound_is_adpcm, char sample_has_base_setup_from_the_start);
|
||||
bool sound_is_adpcm);
|
||||
void decode_ongakukan_adp(VGMSTREAM* vgmstream, sample_t* outbuf, int32_t samples_to_do);
|
||||
void reset_ongakukan_adp(ongakukan_adp_data* data);
|
||||
void seek_ongakukan_adp(ongakukan_adp_data* data, int32_t current_sample);
|
||||
|
@ -14,6 +14,7 @@
|
||||
* -- However, much of that support is reliant on a flag that's set to either one of the two codecs depending on the opened file extension.
|
||||
* Basically, how it works is: if sound data is "PCM16" (available to "wav" and "ads" files), set flag to 0.
|
||||
* If sound data is "ADPCM" (available to "adp" files), set it to 1.
|
||||
* Code handles this flag as a boolean var; 0 is "false" and 1 is "true".
|
||||
* -- As vgmstream has built-in support for the former codec (and the many metas that use it) however, despite being fairly easy to add here,
|
||||
* re-implementing one from scratch would be a wasted effort regardless; it is consequentially not included. */
|
||||
|
||||
@ -36,15 +37,14 @@ struct ongakukan_adp_t
|
||||
long int samples_filled; /* how many samples were filled to vgmstream buffer. */
|
||||
long int samples_consumed; /* how many samples vgmstream buffer had to consume. */
|
||||
|
||||
char sound_is_adpcm; /* 0 = no (see "additional notes" above) , 1 = yes */
|
||||
char sample_startpoint_present; /* -1 = failed to make startpoint, 1 = startpoint present */
|
||||
bool sound_is_adpcm; /* false = no (see "additional notes" above) , true = yes */
|
||||
bool sample_startpoint_present; /* false = failed to make startpoint, true = startpoint present */
|
||||
char sample_mode; /* 0 = creates decoding setup, 1 = continue decoding data with setup in place */
|
||||
char sample_has_base_setup_from_the_start; /* 0 = no, 1 = yes */
|
||||
char sample_pair_is_decoded; /* 0 = no, 1 = yes */
|
||||
bool sample_pair_is_decoded; /* false = no, true = yes */
|
||||
|
||||
unsigned char base_pair; /* represents a read byte from ADPCM data, consisting of two 4-bit nibbles each.*/
|
||||
long int base_scale; /* how loud should this sample be. */
|
||||
void* sample_hist; /* two pairs of signed 16-bit data, representing samples. yes, it is void. */
|
||||
short int sample_hist[2]; /* two pairs of signed 16-bit data, representing samples. yes, it is void. */
|
||||
};
|
||||
|
||||
/* filter table consisting of 16 numbers each. */
|
||||
@ -53,18 +53,18 @@ const short int ongakukan_adpcm_filter[16] = { 233, 549, 453, 375, 310, 233, 233
|
||||
|
||||
/* streamfile read function declararion, more may be added in the future. */
|
||||
|
||||
uint8_t read_u8_wrapper(ongakukan_adp_t* handle);
|
||||
static uint8_t read_u8_wrapper(ongakukan_adp_t* handle);
|
||||
|
||||
/* function declarations for the inner workings of codec data. */
|
||||
|
||||
char set_up_sample_startpoint(ongakukan_adp_t* handle);
|
||||
void decode_ongakukan_adpcm_sample(ongakukan_adp_t* handle, short int* sample_hist);
|
||||
static bool set_up_sample_startpoint(ongakukan_adp_t* handle);
|
||||
static void decode_ongakukan_adpcm_samples(ongakukan_adp_t* handle);
|
||||
|
||||
/* codec management functions, meant to oversee and supervise ADP data from the top-down.
|
||||
* in layman terms, they control how ADP data should be handled and when. */
|
||||
|
||||
ongakukan_adp_t* boot_ongakukan_adpcm(STREAMFILE* sf, long int data_offset, long int data_size,
|
||||
char sound_is_adpcm, char sample_has_base_setup_from_the_start)
|
||||
ongakukan_adp_t* init_ongakukan_adpcm(STREAMFILE* sf, long int data_offset, long int data_size,
|
||||
bool sound_is_adpcm)
|
||||
{
|
||||
ongakukan_adp_t* handle = NULL;
|
||||
|
||||
@ -77,50 +77,45 @@ ongakukan_adp_t* boot_ongakukan_adpcm(STREAMFILE* sf, long int data_offset, long
|
||||
handle->data_offset = data_offset;
|
||||
handle->start_offset = data_offset;
|
||||
handle->data_size = data_size;
|
||||
handle->samples_filled = 0;
|
||||
handle->samples_consumed = 0;
|
||||
handle->sample_mode = 0;
|
||||
handle->sound_is_adpcm = sound_is_adpcm;
|
||||
handle->sample_has_base_setup_from_the_start = sample_has_base_setup_from_the_start;
|
||||
handle->sample_startpoint_present = set_up_sample_startpoint(handle);
|
||||
/* if we failed in planting up the seeds for an ADPCM decoder, we simply throw in the towel and take a walk in the park. */
|
||||
if (handle->sample_startpoint_present == -1) { goto fail; }
|
||||
if (handle->sample_startpoint_present == false) { goto fail; }
|
||||
|
||||
return handle;
|
||||
fail:
|
||||
free_all_ongakukan_adpcm(handle);
|
||||
ongakukan_adpcm_free(handle);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void free_all_ongakukan_adpcm(ongakukan_adp_t* handle)
|
||||
void ongakukan_adpcm_free(ongakukan_adp_t* handle)
|
||||
{
|
||||
if (!handle) return;
|
||||
close_streamfile(handle->sf);
|
||||
free(handle->sample_hist);
|
||||
free(handle);
|
||||
}
|
||||
|
||||
void reset_all_ongakukan_adpcm(ongakukan_adp_t* handle)
|
||||
void ongakukan_adpcm_reset(ongakukan_adp_t* handle)
|
||||
{
|
||||
if (!handle) return;
|
||||
|
||||
/* wipe out certain values from handle so we can start over. */
|
||||
handle->data_offset = handle->start_offset;
|
||||
handle->sample_pair_is_decoded = 0;
|
||||
handle->sample_pair_is_decoded = false;
|
||||
handle->sample_mode = 0;
|
||||
handle->sample_has_base_setup_from_the_start = 1;
|
||||
handle->alt_sample_work1 = 0;
|
||||
handle->alt_sample_work2 = handle->sample_work;
|
||||
}
|
||||
|
||||
void seek_ongakukan_adpcm_pos(ongakukan_adp_t* handle, long int target_sample)
|
||||
void ongakukan_adpcm_seek(ongakukan_adp_t* handle, long int target_sample)
|
||||
{
|
||||
if (!handle) return;
|
||||
|
||||
char ts_modulus = 0; /* ts_modulus is here to ensure target_sample gets rounded to a multiple of 2. */
|
||||
long int ts_data_offset = 0; /* ts_data_offset is basically data_offset but with (left(if PCM)/right(if ADPCM))-shifted target_sample calc by 1. */
|
||||
if (handle->sound_is_adpcm == 0) { ts_data_offset = target_sample << 1; }
|
||||
else { ts_data_offset = target_sample >> 1; ts_modulus = target_sample % 2; target_sample = target_sample - ts_modulus; }
|
||||
ts_data_offset = target_sample >> 1;
|
||||
ts_modulus = target_sample % 2;
|
||||
target_sample = target_sample - ts_modulus;
|
||||
/* if ADPCM, right-shift the former first then have ts_modulus calc remainder of target_sample by 2 so we can subtract it with ts_modulus.
|
||||
* this is needed for the two counters that the decoder has that can both add and subtract with 2, respectively
|
||||
* (and in order, too; meaning one counter does "plus 2" while the other does "minus 2",
|
||||
@ -130,9 +125,8 @@ void seek_ongakukan_adpcm_pos(ongakukan_adp_t* handle, long int target_sample)
|
||||
* so we'll have data_offset reposition itself to where sound data for that sample ought to be
|
||||
* and (as of now) reset basically all decode state up to this point so we can continue to decode all sample pairs without issue. */
|
||||
handle->data_offset = handle->start_offset + ts_data_offset;
|
||||
handle->sample_pair_is_decoded = 0;
|
||||
handle->sample_pair_is_decoded = false;
|
||||
handle->sample_mode = 0;
|
||||
handle->sample_has_base_setup_from_the_start = 1;
|
||||
handle->alt_sample_work1 = target_sample;
|
||||
handle->alt_sample_work2 = handle->sample_work - target_sample;
|
||||
|
||||
@ -140,139 +134,88 @@ void seek_ongakukan_adpcm_pos(ongakukan_adp_t* handle, long int target_sample)
|
||||
* seek_ongakukan_adpcm_pos in its current state is a bit more involved than the above, but works. */
|
||||
}
|
||||
|
||||
long int grab_num_samples_from_ongakukan_adp(ongakukan_adp_t* handle)
|
||||
long int get_num_samples_from_ongakukan_adpcm(ongakukan_adp_t* handle)
|
||||
{
|
||||
if (!handle) return 0;
|
||||
return handle->sample_work;
|
||||
}
|
||||
|
||||
long int grab_samples_filled_from_ongakukan_adp(ongakukan_adp_t* handle)
|
||||
void* get_sample_hist_from_ongakukan_adpcm(ongakukan_adp_t* handle)
|
||||
{
|
||||
if (!handle) return 0;
|
||||
return handle->samples_filled;
|
||||
}
|
||||
|
||||
void send_samples_filled_to_ongakukan_adp(long int samples_filled, ongakukan_adp_t* handle)
|
||||
{
|
||||
if (!handle) return;
|
||||
handle->samples_filled = samples_filled;
|
||||
}
|
||||
|
||||
long int grab_samples_consumed_from_ongakukan_adp(ongakukan_adp_t* handle)
|
||||
{
|
||||
if (!handle) return 0;
|
||||
return handle->samples_consumed;
|
||||
}
|
||||
|
||||
void send_samples_consumed_to_ongakukan_adp(long int samples_consumed, ongakukan_adp_t* handle)
|
||||
{
|
||||
if (!handle) return;
|
||||
handle->samples_consumed = samples_consumed;
|
||||
}
|
||||
|
||||
void* grab_sample_hist_from_ongakukan_adp(ongakukan_adp_t* handle)
|
||||
{
|
||||
if (!handle) return 0;
|
||||
return handle->sample_hist;
|
||||
return &handle->sample_hist;
|
||||
}
|
||||
|
||||
/* function definitions for the inner workings of codec data. */
|
||||
|
||||
char set_up_sample_startpoint(ongakukan_adp_t* handle)
|
||||
static bool set_up_sample_startpoint(ongakukan_adp_t* handle)
|
||||
{
|
||||
/* malloc "sample hist" to 2 short ints */
|
||||
handle->sample_hist = malloc(2 * sizeof(short int));
|
||||
/* make decoder fail hard if streamfile object isn't opened or downright useless. */
|
||||
if (!handle->sf) return false;
|
||||
|
||||
/* and make decoder fail hard if they don't have "sample hist" and opened streamfile object. */
|
||||
if (!handle->sample_hist) return -1;
|
||||
if (!handle->sf) return -1;
|
||||
|
||||
if (handle->sound_is_adpcm == 0) {
|
||||
/* num_samples but for PCM16 sound data. */
|
||||
handle->sample_work = handle->data_size >> 1;
|
||||
}
|
||||
else {
|
||||
/* num_samples but for Ongakukan ADPCM sound data. */
|
||||
handle->sample_work = handle->data_size << 1;
|
||||
}
|
||||
if (handle->sound_is_adpcm == 0) { return false; }
|
||||
else { /* num_samples but for Ongakukan ADPCM sound data. */ handle->sample_work = handle->data_size << 1; }
|
||||
/* set "beginning" and "end" sample vars and send a "message" that we went through no sample yet.*/
|
||||
handle->alt_sample_work1 = 0;
|
||||
handle->alt_sample_work2 = handle->sample_work;
|
||||
handle->sample_pair_is_decoded = 0;
|
||||
handle->sample_pair_is_decoded = false;
|
||||
|
||||
return 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
void decode_ongakukan_adp_data(ongakukan_adp_t* handle)
|
||||
void decode_ongakukan_adpcm_data(ongakukan_adp_t* handle)
|
||||
{
|
||||
/* set samples_filled to 0 and have our decoder go through every sample that exists in the sound data.*/
|
||||
handle->samples_filled = 0;
|
||||
if (handle->sample_pair_is_decoded == 0)
|
||||
{
|
||||
handle->samples_filled = 0;
|
||||
handle->samples_consumed = 0;
|
||||
}
|
||||
decode_ongakukan_adpcm_sample(handle, handle->sample_hist);
|
||||
decode_ongakukan_adpcm_samples(handle);
|
||||
/* if setup is established for further decoding, switch gears and have the decoder use that setup for as long as possible. */
|
||||
if (handle->sample_has_base_setup_from_the_start == 1)
|
||||
{
|
||||
handle->sample_has_base_setup_from_the_start = 0;
|
||||
handle->sample_mode = 1;
|
||||
}
|
||||
/* if sample pair is decoded, advance to next byte, tell our handle that we went through 2 samples and make decoder go through next available data again. */
|
||||
if (handle->sample_pair_is_decoded == 1)
|
||||
if (handle->sample_pair_is_decoded == true)
|
||||
{
|
||||
handle->data_offset++;
|
||||
handle->alt_sample_work1 += 2;
|
||||
handle->alt_sample_work2 -= 2;
|
||||
handle->samples_filled = 2;
|
||||
handle->sample_pair_is_decoded = 0;
|
||||
handle->sample_pair_is_decoded = false;
|
||||
}
|
||||
}
|
||||
|
||||
void decode_ongakukan_adpcm_sample(ongakukan_adp_t* handle, short int* sample_hist)
|
||||
static void decode_ongakukan_adpcm_samples(ongakukan_adp_t* handle)
|
||||
{
|
||||
unsigned char nibble1 = 0, nibble2 = 0; /* two chars representing a 4-bit nibble. */
|
||||
long int nibble1_1 = 0, nibble2_1 = 0; /* two long ints representing pure sample data. */
|
||||
|
||||
if (handle->sample_pair_is_decoded == 0)
|
||||
if (handle->sample_pair_is_decoded == false)
|
||||
{
|
||||
/* sample_mode being 0 means we can just do a setup for future sample decoding so we have nothing to worry about in the future. */
|
||||
if (handle->sample_mode == 0)
|
||||
{
|
||||
/* set "base scale", two "sample hist"s, and "base pair", respectively. */
|
||||
handle->base_scale = 0x10; /* yes, this is how "base scale" is set; to 16. */
|
||||
*(sample_hist+0) = 0; /* dereference first sample hist pos to something we can use. */
|
||||
*(sample_hist+1) = 0; /* dereference second sample hist pos to something we can use. */
|
||||
handle->base_pair = 0; /* set representing byte to 0 while we're at it. */
|
||||
handle->base_scale = 0x10;
|
||||
handle->sample_hist[0] = 0;
|
||||
handle->sample_hist[1] = 0;
|
||||
handle->base_pair = 0;
|
||||
handle->sample_mode = 1; /* indicates we have the setup we need to decode samples. */
|
||||
}
|
||||
/* "pinch off" of a single-byte read. remember that we need two nibbles. */
|
||||
handle->base_pair = (uint8_t)read_u8_wrapper(handle);
|
||||
|
||||
/* now pick two nibbles, subtract them, reverse the order of said nibbles so we can calc them one-by-one,
|
||||
* and finally tell handle that we're done with two samples and would like to take a break, please. */
|
||||
nibble1 = handle->base_pair & 0xf;
|
||||
nibble1_1 = nibble1 + -8;
|
||||
nibble2 = (handle->base_pair >> 4) & 0xf;
|
||||
nibble2_1 = nibble2 + -8;
|
||||
nibble2_1 = nibble2_1 * handle->base_scale;
|
||||
*(sample_hist+0) = *(sample_hist+1) + nibble2_1;
|
||||
handle->sample_hist[0] = handle->sample_hist[1] + nibble2_1;
|
||||
handle->base_scale = (handle->base_scale * (ongakukan_adpcm_filter[nibble2])) >> 8;
|
||||
nibble1_1 = nibble1_1 * handle->base_scale;
|
||||
*(sample_hist+1) = *(sample_hist+0) + nibble1_1;
|
||||
handle->sample_hist[1] = handle->sample_hist[0] + nibble1_1;
|
||||
handle->base_scale = (handle->base_scale * (ongakukan_adpcm_filter[nibble1])) >> 8;
|
||||
handle->sample_pair_is_decoded = 1;
|
||||
handle->sample_pair_is_decoded = true;
|
||||
}
|
||||
}
|
||||
|
||||
/* streamfile read function definitions at the very bottom. */
|
||||
|
||||
uint8_t read_u8_wrapper(ongakukan_adp_t* handle)
|
||||
static uint8_t read_u8_wrapper(ongakukan_adp_t* handle)
|
||||
{
|
||||
/* if data_offset minus start_offset goes beyond data_size, return 0. */
|
||||
if ((handle->data_offset - handle->start_offset) > handle->data_size) return 0;
|
||||
/* if data_offset minus start_offset goes below 0, same. */
|
||||
if ((handle->data_offset - handle->start_offset) < 0) return 0;
|
||||
/* otherwise, read the byte from data_offset and see history get made. */
|
||||
return read_u8((off_t)(handle->data_offset), handle->sf);
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
#ifndef _ONGAKUKAN_ADP_LIB_
|
||||
#define _ONGAKUKAN_ADP_LIB_
|
||||
#ifndef _ONGAKUKAN_ADP_LIB_H_
|
||||
#define _ONGAKUKAN_ADP_LIB_H_
|
||||
|
||||
/* Ongakukan ADP codec, found in PS2 and PSP games. */
|
||||
/* Ongakukan ADPCM codec, found in PS2 and PSP games. */
|
||||
|
||||
#include "../../util/reader_sf.h"
|
||||
|
||||
@ -9,23 +9,19 @@
|
||||
typedef struct ongakukan_adp_t ongakukan_adp_t;
|
||||
|
||||
/* function declaration for we need to set up the codec data. */
|
||||
ongakukan_adp_t* boot_ongakukan_adpcm(STREAMFILE* sf, long int data_offset, long int data_size,
|
||||
char sample_needs_setup, char sample_has_base_setup_from_the_start);
|
||||
ongakukan_adp_t* init_ongakukan_adpcm(STREAMFILE* sf, long int data_offset, long int data_size,
|
||||
bool sound_is_adpcm);
|
||||
|
||||
/* function declaration for freeing all memory related to ongakukan_adp_t struct var. */
|
||||
void free_all_ongakukan_adpcm(ongakukan_adp_t* handle);
|
||||
void reset_all_ongakukan_adpcm(ongakukan_adp_t* handle);
|
||||
void seek_ongakukan_adpcm_pos(ongakukan_adp_t* handle, long int target_sample);
|
||||
void ongakukan_adpcm_free(ongakukan_adp_t* handle);
|
||||
void ongakukan_adpcm_reset(ongakukan_adp_t* handle);
|
||||
void ongakukan_adpcm_seek(ongakukan_adp_t* handle, long int target_sample);
|
||||
|
||||
/* function declaration for when we need to get (and send) certain values from ongakukan_adp_t handle */
|
||||
long int grab_num_samples_from_ongakukan_adp(ongakukan_adp_t* handle);
|
||||
long int grab_samples_filled_from_ongakukan_adp(ongakukan_adp_t* handle);
|
||||
void send_samples_filled_to_ongakukan_adp(long int samples_filled, ongakukan_adp_t* handle);
|
||||
long int grab_samples_consumed_from_ongakukan_adp(ongakukan_adp_t* handle);
|
||||
void send_samples_consumed_to_ongakukan_adp(long int samples_consumed, ongakukan_adp_t* handle);
|
||||
void* grab_sample_hist_from_ongakukan_adp(ongakukan_adp_t* handle);
|
||||
long int get_num_samples_from_ongakukan_adpcm(ongakukan_adp_t* handle);
|
||||
void* get_sample_hist_from_ongakukan_adpcm(ongakukan_adp_t* handle);
|
||||
|
||||
/* function declaration for actually decoding samples, can't be that hard, right? */
|
||||
void decode_ongakukan_adp_data(ongakukan_adp_t* handle);
|
||||
void decode_ongakukan_adpcm_data(ongakukan_adp_t* handle);
|
||||
|
||||
#endif /* _ONGAKUKAN_ADP_LIB_ */
|
||||
#endif // _ONGAKUKAN_ADP_LIB_H_
|
||||
|
@ -4,94 +4,87 @@
|
||||
|
||||
struct ongakukan_adp_data
|
||||
{
|
||||
void* handle;
|
||||
int16_t* samples;
|
||||
int32_t samples_done;
|
||||
int32_t samples_filled;
|
||||
int32_t samples_consumed;
|
||||
int32_t getting_samples;
|
||||
STREAMFILE* sf;
|
||||
void* handle;
|
||||
int16_t* samples;
|
||||
int32_t samples_done;
|
||||
bool samples_filled; /* false - no, true - yes */
|
||||
int32_t getting_samples; /* initialized to 2 on decode_ongakukan_adp. i mean, we literally get two decoded samples here. */
|
||||
STREAMFILE* sf;
|
||||
};
|
||||
|
||||
ongakukan_adp_data* init_ongakukan_adp(STREAMFILE* sf, int32_t data_offset, int32_t data_size,
|
||||
char sample_needs_setup, char sample_has_base_setup_from_the_start)
|
||||
bool sound_is_adpcm)
|
||||
{
|
||||
ongakukan_adp_data* data = NULL;
|
||||
ongakukan_adp_data* data = NULL;
|
||||
|
||||
data = calloc(1, sizeof(ongakukan_adp_data));
|
||||
if (!data) goto fail;
|
||||
data = calloc(1, sizeof(ongakukan_adp_data));
|
||||
if (!data) goto fail;
|
||||
|
||||
/* reopen STREAMFILE from here, then pass it as an argument for our init function. we need to be able to read the file directly. */
|
||||
data->sf = reopen_streamfile(sf, 0);
|
||||
if (!data->sf) goto fail;
|
||||
data->handle = boot_ongakukan_adpcm(data->sf, (long int)(data_offset), (long int)(data_size),
|
||||
sample_needs_setup, sample_has_base_setup_from_the_start);
|
||||
if (!data->handle) goto fail;
|
||||
/* reopen STREAMFILE from here, then pass it as an argument for our init function. */
|
||||
data->sf = reopen_streamfile(sf, 0);
|
||||
if (!data->sf) goto fail;
|
||||
data->handle = init_ongakukan_adpcm(data->sf, (long int)(data_offset), (long int)(data_size),
|
||||
sound_is_adpcm);
|
||||
if (!data->handle) goto fail;
|
||||
|
||||
printf(" return data; \n");
|
||||
return data;
|
||||
return data;
|
||||
fail:
|
||||
free_ongakukan_adp(data);
|
||||
return NULL;
|
||||
free_ongakukan_adp(data);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void decode_ongakukan_adp(VGMSTREAM* vgmstream, sample_t* outbuf, int32_t samples_to_do)
|
||||
{
|
||||
ongakukan_adp_data* data = vgmstream->codec_data;
|
||||
ongakukan_adp_data* data = vgmstream->codec_data;
|
||||
|
||||
data->samples_done = 0;
|
||||
data->samples_filled = 0;
|
||||
data->samples_consumed = 0;
|
||||
data->samples = (int16_t*)grab_sample_hist_from_ongakukan_adp(data->handle); /* this'll return a pointer from the handle containing the samples themselves. */
|
||||
while (data->samples_done < samples_to_do)
|
||||
{
|
||||
if (data->samples_filled)
|
||||
{
|
||||
data->getting_samples = data->samples_filled;
|
||||
if (data->getting_samples > samples_to_do - data->samples_done)
|
||||
data->getting_samples = samples_to_do - data->samples_done;
|
||||
data->getting_samples = 2;
|
||||
data->samples_done = 0;
|
||||
data->samples_filled = false;
|
||||
/* ^ samples_filled is boolean here because we need to simplify how decoding will work here.
|
||||
* so, rather than making samples_filled into a long int counter,
|
||||
* we make it into a boolean flag instead so as to let data->samples_done shine as a counter
|
||||
* and the decoder to do its job without worry. */
|
||||
|
||||
memcpy(outbuf + data->samples_done,
|
||||
data->samples + data->samples_consumed,
|
||||
data->getting_samples * sizeof(int16_t));
|
||||
data->samples_done += data->getting_samples;
|
||||
while (data->samples_done < samples_to_do)
|
||||
{
|
||||
if (data->samples_filled)
|
||||
{
|
||||
memcpy(outbuf + data->samples_done,
|
||||
data->samples,
|
||||
data->getting_samples * sizeof(int16_t));
|
||||
data->samples_done += data->getting_samples;
|
||||
|
||||
/* mark consumed samples. */
|
||||
data->samples_consumed += data->getting_samples;
|
||||
data->samples_filled -= data->getting_samples;
|
||||
|
||||
/* and keep the lib updated while doing so. */
|
||||
send_samples_consumed_to_ongakukan_adp((long int)(data->samples_consumed), data->handle);
|
||||
send_samples_filled_to_ongakukan_adp((long int)(data->samples_filled), data->handle);
|
||||
}
|
||||
else { decode_ongakukan_adp_data(data->handle);
|
||||
data->samples_filled = (int32_t)grab_samples_filled_from_ongakukan_adp(data->handle);
|
||||
data->samples_consumed = (int32_t)grab_samples_consumed_from_ongakukan_adp(data->handle);
|
||||
data->samples = (int16_t*)grab_sample_hist_from_ongakukan_adp(data->handle); }
|
||||
}
|
||||
data->samples_filled = false;
|
||||
}
|
||||
else { decode_ongakukan_adpcm_data(data->handle);
|
||||
data->samples_filled = true;
|
||||
data->samples = (int16_t*)get_sample_hist_from_ongakukan_adpcm(data->handle); }
|
||||
}
|
||||
}
|
||||
|
||||
void reset_ongakukan_adp(ongakukan_adp_data* data)
|
||||
{
|
||||
if (!data) return;
|
||||
reset_all_ongakukan_adpcm(data->handle);
|
||||
if (!data) return;
|
||||
ongakukan_adpcm_reset(data->handle);
|
||||
}
|
||||
|
||||
void seek_ongakukan_adp(ongakukan_adp_data* data, int32_t current_sample)
|
||||
{
|
||||
if (!data) return;
|
||||
seek_ongakukan_adpcm_pos(data->handle, current_sample);
|
||||
if (!data) return;
|
||||
ongakukan_adpcm_seek(data->handle, current_sample);
|
||||
}
|
||||
|
||||
void free_ongakukan_adp(ongakukan_adp_data* data)
|
||||
{
|
||||
if (!data) return;
|
||||
free_all_ongakukan_adpcm(data->handle);
|
||||
free(data);
|
||||
if (!data) return;
|
||||
close_streamfile(data->sf);
|
||||
ongakukan_adpcm_free(data->handle);
|
||||
free(data->samples);
|
||||
free(data);
|
||||
}
|
||||
|
||||
int32_t ongakukan_adp_get_samples(ongakukan_adp_data* data)
|
||||
{
|
||||
if (!data) return 0;
|
||||
return (int32_t)(grab_num_samples_from_ongakukan_adp(data->handle));
|
||||
if (!data) return 0;
|
||||
return (int32_t)get_num_samples_from_ongakukan_adpcm(data->handle);
|
||||
}
|
||||
|
@ -4,113 +4,100 @@
|
||||
/* Ongakukan RIFF with "ADP" extension [Train Simulator - Midousuji-sen (PS2)] */
|
||||
VGMSTREAM* init_vgmstream_ongakukan_adp(STREAMFILE* sf)
|
||||
{
|
||||
VGMSTREAM* vgmstream = NULL;
|
||||
off_t start_offset;
|
||||
size_t file_size;
|
||||
int has_data_chunk = 0, has_fact_chunk = 0, found_start_offset = 0;
|
||||
int loop_flag = 0;
|
||||
int riff_wave_header_size = 0x2c;
|
||||
char sound_is_adpcm = 0, sample_has_base_setup_from_the_start = 0;
|
||||
/* ^ the entire RIFF WAVE header size, set to this fixed number
|
||||
* because *surprise* this is also how sound data begins. */
|
||||
int32_t fmt_size, fmt_offset, offset_of_supposed_last_chunk;
|
||||
int32_t sample_rate, bitrate, data_size;
|
||||
int16_t num_channels, block_size;
|
||||
VGMSTREAM* vgmstream = NULL;
|
||||
off_t start_offset;
|
||||
size_t file_size;
|
||||
bool has_data_chunk = false, has_fact_chunk = false;
|
||||
int loop_flag = 0;
|
||||
int riff_wave_header_size = 0x2c;
|
||||
/* ^ where sound data begins, as a consequence their tools couldn't even write full RIFF WAVE header to file beyond that point.. */
|
||||
bool sound_is_adpcm = false;
|
||||
int32_t supposed_size, fmt_size, fmt_offset, offset_of_supposed_last_chunk;
|
||||
int32_t sample_rate, data_size;
|
||||
int16_t num_channels, block_size;
|
||||
|
||||
/* RIFF+WAVE checks */
|
||||
if (!is_id32be(0x00, sf, "RIFF")) goto fail;
|
||||
if (!is_id32be(0x08, sf, "WAVE")) goto fail;
|
||||
/* WAVE "fmt " check */
|
||||
if (!is_id32be(0x0c, sf, "fmt ")) goto fail;
|
||||
/* "adp" extension check (literally only one) */
|
||||
if (!check_extensions(sf, "adp")) goto fail;
|
||||
|
||||
/* catch adp file size from here and use it whenever needed. */
|
||||
file_size = get_streamfile_size(sf);
|
||||
/* RIFF+WAVE checks */
|
||||
if (!is_id32be(0x00, sf, "RIFF")) goto fail;
|
||||
if (!is_id32be(0x08, sf, "WAVE")) goto fail;
|
||||
/* WAVE "fmt " check */
|
||||
if (!is_id32be(0x0c, sf, "fmt ")) goto fail;
|
||||
/* "adp" extension check (literally only one) */
|
||||
if (!check_extensions(sf, "adp")) goto fail;
|
||||
|
||||
/* RIFF size from adp file (e.g: 10MB) can go beyond actual adp file size (e.g: 2MB),
|
||||
* have vgmstream call it quits if the former is reported to be less than the latter. */
|
||||
if (read_s32le(0x04, sf) < file_size) goto fail;
|
||||
/* catch adp file size from here and use it whenever needed. */
|
||||
file_size = get_streamfile_size(sf);
|
||||
|
||||
/* read entire WAVE "fmt " chunk. we start by reading fmt_size from yours truly and setting fmt_offset. */
|
||||
fmt_size = read_s32le(0x10, sf);
|
||||
fmt_offset = 0x14;
|
||||
if ((fmt_size >= 0x10) && (fmt_size <= 0x12)) /* fmt_size is mostly 0x10, rarely 0x12 */
|
||||
{
|
||||
if (read_s16le(fmt_offset + 0, sf) != 1) goto fail; /* chunk reports codec number as signed little-endian PCM, couldn't be more wrong. */
|
||||
num_channels = read_s16le(fmt_offset + 2, sf);
|
||||
sample_rate = read_s32le(fmt_offset + 4, sf);
|
||||
bitrate = read_s32le(fmt_offset + 8, sf);
|
||||
/* ^ yes, this is technically correct, tho does not reflect actual data.
|
||||
* chunk reports bitrate as if it was a 16-bit PCM file. */
|
||||
block_size = read_s16le(fmt_offset + 12, sf); /* mostly 2, rarely 4. */
|
||||
if (read_s16le(fmt_offset + 14, sf) != 0x10) goto fail; /* bit depth as chunk reports it. */
|
||||
/* additional checks, this time with bitrate field in the chunk. */
|
||||
if (bitrate != (sample_rate * block_size)) goto fail;
|
||||
/* if fmt_size == 0x12 there is an additional s16 field that's always zero. */
|
||||
}
|
||||
else {
|
||||
goto fail;
|
||||
}
|
||||
/* RIFF size from adp file can go beyond actual size (e.g: reported 10MB vs 2MB). do quick calcs around this. */
|
||||
supposed_size = ((read_s32le(0x04, sf) - 0x24) >> 2) + 0x2c;
|
||||
if (file_size != supposed_size) goto fail;
|
||||
|
||||
/* now calc the var so we can read either "data" or "fact" chunk; */
|
||||
offset_of_supposed_last_chunk = fmt_offset + fmt_size;
|
||||
/* read entire WAVE "fmt " chunk. we start by reading fmt_size from yours truly and setting fmt_offset. */
|
||||
fmt_size = read_s32le(0x10, sf);
|
||||
fmt_offset = 0x14;
|
||||
if ((fmt_size >= 0x10) && (fmt_size <= 0x12)) /* depending on the adp, fmt_size alternates between 0x10 and 0x12 */
|
||||
{
|
||||
if (read_s16le(fmt_offset + 0, sf) != 1) goto fail; /* chunk reports codec number as signed little-endian PCM, couldn't be more wrong. */
|
||||
num_channels = read_s16le(fmt_offset + 2, sf);
|
||||
sample_rate = read_s32le(fmt_offset + 4, sf);
|
||||
if (read_s16le(fmt_offset + 14, sf) != 0x10) goto fail; /* bit depth as chunk reports it. */
|
||||
/* rest of fmt header is the usual header for 16-bit PCM wav files: bitrate, block size, and the like (see riff.c) */
|
||||
/* if fmt_size == 0x12 there is an additional s16 field that's always zero. */
|
||||
}
|
||||
else {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* then read either one of the two chunks, both cannot co-exist it seems.
|
||||
* while there, we set start_offset by themselves instead of relying on both chunks to do so.
|
||||
* see comments for code handling both chunks below for more info. */
|
||||
if (is_id32be(offset_of_supposed_last_chunk + 0, sf, "data")) has_data_chunk = 1;
|
||||
if (is_id32be(offset_of_supposed_last_chunk + 0, sf, "fact")) has_fact_chunk = 1;
|
||||
/* now calc the var so we can read either "data" or "fact" chunk; */
|
||||
offset_of_supposed_last_chunk = fmt_offset + fmt_size;
|
||||
|
||||
/* and because sound data *must* start at 0x2c, they have to bork both chunks too, so they're now essentially useless.
|
||||
* well, except for trying to deduct how many samples a sound actually has*/
|
||||
if (has_data_chunk)
|
||||
{
|
||||
/* RIFF adp files have borked "data" chunk so much it's not even remotely useful for... well, *anything*, really.
|
||||
* it doesn't report actual data size as RIFF WAVE files usually do, instead we're left with basically RIFF size with ~50 less numbers now. */
|
||||
if (read_s32le(offset_of_supposed_last_chunk + 4, sf) < file_size) goto fail;
|
||||
/* ^ reported data size is meant to be bigger than actual adp size, have vgmstream throw out the towel if it isn't. */
|
||||
}
|
||||
/* we need to get to the last WAVE chunk manually, and that means the calc below. */
|
||||
offset_of_supposed_last_chunk = fmt_offset + fmt_size;
|
||||
if (is_id32be(offset_of_supposed_last_chunk + 0, sf, "data")) has_data_chunk = true;
|
||||
if (is_id32be(offset_of_supposed_last_chunk + 0, sf, "fact")) has_fact_chunk = true;
|
||||
|
||||
if (has_fact_chunk)
|
||||
{
|
||||
/* RIFF adp files also borked "fact" chunk so it no longer reports useful info.
|
||||
* instead it just leaves out a s16 field containing a static number. */
|
||||
if (read_s16le(offset_of_supposed_last_chunk + 4, sf) != 4) goto fail;
|
||||
/* ^ this number is supposed to be 4, have vgmstream ragequit if it isn't. */
|
||||
}
|
||||
/* and because sound data *must* start at 0x2c, they have to bork both chunks too, so they're now essentially useless.
|
||||
* they're basically leftovers from original (lossless) WAV files at this point. */
|
||||
if (has_data_chunk)
|
||||
{
|
||||
/* RIFF adp files have leftover "data" chunk size... that does NOT match the ADP file size at hand. */
|
||||
supposed_size = (read_s32le(offset_of_supposed_last_chunk + 4, sf) >> 2) + 0x2c;
|
||||
if (file_size != supposed_size) goto fail;
|
||||
}
|
||||
|
||||
/* set start_offset value to riff_wave_header_size
|
||||
* and calculate data_size by ourselves */
|
||||
start_offset = riff_wave_header_size;
|
||||
data_size = (int32_t)(file_size) - riff_wave_header_size;
|
||||
if (has_fact_chunk)
|
||||
{
|
||||
/* RIFF adp files have also cut off "fact" chunk so we're just left with a useless number now. */
|
||||
if (read_s16le(offset_of_supposed_last_chunk + 4, sf) != 4) goto fail;
|
||||
}
|
||||
|
||||
/* Ongagukan games using this format just read it by checking "ADP" extension
|
||||
* in an provided file name of a programmer's own choosing,
|
||||
* and if it's there they just read the reported "number of samples" and sample_rate from RIFF WAVE "fmt " chunk
|
||||
* based on an already-opened file with that same name.
|
||||
* they also calculate start_offset and data_size in much the same manner. */
|
||||
/* set start_offset value to riff_wave_header_size and calculate data_size by ourselves, basically how Ongakukan does it also. */
|
||||
start_offset = riff_wave_header_size;
|
||||
data_size = (int32_t)(file_size) - riff_wave_header_size;
|
||||
|
||||
/* silly flags, needed to init our custom decoder. */
|
||||
sound_is_adpcm = 1;
|
||||
sample_has_base_setup_from_the_start = 1;
|
||||
/* Ongagukan games using this format just read it by checking "ADP" extension in an provided file name of a programmer's own choosing,
|
||||
* and if extension is there they just read the reported "number of samples" and "sample_rate" vars
|
||||
* from RIFF WAVE "fmt " chunk based on an already-opened file with that same name.
|
||||
* and they don't even read RIFF chunks, they just pick these two vars and that's basically it. */
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(num_channels, loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
/* our custom decoder needs at least one flag set. */
|
||||
sound_is_adpcm = true;
|
||||
|
||||
vgmstream->meta_type = meta_ONGAKUKAN_RIFF_ADP;
|
||||
vgmstream->sample_rate = sample_rate;
|
||||
vgmstream->codec_data = init_ongakukan_adp(sf, start_offset, data_size, sound_is_adpcm, sample_has_base_setup_from_the_start);
|
||||
if (!vgmstream->codec_data) goto fail;
|
||||
vgmstream->coding_type = coding_ONGAKUKAN_ADPCM;
|
||||
vgmstream->layout_type = layout_none;
|
||||
vgmstream->num_samples = ongakukan_adp_get_samples(vgmstream->codec_data);
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(num_channels, loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
if (!vgmstream_open_stream(vgmstream, sf, start_offset))
|
||||
goto fail;
|
||||
return vgmstream;
|
||||
vgmstream->meta_type = meta_ONGAKUKAN_RIFF_ADP;
|
||||
vgmstream->sample_rate = sample_rate;
|
||||
vgmstream->codec_data = init_ongakukan_adp(sf, start_offset, data_size, sound_is_adpcm);
|
||||
if (!vgmstream->codec_data) goto fail;
|
||||
vgmstream->coding_type = coding_ONGAKUKAN_ADPCM;
|
||||
vgmstream->layout_type = layout_none;
|
||||
vgmstream->num_samples = ongakukan_adp_get_samples(vgmstream->codec_data);
|
||||
|
||||
if (!vgmstream_open_stream(vgmstream, sf, start_offset))
|
||||
goto fail;
|
||||
return vgmstream;
|
||||
fail:
|
||||
close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
||||
|
@ -350,7 +350,6 @@ VGMSTREAM* init_vgmstream_riff(STREAMFILE* sf) {
|
||||
off_t mwv_pflt_offset = 0;
|
||||
off_t mwv_ctrl_offset = 0;
|
||||
int ignore_riff_size = 0;
|
||||
int riff_size_bigger_than_pcm_file = 0;
|
||||
|
||||
|
||||
/* checks*/
|
||||
@ -474,17 +473,8 @@ VGMSTREAM* init_vgmstream_riff(STREAMFILE* sf) {
|
||||
file_size -= 0x40; /* [Megami no Etsubo (PSP)] (has extra padding in all files) */
|
||||
|
||||
else if (codec == 0x0011 && file_size - riff_size - 0x08 <= 0x900 && is_id32be(riff_size + 0x08, sf, "cont"))
|
||||
riff_size = file_size - 0x08; /* [Shin Megami Tensei: Imagine (PC)] (extra "cont" info 0x800/0x900 chunk) */
|
||||
riff_size = file_size - 0x08; /* [Shin Megami Tensei: Imagine (PC)] (extra "cont" info 0x800/0x900 chunk) */
|
||||
|
||||
else if (codec == 0x0001 && riff_size > file_size + 0x8000)
|
||||
riff_size_bigger_than_pcm_file = 1; /* [Train Simulator: Midousuji-hen (PS2)] (reports bigger RIFF sizes than should be possible) */
|
||||
}
|
||||
|
||||
/* throw out when RIFF size is bigger than actual file size */
|
||||
if (riff_size_bigger_than_pcm_file)
|
||||
{
|
||||
/* vgm_logi("RIFF: reported size (%d) bigger than actual file (%d)\n", riff_size, file_size); */
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* check for truncated RIFF */
|
||||
@ -1205,7 +1195,7 @@ VGMSTREAM* init_vgmstream_rifx(STREAMFILE* sf) {
|
||||
/* end must add +1, but check in case of faulty tools */
|
||||
if (vgmstream->loop_end_sample - 1 == vgmstream->num_samples)
|
||||
vgmstream->loop_end_sample--;
|
||||
|
||||
|
||||
vgmstream->meta_type = meta_RIFX_WAVE_smpl;
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user