Improve XOpus handling + prepare Wwise Opus

This commit is contained in:
bnnm 2020-11-09 14:50:33 +01:00
parent fa51ac7738
commit ba88be7a47
3 changed files with 200 additions and 138 deletions

View File

@ -540,14 +540,18 @@ typedef struct {
int coupled_count; int coupled_count;
int stream_count; int stream_count;
int channel_mapping[8]; int channel_mapping[8];
/* frame table */
off_t table_offset;
int table_count;
} opus_config; } opus_config;
ffmpeg_codec_data* init_ffmpeg_switch_opus_config(STREAMFILE* sf, off_t start_offset, size_t data_size, opus_config* cfg); ffmpeg_codec_data* init_ffmpeg_switch_opus_config(STREAMFILE* sf, off_t start_offset, size_t data_size, opus_config* cfg);
ffmpeg_codec_data* init_ffmpeg_switch_opus(STREAMFILE* sf, off_t start_offset, size_t data_size, int channels, int skip, int sample_rate); ffmpeg_codec_data* init_ffmpeg_switch_opus(STREAMFILE* sf, off_t start_offset, size_t data_size, int channels, int skip, int sample_rate);
ffmpeg_codec_data* init_ffmpeg_ue4_opus(STREAMFILE* sf, off_t start_offset, size_t data_size, int channels, int skip, int sample_rate); ffmpeg_codec_data* init_ffmpeg_ue4_opus(STREAMFILE* sf, off_t start_offset, size_t data_size, int channels, int skip, int sample_rate);
ffmpeg_codec_data* init_ffmpeg_ea_opus(STREAMFILE* sf, off_t start_offset, size_t data_size, int channels, int skip, int sample_rate); ffmpeg_codec_data* init_ffmpeg_ea_opus(STREAMFILE* sf, off_t start_offset, size_t data_size, int channels, int skip, int sample_rate);
ffmpeg_codec_data* init_ffmpeg_x_opus(STREAMFILE* sf, off_t start_offset, size_t data_size, int channels, int skip, int sample_rate); ffmpeg_codec_data* init_ffmpeg_x_opus(STREAMFILE* sf, off_t table_offset, int table_count, off_t data_offset, size_t data_size, int channels, int skip);
ffmpeg_codec_data* init_ffmpeg_fsb_opus(STREAMFILE* sf, off_t start_offset, size_t data_size, int channels, int skip, int sample_rate); ffmpeg_codec_data* init_ffmpeg_fsb_opus(STREAMFILE* sf, off_t start_offset, size_t data_size, int channels, int skip, int sample_rate);
ffmpeg_codec_data* init_ffmpeg_wwise_opus(STREAMFILE* sf, off_t table_offset, int table_count, off_t data_offset, size_t data_size, int channels, int skip);
size_t switch_opus_get_samples(off_t offset, size_t stream_size, STREAMFILE* sf); size_t switch_opus_get_samples(off_t offset, size_t stream_size, STREAMFILE* sf);

View File

@ -17,13 +17,12 @@
* https://github.com/hcs64/ww2ogg * https://github.com/hcs64/ww2ogg
*/ */
typedef enum { OPUS_SWITCH, OPUS_UE4_v1, OPUS_UE4_v2, OPUS_EA, OPUS_X, OPUS_FSB } opus_type_t; typedef enum { OPUS_SWITCH, OPUS_UE4_v1, OPUS_UE4_v2, OPUS_EA, OPUS_X, OPUS_FSB, OPUS_WWISE } opus_type_t;
static size_t make_oggs_first(uint8_t *buf, int buf_size, opus_config *cfg); static size_t make_oggs_first(uint8_t *buf, int buf_size, opus_config *cfg);
static size_t make_oggs_page(uint8_t *buf, int buf_size, size_t data_size, int page_sequence, int granule); static size_t make_oggs_page(uint8_t *buf, int buf_size, size_t data_size, int page_sequence, int granule);
static size_t opus_get_packet_samples(const uint8_t *buf, int len); static size_t opus_get_packet_samples(const uint8_t *buf, int len);
static size_t opus_get_packet_samples_sf(STREAMFILE* sf, off_t offset); static size_t opus_get_packet_samples_sf(STREAMFILE* sf, off_t offset);
static size_t get_xopus_packet_size(int packet, STREAMFILE* sf);
static opus_type_t get_ue4opus_version(STREAMFILE* sf, off_t offset); static opus_type_t get_ue4opus_version(STREAMFILE* sf, off_t offset);
typedef struct { typedef struct {
@ -32,6 +31,11 @@ typedef struct {
off_t stream_offset; off_t stream_offset;
size_t stream_size; size_t stream_size;
/* list of OPUS frame sizes, for variations that preload this (must alloc/dealloc on init/close) */
off_t table_offset;
int table_count;
uint16_t* frame_table;
/* state */ /* state */
off_t logical_offset; /* offset that corresponds to physical_offset */ off_t logical_offset; /* offset that corresponds to physical_offset */
off_t physical_offset; /* actual file offset */ off_t physical_offset; /* actual file offset */
@ -46,8 +50,11 @@ typedef struct {
size_t head_size; /* OggS head page size */ size_t head_size; /* OggS head page size */
size_t logical_size; size_t logical_size;
} opus_io_data; } opus_io_data;
static size_t get_table_frame_size(opus_io_data* data, int packet);
/* Convers custom Opus packets to Ogg Opus, so the resulting data is larger than physical data. */ /* Convers custom Opus packets to Ogg Opus, so the resulting data is larger than physical data. */
static size_t opus_io_read(STREAMFILE* sf, uint8_t *dest, off_t offset, size_t length, opus_io_data* data) { static size_t opus_io_read(STREAMFILE* sf, uint8_t *dest, off_t offset, size_t length, opus_io_data* data) {
@ -59,12 +66,12 @@ static size_t opus_io_read(STREAMFILE* sf, uint8_t *dest, off_t offset, size_t l
} }
/* previous offset: re-start as we can't map logical<>physical offsets */ /* previous offset: re-start as we can't map logical<>physical offsets */
if (offset < data->logical_offset) { if (offset < data->logical_offset || data->logical_offset < 0) {
data->physical_offset = data->stream_offset; data->physical_offset = data->stream_offset;
data->logical_offset = 0x00; data->logical_offset = 0x00;
data->page_size = 0; data->page_size = 0;
data->samples_done = 0; data->samples_done = 0;
data->sequence = 2; /* appended header is 0/1 */ data->sequence = 2; /* appended header+comment is 0/1 */
if (offset >= data->head_size) if (offset >= data->head_size)
data->logical_offset = data->head_size; data->logical_offset = data->head_size;
@ -119,7 +126,8 @@ static size_t opus_io_read(STREAMFILE* sf, uint8_t *dest, off_t offset, size_t l
skip_size = 0x02; skip_size = 0x02;
break; break;
case OPUS_X: case OPUS_X:
data_size = get_xopus_packet_size(data->sequence - 2, sf); case OPUS_WWISE:
data_size = get_table_frame_size(data, data->sequence - 2);
skip_size = 0; skip_size = 0;
break; break;
default: default:
@ -132,7 +140,7 @@ static size_t opus_io_read(STREAMFILE* sf, uint8_t *dest, off_t offset, size_t l
data->page_size = oggs_size + data_size; data->page_size = oggs_size + data_size;
if (data->page_size > sizeof(data->page_buffer)) { /* happens on bad reads/EOF too */ if (data->page_size > sizeof(data->page_buffer)) { /* happens on bad reads/EOF too */
VGM_LOG("OPUS: buffer can't hold OggS at %x\n", (uint32_t)data->physical_offset); VGM_LOG("OPUS: buffer can't hold OggS at %x, size=%x\n", (uint32_t)data->physical_offset, data->page_size);
data->page_size = 0; data->page_size = 0;
break; break;
} }
@ -219,7 +227,8 @@ static size_t opus_io_size(STREAMFILE* sf, opus_io_data* data) {
skip_size = 0x02; skip_size = 0x02;
break; break;
case OPUS_X: case OPUS_X:
data_size = get_xopus_packet_size(packet, sf); case OPUS_WWISE:
data_size = get_table_frame_size(data, packet);
skip_size = 0x00; skip_size = 0x00;
break; break;
default: default:
@ -253,39 +262,62 @@ static size_t opus_io_size(STREAMFILE* sf, opus_io_data* data) {
return data->logical_size; return data->logical_size;
} }
static int opus_io_init(STREAMFILE* sf, opus_io_data* data) {
//;VGM_LOG("OPUS: init\n");
/* read table containing frame sizes */
if (data->table_count) {
int i;
//;VGM_LOG("OPUS: reading table, offset=%lx, entries=%i\n", data->table_offset, data->table_count);
data->frame_table = malloc(data->table_count * sizeof(uint16_t));
if (!data->frame_table) goto fail;
for (i = 0; i < data->table_count; i++) {
data->frame_table[i] = read_u16le(data->table_offset + i * 0x02, sf);
}
}
data->logical_offset = -1; /* force reset in case old data was cloned when re-opening SFs */
data->logical_size = opus_io_size(sf, data); /* force size */
return 1;
fail:
free(data->frame_table);
return 0;
}
static void opus_io_close(STREAMFILE* sf, opus_io_data* data) {
//;VGM_LOG("OPUS: closing\n");
free(data->frame_table);
}
/* Prepares custom IO for custom Opus, that is converted to Ogg Opus on the fly */ /* Prepares custom IO for custom Opus, that is converted to Ogg Opus on the fly */
static STREAMFILE* setup_opus_streamfile(STREAMFILE* sf, opus_config *cfg, off_t stream_offset, size_t stream_size, opus_type_t type) { static STREAMFILE* setup_opus_streamfile(STREAMFILE* sf, opus_config *cfg, off_t stream_offset, size_t stream_size, opus_type_t type) {
STREAMFILE* temp_sf = NULL, *new_sf = NULL; STREAMFILE* new_sf = NULL;
opus_io_data io_data = {0}; opus_io_data io_data = {0};
size_t io_data_size = sizeof(opus_io_data);
if (!cfg->sample_rate)
cfg->sample_rate = 48000; /* default / only value for opus */
io_data.type = type; io_data.type = type;
io_data.stream_offset = stream_offset; io_data.stream_offset = stream_offset;
io_data.stream_size = stream_size; io_data.stream_size = stream_size;
io_data.physical_offset = stream_offset; io_data.physical_offset = stream_offset;
io_data.table_offset = cfg->table_offset;
io_data.table_count = cfg->table_count;
io_data.head_size = make_oggs_first(io_data.head_buffer, sizeof(io_data.head_buffer), cfg); io_data.head_size = make_oggs_first(io_data.head_buffer, sizeof(io_data.head_buffer), cfg);
if (!io_data.head_size) goto fail; if (!io_data.head_size) goto fail;
io_data.sequence = 2;
io_data.logical_size = opus_io_size(sf, &io_data); /* force init */
/* setup subfile */ /* setup subfile */
new_sf = open_wrap_streamfile(sf); new_sf = open_wrap_streamfile(sf);
if (!new_sf) goto fail; new_sf = open_io_streamfile_ex_f(new_sf, &io_data, sizeof(opus_io_data), opus_io_read, opus_io_size, opus_io_init, opus_io_close);
temp_sf = new_sf; new_sf = open_buffer_streamfile_f(new_sf, 0);
return new_sf;
new_sf = open_io_streamfile(temp_sf, &io_data,io_data_size, opus_io_read,opus_io_size);
if (!new_sf) goto fail;
temp_sf = new_sf;
new_sf = open_buffer_streamfile(new_sf,0);
if (!new_sf) goto fail;
temp_sf = new_sf;
return temp_sf;
fail: fail:
close_streamfile(temp_sf);
return NULL; return NULL;
} }
@ -328,7 +360,7 @@ static uint32_t crc_lookup[256]={
}; };
/* from ww2ogg */ /* from ww2ogg */
static uint32_t get_oggs_checksum(uint8_t * data, int bytes) { static uint32_t get_oggs_checksum(uint8_t* data, int bytes) {
uint32_t crc_reg=0; uint32_t crc_reg=0;
int i; int i;
@ -339,7 +371,7 @@ static uint32_t get_oggs_checksum(uint8_t * data, int bytes) {
} }
/* from opus_decoder.c's opus_packet_get_samples_per_frame */ /* from opus_decoder.c's opus_packet_get_samples_per_frame */
static uint32_t opus_packet_get_samples_per_frame(const uint8_t * data, int Fs) { static uint32_t opus_packet_get_samples_per_frame(const uint8_t* data, int Fs) {
int audiosize; int audiosize;
if (data[0]&0x80) if (data[0]&0x80)
{ {
@ -359,7 +391,7 @@ static uint32_t opus_packet_get_samples_per_frame(const uint8_t * data, int Fs)
} }
/* from opus_decoder.c's opus_packet_get_nb_frames */ /* from opus_decoder.c's opus_packet_get_nb_frames */
static int opus_packet_get_nb_frames(const uint8_t * packet, int len) { static int opus_packet_get_nb_frames(const uint8_t* packet, int len) {
int count; int count;
if (len<1) if (len<1)
return 0; return 0;
@ -376,7 +408,7 @@ static int opus_packet_get_nb_frames(const uint8_t * packet, int len) {
/* ******************************** */ /* ******************************** */
static size_t make_oggs_page(uint8_t * buf, int buf_size, size_t data_size, int page_sequence, int granule) { static size_t make_oggs_page(uint8_t* buf, int buf_size, size_t data_size, int page_sequence, int granule) {
size_t page_done, lacing_done = 0; size_t page_done, lacing_done = 0;
uint64_t absolute_granule = granule; /* wrong values seem validated (0, less than real samples, etc) */ uint64_t absolute_granule = granule; /* wrong values seem validated (0, less than real samples, etc) */
int header_type_flag = (page_sequence==0 ? 2 : 0); int header_type_flag = (page_sequence==0 ? 2 : 0);
@ -390,15 +422,15 @@ static size_t make_oggs_page(uint8_t * buf, int buf_size, size_t data_size, int
} }
segment_count = (int)(data_size / 0xFF + 1); segment_count = (int)(data_size / 0xFF + 1);
put_32bitBE(buf+0x00, 0x4F676753); /* capture pattern ("OggS") */ put_u32be(buf+0x00, 0x4F676753); /* capture pattern ("OggS") */
put_8bit (buf+0x04, 0); /* stream structure version, fixed */ put_u8 (buf+0x04, 0); /* stream structure version, fixed */
put_8bit (buf+0x05, header_type_flag); /* bitflags (0: normal, continued = 1, first = 2, last = 4) */ put_u8 (buf+0x05, header_type_flag); /* bitflags (0: normal, continued = 1, first = 2, last = 4) */
put_32bitLE(buf+0x06, (uint32_t)(absolute_granule >> 0 & 0xFFFFFFFF)); /* lower */ put_u32le(buf+0x06, (uint32_t)(absolute_granule >> 0 & 0xFFFFFFFF)); /* lower */
put_32bitLE(buf+0x0A, (uint32_t)(absolute_granule >> 32 & 0xFFFFFFFF)); /* upper */ put_u32le(buf+0x0A, (uint32_t)(absolute_granule >> 32 & 0xFFFFFFFF)); /* upper */
put_32bitLE(buf+0x0E, stream_serial_number); /* for interleaved multi-streams */ put_u32le(buf+0x0E, stream_serial_number); /* for interleaved multi-streams */
put_32bitLE(buf+0x12, page_sequence); put_u32le(buf+0x12, page_sequence);
put_32bitLE(buf+0x16, checksum); /* 0 for now, until all data is written */ put_u32le(buf+0x16, checksum); /* 0 for now, until all data is written */
put_8bit (buf+0x1A, segment_count); /* count of all lacing values */ put_u8 (buf+0x1A, segment_count); /* count of all lacing values */
/* segment table: size N in "lacing values" (ex. 0x20E=0xFF+FF+10; 0xFF=0xFF+00) */ /* segment table: size N in "lacing values" (ex. 0x20E=0xFF+FF+10; 0xFF=0xFF+00) */
page_done = 0x1B; page_done = 0x1B;
@ -407,12 +439,12 @@ static size_t make_oggs_page(uint8_t * buf, int buf_size, size_t data_size, int
if (bytes > 0xFF) if (bytes > 0xFF)
bytes = 0xFF; bytes = 0xFF;
put_8bit(buf+page_done, bytes); put_u8(buf+page_done, bytes);
page_done++; page_done++;
lacing_done += bytes; lacing_done += bytes;
if (lacing_done == data_size && bytes == 0xFF) { if (lacing_done == data_size && bytes == 0xFF) {
put_8bit(buf+page_done, 0x00); put_u8(buf+page_done, 0x00);
page_done++; page_done++;
} }
} }
@ -423,14 +455,14 @@ static size_t make_oggs_page(uint8_t * buf, int buf_size, size_t data_size, int
/* final checksum */ /* final checksum */
checksum = get_oggs_checksum(buf, page_done); checksum = get_oggs_checksum(buf, page_done);
put_32bitLE(buf+0x16, checksum); put_u32le(buf+0x16, checksum);
return page_done; return page_done;
fail: fail:
return 0; return 0;
} }
static size_t make_opus_header(uint8_t * buf, int buf_size, opus_config *cfg) { static size_t make_opus_header(uint8_t* buf, int buf_size, opus_config *cfg) {
size_t header_size = 0x13; size_t header_size = 0x13;
int mapping_family = 0; int mapping_family = 0;
@ -446,25 +478,25 @@ static size_t make_opus_header(uint8_t * buf, int buf_size, opus_config *cfg) {
goto fail; goto fail;
} }
put_32bitBE(buf+0x00, 0x4F707573); /* "Opus" header magic */ put_u32be(buf+0x00, 0x4F707573); /* "Opus" header magic */
put_32bitBE(buf+0x04, 0x48656164); /* "Head" header magic */ put_u32be(buf+0x04, 0x48656164); /* "Head" header magic */
put_8bit (buf+0x08, 1); /* version */ put_u8 (buf+0x08, 1); /* version */
put_8bit (buf+0x09, cfg->channels); put_u8 (buf+0x09, cfg->channels);
put_16bitLE(buf+0x0A, cfg->skip); put_s16le(buf+0x0A, cfg->skip);
put_32bitLE(buf+0x0c, cfg->sample_rate); put_u32le(buf+0x0c, cfg->sample_rate);
put_16bitLE(buf+0x10, 0); /* output gain */ put_u16le(buf+0x10, 0); /* output gain */
put_8bit (buf+0x12, mapping_family); put_u8 (buf+0x12, mapping_family);
if (mapping_family > 0) { if (mapping_family > 0) {
int i; int i;
/* internal mono/stereo streams (N mono/stereo streams form M channels) */ /* internal mono/stereo streams (N mono/stereo streams form M channels) */
put_8bit(buf+0x13, cfg->stream_count); put_u8(buf+0x13, cfg->stream_count);
/* joint stereo streams (rest would be mono, so 6ch can be 2ch+2ch+1ch+1ch = 2 coupled */ /* joint stereo streams (rest would be mono, so 6ch can be 2ch+2ch+1ch+1ch = 2 coupled */
put_8bit(buf+0x14, cfg->coupled_count); put_u8(buf+0x14, cfg->coupled_count);
/* mapping bits per channel? */ /* mapping bits per channel? */
for (i = 0; i < cfg->channels; i++) { for (i = 0; i < cfg->channels; i++) {
put_8bit(buf+0x15+i, cfg->channel_mapping[i]); put_u8(buf+0x15+i, cfg->channel_mapping[i]);
} }
} }
@ -473,9 +505,9 @@ fail:
return 0; return 0;
} }
static size_t make_opus_comment(uint8_t * buf, int buf_size) { static size_t make_opus_comment(uint8_t* buf, int buf_size) {
const char * vendor_string = "vgmstream"; const char* vendor_string = "vgmstream";
const char * user_comment_0_string = "vgmstream Opus converter"; const char* user_comment_0_string = "vgmstream Opus converter";
size_t comment_size; size_t comment_size;
int vendor_string_length, user_comment_0_length; int vendor_string_length, user_comment_0_length;
@ -488,20 +520,20 @@ static size_t make_opus_comment(uint8_t * buf, int buf_size) {
goto fail; goto fail;
} }
put_32bitBE(buf+0x00, 0x4F707573); /* "Opus" header magic */ put_u32be(buf+0x00, 0x4F707573); /* "Opus" header magic */
put_32bitBE(buf+0x04, 0x54616773); /* "Tags" header magic */ put_u32be(buf+0x04, 0x54616773); /* "Tags" header magic */
put_32bitLE(buf+0x08, vendor_string_length); put_u32le(buf+0x08, vendor_string_length);
memcpy (buf+0x0c, vendor_string, vendor_string_length); memcpy (buf+0x0c, vendor_string, vendor_string_length);
put_32bitLE(buf+0x0c + vendor_string_length+0x00, 1); /* user_comment_list_length */ put_u32le(buf+0x0c + vendor_string_length+0x00, 1); /* user_comment_list_length */
put_32bitLE(buf+0x0c + vendor_string_length+0x04, user_comment_0_length); put_u32le(buf+0x0c + vendor_string_length+0x04, user_comment_0_length);
memcpy (buf+0x0c + vendor_string_length+0x08, user_comment_0_string, user_comment_0_length); memcpy (buf+0x0c + vendor_string_length+0x08, user_comment_0_string, user_comment_0_length);
return comment_size; return comment_size;
fail: fail:
return 0; return 0;
} }
static size_t make_oggs_first(uint8_t * buf, int buf_size, opus_config *cfg) { static size_t make_oggs_first(uint8_t* buf, int buf_size, opus_config* cfg) {
int buf_done = 0; int buf_done = 0;
size_t bytes; size_t bytes;
@ -523,7 +555,7 @@ fail:
return 0; return 0;
} }
static size_t opus_get_packet_samples(const uint8_t * buf, int len) { static size_t opus_get_packet_samples(const uint8_t* buf, int len) {
return opus_packet_get_nb_frames(buf, len) * opus_packet_get_samples_per_frame(buf, 48000); return opus_packet_get_nb_frames(buf, len) * opus_packet_get_samples_per_frame(buf, 48000);
} }
static size_t opus_get_packet_samples_sf(STREAMFILE* sf, off_t offset) { static size_t opus_get_packet_samples_sf(STREAMFILE* sf, off_t offset) {
@ -534,11 +566,15 @@ static size_t opus_get_packet_samples_sf(STREAMFILE* sf, off_t offset) {
/************************** */ /************************** */
static size_t get_xopus_packet_size(int packet, STREAMFILE* sf) { /* some formats store all frames in a table, rather than right before the frame */
/* XOPUS has a packet size table at the beginning, get size from there. static size_t get_table_frame_size(opus_io_data* data, int frame) {
* Maybe should copy the table during setup to avoid IO, but all XOPUS are if (frame < 0 || frame >= data->table_count) {
* quite small so it isn't very noticeable. */ VGM_LOG("OPUS: wrong requested frame %i, count=%i\n", frame, data->table_count);
return read_u16le(0x20 + packet*0x02, sf); return 0;
}
//;VGM_LOG("OPUS: frame %i size=%x\n", frame, data->frame_table[frame]);
return data->frame_table[frame];
} }
@ -575,10 +611,14 @@ static size_t custom_opus_get_samples(off_t offset, size_t stream_size, STREAMFI
data_size = read_u16be(offset, sf); data_size = read_u16be(offset, sf);
skip_size = 0x02; skip_size = 0x02;
break; break;
#if 0 // needs data* for frame table, but num_samples should exist on header
case OPUS_X: case OPUS_X:
data_size = get_xopus_packet_size(packet, sf); case OPUS_WWISE:
data_size = get_table_frame_size(data, packet);
skip_size = 0x00; skip_size = 0x00;
break; break;
#endif
default: default:
return 0; return 0;
} }
@ -618,6 +658,7 @@ static size_t custom_opus_get_encoder_delay(off_t offset, STREAMFILE* sf, opus_t
skip_size = 0x02; skip_size = 0x02;
break; break;
case OPUS_X: case OPUS_X:
case OPUS_WWISE:
skip_size = 0x00; skip_size = 0x00;
break; break;
default: default:
@ -645,7 +686,7 @@ size_t fsb_opus_get_encoder_delay(off_t offset, STREAMFILE* sf) {
/* ******************************************************* */ /* ******************************************************* */
/* actual FFmpeg only-code starts here (the above is universal enough but no point to compile) */ /* actual FFmpeg only-code starts here (the above is universal enough but no point to compile separatedly) */
//#ifdef VGM_USE_FFMPEG //#ifdef VGM_USE_FFMPEG
static ffmpeg_codec_data* init_ffmpeg_custom_opus_config(STREAMFILE* sf, off_t start_offset, size_t data_size, opus_config *cfg, opus_type_t type) { static ffmpeg_codec_data* init_ffmpeg_custom_opus_config(STREAMFILE* sf, off_t start_offset, size_t data_size, opus_config *cfg, opus_type_t type) {
@ -672,6 +713,7 @@ fail:
close_streamfile(temp_sf); close_streamfile(temp_sf);
return NULL; return NULL;
} }
static ffmpeg_codec_data* init_ffmpeg_custom_opus(STREAMFILE* sf, off_t start_offset, size_t data_size, int channels, int skip, int sample_rate, opus_type_t type) { static ffmpeg_codec_data* init_ffmpeg_custom_opus(STREAMFILE* sf, off_t start_offset, size_t data_size, int channels, int skip, int sample_rate, opus_type_t type) {
opus_config cfg = {0}; opus_config cfg = {0};
cfg.channels = channels; cfg.channels = channels;
@ -681,6 +723,18 @@ static ffmpeg_codec_data* init_ffmpeg_custom_opus(STREAMFILE* sf, off_t start_of
return init_ffmpeg_custom_opus_config(sf, start_offset, data_size, &cfg, type); return init_ffmpeg_custom_opus_config(sf, start_offset, data_size, &cfg, type);
} }
ffmpeg_codec_data* init_ffmpeg_custom_table_opus(STREAMFILE* sf, off_t table_offset, int table_count, off_t data_offset, size_t data_size, int channels, int skip, int sample_rate, opus_type_t type) {
opus_config cfg = {0};
cfg.channels = channels;
cfg.skip = skip;
cfg.sample_rate = sample_rate;
cfg.table_offset = table_offset;
cfg.table_count = table_count;
return init_ffmpeg_custom_opus_config(sf, data_offset, data_size, &cfg, type);
}
ffmpeg_codec_data* init_ffmpeg_switch_opus_config(STREAMFILE* sf, off_t start_offset, size_t data_size, opus_config* cfg) { ffmpeg_codec_data* init_ffmpeg_switch_opus_config(STREAMFILE* sf, off_t start_offset, size_t data_size, opus_config* cfg) {
return init_ffmpeg_custom_opus_config(sf, start_offset, data_size, cfg, OPUS_SWITCH); return init_ffmpeg_custom_opus_config(sf, start_offset, data_size, cfg, OPUS_SWITCH);
} }
@ -693,12 +747,15 @@ ffmpeg_codec_data* init_ffmpeg_ue4_opus(STREAMFILE* sf, off_t start_offset, size
ffmpeg_codec_data* init_ffmpeg_ea_opus(STREAMFILE* sf, off_t start_offset, size_t data_size, int channels, int skip, int sample_rate) { ffmpeg_codec_data* init_ffmpeg_ea_opus(STREAMFILE* sf, off_t start_offset, size_t data_size, int channels, int skip, int sample_rate) {
return init_ffmpeg_custom_opus(sf, start_offset, data_size, channels, skip, sample_rate, OPUS_EA); return init_ffmpeg_custom_opus(sf, start_offset, data_size, channels, skip, sample_rate, OPUS_EA);
} }
ffmpeg_codec_data* init_ffmpeg_x_opus(STREAMFILE* sf, off_t start_offset, size_t data_size, int channels, int skip, int sample_rate) { ffmpeg_codec_data* init_ffmpeg_x_opus(STREAMFILE* sf, off_t table_offset, int table_count, off_t data_offset, size_t data_size, int channels, int skip) {
return init_ffmpeg_custom_opus(sf, start_offset, data_size, channels, skip, sample_rate, OPUS_X); return init_ffmpeg_custom_table_opus(sf, table_offset, table_count, data_offset, data_size, channels, skip, 0, OPUS_X);
} }
ffmpeg_codec_data* init_ffmpeg_fsb_opus(STREAMFILE* sf, off_t start_offset, size_t data_size, int channels, int skip, int sample_rate) { ffmpeg_codec_data* init_ffmpeg_fsb_opus(STREAMFILE* sf, off_t start_offset, size_t data_size, int channels, int skip, int sample_rate) {
return init_ffmpeg_custom_opus(sf, start_offset, data_size, channels, skip, sample_rate, OPUS_FSB); return init_ffmpeg_custom_opus(sf, start_offset, data_size, channels, skip, sample_rate, OPUS_FSB);
} }
ffmpeg_codec_data* init_ffmpeg_wwise_opus(STREAMFILE* sf, off_t table_offset, int table_count, off_t data_offset, size_t data_size, int channels, int skip) {
return init_ffmpeg_custom_table_opus(sf, table_offset, table_count, data_offset, data_size, channels, skip, 0, OPUS_WWISE);
}
static opus_type_t get_ue4opus_version(STREAMFILE* sf, off_t offset) { static opus_type_t get_ue4opus_version(STREAMFILE* sf, off_t offset) {
int read_samples, calc_samples; int read_samples, calc_samples;

View File

@ -1,63 +1,64 @@
#include "meta.h" #include "meta.h"
#include "../coding/coding.h" #include "../coding/coding.h"
/* XOPUS - from Exient games [Angry Birds: Transformers (Android), Angry Birds: Go (Android)] */ /* XOPUS - from Exient games [Angry Birds: Transformers (Android), Angry Birds: Go (Android)] */
VGMSTREAM * init_vgmstream_xopus(STREAMFILE *streamFile) { VGMSTREAM* init_vgmstream_xopus(STREAMFILE* sf) {
VGMSTREAM * vgmstream = NULL; VGMSTREAM* vgmstream = NULL;
off_t start_offset; off_t start_offset, table_offset;
int loop_flag = 0, channel_count, sample_rate, num_samples, skip; int loop_flag = 0, channels, sample_rate, num_samples, skip;
size_t data_size; size_t data_size;
int entries; int entries;
/* checks*/ /* checks*/
if (!check_extensions(streamFile, "xopus")) if (!check_extensions(sf, "xopus"))
goto fail; goto fail;
if (read_32bitBE(0x00, streamFile) != 0x584F7075) /* "XOpu" */ if (read_u32be(0x00, sf) != 0x584F7075) /* "XOpu" */
goto fail; goto fail;
/* 0x04: always 0x01? */ /* 0x04: always 0x01? */
channel_count = read_8bit(0x05, streamFile); channels = read_u8(0x05, sf);
/* 0x06: always 0x30? */ /* 0x06: always 0x30? */
/* 0x08: always 0xc8? max allowed packet size? */ /* 0x08: always 0xc8? max allowed packet size? */
num_samples = read_32bitLE(0x0c, streamFile); num_samples = read_s32le(0x0c, sf);
skip = read_32bitLE(0x10, streamFile); skip = read_s32le(0x10, sf);
entries = read_32bitLE(0x14, streamFile); entries = read_u32le(0x14, sf);
data_size = read_32bitLE(0x18, streamFile); data_size = read_u32le(0x18, sf);
/* 0x1c: unused */ /* 0x1c: unused */
/* 0x20+: packet sizes table */ /* 0x20+: packet sizes table */
sample_rate = 48000; sample_rate = 48000;
loop_flag = 0; loop_flag = 0;
start_offset = 0x20 + 0x02*entries; table_offset = 0x20;
start_offset = table_offset + 0x02*entries;
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count, loop_flag); /* build the VGMSTREAM */
if (!vgmstream) goto fail; vgmstream = allocate_vgmstream(channels, loop_flag);
if (!vgmstream) goto fail;
vgmstream->meta_type = meta_XOPUS;
vgmstream->sample_rate = sample_rate; vgmstream->meta_type = meta_XOPUS;
vgmstream->num_samples = num_samples; vgmstream->sample_rate = sample_rate;
vgmstream->num_samples = num_samples;
#ifdef VGM_USE_FFMPEG
{ #ifdef VGM_USE_FFMPEG
vgmstream->codec_data = init_ffmpeg_x_opus(streamFile, start_offset,data_size, vgmstream->channels, skip, vgmstream->sample_rate); {
if (!vgmstream->codec_data) goto fail; vgmstream->codec_data = init_ffmpeg_x_opus(sf, table_offset, entries, start_offset, data_size, vgmstream->channels, skip);
vgmstream->coding_type = coding_FFmpeg; if (!vgmstream->codec_data) goto fail;
vgmstream->layout_type = layout_none; vgmstream->coding_type = coding_FFmpeg;
} vgmstream->layout_type = layout_none;
#else }
goto fail; #else
#endif goto fail;
#endif
if (!vgmstream_open_stream(vgmstream, streamFile, start_offset))
goto fail; if (!vgmstream_open_stream(vgmstream, sf, start_offset))
return vgmstream; goto fail;
return vgmstream;
fail:
close_vgmstream(vgmstream); fail:
return NULL; close_vgmstream(vgmstream);
} return NULL;
}