Merge branch 'master' of git://github.com/kode54/vgmstream

This commit is contained in:
bxaimc 2019-07-24 10:59:22 -04:00
commit 837b04866b
5 changed files with 132 additions and 66 deletions

View File

@ -293,7 +293,19 @@ void ffmpeg_set_skip_samples(ffmpeg_codec_data * data, int skip_samples);
uint32_t ffmpeg_get_channel_layout(ffmpeg_codec_data * data); uint32_t ffmpeg_get_channel_layout(ffmpeg_codec_data * data);
void ffmpeg_set_channel_remapping(ffmpeg_codec_data * data, int *channels_remap); void ffmpeg_set_channel_remapping(ffmpeg_codec_data * data, int *channels_remap);
typedef struct {
int channels;
int skip;
int sample_rate;
/* multichannel-only */
int coupled_count;
int stream_count;
int channel_mapping[8];
} opus_config;
/* ffmpeg_decoder_custom_opus.c (helper-things) */ /* ffmpeg_decoder_custom_opus.c (helper-things) */
ffmpeg_codec_data * init_ffmpeg_switch_opus_config(STREAMFILE *streamFile, off_t start_offset, size_t data_size, opus_config* cfg);
ffmpeg_codec_data * init_ffmpeg_switch_opus(STREAMFILE *streamFile, off_t start_offset, size_t data_size, int channels, int skip, int sample_rate); ffmpeg_codec_data * init_ffmpeg_switch_opus(STREAMFILE *streamFile, off_t start_offset, size_t data_size, int channels, int skip, int sample_rate);
ffmpeg_codec_data * init_ffmpeg_ue4_opus(STREAMFILE *streamFile, off_t start_offset, size_t data_size, int channels, int skip, int sample_rate); ffmpeg_codec_data * init_ffmpeg_ue4_opus(STREAMFILE *streamFile, off_t start_offset, size_t data_size, int channels, int skip, int sample_rate);
ffmpeg_codec_data * init_ffmpeg_ea_opus(STREAMFILE *streamFile, off_t start_offset, size_t data_size, int channels, int skip, int sample_rate); ffmpeg_codec_data * init_ffmpeg_ea_opus(STREAMFILE *streamFile, off_t start_offset, size_t data_size, int channels, int skip, int sample_rate);

View File

@ -2,18 +2,22 @@
#include "../streamfile.h" #include "../streamfile.h"
#include <string.h> #include <string.h>
#ifdef VGM_USE_FFMPEG
/** /**
* Transmogrifies custom Opus (no Ogg layer and custom packet headers) into is Xiph Opus, creating * Transmogrifies custom Opus (no Ogg layer and custom packet headers) into is Xiph Opus, creating
* valid Ogg pages with single Opus packets. * valid Ogg pages with single Opus packets.
* Uses an intermediate buffer to make full Ogg pages, since checksums are calculated with the whole page. * Uses an intermediate buffer to make full Ogg pages, since checksums are calculated with the whole page.
* *
* Mostly as an experiment/demonstration, until some details are sorted out before adding actual libopus.
*
* Info, CRC and stuff: * Info, CRC and stuff:
* https://www.opus-codec.org/docs/ * https://www.opus-codec.org/docs/
* https://tools.ietf.org/html/rfc7845.html * https://tools.ietf.org/html/rfc7845.html
* https://github.com/hcs64/ww2ogg * https://github.com/hcs64/ww2ogg
*/ */
static size_t make_oggs_first(uint8_t * buf, int buf_size, int channels, int skip, int sample_rate); 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 get_xopus_packet_size(int packet, STREAMFILE * streamfile); static size_t get_xopus_packet_size(int packet, STREAMFILE * streamfile);
@ -230,7 +234,7 @@ static size_t opus_io_size(STREAMFILE *streamfile, opus_io_data* data) {
/* 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 *streamFile, int channels, int skip, int sample_rate, off_t stream_offset, size_t stream_size, opus_type_t type) { static STREAMFILE* setup_opus_streamfile(STREAMFILE *streamFile, opus_config *cfg, off_t stream_offset, size_t stream_size, opus_type_t type) {
STREAMFILE *temp_streamFile = NULL, *new_streamFile = NULL; STREAMFILE *temp_streamFile = NULL, *new_streamFile = NULL;
opus_io_data io_data = {0}; opus_io_data io_data = {0};
size_t io_data_size = sizeof(opus_io_data); size_t io_data_size = sizeof(opus_io_data);
@ -239,7 +243,7 @@ static STREAMFILE* setup_opus_streamfile(STREAMFILE *streamFile, int channels, i
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.head_size = make_oggs_first(io_data.head_buffer, sizeof(io_data.head_buffer), channels, skip, sample_rate); 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.sequence = 2;
io_data.logical_size = opus_io_size(streamFile, &io_data); /* force init */ io_data.logical_size = opus_io_size(streamFile, &io_data); /* force init */
@ -405,26 +409,16 @@ fail:
return 0; return 0;
} }
static size_t make_opus_header(uint8_t * buf, int buf_size, int channels, int skip, int sample_rate) { 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; /* channel config: 0=standard (single stream mono/stereo), 1=vorbis, 255: not defined */ int mapping_family = 0;
#if 0
int stream_count = 1; /* number of internal mono/stereo streams (N mono/stereo streams form M channels) */
int coupled_count = channels - 1; /* number of stereo streams (packet has which one is mono or stereo) */
/* special multichannel config */ /* special multichannel config */
if (channels > 2) { if (cfg->channels > 2) {
header_size += 0x01+0x01+channels; /* channel config: 0=standard (single stream mono/stereo), 1=vorbis, 255: not defined */
switch(type) { mapping_family = 1;
header_size += 0x01+0x01+cfg->channels;
case ...:
...
break;
default:
goto fail;
}
} }
#endif
if (header_size > buf_size) { if (header_size > buf_size) {
VGM_LOG("OPUS: buffer can't hold header\n"); VGM_LOG("OPUS: buffer can't hold header\n");
@ -434,23 +428,24 @@ static size_t make_opus_header(uint8_t * buf, int buf_size, int channels, int sk
put_32bitBE(buf+0x00, 0x4F707573); /* "Opus" header magic */ put_32bitBE(buf+0x00, 0x4F707573); /* "Opus" header magic */
put_32bitBE(buf+0x04, 0x48656164); /* "Head" header magic */ put_32bitBE(buf+0x04, 0x48656164); /* "Head" header magic */
put_8bit (buf+0x08, 1); /* version */ put_8bit (buf+0x08, 1); /* version */
put_8bit (buf+0x09, channels); put_8bit (buf+0x09, cfg->channels);
put_16bitLE(buf+0x0A, skip); put_16bitLE(buf+0x0A, cfg->skip);
put_32bitLE(buf+0x0c, sample_rate); put_32bitLE(buf+0x0c, cfg->sample_rate);
put_16bitLE(buf+0x10, 0); /* output gain */ put_16bitLE(buf+0x10, 0); /* output gain */
put_8bit (buf+0x12, mapping_family); put_8bit (buf+0x12, mapping_family);
#if 0
if (mapping_family > 0) { if (mapping_family > 0) {
int 0; int i;
put_8bit(buf+0x13, stream_count); /* internal mono/stereo streams (N mono/stereo streams form M channels) */
put_8bit(buf+0x14, coupled_count); put_8bit(buf+0x13, cfg->stream_count);
for (i = 0; i < channels; i++) { /* joint stereo streams (rest would be mono, so 6ch can be 2ch+2ch+1ch+1ch = 2 coupled */
put_8bit(buf+0x15+i, i); /* channel mapping (may need to change per family) */ put_8bit(buf+0x14, cfg->coupled_count);
/* mapping bits per channel? */
for (i = 0; i < cfg->channels; i++) {
put_8bit(buf+0x15+i, cfg->channel_mapping[i]);
} }
} }
#endif
return header_size; return header_size;
fail: fail:
@ -485,7 +480,7 @@ fail:
return 0; return 0;
} }
static size_t make_oggs_first(uint8_t * buf, int buf_size, int channels, int skip, int sample_rate) { 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;
@ -493,7 +488,7 @@ static size_t make_oggs_first(uint8_t * buf, int buf_size, int channels, int ski
goto fail; goto fail;
/* make header */ /* make header */
bytes = make_opus_header(buf+buf_done + 0x1c,buf_size, channels, skip, sample_rate); bytes = make_opus_header(buf+buf_done + 0x1c,buf_size, cfg);
make_oggs_page(buf+buf_done + 0x00,buf_size, bytes, 0, 0); make_oggs_page(buf+buf_done + 0x00,buf_size, bytes, 0, 0);
buf_done += 0x1c + bytes; buf_done += 0x1c + bytes;
@ -521,8 +516,6 @@ static size_t get_xopus_packet_size(int packet, STREAMFILE * streamfile) {
} }
#ifdef VGM_USE_FFMPEG
static size_t custom_opus_get_samples(off_t offset, size_t stream_size, STREAMFILE *streamFile, opus_type_t type) { static size_t custom_opus_get_samples(off_t offset, size_t stream_size, STREAMFILE *streamFile, opus_type_t type) {
size_t num_samples = 0; size_t num_samples = 0;
off_t end_offset = offset + stream_size; off_t end_offset = offset + stream_size;
@ -610,14 +603,16 @@ size_t ea_opus_get_encoder_delay(off_t offset, STREAMFILE *streamFile) {
} }
/* ******************************************************* */ /* ******************************************************* */
static ffmpeg_codec_data * init_ffmpeg_custom_opus(STREAMFILE *streamFile, off_t start_offset, size_t data_size, int channels, int skip, int sample_rate, opus_type_t type) { /* actual FFmpeg only-code starts here (the above is universal enough but no point to compile) */
//#ifdef VGM_USE_FFMPEG
static ffmpeg_codec_data * init_ffmpeg_custom_opus_config(STREAMFILE *streamFile, off_t start_offset, size_t data_size, opus_config *cfg, opus_type_t type) {
ffmpeg_codec_data * ffmpeg_data = NULL; ffmpeg_codec_data * ffmpeg_data = NULL;
STREAMFILE *temp_streamFile = NULL; STREAMFILE *temp_streamFile = NULL;
temp_streamFile = setup_opus_streamfile(streamFile, channels, skip, sample_rate, start_offset, data_size, type); temp_streamFile = setup_opus_streamfile(streamFile, cfg, start_offset, data_size, type);
if (!temp_streamFile) goto fail; if (!temp_streamFile) goto fail;
ffmpeg_data = init_ffmpeg_offset(temp_streamFile, 0x00,get_streamfile_size(temp_streamFile)); ffmpeg_data = init_ffmpeg_offset(temp_streamFile, 0x00,get_streamfile_size(temp_streamFile));
@ -637,7 +632,18 @@ fail:
close_streamfile(temp_streamFile); close_streamfile(temp_streamFile);
return NULL; return NULL;
} }
static ffmpeg_codec_data * init_ffmpeg_custom_opus(STREAMFILE *streamFile, off_t start_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;
return init_ffmpeg_custom_opus_config(streamFile, start_offset, data_size, &cfg, type);
}
ffmpeg_codec_data * init_ffmpeg_switch_opus_config(STREAMFILE *streamFile, off_t start_offset, size_t data_size, opus_config* cfg) {
return init_ffmpeg_custom_opus_config(streamFile, start_offset, data_size, cfg, OPUS_SWITCH);
}
ffmpeg_codec_data * init_ffmpeg_switch_opus(STREAMFILE *streamFile, off_t start_offset, size_t data_size, int channels, int skip, int sample_rate) { ffmpeg_codec_data * init_ffmpeg_switch_opus(STREAMFILE *streamFile, off_t start_offset, size_t data_size, int channels, int skip, int sample_rate) {
return init_ffmpeg_custom_opus(streamFile, start_offset, data_size, channels, skip, sample_rate, OPUS_SWITCH); return init_ffmpeg_custom_opus(streamFile, start_offset, data_size, channels, skip, sample_rate, OPUS_SWITCH);
} }

View File

@ -1,3 +1,4 @@
#include <math.h>
#include "meta.h" #include "meta.h"
#include "../layout/layout.h" #include "../layout/layout.h"
#include "../coding/coding.h" #include "../coding/coding.h"

View File

@ -32,53 +32,79 @@ VGMSTREAM * init_vgmstream_ktss(STREAMFILE *streamFile) {
vgmstream = allocate_vgmstream(channel_count, loop_flag); vgmstream = allocate_vgmstream(channel_count, loop_flag);
if (!vgmstream) goto fail; if (!vgmstream) goto fail;
/* fill in the vital statistics */
vgmstream->num_samples = read_32bitLE(0x30, streamFile);
vgmstream->sample_rate = read_32bitLE(0x2c, streamFile); vgmstream->sample_rate = read_32bitLE(0x2c, streamFile);
vgmstream->num_samples = read_32bitLE(0x30, streamFile);
vgmstream->loop_start_sample = read_32bitLE(0x34, streamFile); vgmstream->loop_start_sample = read_32bitLE(0x34, streamFile);
vgmstream->loop_end_sample = vgmstream->loop_start_sample + loop_length; vgmstream->loop_end_sample = vgmstream->loop_start_sample + loop_length;
vgmstream->meta_type = meta_KTSS; vgmstream->meta_type = meta_KTSS;
start_offset = read_32bitLE(0x24, streamFile) + 0x20; start_offset = read_32bitLE(0x24, streamFile) + 0x20;
switch (codec_id) { switch (codec_id) {
case 0x2: /* DSP ADPCM - Hyrule Warriors, Fire Emblem Warriors, and other Koei Tecmo games */ case 0x2: /* DSP ADPCM - Hyrule Warriors, Fire Emblem Warriors, and other Koei Tecmo games */
/* check type details */ /* check type details */
version = read_8bit(0x22, streamFile); version = read_8bit(0x22, streamFile);
if (version == 1) { if (version == 1) {
coef_start_offset = 0x40; coef_start_offset = 0x40;
coef_spacing = 0x2e; coef_spacing = 0x2e;
} }
else if (version == 3) { // Fire Emblem Warriors (Switch) else if (version == 3) { // Fire Emblem Warriors (Switch)
coef_start_offset = 0x5c; coef_start_offset = 0x5c;
coef_spacing = 0x60; coef_spacing = 0x60;
} }
else else
goto fail; goto fail;
vgmstream->coding_type = coding_NGC_DSP; vgmstream->coding_type = coding_NGC_DSP;
vgmstream->layout_type = layout_interleave; vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = 0x8; vgmstream->interleave_block_size = 0x8;
dsp_read_coefs_le(vgmstream, streamFile, coef_start_offset, coef_spacing); dsp_read_coefs_le(vgmstream, streamFile, coef_start_offset, coef_spacing);
break; break;
#ifdef VGM_USE_FFMPEG #ifdef VGM_USE_FFMPEG
case 0x9: /* Opus - Dead or Alive Xtreme 3: Scarlet */ case 0x9: { /* Opus - Dead or Alive Xtreme 3: Scarlet, Fire Emblem: Three Houses */
data_size = read_32bitLE(0x44, streamFile); opus_config cfg = {0};
{
vgmstream->codec_data = init_ffmpeg_switch_opus(streamFile, start_offset, data_size, vgmstream->channels, skip, vgmstream->sample_rate); data_size = read_32bitLE(0x44, streamFile);
cfg.channels = vgmstream->channels;
cfg.skip = read_32bitLE(0x58, streamFile);
cfg.sample_rate = vgmstream->sample_rate; /* also at 0x54 */
/* this info seems always included even for stereo streams */
if (vgmstream->channels <= 8) {
int i;
cfg.stream_count = read_8bit(0x5a,streamFile);
cfg.coupled_count = read_8bit(0x5b,streamFile);
for (i = 0; i < vgmstream->channels; i++) {
cfg.channel_mapping[i] = read_8bit(0x5c + i,streamFile);
}
}
vgmstream->codec_data = init_ffmpeg_switch_opus_config(streamFile, start_offset, data_size, &cfg);
if (!vgmstream->codec_data) goto fail; if (!vgmstream->codec_data) goto fail;
vgmstream->coding_type = coding_FFmpeg; vgmstream->coding_type = coding_FFmpeg;
vgmstream->layout_type = layout_none; vgmstream->layout_type = layout_none;
vgmstream->channel_layout = ffmpeg_get_channel_layout(vgmstream->codec_data);
/* apparently KTSS doesn't need standard Opus reordering, so we undo their thing */
switch(vgmstream->channels) {
case 6: {
/* FL FC FR BL LFE BR > FL FR FC LFE BL BR */
int channel_remap[] = { 0, 2, 2, 3, 3, 5 };
ffmpeg_set_channel_remapping(vgmstream->codec_data, channel_remap);
break;
}
default:
break;
}
if (vgmstream->num_samples == 0) { if (vgmstream->num_samples == 0) {
vgmstream->num_samples = switch_opus_get_samples(start_offset, data_size, streamFile) - skip; vgmstream->num_samples = switch_opus_get_samples(start_offset, data_size, streamFile) - skip;
} }
break;
} }
break;
default:
goto fail;
#endif #endif
default:
goto fail;
} }
if (!vgmstream_open_stream(vgmstream, streamFile, start_offset)) if (!vgmstream_open_stream(vgmstream, streamFile, start_offset))

View File

@ -9,7 +9,7 @@ static VGMSTREAM * init_vgmstream_opus(STREAMFILE *streamFile, meta_t meta_type,
VGMSTREAM * vgmstream = NULL; VGMSTREAM * vgmstream = NULL;
off_t start_offset; off_t start_offset;
int loop_flag = 0, channel_count; int loop_flag = 0, channel_count;
off_t data_offset; off_t data_offset, multichannel_offset = 0;
size_t data_size, skip = 0; size_t data_size, skip = 0;
@ -22,6 +22,11 @@ static VGMSTREAM * init_vgmstream_opus(STREAMFILE *streamFile, meta_t meta_type,
skip = read_16bitLE(offset + 0x1c, streamFile); skip = read_16bitLE(offset + 0x1c, streamFile);
/* 0x1e: ? (seen in Lego Movie 2 (Switch)) */ /* 0x1e: ? (seen in Lego Movie 2 (Switch)) */
/* recent >2ch info [Clannad (Switch)] */
if ((uint32_t)read_32bitLE(offset + 0x20, streamFile) == 0x80000005) {
multichannel_offset = offset + 0x20;
}
if ((uint32_t)read_32bitLE(data_offset, streamFile) != 0x80000004) if ((uint32_t)read_32bitLE(data_offset, streamFile) != 0x80000004)
goto fail; goto fail;
@ -45,10 +50,26 @@ static VGMSTREAM * init_vgmstream_opus(STREAMFILE *streamFile, meta_t meta_type,
#ifdef VGM_USE_FFMPEG #ifdef VGM_USE_FFMPEG
{ {
vgmstream->codec_data = init_ffmpeg_switch_opus(streamFile, start_offset,data_size, vgmstream->channels, skip, vgmstream->sample_rate); opus_config cfg = {0};
cfg.channels = vgmstream->channels;
cfg.skip = skip;
cfg.sample_rate = vgmstream->sample_rate;
if (multichannel_offset && vgmstream->channels <= 8) {
int i;
cfg.stream_count = read_8bit(multichannel_offset + 0x08,streamFile);
cfg.coupled_count = read_8bit(multichannel_offset + 0x09,streamFile);
for (i = 0; i < vgmstream->channels; i++) {
cfg.channel_mapping[i] = read_8bit(multichannel_offset + 0x0a + i,streamFile);
}
}
vgmstream->codec_data = init_ffmpeg_switch_opus_config(streamFile, start_offset,data_size, &cfg);
if (!vgmstream->codec_data) goto fail; if (!vgmstream->codec_data) goto fail;
vgmstream->coding_type = coding_FFmpeg; vgmstream->coding_type = coding_FFmpeg;
vgmstream->layout_type = layout_none; vgmstream->layout_type = layout_none;
vgmstream->channel_layout = ffmpeg_get_channel_layout(vgmstream->codec_data);
if (vgmstream->num_samples == 0) { if (vgmstream->num_samples == 0) {
vgmstream->num_samples = switch_opus_get_samples(start_offset, data_size, streamFile) - skip; vgmstream->num_samples = switch_opus_get_samples(start_offset, data_size, streamFile) - skip;