diff --git a/README.md b/README.md index fda23746..3ce229b7 100644 --- a/README.md +++ b/README.md @@ -294,7 +294,9 @@ in foobar's preferences): If your player isn't picking tags make sure vgmstream is detecting the song (as other plugins can steal its extensions, see above), .m3u is properly -named and that filenames inside match the song filename. +named and that filenames inside match the song filename. For Winamp you need +to make sure options > titles > advanced title formatting checkbox is set and +the format defined. ## Supported codec types diff --git a/cli/txtp_maker.py b/cli/txtp_maker.py index bbfa5b22..b1b10d92 100644 --- a/cli/txtp_maker.py +++ b/cli/txtp_maker.py @@ -38,8 +38,9 @@ def print_help(appname): print("Options:\n" " -r: find recursive (writes files to current dir, with dir in TXTP)\n" " -c (name): set path to CLI (default: test.exe)\n" - " -n (name): use (name)_(subsong).txtp format\n" - " You can put '{filename}' somewhere to get it substituted by the base name\n" + " -n (name): use (name).txtp, that can be formatted using:\n" + " {filename}, {subsong}, {internal-name}\n" + " ex. -n BGM_{subsong}, -n {subsong}__{internal-name} " " -z N: zero-fill subsong number (default: auto fill up to total subsongs)\n" " -d (dir): add dir in TXTP (if the file will reside in a subdir)\n" " -m: create mini-txtp\n" @@ -436,18 +437,25 @@ class TxtpMaker(object): pos = fname_base.rfind(".") #remove ext if (pos != -1 and pos > 1): fname_base = fname_base[:pos] + + internal_name = self.stream_name txt = cfg.base_name txt = txt.replace("{filename}",fname_base) - + txt = txt.replace("{subsong}",index) + txt = txt.replace("{internal-name}",internal_name) + + outname = "{}".format(txt) + else: txt = fname_path pos = txt.rfind(".") #remove ext if (pos != -1 and pos > 1): txt = txt[:pos] - outname = "{}".format(txt) - if index != "": - outname += "_" + index + + outname = "{}".format(txt) + if index != "": + outname += "_" + index line = '' if cfg.subdir != '': diff --git a/doc/TXTP.md b/doc/TXTP.md index 2eb79e2d..0c94ea12 100644 --- a/doc/TXTP.md +++ b/doc/TXTP.md @@ -134,6 +134,12 @@ music_Home.ps3.scd#c1~3 ``` Doesn't change the final number of channels though, just mutes non-selected channels. +If you use **`C(number)`** it will remove non-selected channels (not done directly for backwards compatibility). This just a shortcut for macro `#@track` (described later): +``` +#plays channels 3 and 4 = 2nd subsong and removes other channels +music_Home.ps3.scd#C3 4 +``` + ### Custom play settings **`#l(loops)`**, **`#f(fade)`**, **`#d(fade-delay)`**, **`#i(ignore loop)`**, **`#F(ignore fade)`**, **`#E(end-to-end loop)`** diff --git a/src/coding/coding.h b/src/coding/coding.h index bf770b7a..7d0ff2ef 100644 --- a/src/coding/coding.h +++ b/src/coding/coding.h @@ -291,6 +291,7 @@ 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); +void ffmpeg_set_channel_remapping(ffmpeg_codec_data * data, int *channels_remap); /* 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); diff --git a/src/coding/ffmpeg_decoder.c b/src/coding/ffmpeg_decoder.c index 1822be16..a2c5b140 100644 --- a/src/coding/ffmpeg_decoder.c +++ b/src/coding/ffmpeg_decoder.c @@ -28,6 +28,25 @@ static void g_init_ffmpeg() { } } +static void remap_audio(sample_t *outbuf, int sample_count, int channels, int channel_mappings[]) { + int ch_from,ch_to,s; + sample_t temp; + for (s = 0; s < sample_count; s++) { + for (ch_from = 0; ch_from < channels; ch_from++) { + if (ch_from > 32) + continue; + + ch_to = channel_mappings[ch_from]; + if (ch_to < 1 || ch_to > 32 || ch_to > channels-1 || ch_from == ch_to) + continue; + + temp = outbuf[s*channels + ch_from]; + outbuf[s*channels + ch_from] = outbuf[s*channels + ch_to]; + outbuf[s*channels + ch_to] = temp; + } + } +} + /* converts codec's samples (can be in any format, ex. Ogg's float32) to PCM16 */ static void convert_audio_pcm16(sample_t *outbuf, const uint8_t *inbuf, int fullSampleCount, int bitsPerSample, int floatingPoint) { int s; @@ -656,6 +675,8 @@ end: /* convert native sample format into PCM16 outbuf */ samplesReadNow = bytesRead / (bytesPerSample * channels); convert_audio_pcm16(outbuf, data->sampleBuffer, samplesReadNow * channels, data->bitsPerSample, data->floatingPoint); + if (data->channel_remap_set) + remap_audio(outbuf, samplesReadNow, data->channels, data->channel_remap); /* clean buffer when requested more samples than possible */ if (endOfAudio && samplesReadNow < samples_to_do) { @@ -814,4 +835,19 @@ uint32_t ffmpeg_get_channel_layout(ffmpeg_codec_data * data) { return (uint32_t)data->codecCtx->channel_layout; /* uint64 but there ain't so many speaker mappings */ } +/* yet another hack to fix codecs that encode channels in different order and reorder on decoder + * but FFmpeg doesn't do it automatically + * (maybe should be done via mixing, but could clash with other stuff?) */ +void ffmpeg_set_channel_remapping(ffmpeg_codec_data * data, int *channel_remap) { + int i; + + if (data->channels > 32) + return; + + for (i = 0; i < data->channels; i++) { + data->channel_remap[i] = channel_remap[i]; + } + data->channel_remap_set = 1; +} + #endif diff --git a/src/layout/layered.c b/src/layout/layered.c index 0a9426b2..0c27f7e8 100644 --- a/src/layout/layered.c +++ b/src/layout/layered.c @@ -50,7 +50,9 @@ void render_vgmstream_layered(sample_t * outbuf, int32_t sample_count, VGMSTREAM } samples_written += samples_to_do; - vgmstream->current_sample = data->layers[0]->current_sample; /* just in case it's used for info */ + /* needed for info (ex. for mixing) */ + vgmstream->current_sample = data->layers[0]->current_sample; + vgmstream->loop_count = data->layers[0]->loop_count; //vgmstream->samples_into_block = 0; /* handled in each layer */ } } diff --git a/src/meta/akb.c b/src/meta/akb.c index 91bedb81..ab19920d 100644 --- a/src/meta/akb.c +++ b/src/meta/akb.c @@ -192,7 +192,7 @@ VGMSTREAM * init_vgmstream_akb2(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; off_t start_offset, material_offset, extradata_offset; size_t material_size, extradata_size, stream_size; - int loop_flag = 0, channel_count, encryption_flag, codec, sample_rate, /*num_samples,*/ loop_start, loop_end; + int loop_flag = 0, channel_count, encryption_flag, codec, sample_rate, num_samples, loop_start, loop_end; int total_subsongs, target_subsong = streamFile->stream_index; /* check extensions */ @@ -237,7 +237,7 @@ VGMSTREAM * init_vgmstream_akb2(STREAMFILE *streamFile) { material_size = read_16bitLE(material_offset+0x04,streamFile); sample_rate = (uint16_t)read_16bitLE(material_offset+0x06,streamFile); stream_size = read_32bitLE(material_offset+0x08,streamFile); - //num_samples = read_32bitLE(material_offset+0x0c,streamFile); + num_samples = read_32bitLE(material_offset+0x0c,streamFile); loop_start = read_32bitLE(material_offset+0x10,streamFile); loop_end = read_32bitLE(material_offset+0x14,streamFile); @@ -263,6 +263,17 @@ VGMSTREAM * init_vgmstream_akb2(STREAMFILE *streamFile) { vgmstream->meta_type = meta_AKB; switch (codec) { + case 0x01: /* PCM16LE [Mobius: Final Fantasy (Android)] */ + vgmstream->coding_type = coding_PCM16LE; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = 0x02; + + vgmstream->num_samples = num_samples; + vgmstream->loop_start_sample = loop_start; + vgmstream->loop_end_sample = loop_end; + break; + + case 0x02: { /* MSADPCM [The Irregular at Magic High School Lost Zero (Android)] */ vgmstream->coding_type = coding_MSADPCM; vgmstream->layout_type = layout_none; @@ -323,7 +334,6 @@ VGMSTREAM * init_vgmstream_akb2(STREAMFILE *streamFile) { } #endif - case 0x01: /* PCM16LE */ default: goto fail; } diff --git a/src/meta/riff.c b/src/meta/riff.c index 3a4f090c..54572ffd 100644 --- a/src/meta/riff.c +++ b/src/meta/riff.c @@ -608,6 +608,21 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) { ffmpeg_set_skip_samples(ffmpeg_data, fact_sample_skip); } + /* LFE channel should be reordered on decode, but FFmpeg doesn't do it automatically: + * - 6ch: FL FR FC BL BR LFE > FL FR FC LFE BL BR + * - 8ch: FL FR FC BL BR SL SR LFE > FL FR FC LFE BL BR SL SR + * (ATRAC3Plus only, 5/7ch can't be encoded) */ + if (ffmpeg_data->channels == 6) { + /* LFE BR BL > LFE BL BR > same */ + int channel_remap[] = { 0, 1, 2, 5, 5, 5, }; + ffmpeg_set_channel_remapping(ffmpeg_data, channel_remap); + } + else if (ffmpeg_data->channels == 8) { + /* LFE BR SL SR BL > LFE BL SL SR BR > LFE BL BR SR SL > LFE BL BR SL SR > same */ + int channel_remap[] = { 0, 1, 2, 7, 7, 7, 7, 7}; + ffmpeg_set_channel_remapping(ffmpeg_data, channel_remap); + } + /* RIFF loop/sample values are absolute (with skip samples), adjust */ if (loop_flag) { loop_start_smpl -= (int32_t)ffmpeg_data->skipSamples; diff --git a/src/meta/txtp.c b/src/meta/txtp.c index d2b147c9..96f0be07 100644 --- a/src/meta/txtp.c +++ b/src/meta/txtp.c @@ -285,7 +285,7 @@ static void apply_config(VGMSTREAM *vgmstream, txtp_entry *current) { for (ch = 0; ch < vgmstream->channels; ch++) { if (!((current->channel_mask >> ch) & 1)) { txtp_mix_data mix = {0}; - mix.ch_dst = ch; + mix.ch_dst = ch + 1; mix.vol = 0.0f; add_mixing(current, &mix, MIX_VOLUME); } @@ -880,7 +880,8 @@ static int add_filename(txtp_header * txtp, char *filename, int is_default) { add_mixing(&cfg, &mix, MACRO_VOLUME); } - else if (strcmp(command,"@track") == 0) { + else if (strcmp(command,"@track") == 0 || + strcmp(command,"C") == 0 ) { txtp_mix_data mix = {0}; nm = get_mask(config, &mix.mask); @@ -930,7 +931,9 @@ static int add_filename(txtp_header * txtp, char *filename, int is_default) { } else { //;VGM_LOG("TXTP: unknown command\n"); - break; /* end, incorrect command, or possibly a comment or double ## comment too */ + /* end, incorrect command, or possibly a comment or double ## comment too + * (shouldn't fail for forward compatibility) */ + break; } } } diff --git a/src/mixing.c b/src/mixing.c index 53e7bd36..95b8d8ef 100644 --- a/src/mixing.c +++ b/src/mixing.c @@ -468,6 +468,7 @@ void mixing_push_add(VGMSTREAM* vgmstream, int ch_dst, int ch_src, double volume mix.ch_src = ch_src; mix.vol = volume; + //;VGM_LOG("MIX: add %i+%i*%f\n", ch_dst,ch_src,volume); add_mixing(vgmstream, &mix); } @@ -484,6 +485,7 @@ void mixing_push_volume(VGMSTREAM* vgmstream, int ch_dst, double volume) { mix.ch_dst = ch_dst; mix.vol = volume; + //;VGM_LOG("MIX: volume %i*%f\n", ch_dst,volume); add_mixing(vgmstream, &mix); } @@ -648,7 +650,7 @@ void mixing_push_fade(VGMSTREAM* vgmstream, int ch_dst, double vol_start, double /* should only modify prev if add_mixing but meh */ } - //;VGM_LOG("MIX: fade: %i^%f~%f=%c@%i~%i~%i~%i\n", ch_dst, vol_start, vol_end, shape, time_pre, time_start, time_end, time_post); + //;VGM_LOG("MIX: fade %i^%f~%f=%c@%i~%i~%i~%i\n", ch_dst, vol_start, vol_end, shape, time_pre, time_start, time_end, time_post); add_mixing(vgmstream, &mix); } diff --git a/src/vgmstream.h b/src/vgmstream.h index 6e0e0704..2a5c04e0 100644 --- a/src/vgmstream.h +++ b/src/vgmstream.h @@ -1205,6 +1205,9 @@ typedef struct { int64_t skipSamples; // number of start samples that will be skipped (encoder delay), for looping adjustments int streamCount; // number of FFmpeg audio streams + int channel_remap_set; + int channel_remap[32]; /* map of channel > new position */ + /*** internal state ***/ // Intermediate byte buffer uint8_t *sampleBuffer;