Add multichannel layout for OGG/MP4/WEM/FFmpeg and use in foobar

This commit is contained in:
bnnm 2019-03-02 15:57:41 +01:00
parent 3d279ea42c
commit d9296c6693
10 changed files with 124 additions and 17 deletions

View File

@ -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);
}

View File

@ -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);

View File

@ -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

View File

@ -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:

View File

@ -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;

View File

@ -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

View File

@ -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:

View File

@ -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 */

View File

@ -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"

View File

@ -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