mirror of
https://github.com/vgmstream/vgmstream.git
synced 2025-01-17 23:36:41 +01:00
Add multichannel layout for OGG/MP4/WEM/FFmpeg and use in foobar
This commit is contained in:
parent
3d279ea42c
commit
d9296c6693
@ -210,6 +210,8 @@ void input_vgmstream::get_info(t_uint32 p_subsong, file_info & p_info, abort_cal
|
||||
if (get_description_tag(temp,description,"stream count: ")) p_info.info_set("stream_count",temp);
|
||||
if (get_description_tag(temp,description,"stream index: ")) p_info.info_set("stream_index",temp);
|
||||
if (get_description_tag(temp,description,"stream name: ")) p_info.info_set("stream_name",temp);
|
||||
|
||||
if (get_description_tag(temp,description,"channel mask: ")) p_info.info_set("channel_mask",temp);
|
||||
}
|
||||
|
||||
t_filestats input_vgmstream::get_file_stats(abort_callback & p_abort) {
|
||||
@ -293,12 +295,16 @@ bool input_vgmstream::decode_run(audio_chunk & p_chunk,abort_callback & p_abort)
|
||||
/* copy back to global buffer... in case of multithreading stuff? */
|
||||
memcpy(sample_buffer,temp_buffer, samples_to_do*downmix_channels*sizeof(short));
|
||||
|
||||
unsigned channel_config = audio_chunk::g_guess_channel_config(downmix_channels);
|
||||
bytes = (samples_to_do*downmix_channels * sizeof(sample_buffer[0]));
|
||||
p_chunk.set_data_fixedpoint((char*)sample_buffer, bytes, vgmstream->sample_rate, downmix_channels, 16, audio_chunk::g_guess_channel_config(downmix_channels));
|
||||
p_chunk.set_data_fixedpoint((char*)sample_buffer, bytes, vgmstream->sample_rate, downmix_channels, 16, channel_config);
|
||||
}
|
||||
else {
|
||||
unsigned channel_config = vgmstream->channel_layout;
|
||||
if (!channel_config)
|
||||
channel_config = audio_chunk::g_guess_channel_config(vgmstream->channels);
|
||||
bytes = (samples_to_do*vgmstream->channels * sizeof(sample_buffer[0]));
|
||||
p_chunk.set_data_fixedpoint((char*)sample_buffer, bytes, vgmstream->sample_rate, vgmstream->channels, 16, audio_chunk::g_guess_channel_config(vgmstream->channels));
|
||||
p_chunk.set_data_fixedpoint((char*)sample_buffer, bytes, vgmstream->sample_rate, vgmstream->channels, 16, channel_config);
|
||||
}
|
||||
|
||||
|
||||
|
@ -215,7 +215,7 @@ void free_vorbis_custom(vorbis_custom_codec_data *data);
|
||||
/* mpeg_decoder */
|
||||
mpeg_codec_data *init_mpeg(STREAMFILE *streamfile, off_t start_offset, coding_t *coding_type, int channels);
|
||||
mpeg_codec_data *init_mpeg_custom(STREAMFILE *streamFile, off_t start_offset, coding_t *coding_type, int channels, mpeg_custom_t custom_type, mpeg_custom_config *config);
|
||||
void decode_mpeg(VGMSTREAM * vgmstream, sample * outbuf, int32_t samples_to_do, int channels);
|
||||
void decode_mpeg(VGMSTREAM * vgmstream, sample_t * outbuf, int32_t samples_to_do, int channels);
|
||||
void reset_mpeg(VGMSTREAM *vgmstream);
|
||||
void seek_mpeg(VGMSTREAM *vgmstream, int32_t num_sample);
|
||||
void free_mpeg(mpeg_codec_data *data);
|
||||
@ -289,6 +289,7 @@ void seek_ffmpeg(VGMSTREAM *vgmstream, int32_t num_sample);
|
||||
void free_ffmpeg(ffmpeg_codec_data *data);
|
||||
|
||||
void ffmpeg_set_skip_samples(ffmpeg_codec_data * data, int skip_samples);
|
||||
uint32_t ffmpeg_get_channel_layout(ffmpeg_codec_data * data);
|
||||
|
||||
/* ffmpeg_decoder_custom_opus.c (helper-things) */
|
||||
ffmpeg_codec_data * init_ffmpeg_switch_opus(STREAMFILE *streamFile, off_t start_offset, size_t data_size, int channels, int skip, int sample_rate);
|
||||
|
@ -808,4 +808,10 @@ void ffmpeg_set_skip_samples(ffmpeg_codec_data * data, int skip_samples) {
|
||||
data->skipSamples = skip_samples;
|
||||
}
|
||||
|
||||
/* returns channel layout if set */
|
||||
uint32_t ffmpeg_get_channel_layout(ffmpeg_codec_data * data) {
|
||||
if (!data || !data->codecCtx) return 0;
|
||||
return (uint32_t)data->codecCtx->channel_layout; /* uint64 but there ain't so many speaker mappings */
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -66,6 +66,8 @@ VGMSTREAM * init_vgmstream_ffmpeg_offset(STREAMFILE *streamFile, uint64_t start,
|
||||
if (vgmstream->num_samples <= 0)
|
||||
goto fail;
|
||||
|
||||
vgmstream->channel_layout = ffmpeg_get_channel_layout(vgmstream->codec_data);
|
||||
|
||||
return vgmstream;
|
||||
|
||||
fail:
|
||||
|
@ -216,12 +216,13 @@ VGMSTREAM * init_vgmstream_mp4_aac_ffmpeg(STREAMFILE *streamFile) {
|
||||
vgmstream->layout_type = layout_none;
|
||||
vgmstream->meta_type = meta_MP4;
|
||||
|
||||
vgmstream->num_samples = ffmpeg_data->totalSamples;
|
||||
vgmstream->sample_rate = ffmpeg_data->sampleRate;
|
||||
vgmstream->channels = ffmpeg_data->channels;
|
||||
vgmstream->num_samples = ffmpeg_data->totalSamples;
|
||||
vgmstream->loop_start_sample = loop_start_sample;
|
||||
vgmstream->loop_end_sample = loop_end_sample;
|
||||
|
||||
vgmstream->channel_layout = ffmpeg_get_channel_layout(vgmstream->codec_data);
|
||||
|
||||
return vgmstream;
|
||||
|
||||
fail:
|
||||
@ -233,11 +234,7 @@ fail:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Almost the same as streamfile.c's find_chunk but for "atom" chunks, which have chunk_size first because Apple.
|
||||
*
|
||||
* returns 0 on failure
|
||||
*/
|
||||
/* Almost the same as streamfile.c's find_chunk but for "atom" chunks, which have chunk_size first because Apple, returns 0 on failure */
|
||||
static int find_atom_be(STREAMFILE *streamFile, uint32_t atom_id, off_t start_offset, off_t *out_atom_offset, size_t *out_atom_size) {
|
||||
size_t filesize;
|
||||
off_t current_atom = start_offset;
|
||||
|
@ -111,6 +111,7 @@ VGMSTREAM * init_vgmstream_ogg_opus(STREAMFILE *streamFile) {
|
||||
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);
|
||||
/* FFmpeg+libopus handles skip samples ok, FFmpeg+opus doesn't */
|
||||
}
|
||||
#else
|
||||
|
@ -275,6 +275,18 @@ static void lse_ff_ogg_decryption_callback(void *ptr, size_t size, size_t nmemb,
|
||||
}
|
||||
}
|
||||
|
||||
static const uint32_t xiph_mappings[] = {
|
||||
0,
|
||||
mapping_MONO,
|
||||
mapping_STEREO,
|
||||
mapping_2POINT1_xiph,
|
||||
mapping_QUAD,
|
||||
mapping_5POINT0_xiph,
|
||||
mapping_5POINT1,
|
||||
mapping_7POINT0,
|
||||
mapping_7POINT1,
|
||||
};
|
||||
|
||||
|
||||
/* Ogg Vorbis, by way of libvorbisfile; may contain loop comments */
|
||||
VGMSTREAM * init_vgmstream_ogg_vorbis(STREAMFILE *streamFile) {
|
||||
@ -634,6 +646,10 @@ VGMSTREAM * init_vgmstream_ogg_vorbis_callbacks(STREAMFILE *streamFile, ov_callb
|
||||
vgmstream->layout_type = layout_none;
|
||||
vgmstream->meta_type = ovmi->meta_type;
|
||||
|
||||
if (vgmstream->channels <= 8) {
|
||||
vgmstream->channel_layout = xiph_mappings[vgmstream->channels];
|
||||
}
|
||||
|
||||
return vgmstream;
|
||||
|
||||
fail:
|
||||
|
@ -29,6 +29,7 @@ typedef struct {
|
||||
int block_align;
|
||||
int average_bps;
|
||||
int bits_per_sample;
|
||||
uint32_t channel_layout;
|
||||
size_t extra_size;
|
||||
|
||||
int32_t num_samples;
|
||||
@ -104,11 +105,12 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile) {
|
||||
if (ww.fmt_size < 0x12) goto fail;
|
||||
ww.format = (uint16_t)read_16bit(ww.fmt_offset+0x00,streamFile);
|
||||
|
||||
if (ww.format == 0x0165) { /* XMA2WAVEFORMAT (always "fmt"+"XMA2", unlike .xma that may only have "XMA2") */
|
||||
if (ww.format == 0x0165) { /* pseudo-XMA2WAVEFORMAT (always "fmt"+"XMA2", unlike .xma that may only have "XMA2") */
|
||||
if (!find_chunk(streamFile, 0x584D4132,first_offset,0, &ww.chunk_offset,NULL, ww.big_endian, 0))
|
||||
goto fail;
|
||||
xma2_parse_xma2_chunk(streamFile, ww.chunk_offset,&ww.channels,&ww.sample_rate, &ww.loop_flag, &ww.num_samples, &ww.loop_start_sample, &ww.loop_end_sample);
|
||||
} else { /* WAVEFORMATEX */
|
||||
}
|
||||
else { /* pseudo-WAVEFORMATEX */
|
||||
ww.channels = read_16bit(ww.fmt_offset+0x02,streamFile);
|
||||
ww.sample_rate = read_32bit(ww.fmt_offset+0x04,streamFile);
|
||||
ww.average_bps = read_32bit(ww.fmt_offset+0x08,streamFile);/* bytes per sec */
|
||||
@ -116,9 +118,10 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile) {
|
||||
ww.bits_per_sample = (uint16_t)read_16bit(ww.fmt_offset+0x0e,streamFile);
|
||||
if (ww.fmt_size > 0x10 && ww.format != 0x0165 && ww.format != 0x0166) /* ignore XMAWAVEFORMAT */
|
||||
ww.extra_size = (uint16_t)read_16bit(ww.fmt_offset+0x10,streamFile);
|
||||
/* channel bitmask, see AkSpeakerConfig.h (ex. 1ch uses FRONT_CENTER 0x4, 2ch FRONT_LEFT 0x1 | FRONT_RIGHT 0x2, etc) */
|
||||
//if (ww.extra_size >= 6)
|
||||
// ww.channel_config = read_32bit(ww.fmt_offset+0x14,streamFile);
|
||||
if (ww.extra_size >= 0x06) { /* mostly WAVEFORMATEXTENSIBLE's bitmask, see AkSpeakerConfig.h */
|
||||
/* always present (actual RIFFs only have it in WAVEFORMATEXTENSIBLE) */
|
||||
ww.channel_layout = read_32bit(ww.fmt_offset+0x14,streamFile);
|
||||
}
|
||||
}
|
||||
|
||||
/* find loop info */
|
||||
@ -192,6 +195,7 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
start_offset = ww.data_offset;
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(ww.channels,ww.loop_flag);
|
||||
@ -200,10 +204,9 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile) {
|
||||
vgmstream->sample_rate = ww.sample_rate;
|
||||
vgmstream->loop_start_sample = ww.loop_start_sample;
|
||||
vgmstream->loop_end_sample = ww.loop_end_sample;
|
||||
vgmstream->channel_layout = ww.channel_layout;
|
||||
vgmstream->meta_type = meta_WWISE_RIFF;
|
||||
|
||||
start_offset = ww.data_offset;
|
||||
|
||||
switch(ww.codec) {
|
||||
case PCM: /* common */
|
||||
/* normally riff.c has priority but it's needed when .wem is used */
|
||||
|
@ -2288,6 +2288,36 @@ void describe_vgmstream(VGMSTREAM * vgmstream, char * desc, int length) {
|
||||
vgmstream->channels);
|
||||
concatn(length,desc,temp);
|
||||
|
||||
if (vgmstream->channel_layout) {
|
||||
int cl = vgmstream->channel_layout;
|
||||
|
||||
snprintf(temp,TEMPSIZE,
|
||||
"channel mask: 0x%x /",
|
||||
vgmstream->channel_layout);
|
||||
concatn(length,desc,temp);
|
||||
|
||||
if (cl & speaker_FL) concatn(length,desc," FL");
|
||||
if (cl & speaker_FR) concatn(length,desc," FR");
|
||||
if (cl & speaker_FC) concatn(length,desc," FC");
|
||||
if (cl & speaker_LFE) concatn(length,desc," LFE");
|
||||
if (cl & speaker_BL) concatn(length,desc," BL");
|
||||
if (cl & speaker_BR) concatn(length,desc," BR");
|
||||
if (cl & speaker_FLC) concatn(length,desc," FLC");
|
||||
if (cl & speaker_FRC) concatn(length,desc," FRC");
|
||||
if (cl & speaker_BC) concatn(length,desc," BC");
|
||||
if (cl & speaker_SL) concatn(length,desc," SL");
|
||||
if (cl & speaker_SR) concatn(length,desc," SR");
|
||||
if (cl & speaker_TC) concatn(length,desc," TC");
|
||||
if (cl & speaker_TFL) concatn(length,desc," TFL");
|
||||
if (cl & speaker_TFC) concatn(length,desc," TFC");
|
||||
if (cl & speaker_TFR) concatn(length,desc," TFR");
|
||||
if (cl & speaker_TBL) concatn(length,desc," TBL");
|
||||
if (cl & speaker_TBC) concatn(length,desc," TBC");
|
||||
if (cl & speaker_TBR) concatn(length,desc," TBR");
|
||||
|
||||
concatn(length,desc,"\n");
|
||||
}
|
||||
|
||||
if (vgmstream->loop_start_sample >= 0 && vgmstream->loop_end_sample > vgmstream->loop_start_sample) {
|
||||
snprintf(temp,TEMPSIZE,
|
||||
"looping: %s\n"
|
||||
|
@ -727,6 +727,48 @@ typedef enum {
|
||||
|
||||
} meta_t;
|
||||
|
||||
/* standard WAVEFORMATEXTENSIBLE speaker positions */
|
||||
typedef enum {
|
||||
speaker_FL = (1 << 0), /* front left */
|
||||
speaker_FR = (1 << 1), /* front right */
|
||||
speaker_FC = (1 << 2), /* front center */
|
||||
speaker_LFE = (1 << 3), /* low frequency effects */
|
||||
speaker_BL = (1 << 4), /* back left */
|
||||
speaker_BR = (1 << 5), /* back right */
|
||||
speaker_FLC = (1 << 6), /* front left center */
|
||||
speaker_FRC = (1 << 7), /* front right center */
|
||||
speaker_BC = (1 << 8), /* back center */
|
||||
speaker_SL = (1 << 9), /* side left */
|
||||
speaker_SR = (1 << 10), /* side right */
|
||||
|
||||
speaker_TC = (1 << 11), /* top center*/
|
||||
speaker_TFL = (1 << 12), /* top front left */
|
||||
speaker_TFC = (1 << 13), /* top front center */
|
||||
speaker_TFR = (1 << 14), /* top front right */
|
||||
speaker_TBL = (1 << 15), /* top back left */
|
||||
speaker_TBC = (1 << 16), /* top back center */
|
||||
speaker_TBR = (1 << 17), /* top back left */
|
||||
|
||||
} speaker_t;
|
||||
|
||||
/* typical mappings that metas may use to set channel_layout (but plugin must actually use it)
|
||||
* (in order, so 3ch file could be mapped to FL FR FC or FL FR LFE but not LFE FL FR) */
|
||||
typedef enum {
|
||||
mapping_MONO = speaker_FC,
|
||||
mapping_STEREO = speaker_FL | speaker_FR,
|
||||
mapping_2POINT1 = speaker_FL | speaker_FR | speaker_LFE,
|
||||
mapping_2POINT1_xiph = speaker_FL | speaker_FR | speaker_FC,
|
||||
mapping_QUAD = speaker_FL | speaker_FR | speaker_BL | speaker_BR,
|
||||
mapping_QUAD_surround = speaker_FL | speaker_FR | speaker_FC | speaker_BC,
|
||||
mapping_5POINT0 = speaker_FL | speaker_FR | speaker_LFE | speaker_BL | speaker_BR,
|
||||
mapping_5POINT0_xiph = speaker_FL | speaker_FR | speaker_FC | speaker_BL | speaker_BR,
|
||||
mapping_5POINT1 = speaker_FL | speaker_FR | speaker_FC | speaker_LFE | speaker_BL | speaker_BR,
|
||||
mapping_5POINT1_surround = speaker_FL | speaker_FR | speaker_FC | speaker_LFE | speaker_SL | speaker_SR,
|
||||
mapping_7POINT0 = speaker_FL | speaker_FR | speaker_FC | speaker_LFE | speaker_BC | speaker_FLC | speaker_FRC,
|
||||
mapping_7POINT1 = speaker_FL | speaker_FR | speaker_FC | speaker_LFE | speaker_BL | speaker_BR | speaker_FLC | speaker_FRC,
|
||||
mapping_7POINT1_surround = speaker_FL | speaker_FR | speaker_FC | speaker_LFE | speaker_BL | speaker_BR | speaker_SL | speaker_SR,
|
||||
} mapping_t;
|
||||
|
||||
#ifdef VGMSTREAM_MIXING
|
||||
/* mixing info */
|
||||
typedef enum {
|
||||
@ -832,6 +874,9 @@ typedef struct {
|
||||
size_t stream_size; /* info to properly calculate bitrate in case of subsongs */
|
||||
char stream_name[STREAM_NAME_SIZE]; /* name of the current stream (info), if the file stores it and it's filled */
|
||||
|
||||
/* mapping config (info for plugins) */
|
||||
uint32_t channel_layout; /* order: FL FR FC LFE BL BR FLC FRC BC SL SR etc (WAVEFORMATEX flags where FL=lowest bit set) */
|
||||
|
||||
/* other config */
|
||||
int allow_dual_stereo; /* search for dual stereo (file_L.ext + file_R.ext = single stereo file) */
|
||||
#ifndef VGMSTREAM_MIXING
|
||||
|
Loading…
x
Reference in New Issue
Block a user