mirror of
https://github.com/vgmstream/vgmstream.git
synced 2024-11-28 08:20:54 +01:00
Add multichannel Switch Opus
This commit is contained in:
parent
b570337e7d
commit
eac44c37c9
@ -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);
|
||||
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_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_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);
|
||||
|
@ -13,7 +13,7 @@
|
||||
* 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 opus_get_packet_samples(const uint8_t * buf, int len);
|
||||
static size_t get_xopus_packet_size(int packet, STREAMFILE * streamfile);
|
||||
@ -230,7 +230,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 */
|
||||
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;
|
||||
opus_io_data io_data = {0};
|
||||
size_t io_data_size = sizeof(opus_io_data);
|
||||
@ -239,7 +239,7 @@ static STREAMFILE* setup_opus_streamfile(STREAMFILE *streamFile, int channels, i
|
||||
io_data.stream_offset = stream_offset;
|
||||
io_data.stream_size = stream_size;
|
||||
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;
|
||||
io_data.sequence = 2;
|
||||
io_data.logical_size = opus_io_size(streamFile, &io_data); /* force init */
|
||||
@ -405,26 +405,16 @@ fail:
|
||||
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;
|
||||
int mapping_family = 0; /* channel config: 0=standard (single stream mono/stereo), 1=vorbis, 255: not defined */
|
||||
#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) */
|
||||
int mapping_family = 0;
|
||||
|
||||
/* special multichannel config */
|
||||
if (channels > 2) {
|
||||
header_size += 0x01+0x01+channels;
|
||||
switch(type) {
|
||||
|
||||
case ...:
|
||||
...
|
||||
break;
|
||||
default:
|
||||
goto fail;
|
||||
if (cfg->channels > 2) {
|
||||
/* channel config: 0=standard (single stream mono/stereo), 1=vorbis, 255: not defined */
|
||||
mapping_family = 1;
|
||||
header_size += 0x01+0x01+cfg->channels;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (header_size > buf_size) {
|
||||
VGM_LOG("OPUS: buffer can't hold header\n");
|
||||
@ -434,23 +424,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+0x04, 0x48656164); /* "Head" header magic */
|
||||
put_8bit (buf+0x08, 1); /* version */
|
||||
put_8bit (buf+0x09, channels);
|
||||
put_16bitLE(buf+0x0A, skip);
|
||||
put_32bitLE(buf+0x0c, sample_rate);
|
||||
put_8bit (buf+0x09, cfg->channels);
|
||||
put_16bitLE(buf+0x0A, cfg->skip);
|
||||
put_32bitLE(buf+0x0c, cfg->sample_rate);
|
||||
put_16bitLE(buf+0x10, 0); /* output gain */
|
||||
put_8bit (buf+0x12, mapping_family);
|
||||
|
||||
#if 0
|
||||
if (mapping_family > 0) {
|
||||
int 0;
|
||||
int i;
|
||||
|
||||
put_8bit(buf+0x13, stream_count);
|
||||
put_8bit(buf+0x14, coupled_count);
|
||||
for (i = 0; i < channels; i++) {
|
||||
put_8bit(buf+0x15+i, i); /* channel mapping (may need to change per family) */
|
||||
/* internal mono/stereo streams (N mono/stereo streams form M channels) */
|
||||
put_8bit(buf+0x13, cfg->stream_count);
|
||||
/* joint stereo streams (rest would be mono, so 6ch can be 2ch+2ch+1ch+1ch = 2 coupled */
|
||||
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;
|
||||
fail:
|
||||
@ -485,7 +476,7 @@ fail:
|
||||
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;
|
||||
size_t bytes;
|
||||
|
||||
@ -493,7 +484,7 @@ static size_t make_oggs_first(uint8_t * buf, int buf_size, int channels, int ski
|
||||
goto fail;
|
||||
|
||||
/* 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);
|
||||
buf_done += 0x1c + bytes;
|
||||
|
||||
@ -613,11 +604,11 @@ 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) {
|
||||
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;
|
||||
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;
|
||||
|
||||
ffmpeg_data = init_ffmpeg_offset(temp_streamFile, 0x00,get_streamfile_size(temp_streamFile));
|
||||
@ -637,7 +628,18 @@ fail:
|
||||
close_streamfile(temp_streamFile);
|
||||
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) {
|
||||
return init_ffmpeg_custom_opus(streamFile, start_offset, data_size, channels, skip, sample_rate, OPUS_SWITCH);
|
||||
}
|
||||
|
@ -32,9 +32,8 @@ VGMSTREAM * init_vgmstream_ktss(STREAMFILE *streamFile) {
|
||||
vgmstream = allocate_vgmstream(channel_count, loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
/* fill in the vital statistics */
|
||||
vgmstream->num_samples = read_32bitLE(0x30, 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_end_sample = vgmstream->loop_start_sample + loop_length;
|
||||
vgmstream->meta_type = meta_KTSS;
|
||||
@ -62,23 +61,50 @@ VGMSTREAM * init_vgmstream_ktss(STREAMFILE *streamFile) {
|
||||
break;
|
||||
|
||||
#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 */
|
||||
opus_config cfg = {0};
|
||||
|
||||
data_size = read_32bitLE(0x44, streamFile);
|
||||
{
|
||||
vgmstream->codec_data = init_ffmpeg_switch_opus(streamFile, start_offset, data_size, vgmstream->channels, skip, vgmstream->sample_rate);
|
||||
|
||||
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;
|
||||
vgmstream->coding_type = coding_FFmpeg;
|
||||
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) {
|
||||
vgmstream->num_samples = switch_opus_get_samples(start_offset, data_size, streamFile) - skip;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
}
|
||||
#endif
|
||||
default:
|
||||
goto fail;
|
||||
#endif
|
||||
}
|
||||
|
||||
if (!vgmstream_open_stream(vgmstream, streamFile, start_offset))
|
||||
|
@ -9,7 +9,7 @@ static VGMSTREAM * init_vgmstream_opus(STREAMFILE *streamFile, meta_t meta_type,
|
||||
VGMSTREAM * vgmstream = NULL;
|
||||
off_t start_offset;
|
||||
int loop_flag = 0, channel_count;
|
||||
off_t data_offset;
|
||||
off_t data_offset, multichannel_offset = 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);
|
||||
/* 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)
|
||||
goto fail;
|
||||
|
||||
@ -45,10 +50,26 @@ static VGMSTREAM * init_vgmstream_opus(STREAMFILE *streamFile, meta_t meta_type,
|
||||
|
||||
#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;
|
||||
vgmstream->coding_type = coding_FFmpeg;
|
||||
vgmstream->layout_type = layout_none;
|
||||
vgmstream->channel_layout = ffmpeg_get_channel_layout(vgmstream->codec_data);
|
||||
|
||||
if (vgmstream->num_samples == 0) {
|
||||
vgmstream->num_samples = switch_opus_get_samples(start_offset, data_size, streamFile) - skip;
|
||||
|
Loading…
Reference in New Issue
Block a user