vgmstream/src/meta/awc_xma_streamfile.h

115 lines
4.7 KiB
C
Raw Normal View History

2020-03-09 00:58:50 +01:00
#ifndef _AWC_XMA_STREAMFILE_H_
#define _AWC_XMA_STREAMFILE_H_
#include "deblock_streamfile.h"
static size_t get_block_header_size(STREAMFILE* sf, off_t offset, int channels);
static size_t get_repeated_data_size(STREAMFILE* sf, off_t next_offset, size_t repeat_samples);
static size_t get_block_skip_count(STREAMFILE* sf, off_t offset, int channel);
static void block_callback(STREAMFILE *sf, deblock_io_data* data) {
const size_t frame_size = 0x800;
int channel = data->cfg.track_number;
int channels = data->cfg.track_count;
/* Blocks have a header then data per channel, each with a different num_samples/frames,
* separate (first all frames of ch0, then ch1, etc), padded, and sometimes the last few
* frames of a channel are repeated in the new block (marked with "repeat samples"). */
size_t header_size = get_block_header_size(sf, data->physical_offset, channels);
/* header table entries = frames... I hope */
size_t others_size = get_block_skip_count(sf, data->physical_offset, channel) * frame_size;
//size_t skip_size = read_u32be(data->physical_offset + 0x10*channel + 0x00, sf) * frame_size;
size_t data_size = read_u32be(data->physical_offset + 0x10*channel + 0x04, sf) * frame_size;
size_t repeat_samples = read_u32be(data->physical_offset + 0x10*channel + 0x08, sf);
size_t repeat_size = 0;
data->block_size = data->cfg.chunk_size;
/* if there are repeat samples current block repeats some frames from last block, find out size */
if (repeat_samples) {
off_t data_offset = data->physical_offset + header_size + others_size;
repeat_size = get_repeated_data_size(sf, data_offset, repeat_samples);
}
data->skip_size = header_size + others_size + repeat_size;
data->data_size = data_size - repeat_size;
}
/* block header size, aligned/padded to 0x800 */
static size_t get_block_header_size(STREAMFILE* sf, off_t offset, int channels) {
size_t header_size = 0;
int i;
for (i = 0; i < channels; i++) {
header_size += 0x10;
header_size += read_u32be(offset + 0x10*i + 0x04, sf) * 0x04; /* entries in the table */
}
if (header_size % 0x800) /* padded */
header_size += 0x800 - (header_size % 0x800);
return header_size;
}
/* find data that repeats in the beginning of a new block at the end of last block */
static size_t get_repeated_data_size(STREAMFILE* sf, off_t next_offset, size_t repeat_samples) {
const size_t frame_size = 0x800;
const size_t samples_per_subframe = 512;
size_t samples_this_frame;
uint8_t subframes;
//todo: fix this
/* Repeat samples are the number of decoded samples to discard, but in this streamfile we can't do that.
* Also XMA is VBR, and may encode silent frames with up to 63 subframes yet we may have few repeat samples.
* We could find out how many subframes of 512 samples to skip, then adjust the XMA frame header, though
* output will be slightly off since subframes are related.
*
* For now just skip a full frame depending on the number of subframes vs repeat samples.
* Most files work ok-ish but channels may desync slightly. */
subframes = ((uint8_t)read_8bit(next_offset,sf) >> 2) & 0x3F; /* peek into frame header */
samples_this_frame = subframes*samples_per_subframe;
if (repeat_samples >= (int)(samples_this_frame*0.13)) { /* skip mosts */
return frame_size;
}
else {
return 0;
}
}
/* header has a skip value, but somehow it's sometimes bigger than expected (WHY!?!?) so just sum all */
static size_t get_block_skip_count(STREAMFILE* sf, off_t offset, int channel) {
size_t skip_count = 0;
int i;
//skip_size = read_u32be(offset + 0x10*channel + 0x00, sf); /* wrong! */
for (i = 0; i < channel; i++) {
skip_count += read_u32be(offset + 0x10*i + 0x04, sf); /* number of frames of this channel */
}
return skip_count;
}
/* Deblocks interleaved XMA in AWC blocks */
static STREAMFILE* setup_awc_xma_streamfile(STREAMFILE *sf, off_t stream_offset, size_t stream_size, size_t block_size, int channel_count, int channel) {
STREAMFILE *new_sf = NULL;
deblock_config_t cfg = {0};
cfg.track_number = channel;
cfg.track_count = channel_count;
cfg.stream_start = stream_offset;
cfg.stream_size = stream_size;
cfg.chunk_size = block_size;
//cfg.physical_offset = stream_offset;
//cfg.logical_size = awc_xma_io_size(sf, &cfg); /* force init */
cfg.block_callback = block_callback;
new_sf = open_wrap_streamfile(sf);
new_sf = open_io_deblock_streamfile_f(new_sf, &cfg);
//new_sf = open_buffer_streamfile_f(new_sf, 0);
return new_sf;
}
#endif /* _AWC_XMA_STREAMFILE_H_ */