mirror of
https://github.com/vgmstream/vgmstream.git
synced 2025-02-17 19:19:16 +01:00
Merge pull request #902 from bnnm/ffmpeg-tad
- Fix FFmpeg 4.4 encoder delay issues and cleanup - Ignore silence codec in bitrate and info - ffmpeg: cleanup - Allow wrong .AT3 again for old demuxed videos - Fix some .spf [Hugo: Magic in the Trollwoods (DS)] - Add Tantalus .tad decoder [House of the Dead (SAT)] - vgmstream123: fix segfault with downmixed audio
This commit is contained in:
commit
4e2cad5625
@ -751,6 +751,7 @@ are used in few games.
|
||||
- Konami XMD 4-bit ADPCM
|
||||
- Platinum 4-bit ADPCM
|
||||
- Argonaut ASF 4-bit ADPCM
|
||||
- Tantalus 4-bit ADPCM
|
||||
- Ocean DSA 4-bit ADPCM
|
||||
- Circus XPCM ADPCM
|
||||
- Circus XPCM VQ
|
||||
|
@ -264,7 +264,7 @@ static int play_vgmstream(const char *filename, song_settings_t *cfg) {
|
||||
size_t buffer_size;
|
||||
int32_t max_buffer_samples;
|
||||
int i;
|
||||
int output_channels;
|
||||
int output_channels, input_channels;
|
||||
|
||||
|
||||
sf = open_stdio_streamfile(filename);
|
||||
@ -328,8 +328,9 @@ static int play_vgmstream(const char *filename, song_settings_t *cfg) {
|
||||
*/
|
||||
apply_config(vgmstream, cfg);
|
||||
|
||||
input_channels = vgmstream->channels;
|
||||
output_channels = vgmstream->channels;
|
||||
vgmstream_mixing_enable(vgmstream, 0, NULL, &output_channels); /* query */
|
||||
vgmstream_mixing_enable(vgmstream, 0, &input_channels, &output_channels); /* query */
|
||||
|
||||
|
||||
/* Buffer size in bytes (after getting channels)
|
||||
@ -345,7 +346,7 @@ static int play_vgmstream(const char *filename, song_settings_t *cfg) {
|
||||
if (!buffer) goto fail;
|
||||
}
|
||||
|
||||
max_buffer_samples = buffer_size / (output_channels * sizeof(sample));
|
||||
max_buffer_samples = buffer_size / (input_channels * sizeof(sample));
|
||||
|
||||
vgmstream_mixing_enable(vgmstream, max_buffer_samples, NULL, NULL); /* enable */
|
||||
|
||||
@ -362,6 +363,9 @@ static int play_vgmstream(const char *filename, song_settings_t *cfg) {
|
||||
int time_total_min;
|
||||
double time_total_sec;
|
||||
int play_forever = vgmstream_get_play_forever(vgmstream);
|
||||
int32_t decode_pos_samples = 0;
|
||||
int32_t length_samples = vgmstream_get_samples(vgmstream);
|
||||
if (length_samples <= 0) goto fail;
|
||||
|
||||
if (out_filename && play_forever) {
|
||||
fprintf(stderr, "%s: cannot play forever and use output filename\n", filename);
|
||||
@ -369,10 +373,6 @@ static int play_vgmstream(const char *filename, song_settings_t *cfg) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
int32_t decode_pos_samples = 0;
|
||||
int32_t length_samples = vgmstream_get_samples(vgmstream);
|
||||
if (length_samples <= 0) goto fail;
|
||||
|
||||
total = (double)length_samples / vgmstream->sample_rate;
|
||||
time_total_min = (int)total / 60;
|
||||
time_total_sec = total - 60 * time_total_min;
|
||||
|
25
doc/TXTP.md
25
doc/TXTP.md
@ -2,7 +2,30 @@
|
||||
|
||||
TXTP is a text file with commands, to improve support for games using audio in certain uncommon or undesirable ways. It's in the form of a mini-playlist or a wrapper with play settings, meant to do post-processing over playable files.
|
||||
|
||||
Simply create a file named `(filename).txtp`, and inside write the commands described below.
|
||||
Simply create a file named `(filename).txtp`, and inside write the song name and commands described below. Then open the new file directly and vgmstream should play it.
|
||||
|
||||
Common case examples:
|
||||
|
||||
**stage01-intro+loop.txtp**
|
||||
```
|
||||
stage01_intro.vag
|
||||
stage01_loop.vag
|
||||
```
|
||||
|
||||
**bgm01-subsong2.txtp**
|
||||
```
|
||||
bgm01.fsb #2
|
||||
```
|
||||
|
||||
**sfx01-22khz.txtp**
|
||||
```
|
||||
sfx01.wav #h22050
|
||||
```
|
||||
|
||||
**field-remove-first-channels.txtp**
|
||||
```
|
||||
field.bfstm #C3,4
|
||||
```
|
||||
|
||||
|
||||
## TXTP MODES
|
||||
|
@ -224,6 +224,11 @@ void decode_dsa(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing,
|
||||
void decode_xmd(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, size_t frame_size);
|
||||
|
||||
|
||||
/* tantalus_decoder */
|
||||
void decode_tantalus(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do);
|
||||
int32_t tantalus_bytes_to_samples(size_t bytes, int channels);
|
||||
|
||||
|
||||
/* derf_decoder */
|
||||
void decode_derf(VGMSTREAMCHANNEL* stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do);
|
||||
|
||||
@ -552,6 +557,8 @@ void free_speex(speex_codec_data* data);
|
||||
|
||||
#ifdef VGM_USE_FFMPEG
|
||||
/* ffmpeg_decoder */
|
||||
typedef struct ffmpeg_codec_data ffmpeg_codec_data;
|
||||
|
||||
ffmpeg_codec_data* init_ffmpeg_offset(STREAMFILE* sf, uint64_t start, uint64_t size);
|
||||
ffmpeg_codec_data* init_ffmpeg_header_offset(STREAMFILE* sf, uint8_t* header, uint64_t header_size, uint64_t start, uint64_t size);
|
||||
ffmpeg_codec_data* init_ffmpeg_header_offset_subsong(STREAMFILE* sf, uint8_t* header, uint64_t header_size, uint64_t start, uint64_t size, int target_subsong);
|
||||
@ -566,13 +573,20 @@ uint32_t ffmpeg_get_channel_layout(ffmpeg_codec_data* data);
|
||||
void ffmpeg_set_channel_remapping(ffmpeg_codec_data* data, int* channels_remap);
|
||||
const char* ffmpeg_get_codec_name(ffmpeg_codec_data* data);
|
||||
void ffmpeg_set_force_seek(ffmpeg_codec_data* data);
|
||||
void ffmpeg_set_invert_floats(ffmpeg_codec_data* data);
|
||||
const char* ffmpeg_get_metadata_value(ffmpeg_codec_data* data, const char* key);
|
||||
|
||||
int32_t ffmpeg_get_samples(ffmpeg_codec_data* data);
|
||||
int ffmpeg_get_sample_rate(ffmpeg_codec_data* data);
|
||||
int ffmpeg_get_channels(ffmpeg_codec_data* data);
|
||||
int ffmpeg_get_subsong_count(ffmpeg_codec_data* data);
|
||||
|
||||
STREAMFILE* ffmpeg_get_streamfile(ffmpeg_codec_data* data);
|
||||
|
||||
/* ffmpeg_decoder_utils.c (helper-things) */
|
||||
ffmpeg_codec_data* init_ffmpeg_atrac3_raw(STREAMFILE* sf, off_t offset, size_t data_size, int sample_count, int channels, int sample_rate, int block_align, int encoder_delay);
|
||||
ffmpeg_codec_data* init_ffmpeg_atrac3_riff(STREAMFILE* sf, off_t offset, int* out_samples);
|
||||
ffmpeg_codec_data* init_ffmpeg_aac(STREAMFILE* sf, off_t offset, size_t size);
|
||||
ffmpeg_codec_data* init_ffmpeg_aac(STREAMFILE* sf, off_t offset, size_t size, int skip_samples);
|
||||
|
||||
|
||||
/* ffmpeg_decoder_custom_opus.c (helper-things) */
|
||||
|
@ -829,9 +829,7 @@ void xma_fix_raw_samples_ch(VGMSTREAM* vgmstream, STREAMFILE* sf, off_t stream_o
|
||||
* Somehow also needs to skip 64 extra samples (looks like another FFmpeg bug
|
||||
* where XMA outputs half a subframe samples late, WMAPRO isn't affected),
|
||||
* which sometimes makes FFmpeg complain (=reads after end) but doesn't seem audible. */
|
||||
if (data->skipSamples == 0) {
|
||||
ffmpeg_set_skip_samples(data, start_skip+64);
|
||||
}
|
||||
ffmpeg_set_skip_samples(data, start_skip+64);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
@ -2,10 +2,61 @@
|
||||
#include "coding.h"
|
||||
|
||||
#ifdef VGM_USE_FFMPEG
|
||||
#include <libavcodec/avcodec.h>
|
||||
#include <libavformat/avformat.h>
|
||||
#include <libswresample/swresample.h>
|
||||
|
||||
/* opaque struct */
|
||||
struct ffmpeg_codec_data {
|
||||
/*** IO internals ***/
|
||||
STREAMFILE* sf;
|
||||
|
||||
uint64_t start; // absolute start within the streamfile
|
||||
uint64_t offset; // absolute offset within the streamfile
|
||||
uint64_t size; // max size within the streamfile
|
||||
uint64_t logical_offset; // computed offset FFmpeg sees (including fake header)
|
||||
uint64_t logical_size; // computed size FFmpeg sees (including fake header)
|
||||
|
||||
uint64_t header_size; // fake header (parseable by FFmpeg) prepended on reads
|
||||
uint8_t* header_block; // fake header data (ie. RIFF)
|
||||
|
||||
/*** internal state ***/
|
||||
// config
|
||||
int stream_count; /* FFmpeg audio streams (ignores video/etc) */
|
||||
int stream_index;
|
||||
int64_t total_samples; /* may be 0 and innacurate */
|
||||
int64_t skip_samples; /* number of start samples that will be skipped (encoder delay) */
|
||||
int channel_remap_set;
|
||||
int channel_remap[32]; /* map of channel > new position */
|
||||
int invert_floats_set;
|
||||
int skip_samples_set; /* flag to know skip samples were manually added from vgmstream */
|
||||
int force_seek; /* flags for special seeking in faulty formats */
|
||||
int bad_init;
|
||||
|
||||
// FFmpeg context used for metadata
|
||||
AVCodec* codec;
|
||||
|
||||
/* FFmpeg decoder state */
|
||||
unsigned char* buffer;
|
||||
AVIOContext* ioCtx;
|
||||
AVFormatContext* formatCtx;
|
||||
AVCodecContext* codecCtx;
|
||||
AVFrame* frame; /* last decoded frame */
|
||||
AVPacket* packet; /* last read data packet */
|
||||
|
||||
int read_packet;
|
||||
int end_of_stream;
|
||||
int end_of_audio;
|
||||
|
||||
/* sample state */
|
||||
int32_t samples_discard;
|
||||
int32_t samples_consumed;
|
||||
int32_t samples_filled;
|
||||
};
|
||||
|
||||
|
||||
#define FFMPEG_DEFAULT_IO_BUFFER_SIZE 128 * 1024
|
||||
|
||||
|
||||
static volatile int g_ffmpeg_initialized = 0;
|
||||
|
||||
static void free_ffmpeg_config(ffmpeg_codec_data* data);
|
||||
@ -69,8 +120,8 @@ static int init_seek(ffmpeg_codec_data* data) {
|
||||
int size = 0; /* data size (block align) */
|
||||
int distance = 0; /* always 0 ("duration") */
|
||||
|
||||
AVStream * stream = data->formatCtx->streams[data->streamIndex];
|
||||
AVPacket * pkt = data->packet;
|
||||
AVStream* stream = data->formatCtx->streams[data->stream_index];
|
||||
AVPacket* pkt = data->packet;
|
||||
|
||||
|
||||
/* read_seek shouldn't need this index, but direct access to FFmpeg's internals is no good */
|
||||
@ -95,7 +146,7 @@ static int init_seek(ffmpeg_codec_data* data) {
|
||||
ret = av_read_frame(data->formatCtx, pkt);
|
||||
if (ret < 0)
|
||||
break;
|
||||
if (pkt->stream_index != data->streamIndex)
|
||||
if (pkt->stream_index != data->stream_index)
|
||||
continue; /* ignore non-selected streams */
|
||||
|
||||
//;VGM_LOG("FFMPEG: packet %i, ret=%i, pos=%i, dts=%i\n", packet_count, ret, (int32_t)pkt->pos, (int32_t)pkt->dts);
|
||||
@ -138,7 +189,7 @@ static int init_seek(ffmpeg_codec_data* data) {
|
||||
|
||||
test_seek:
|
||||
/* seek to 0 test + move back to beginning, since we just consumed packets */
|
||||
ret = avformat_seek_file(data->formatCtx, data->streamIndex, ts, ts, ts, AVSEEK_FLAG_ANY);
|
||||
ret = avformat_seek_file(data->formatCtx, data->stream_index, ts, ts, ts, AVSEEK_FLAG_ANY);
|
||||
if ( ret < 0 ) {
|
||||
//char test[1000] = {0}; av_strerror(ret, test, 1000); VGM_LOG("FFMPEG: ret=%i %s\n", ret, test);
|
||||
return ret; /* we can't even reset_vgmstream the file */
|
||||
@ -186,7 +237,7 @@ static int ffmpeg_read(void* opaque, uint8_t* buf, int read_size) {
|
||||
}
|
||||
|
||||
/* main read */
|
||||
bytes = read_streamfile(buf, data->offset, read_size, data->streamfile);
|
||||
bytes = read_streamfile(buf, data->offset, read_size, data->sf);
|
||||
data->logical_offset += bytes;
|
||||
data->offset += bytes;
|
||||
return bytes + max_to_copy;
|
||||
@ -244,7 +295,7 @@ ffmpeg_codec_data* init_ffmpeg_offset(STREAMFILE* sf, uint64_t start, uint64_t s
|
||||
return init_ffmpeg_header_offset(sf, NULL,0, start,size);
|
||||
}
|
||||
|
||||
ffmpeg_codec_data* init_ffmpeg_header_offset(STREAMFILE* sf, uint8_t * header, uint64_t header_size, uint64_t start, uint64_t size) {
|
||||
ffmpeg_codec_data* init_ffmpeg_header_offset(STREAMFILE* sf, uint8_t* header, uint64_t header_size, uint64_t start, uint64_t size) {
|
||||
return init_ffmpeg_header_offset_subsong(sf, header, header_size, start, size, 0);
|
||||
}
|
||||
|
||||
@ -281,8 +332,8 @@ ffmpeg_codec_data* init_ffmpeg_header_offset_subsong(STREAMFILE* sf, uint8_t* he
|
||||
data = calloc(1, sizeof(ffmpeg_codec_data));
|
||||
if (!data) return NULL;
|
||||
|
||||
data->streamfile = reopen_streamfile(sf, 0);
|
||||
if (!data->streamfile) goto fail;
|
||||
data->sf = reopen_streamfile(sf, 0);
|
||||
if (!data->sf) goto fail;
|
||||
|
||||
/* fake header to trick FFmpeg into demuxing/decoding the stream */
|
||||
if (header_size > 0) {
|
||||
@ -307,14 +358,16 @@ ffmpeg_codec_data* init_ffmpeg_header_offset_subsong(STREAMFILE* sf, uint8_t* he
|
||||
|
||||
/* setup other values */
|
||||
{
|
||||
AVStream *stream = data->formatCtx->streams[data->streamIndex];
|
||||
AVStream* stream = data->formatCtx->streams[data->stream_index];
|
||||
AVRational tb = {0};
|
||||
|
||||
tb.num = 1; tb.den = data->codecCtx->sample_rate;
|
||||
|
||||
#if 0
|
||||
/* derive info */
|
||||
data->sampleRate = data->codecCtx->sample_rate;
|
||||
data->channels = data->codecCtx->channels;
|
||||
data->bitrate = (int)(data->codecCtx->bit_rate);
|
||||
#if 0
|
||||
data->blockAlign = data->codecCtx->block_align;
|
||||
data->frameSize = data->codecCtx->frame_size;
|
||||
if(data->frameSize == 0) /* some formats don't set frame_size but can get on request, and vice versa */
|
||||
@ -322,40 +375,40 @@ ffmpeg_codec_data* init_ffmpeg_header_offset_subsong(STREAMFILE* sf, uint8_t* he
|
||||
#endif
|
||||
|
||||
/* try to guess frames/samples (duration isn't always set) */
|
||||
tb.num = 1; tb.den = data->codecCtx->sample_rate;
|
||||
data->totalSamples = av_rescale_q(stream->duration, stream->time_base, tb);
|
||||
if (data->totalSamples < 0)
|
||||
data->totalSamples = 0; /* caller must consider this */
|
||||
data->total_samples = av_rescale_q(stream->duration, stream->time_base, tb);
|
||||
if (data->total_samples < 0)
|
||||
data->total_samples = 0;
|
||||
|
||||
/* expose start samples to be skipped (encoder delay, usually added by MDCT-based encoders like AAC/MP3/ATRAC3/XMA/etc)
|
||||
* get after init_seek because some demuxers like AAC only fill skip_samples for the first packet */
|
||||
#if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(58, 64, 100)
|
||||
if (stream->start_skip_samples) /* samples to skip in the first packet */
|
||||
data->skipSamples = stream->start_skip_samples;
|
||||
/* read start samples to be skipped (encoder delay), info only.
|
||||
* Not too reliable though, see ffmpeg_set_skip_samples */
|
||||
if (stream->start_time && stream->start_time != AV_NOPTS_VALUE)
|
||||
data->skip_samples = av_rescale_q(stream->start_time, stream->time_base, tb);
|
||||
if (data->skip_samples < 0)
|
||||
data->skip_samples = 0;
|
||||
|
||||
#if 0 //LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(58, 64, 100)
|
||||
/* exposed before but not too reliable either */
|
||||
else if (stream->start_skip_samples) /* samples to skip in the first packet */
|
||||
data->skip_samples = stream->start_skip_samples;
|
||||
else if (stream->skip_samples) /* samples to skip in any packet (first in this case), used sometimes instead (ex. AAC) */
|
||||
data->skipSamples = stream->skip_samples;
|
||||
#else
|
||||
if (stream->start_time)
|
||||
data->skipSamples = av_rescale_q(stream->start_time, stream->time_base, tb);
|
||||
data->skip_samples = stream->skip_samples;
|
||||
#endif
|
||||
|
||||
/* check ways to skip encoder delay/padding, for debugging purposes (some may be old/unused/encoder only/etc) */
|
||||
VGM_ASSERT(data->codecCtx->delay > 0, "FFMPEG: delay %i\n", (int)data->codecCtx->delay);//delay: OPUS
|
||||
//VGM_ASSERT(data->codecCtx->internal->skip_samples > 0, ...); /* for codec use, not accessible */
|
||||
VGM_ASSERT(data->codecCtx->delay > 0, "FFMPEG: delay %i\n", (int)data->codecCtx->delay);//delay: OPUS
|
||||
VGM_ASSERT(stream->codecpar->initial_padding > 0, "FFMPEG: initial_padding %i\n", (int)stream->codecpar->initial_padding);//delay: OPUS
|
||||
VGM_ASSERT(stream->codecpar->trailing_padding > 0, "FFMPEG: trailing_padding %i\n", (int)stream->codecpar->trailing_padding);
|
||||
VGM_ASSERT(stream->codecpar->seek_preroll > 0, "FFMPEG: seek_preroll %i\n", (int)stream->codecpar->seek_preroll);//seek delay: OPUS
|
||||
VGM_ASSERT(stream->start_time > 0, "FFMPEG: start_time %i\n", (int)stream->start_time); //delay
|
||||
VGM_ASSERT(stream->first_discard_sample > 0, "FFMPEG: first_discard_sample %i\n", (int)stream->first_discard_sample); //padding: MP3
|
||||
VGM_ASSERT(stream->last_discard_sample > 0, "FFMPEG: last_discard_sample %i\n", (int)stream->last_discard_sample); //padding: MP3
|
||||
#if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(58, 64, 100)
|
||||
VGM_ASSERT(stream->skip_samples > 0, "FFMPEG: skip_samples %i\n", (int)stream->skip_samples); //delay: MP4
|
||||
VGM_ASSERT(stream->start_skip_samples > 0, "FFMPEG: start_skip_samples %i\n", (int)stream->start_skip_samples); //delay: MP3
|
||||
#else
|
||||
VGM_ASSERT(stream->start_time > 0, "FFMPEG: start_time %i\n", (int)stream->start_time); //delay
|
||||
#endif
|
||||
VGM_ASSERT(stream->first_discard_sample > 0, "FFMPEG: first_discard_sample %i\n", (int)stream->first_discard_sample); //padding: MP3
|
||||
VGM_ASSERT(stream->last_discard_sample > 0, "FFMPEG: last_discard_sample %i\n", (int)stream->last_discard_sample); //padding: MP3
|
||||
/* also negative timestamp for formats like OGG/OPUS */
|
||||
/* not using it: BINK, FLAC, ATRAC3, XMA, MPC, WMA (may use internal skip samples) */
|
||||
//todo: double check Opus behavior
|
||||
}
|
||||
|
||||
|
||||
@ -398,44 +451,46 @@ static int init_ffmpeg_config(ffmpeg_codec_data* data, int target_subsong, int r
|
||||
|
||||
/* find valid audio stream and set other streams to discard */
|
||||
{
|
||||
int i, streamIndex, streamCount;
|
||||
int i, stream_index, stream_count;
|
||||
|
||||
streamIndex = -1;
|
||||
streamCount = 0;
|
||||
stream_index = -1;
|
||||
stream_count = 0;
|
||||
if (reset)
|
||||
streamIndex = data->streamIndex;
|
||||
stream_index = data->stream_index;
|
||||
|
||||
for (i = 0; i < data->formatCtx->nb_streams; ++i) {
|
||||
AVStream *stream = data->formatCtx->streams[i];
|
||||
AVStream* stream = data->formatCtx->streams[i];
|
||||
|
||||
if (stream->codecpar && stream->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
|
||||
streamCount++;
|
||||
stream_count++;
|
||||
|
||||
/* select Nth audio stream if specified, or first one */
|
||||
if (streamIndex < 0 || (target_subsong > 0 && streamCount == target_subsong)) {
|
||||
streamIndex = i;
|
||||
if (stream_index < 0 || (target_subsong > 0 && stream_count == target_subsong)) {
|
||||
stream_index = i;
|
||||
}
|
||||
}
|
||||
|
||||
if (i != streamIndex)
|
||||
if (i != stream_index)
|
||||
stream->discard = AVDISCARD_ALL; /* disable demuxing for other streams */
|
||||
}
|
||||
if (streamCount < target_subsong) goto fail;
|
||||
if (streamIndex < 0) goto fail;
|
||||
if (stream_count < target_subsong) goto fail;
|
||||
if (stream_index < 0) goto fail;
|
||||
|
||||
data->streamIndex = streamIndex;
|
||||
data->streamCount = streamCount;
|
||||
data->stream_index = stream_index;
|
||||
data->stream_count = stream_count;
|
||||
}
|
||||
|
||||
/* setup codec with stream info */
|
||||
data->codecCtx = avcodec_alloc_context3(NULL);
|
||||
if (!data->codecCtx) goto fail;
|
||||
|
||||
errcode = avcodec_parameters_to_context(data->codecCtx, ((AVStream*)data->formatCtx->streams[data->streamIndex])->codecpar);
|
||||
errcode = avcodec_parameters_to_context(data->codecCtx, data->formatCtx->streams[data->stream_index]->codecpar);
|
||||
if (errcode < 0) goto fail;
|
||||
|
||||
//av_codec_set_pkt_timebase(data->codecCtx, stream->time_base); /* deprecated and seemingly not needed */
|
||||
/* deprecated and seemingly not needed */
|
||||
//av_codec_set_pkt_timebase(data->codecCtx, stream->time_base);
|
||||
|
||||
/* not useddeprecated and seemingly not needed */
|
||||
data->codec = avcodec_find_decoder(data->codecCtx->codec_id);
|
||||
if (!data->codec) goto fail;
|
||||
|
||||
@ -501,7 +556,7 @@ static int decode_ffmpeg_frame(ffmpeg_codec_data* data) {
|
||||
}
|
||||
|
||||
/* ignore non-selected streams */
|
||||
if (data->packet->stream_index != data->streamIndex)
|
||||
if (data->packet->stream_index != data->stream_index)
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -788,7 +843,7 @@ void seek_ffmpeg(ffmpeg_codec_data* data, int32_t num_sample) {
|
||||
if (errcode < 0) goto fail;
|
||||
}
|
||||
else {
|
||||
avformat_seek_file(data->formatCtx, data->streamIndex, 0, 0, 0, AVSEEK_FLAG_ANY);
|
||||
avformat_seek_file(data->formatCtx, data->stream_index, 0, 0, 0, AVSEEK_FLAG_ANY);
|
||||
avcodec_flush_buffers(data->codecCtx);
|
||||
}
|
||||
|
||||
@ -800,18 +855,10 @@ void seek_ffmpeg(ffmpeg_codec_data* data, int32_t num_sample) {
|
||||
data->end_of_stream = 0;
|
||||
data->end_of_audio = 0;
|
||||
|
||||
/* consider skip samples (encoder delay), if manually set (otherwise let FFmpeg handle it) */
|
||||
/* consider skip samples (encoder delay), if manually set */
|
||||
if (data->skip_samples_set) {
|
||||
AVStream *stream = data->formatCtx->streams[data->streamIndex];
|
||||
/* sometimes (ex. AAC) after seeking to the first packet skip_samples is restored, but we want our value */
|
||||
#if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(58, 64, 100)
|
||||
stream->skip_samples = 0;
|
||||
stream->start_skip_samples = 0;
|
||||
#else
|
||||
stream->start_time = 0;
|
||||
#endif
|
||||
|
||||
data->samples_discard += data->skipSamples;
|
||||
data->samples_discard += data->skip_samples;
|
||||
/* internally FFmpeg may skip (skip_samples/start_skip_samples) too */
|
||||
}
|
||||
|
||||
return;
|
||||
@ -872,40 +919,53 @@ void free_ffmpeg(ffmpeg_codec_data* data) {
|
||||
data->header_block = NULL;
|
||||
}
|
||||
|
||||
close_streamfile(data->streamfile);
|
||||
close_streamfile(data->sf);
|
||||
free(data);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets the number of samples to skip at the beginning of the stream, needed by some "gapless" formats.
|
||||
* (encoder delay, usually added by MDCT-based encoders like AAC/MP3/ATRAC3/XMA/etc to "set up" the decoder).
|
||||
* Sets the number of samples to skip at the beginning of the stream (encoder delay), needed by some "gapless" formats.
|
||||
* - should be used at the beginning of the stream
|
||||
* - should check if there are data->skipSamples before using this, to avoid overwritting FFmpeg's value (ex. AAC).
|
||||
* - should use only if/when FFmpeg's format is known to botch encoder delay.
|
||||
*
|
||||
* This could be added per format in FFmpeg directly, but it's here for flexibility and due to bugs
|
||||
* (FFmpeg's stream->(start_)skip_samples causes glitches in XMA).
|
||||
* encoder delay in FFmpeg is handled in multiple ways:
|
||||
* - avstream/internal->start_skip_samples: skip in the first packet *if* pts=0 (set in MP3 only?)
|
||||
* - avstream/internal->skip_samples: skip in any packet (set in AAC encoded by libfaac, OPUS, MP3 in SWF, MOV/MP4)
|
||||
* - avstream->start_time: usually set same as skip_samples but in pts, info only (most of the above but OPUS)
|
||||
* - codecCtx->delay: seems equivalent to skip_samples, info only (OPUS)
|
||||
* - negative timestamp: Xiph style (Ogg Vorbis/Opus only?).
|
||||
* First two are only exposed in FFmpeg v4.4<, meaning you can't override buggy values after that.
|
||||
* But since FFmpeg only does encoder delay for a handful of formats, shouldn't matter much.
|
||||
* May need to detect exact versions if they start fixing formats.
|
||||
*/
|
||||
void ffmpeg_set_skip_samples(ffmpeg_codec_data* data, int skip_samples) {
|
||||
AVStream *stream = NULL;
|
||||
if (!data || !data->formatCtx)
|
||||
if (!data || !data->formatCtx || !skip_samples)
|
||||
return;
|
||||
|
||||
/* overwrite FFmpeg's skip samples */
|
||||
stream = data->formatCtx->streams[data->streamIndex];
|
||||
/* let FFmpeg handle (may need an option to force override?) */
|
||||
if (data->skip_samples) {
|
||||
VGM_ASSERT(data->skip_samples != skip_samples,
|
||||
"FMPEG: ignored skip_samples %i, already set %i\n", skip_samples, (int)data->skip_samples);
|
||||
return;
|
||||
}
|
||||
|
||||
#if 0
|
||||
{
|
||||
AVStream* stream = data->formatCtx->streams[data->stream_index];
|
||||
#if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(58, 64, 100)
|
||||
stream->start_skip_samples = 0; /* used for the first packet *if* pts=0 */
|
||||
stream->skip_samples = 0; /* skip_samples can be used for any packet */
|
||||
stream->start_skip_samples = 0;
|
||||
stream->skip_samples = 0;
|
||||
#else
|
||||
stream->start_time = 0;
|
||||
//stream->start_time = 0; /* info only = useless */
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
/* set skip samples with our internal discard */
|
||||
data->skip_samples_set = 1;
|
||||
data->samples_discard = skip_samples;
|
||||
|
||||
/* expose (info only) */
|
||||
data->skipSamples = skip_samples;
|
||||
data->skip_samples = skip_samples;
|
||||
}
|
||||
|
||||
/* returns channel layout if set */
|
||||
@ -920,10 +980,10 @@ uint32_t ffmpeg_get_channel_layout(ffmpeg_codec_data* data) {
|
||||
void ffmpeg_set_channel_remapping(ffmpeg_codec_data* data, int *channel_remap) {
|
||||
int i;
|
||||
|
||||
if (data->channels > 32)
|
||||
if (data->codecCtx->channels > 32)
|
||||
return;
|
||||
|
||||
for (i = 0; i < data->channels; i++) {
|
||||
for (i = 0; i < data->codecCtx->channels; i++) {
|
||||
data->channel_remap[i] = channel_remap[i];
|
||||
}
|
||||
data->channel_remap_set = 1;
|
||||
@ -940,12 +1000,20 @@ const char* ffmpeg_get_codec_name(ffmpeg_codec_data* data) {
|
||||
}
|
||||
|
||||
void ffmpeg_set_force_seek(ffmpeg_codec_data* data) {
|
||||
if (!data)
|
||||
return;
|
||||
/* some formats like Smacker are so buggy that any seeking is impossible (even on video players),
|
||||
* or MPC with an incorrectly parsed seek table (using as 0 some non-0 seek offset).
|
||||
* whatever, we'll just kill and reconstruct FFmpeg's config every time */
|
||||
data->force_seek = 1;
|
||||
reset_ffmpeg(data); /* reset state from trying to seek */
|
||||
//stream = data->formatCtx->streams[data->streamIndex];
|
||||
//stream = data->formatCtx->streams[data->stream_index];
|
||||
}
|
||||
|
||||
void ffmpeg_set_invert_floats(ffmpeg_codec_data* data) {
|
||||
if (!data)
|
||||
return;
|
||||
data->invert_floats_set = 1;
|
||||
}
|
||||
|
||||
const char* ffmpeg_get_metadata_value(ffmpeg_codec_data* data, const char* key) {
|
||||
@ -955,7 +1023,7 @@ const char* ffmpeg_get_metadata_value(ffmpeg_codec_data* data, const char* key)
|
||||
if (!data || !data->codec)
|
||||
return NULL;
|
||||
|
||||
avd = data->formatCtx->streams[data->streamIndex]->metadata; /* per stream (like Ogg) */
|
||||
avd = data->formatCtx->streams[data->stream_index]->metadata; /* per stream (like Ogg) */
|
||||
if (!avd)
|
||||
avd = data->formatCtx->metadata; /* per format (like Flac) */
|
||||
if (!avd)
|
||||
@ -968,8 +1036,33 @@ const char* ffmpeg_get_metadata_value(ffmpeg_codec_data* data, const char* key)
|
||||
return avde->value;
|
||||
}
|
||||
|
||||
int32_t ffmpeg_get_samples(ffmpeg_codec_data* data) {
|
||||
if (!data)
|
||||
return 0;
|
||||
return (int32_t)data->total_samples;
|
||||
}
|
||||
|
||||
int ffmpeg_get_sample_rate(ffmpeg_codec_data* data) {
|
||||
if (!data || !data->codecCtx)
|
||||
return 0;
|
||||
return data->codecCtx->sample_rate;
|
||||
}
|
||||
|
||||
int ffmpeg_get_channels(ffmpeg_codec_data* data) {
|
||||
if (!data || !data->codecCtx)
|
||||
return 0;
|
||||
return data->codecCtx->channels;
|
||||
}
|
||||
|
||||
int ffmpeg_get_subsong_count(ffmpeg_codec_data* data) {
|
||||
if (!data)
|
||||
return 0;
|
||||
return data->stream_count;
|
||||
}
|
||||
|
||||
|
||||
STREAMFILE* ffmpeg_get_streamfile(ffmpeg_codec_data* data) {
|
||||
if (!data) return NULL;
|
||||
return data->streamfile;
|
||||
return data->sf;
|
||||
}
|
||||
#endif
|
||||
|
@ -764,10 +764,8 @@ static ffmpeg_codec_data* init_ffmpeg_custom_opus_config(STREAMFILE* sf, off_t s
|
||||
|
||||
/* FFmpeg + libopus: skips samples, notifies skip in codecCtx->delay (not in stream->skip_samples)
|
||||
* FFmpeg + opus: *doesn't* skip, also notifies skip in codecCtx->delay, hurray (possibly fixed in recent versions)
|
||||
* FFmpeg + opus is audibly buggy with some low bitrate SSB Ultimate files too */
|
||||
//if (ffmpeg_data->skipSamples <= 0) {
|
||||
// ffmpeg_set_skip_samples(ffmpeg_data, skip);
|
||||
//}
|
||||
* FFmpeg + opus is audibly buggy with some low bitrate SSB Ultimate files */
|
||||
//ffmpeg_set_skip_samples(ffmpeg_data, skip);
|
||||
|
||||
close_streamfile(temp_sf);
|
||||
return ffmpeg_data;
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
#ifdef VGM_USE_FFMPEG
|
||||
|
||||
static int ffmpeg_make_riff_atrac3(uint8_t * buf, size_t buf_size, size_t sample_count, size_t data_size, int channels, int sample_rate, int block_align, int joint_stereo, int encoder_delay) {
|
||||
static int ffmpeg_make_riff_atrac3(uint8_t* buf, size_t buf_size, size_t sample_count, size_t data_size, int channels, int sample_rate, int block_align, int joint_stereo, int encoder_delay) {
|
||||
uint16_t codec_ATRAC3 = 0x0270;
|
||||
size_t riff_size = 4+4+ 4 + 0x28 + 0x10 + 4+4;
|
||||
|
||||
@ -41,7 +41,7 @@ static int ffmpeg_make_riff_atrac3(uint8_t * buf, size_t buf_size, size_t sample
|
||||
return riff_size;
|
||||
}
|
||||
|
||||
ffmpeg_codec_data * init_ffmpeg_atrac3_raw(STREAMFILE *sf, off_t offset, size_t data_size, int sample_count, int channels, int sample_rate, int block_align, int encoder_delay) {
|
||||
ffmpeg_codec_data* init_ffmpeg_atrac3_raw(STREAMFILE* sf, off_t offset, size_t data_size, int sample_count, int channels, int sample_rate, int block_align, int encoder_delay) {
|
||||
ffmpeg_codec_data *ffmpeg_data = NULL;
|
||||
uint8_t buf[0x100];
|
||||
int bytes;
|
||||
@ -57,16 +57,14 @@ ffmpeg_codec_data * init_ffmpeg_atrac3_raw(STREAMFILE *sf, off_t offset, size_t
|
||||
* in offsets, so calcs are expected to be handled externally (presumably the game would call raw decoding API
|
||||
* and any skips would be handled manually) */
|
||||
|
||||
/* FFmpeg reads this but just in case they fiddle with it in the future */
|
||||
ffmpeg_data->totalSamples = sample_count;
|
||||
|
||||
/* encoder delay: encoder introduces some garbage (not always silent) samples to skip at the beginning (at least 1 frame)
|
||||
* FFmpeg doesn't set this, and even if it ever does it's probably better to force it for the implicit skip. */
|
||||
ffmpeg_set_skip_samples(ffmpeg_data, encoder_delay);
|
||||
//ffmpeg_set_samples(sample_count); /* useful? */
|
||||
|
||||
/* invert ATRAC3: waveform is inverted vs official tools (not noticeable but for accuracy) */
|
||||
if (is_at3) {
|
||||
ffmpeg_data->invert_floats_set = 1;
|
||||
ffmpeg_set_invert_floats(ffmpeg_data);
|
||||
}
|
||||
|
||||
return ffmpeg_data;
|
||||
@ -76,7 +74,7 @@ fail:
|
||||
}
|
||||
|
||||
/* init ATRAC3/plus while adding some fixes */
|
||||
ffmpeg_codec_data * init_ffmpeg_atrac3_riff(STREAMFILE *sf, off_t offset, int* out_samples) {
|
||||
ffmpeg_codec_data* init_ffmpeg_atrac3_riff(STREAMFILE* sf, off_t offset, int* p_samples) {
|
||||
ffmpeg_codec_data *ffmpeg_data = NULL;
|
||||
int is_at3 = 0, is_at3p = 0, codec;
|
||||
size_t riff_size;
|
||||
@ -151,35 +149,33 @@ ffmpeg_codec_data * init_ffmpeg_atrac3_riff(STREAMFILE *sf, off_t offset, int* o
|
||||
implicit_skip = 0;
|
||||
}
|
||||
|
||||
/* FFmpeg reads this but just in case they fiddle with it in the future */
|
||||
ffmpeg_data->totalSamples = fact_samples;
|
||||
|
||||
/* encoder delay: encoder introduces some garbage (not always silent) samples to skip at the beginning (at least 1 frame)
|
||||
* FFmpeg doesn't set this, and even if it ever does it's probably better to force it for the implicit skip. */
|
||||
ffmpeg_set_skip_samples(ffmpeg_data, skip_samples + implicit_skip);
|
||||
//ffmpeg_set_samples(sample_count); /* useful? */
|
||||
|
||||
/* invert ATRAC3: waveform is inverted vs official tools (not noticeable but for accuracy) */
|
||||
if (is_at3) {
|
||||
ffmpeg_data->invert_floats_set = 1;
|
||||
ffmpeg_set_invert_floats(ffmpeg_data);
|
||||
}
|
||||
|
||||
/* multichannel fix: LFE channel should be reordered on decode (ATRAC3Plus only, only 1/2/6/8ch exist):
|
||||
* - 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 */
|
||||
if (is_at3p && ffmpeg_data->channels == 6) {
|
||||
if (is_at3p && ffmpeg_get_channels(ffmpeg_data) == 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 (is_at3p && ffmpeg_data->channels == 8) {
|
||||
else if (is_at3p && ffmpeg_get_channels(ffmpeg_data) == 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);
|
||||
}
|
||||
|
||||
|
||||
if (out_samples)
|
||||
*out_samples = fact_samples;
|
||||
if (p_samples)
|
||||
*p_samples = fact_samples;
|
||||
|
||||
return ffmpeg_data;
|
||||
fail:
|
||||
@ -187,7 +183,7 @@ fail:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ffmpeg_codec_data* init_ffmpeg_aac(STREAMFILE* sf, off_t offset, size_t size) {
|
||||
ffmpeg_codec_data* init_ffmpeg_aac(STREAMFILE* sf, off_t offset, size_t size, int skip_samples) {
|
||||
ffmpeg_codec_data* data = NULL;
|
||||
|
||||
data = init_ffmpeg_offset(sf, offset, size);
|
||||
@ -199,7 +195,7 @@ ffmpeg_codec_data* init_ffmpeg_aac(STREAMFILE* sf, off_t offset, size_t size) {
|
||||
/* raw AAC doesn't set this, while some decoders like FAAD remove 1024,
|
||||
* but should be handled in container as each encoder uses its own value
|
||||
* (Apple: 2112, FAAD: probably 1024, etc) */
|
||||
//ffmpeg_set_skip_samples(data, 1024);
|
||||
ffmpeg_set_skip_samples(data, skip_samples);
|
||||
|
||||
return data;
|
||||
fail:
|
||||
|
63
src/coding/tantalus_decoder.c
Normal file
63
src/coding/tantalus_decoder.c
Normal file
@ -0,0 +1,63 @@
|
||||
#include "coding.h"
|
||||
|
||||
|
||||
/* Decodes Tantalus TADC ADPCM codec, used in Saturn games.
|
||||
* Guessed based on other XA-style codecs values. */
|
||||
void decode_tantalus(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do) {
|
||||
uint8_t frame[0x10] = {0};
|
||||
off_t frame_offset;
|
||||
int i, frames_in, sample_count = 0;
|
||||
size_t bytes_per_frame, samples_per_frame;
|
||||
int shift, filter, coef1, coef2;
|
||||
int32_t hist1 = stream->adpcm_history1_32;
|
||||
int32_t hist2 = stream->adpcm_history2_32;
|
||||
|
||||
|
||||
/* external interleave (fixed size), mono */
|
||||
bytes_per_frame = 0x10;
|
||||
samples_per_frame = (bytes_per_frame - 0x01) * 2;
|
||||
frames_in = first_sample / samples_per_frame;
|
||||
//first_sample = first_sample % samples_per_frame; /* for flat layout */
|
||||
|
||||
/* parse frame header */
|
||||
frame_offset = stream->offset + bytes_per_frame*frames_in;
|
||||
read_streamfile(frame, frame_offset, bytes_per_frame, stream->streamfile); /* ignore EOF errors */
|
||||
filter = (frame[0x00] >> 4) & 0xf; /* 0 in tested files */
|
||||
shift = (frame[0x00] >> 0) & 0xf;
|
||||
if (filter != 0) {
|
||||
VGM_LOG_ONCE("TANTALUS: unknown filter\n");
|
||||
coef1 = 64;
|
||||
coef2 = 64; /* will sound horrid and hopefully reported */
|
||||
}
|
||||
else {
|
||||
coef1 = 64;
|
||||
coef2 = 0;
|
||||
}
|
||||
|
||||
|
||||
/* decode nibbles */
|
||||
for (i = first_sample; i < first_sample + samples_to_do; i++) {
|
||||
uint8_t nibbles = frame[0x01 + i/2];
|
||||
int32_t sample;
|
||||
|
||||
sample = i&1 ? /* low nibble first */
|
||||
get_high_nibble_signed(nibbles) :
|
||||
get_low_nibble_signed(nibbles);
|
||||
sample = sample << (shift + 6);
|
||||
sample = (sample + (hist1 * coef1) + (hist2 * coef2)) >> 6;
|
||||
|
||||
outbuf[sample_count] = clamp16(sample);
|
||||
sample_count += channelspacing;
|
||||
|
||||
hist2 = hist1;
|
||||
hist1 = sample;
|
||||
}
|
||||
|
||||
stream->adpcm_history1_32 = hist1;
|
||||
stream->adpcm_history2_32 = hist2;
|
||||
}
|
||||
|
||||
int32_t tantalus_bytes_to_samples(size_t bytes, int channels) {
|
||||
if (channels <= 0) return 0;
|
||||
return bytes / channels / 0x10 * 30;
|
||||
}
|
10
src/decode.c
10
src/decode.c
@ -503,6 +503,8 @@ int get_vgmstream_samples_per_frame(VGMSTREAM* vgmstream) {
|
||||
return 256; /* (0x8c - 0xc) * 2 */
|
||||
case coding_ASF:
|
||||
return 32; /* (0x11 - 0x1) * 2 */
|
||||
case coding_TANTALUS:
|
||||
return 30; /* (0x10 - 0x01) * 2 */
|
||||
case coding_DSA:
|
||||
return 14; /* (0x08 - 0x1) * 2 */
|
||||
case coding_XMD:
|
||||
@ -716,6 +718,8 @@ int get_vgmstream_frame_size(VGMSTREAM* vgmstream) {
|
||||
return 0x8c;
|
||||
case coding_ASF:
|
||||
return 0x11;
|
||||
case coding_TANTALUS:
|
||||
return 0x10;
|
||||
case coding_DSA:
|
||||
return 0x08;
|
||||
case coding_XMD:
|
||||
@ -1386,6 +1390,12 @@ void decode_vgmstream(VGMSTREAM* vgmstream, int samples_written, int samples_to_
|
||||
vgmstream->channels, vgmstream->samples_into_block, samples_to_do);
|
||||
}
|
||||
break;
|
||||
case coding_TANTALUS:
|
||||
for (ch = 0; ch < vgmstream->channels; ch++) {
|
||||
decode_tantalus(&vgmstream->ch[ch], buffer+ch,
|
||||
vgmstream->channels, vgmstream->samples_into_block, samples_to_do);
|
||||
}
|
||||
break;
|
||||
case coding_DSA:
|
||||
for (ch = 0; ch < vgmstream->channels; ch++) {
|
||||
decode_dsa(&vgmstream->ch[ch], buffer+ch,
|
||||
|
@ -515,6 +515,7 @@ static const char* extension_list[] = {
|
||||
"sxd2",
|
||||
"sxd3",
|
||||
|
||||
"tad",
|
||||
"tec",
|
||||
"tgq",
|
||||
"thp",
|
||||
@ -787,6 +788,7 @@ static const coding_info coding_info_list[] = {
|
||||
{coding_MC3, "Paradigm MC3 3-bit ADPCM"},
|
||||
{coding_FADPCM, "FMOD FADPCM 4-bit ADPCM"},
|
||||
{coding_ASF, "Argonaut ASF 4-bit ADPCM"},
|
||||
{coding_TANTALUS, "Tantalus 4-bit ADPCM"},
|
||||
{coding_DSA, "Ocean DSA 4-bit ADPCM"},
|
||||
{coding_XMD, "Konami XMD 4-bit ADPCM"},
|
||||
{coding_PCFX, "PC-FX 4-bit ADPCM"},
|
||||
@ -1350,6 +1352,7 @@ static const meta_info meta_info_list[] = {
|
||||
{meta_IDSP_TOSE, "TOSE .IDSP header"},
|
||||
{meta_DSP_KWA, "Kuju London .KWA header"},
|
||||
{meta_OGV_3RDEYE, "3rdEye .OGV header"},
|
||||
{meta_PIFF_TPCM, "Tantalus PIFF TPCM header"},
|
||||
};
|
||||
|
||||
void get_vgmstream_coding_description(VGMSTREAM* vgmstream, char* out, size_t out_size) {
|
||||
|
@ -240,6 +240,7 @@ VGMSTREAM* allocate_layered_vgmstream(layered_layout_data* data) {
|
||||
int i, channels, loop_flag, sample_rate, external_looping;
|
||||
int32_t num_samples, loop_start, loop_end;
|
||||
int delta = 1024;
|
||||
coding_t coding_type = data->layers[0]->coding_type;
|
||||
|
||||
/* get data */
|
||||
channels = data->output_channels;
|
||||
@ -250,6 +251,7 @@ VGMSTREAM* allocate_layered_vgmstream(layered_layout_data* data) {
|
||||
loop_end = data->layers[0]->loop_end_sample;
|
||||
external_looping = 0;
|
||||
sample_rate = 0;
|
||||
|
||||
for (i = 0; i < data->layer_count; i++) {
|
||||
int32_t layer_samples = vgmstream_get_samples(data->layers[i]);
|
||||
int layer_loop = data->layers[i]->loop_flag;
|
||||
@ -280,6 +282,9 @@ VGMSTREAM* allocate_layered_vgmstream(layered_layout_data* data) {
|
||||
|
||||
if (sample_rate < layer_rate)
|
||||
sample_rate = layer_rate;
|
||||
|
||||
if (coding_type == coding_SILENCE)
|
||||
coding_type = data->layers[i]->coding_type;
|
||||
}
|
||||
|
||||
data->external_looping = external_looping;
|
||||
@ -289,12 +294,12 @@ VGMSTREAM* allocate_layered_vgmstream(layered_layout_data* data) {
|
||||
vgmstream = allocate_vgmstream(channels, loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
vgmstream->meta_type = data->layers[0]->meta_type;
|
||||
vgmstream->sample_rate = sample_rate;
|
||||
vgmstream->num_samples = num_samples;
|
||||
vgmstream->loop_start_sample = loop_start;
|
||||
vgmstream->loop_end_sample = loop_end;
|
||||
vgmstream->meta_type = data->layers[0]->meta_type; /* info */
|
||||
vgmstream->coding_type = data->layers[0]->coding_type; /* info */
|
||||
vgmstream->coding_type = coding_type;
|
||||
|
||||
vgmstream->layout_type = layout_layered;
|
||||
vgmstream->layout_data = data;
|
||||
|
@ -296,6 +296,7 @@ VGMSTREAM* allocate_segmented_vgmstream(segmented_layout_data* data, int loop_fl
|
||||
int channel_layout;
|
||||
int i, sample_rate;
|
||||
int32_t num_samples, loop_start, loop_end;
|
||||
coding_t coding_type = data->segments[0]->coding_type;
|
||||
|
||||
/* save data */
|
||||
channel_layout = data->segments[0]->channel_layout;
|
||||
@ -322,10 +323,14 @@ VGMSTREAM* allocate_segmented_vgmstream(segmented_layout_data* data, int loop_fl
|
||||
|
||||
if (sample_rate < segment_rate)
|
||||
sample_rate = segment_rate;
|
||||
|
||||
if (coding_type == coding_SILENCE)
|
||||
coding_type = data->segments[i]->coding_type;
|
||||
}
|
||||
|
||||
/* respect loop_flag even when no loop_end found as it's possible file loops are set outside */
|
||||
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(data->output_channels, loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
@ -335,7 +340,7 @@ VGMSTREAM* allocate_segmented_vgmstream(segmented_layout_data* data, int loop_fl
|
||||
vgmstream->num_samples = num_samples;
|
||||
vgmstream->loop_start_sample = loop_start;
|
||||
vgmstream->loop_end_sample = loop_end;
|
||||
vgmstream->coding_type = data->segments[0]->coding_type;
|
||||
vgmstream->coding_type = coding_type;
|
||||
vgmstream->channel_layout = channel_layout;
|
||||
|
||||
vgmstream->layout_type = layout_segmented;
|
||||
|
@ -1201,6 +1201,10 @@
|
||||
<File
|
||||
RelativePath=".\meta\pcm_success.c"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\meta\piff_tpcm.c"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\meta\scd_pcm.c"
|
||||
@ -2354,6 +2358,10 @@
|
||||
RelativePath=".\coding\tac_decoder_lib.c"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\coding\tantalus_decoder.c"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\coding\ubi_adpcm_decoder.c"
|
||||
>
|
||||
|
@ -430,6 +430,7 @@
|
||||
<ClCompile Include="meta\xa_xa30.c" />
|
||||
<ClCompile Include="meta\pcm_sre.c" />
|
||||
<ClCompile Include="meta\pcm_success.c" />
|
||||
<ClCompile Include="meta\piff_tpcm.c" />
|
||||
<ClCompile Include="meta\scd_pcm.c" />
|
||||
<ClCompile Include="meta\pona.c" />
|
||||
<ClCompile Include="meta\pos.c" />
|
||||
@ -646,6 +647,7 @@
|
||||
<ClCompile Include="coding\speex_decoder.c" />
|
||||
<ClCompile Include="coding\tac_decoder.c" />
|
||||
<ClCompile Include="coding\tac_decoder_lib.c" />
|
||||
<ClCompile Include="coding\tantalus_decoder.c" />
|
||||
<ClCompile Include="coding\ubi_adpcm_decoder.c" />
|
||||
<ClCompile Include="coding\vadpcm_decoder.c" />
|
||||
<ClCompile Include="coding\vorbis_custom_decoder.c" />
|
||||
|
@ -787,6 +787,9 @@
|
||||
<ClCompile Include="meta\pcm_success.c">
|
||||
<Filter>meta\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="meta\piff_tpcm.c">
|
||||
<Filter>meta\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="meta\scd_pcm.c">
|
||||
<Filter>meta\Source Files</Filter>
|
||||
</ClCompile>
|
||||
@ -1408,6 +1411,9 @@
|
||||
<ClCompile Include="coding\tac_decoder.c">
|
||||
<Filter>coding\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="coding\tantalus_decoder.c">
|
||||
<Filter>coding\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="coding\tac_decoder_lib.c">
|
||||
<Filter>coding\Source Files</Filter>
|
||||
</ClCompile>
|
||||
|
@ -23,7 +23,7 @@ static int find_meta_loops(ffmpeg_codec_data* data, int32_t* p_loop_start, int32
|
||||
VGMSTREAM* init_vgmstream_ffmpeg(STREAMFILE* sf) {
|
||||
VGMSTREAM* vgmstream = NULL;
|
||||
ffmpeg_codec_data* data = NULL;
|
||||
int loop_flag = 0;
|
||||
int loop_flag = 0, channels, sample_rate;
|
||||
int32_t loop_start = 0, loop_end = 0, num_samples = 0, encoder_delay = 0;
|
||||
int total_subsongs, target_subsong = sf->stream_index;
|
||||
|
||||
@ -35,9 +35,12 @@ VGMSTREAM* init_vgmstream_ffmpeg(STREAMFILE* sf) {
|
||||
if (get_streamfile_size(sf) <= 0x1000)
|
||||
goto fail;
|
||||
|
||||
// many PSP rips have poorly demuxed videos with a failty RIFF, allow for now
|
||||
#if 0
|
||||
/* reject some formats handled elsewhere (better fail and check there than let buggy FFmpeg take over) */
|
||||
if (check_extensions(sf, "at3"))
|
||||
goto fail;
|
||||
#endif
|
||||
|
||||
if (target_subsong == 0) target_subsong = 1;
|
||||
|
||||
@ -45,18 +48,18 @@ VGMSTREAM* init_vgmstream_ffmpeg(STREAMFILE* sf) {
|
||||
data = init_ffmpeg_header_offset_subsong(sf, NULL, 0, 0, get_streamfile_size(sf), target_subsong);
|
||||
if (!data) return NULL;
|
||||
|
||||
total_subsongs = data->streamCount; /* uncommon, ex. wmv [Lost Odyssey (X360)] */
|
||||
total_subsongs = ffmpeg_get_subsong_count(data); /* uncommon, ex. wmv [Lost Odyssey (X360)] */
|
||||
if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail;
|
||||
|
||||
/* try to get .pos data */
|
||||
{
|
||||
uint8_t posbuf[4+4+4];
|
||||
uint8_t posbuf[0x04*3];
|
||||
|
||||
if (read_pos_file(posbuf, 4+4+4, sf)) {
|
||||
loop_start = get_s32le(posbuf+0);
|
||||
loop_end = get_s32le(posbuf+4);
|
||||
if (read_pos_file(posbuf, sizeof(posbuf), sf)) {
|
||||
loop_start = get_s32le(posbuf+0x00);
|
||||
loop_end = get_s32le(posbuf+0x04);
|
||||
loop_flag = 1; /* incorrect looping will be validated outside */
|
||||
/* FFmpeg can't always determine totalSamples correctly so optionally load it (can be 0/NULL)
|
||||
/* FFmpeg can't always determine samples correctly so optionally load it (can be 0/NULL)
|
||||
* won't crash and will output silence if no loop points and bigger than actual stream's samples */
|
||||
num_samples = get_s32le(posbuf+8);
|
||||
}
|
||||
@ -82,6 +85,9 @@ VGMSTREAM* init_vgmstream_ffmpeg(STREAMFILE* sf) {
|
||||
* .mus: Marc Ecko's Getting Up (PC) */
|
||||
if (!num_samples && check_extensions(sf, "mp3,lmp3,mus")) {
|
||||
num_samples = mpeg_get_samples(sf, 0x00, get_streamfile_size(sf));
|
||||
|
||||
/* this seems correct thankfully */
|
||||
//ffmpeg_set_skip_samples(data, encoder_delay);
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -100,18 +106,22 @@ VGMSTREAM* init_vgmstream_ffmpeg(STREAMFILE* sf) {
|
||||
|
||||
/* default but often inaccurate when calculated using bitrate (wrong for VBR) */
|
||||
if (!num_samples) {
|
||||
num_samples = data->totalSamples; /* may be 0 if FFmpeg can't precalculate it */
|
||||
num_samples = ffmpeg_get_samples(data); /* may be 0 if FFmpeg can't precalculate it */
|
||||
}
|
||||
|
||||
channels = ffmpeg_get_channels(data);
|
||||
sample_rate = ffmpeg_get_sample_rate(data);
|
||||
|
||||
|
||||
/* build VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(data->channels, loop_flag);
|
||||
vgmstream = allocate_vgmstream(channels, loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
vgmstream->sample_rate = data->sampleRate;
|
||||
vgmstream->meta_type = meta_FFMPEG;
|
||||
vgmstream->coding_type = coding_FFmpeg;
|
||||
vgmstream->sample_rate = sample_rate;
|
||||
|
||||
vgmstream->codec_data = data;
|
||||
vgmstream->coding_type = coding_FFmpeg;
|
||||
vgmstream->layout_type = layout_none;
|
||||
|
||||
vgmstream->num_samples = num_samples;
|
||||
@ -211,7 +221,7 @@ static int find_meta_loops(ffmpeg_codec_data* data, int32_t* p_loop_start, int32
|
||||
|
||||
if (loop_end <= 0) {
|
||||
// Looks a calculation was not possible, or tag value is wrongly set. Use the end of track as end value
|
||||
loop_end = data->totalSamples;
|
||||
loop_end = ffmpeg_get_samples(data);
|
||||
}
|
||||
|
||||
if (loop_start <= 0) {
|
||||
|
@ -307,7 +307,7 @@ VGMSTREAM * init_vgmstream_genh(STREAMFILE *streamFile) {
|
||||
if ( !ffmpeg_data ) goto fail;
|
||||
|
||||
//if (vgmstream->num_samples == 0)
|
||||
// vgmstream->num_samples = ffmpeg_data->totalSamples; /* sometimes works */
|
||||
// vgmstream->num_samples = ffmpeg_get_samples(ffmpeg_data); /* sometimes works */
|
||||
}
|
||||
else {
|
||||
/* fake header FFmpeg */
|
||||
|
@ -951,4 +951,6 @@ VGMSTREAM* init_vgmstream_ogv_3rdeye(STREAMFILE* sf);
|
||||
|
||||
VGMSTREAM* init_vgmstream_sspr(STREAMFILE* sf);
|
||||
|
||||
VGMSTREAM* init_vgmstream_piff_tpcm(STREAMFILE* sf);
|
||||
|
||||
#endif /*_META_H*/
|
||||
|
@ -163,11 +163,14 @@ fail:
|
||||
#ifdef VGM_USE_FFMPEG
|
||||
|
||||
typedef struct {
|
||||
int channels;
|
||||
int sample_rate;
|
||||
int32_t num_samples;
|
||||
int loop_flag;
|
||||
int32_t loop_start;
|
||||
int32_t loop_end;
|
||||
int32_t encoder_delay;
|
||||
int subsongs;
|
||||
} mp4_header;
|
||||
|
||||
static void parse_mp4(STREAMFILE* sf, mp4_header* mp4);
|
||||
@ -199,36 +202,45 @@ VGMSTREAM* init_vgmstream_mp4_aac_ffmpeg(STREAMFILE* sf) {
|
||||
|
||||
parse_mp4(sf, &mp4);
|
||||
|
||||
/* most values aren't read directly and use FFmpeg b/c MP4 makes things hard */
|
||||
if (!mp4.num_samples)
|
||||
mp4.num_samples = ffmpeg_get_samples(ffmpeg_data); /* does this take into account encoder delay? see FFV */
|
||||
if (!mp4.channels)
|
||||
mp4.channels = ffmpeg_get_channels(ffmpeg_data);
|
||||
if (!mp4.sample_rate)
|
||||
mp4.sample_rate = ffmpeg_get_sample_rate(ffmpeg_data);
|
||||
if (!mp4.subsongs)
|
||||
mp4.subsongs = ffmpeg_get_subsong_count(ffmpeg_data); /* may contain N tracks */
|
||||
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(ffmpeg_data->channels, mp4.loop_flag);
|
||||
vgmstream = allocate_vgmstream(mp4.channels, mp4.loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
vgmstream->meta_type = meta_MP4;
|
||||
vgmstream->sample_rate = ffmpeg_data->sampleRate;
|
||||
vgmstream->sample_rate = mp4.sample_rate;
|
||||
vgmstream->num_samples = mp4.num_samples;
|
||||
if (vgmstream->num_samples == 0)
|
||||
vgmstream->num_samples = ffmpeg_data->totalSamples;
|
||||
vgmstream->loop_start_sample = mp4.loop_start;
|
||||
vgmstream->loop_end_sample = mp4.loop_end;
|
||||
|
||||
vgmstream->codec_data = ffmpeg_data;
|
||||
vgmstream->coding_type = coding_FFmpeg;
|
||||
vgmstream->layout_type = layout_none;
|
||||
vgmstream->num_streams = ffmpeg_data->streamCount; /* may contain N tracks */
|
||||
vgmstream->num_streams = mp4.subsongs;
|
||||
|
||||
vgmstream->channel_layout = ffmpeg_get_channel_layout(vgmstream->codec_data);
|
||||
if (mp4.encoder_delay)
|
||||
ffmpeg_set_skip_samples(vgmstream->codec_data, mp4.encoder_delay);
|
||||
vgmstream->channel_layout = ffmpeg_get_channel_layout(ffmpeg_data);
|
||||
|
||||
/* needed for CRI MP4, otherwise FFmpeg usually reads standard delay */
|
||||
ffmpeg_set_skip_samples(vgmstream->codec_data, mp4.encoder_delay);
|
||||
|
||||
return vgmstream;
|
||||
|
||||
fail:
|
||||
if (ffmpeg_data) {
|
||||
free_ffmpeg(ffmpeg_data);
|
||||
if (vgmstream) vgmstream->codec_data = NULL;
|
||||
free_ffmpeg(ffmpeg_data);
|
||||
if (vgmstream) {
|
||||
vgmstream->codec_data = NULL;
|
||||
close_vgmstream(vgmstream);
|
||||
}
|
||||
if (vgmstream) close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
@ -144,20 +144,16 @@ VGMSTREAM* init_vgmstream_msf(STREAMFILE* sf) {
|
||||
}
|
||||
#elif defined(VGM_USE_FFMPEG)
|
||||
case 0x07: { /* MPEG (LAME MP3) [Dengeki Bunko Fighting Climax (PS3), Asura's Wrath (PS3)-vbr] */
|
||||
ffmpeg_codec_data *ffmpeg_data = NULL;
|
||||
int is_vbr = (flags & 0x20); /* must calc samples/loop offsets manually */
|
||||
|
||||
ffmpeg_data = init_ffmpeg_offset(sf, start_offset, sf->get_size(sf));
|
||||
if (!ffmpeg_data) goto fail;
|
||||
vgmstream->codec_data = ffmpeg_data;
|
||||
vgmstream->codec_data = init_ffmpeg_offset(sf, start_offset, 0);;
|
||||
if (!vgmstream->codec_data) goto fail;
|
||||
vgmstream->coding_type = coding_FFmpeg;
|
||||
vgmstream->layout_type = layout_none;
|
||||
|
||||
//todo use same calcs as above
|
||||
vgmstream->num_samples = (int64_t)data_size * ffmpeg_data->sampleRate * 8 / ffmpeg_data->bitrate;
|
||||
if (loop_flag) {
|
||||
vgmstream->loop_start_sample = (int64_t)loop_start * ffmpeg_data->sampleRate * 8 / ffmpeg_data->bitrate;
|
||||
vgmstream->loop_end_sample = (int64_t)loop_end * ffmpeg_data->sampleRate * 8 / ffmpeg_data->bitrate;
|
||||
}
|
||||
vgmstream->num_samples = mpeg_get_samples_clean(sf, start_offset, data_size, &loop_start, &loop_end, is_vbr);
|
||||
vgmstream->loop_start_sample = loop_start;
|
||||
vgmstream->loop_end_sample = loop_end;
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
@ -6,7 +6,7 @@
|
||||
VGMSTREAM* init_vgmstream_naac(STREAMFILE* sf) {
|
||||
VGMSTREAM* vgmstream = NULL;
|
||||
off_t start_offset;
|
||||
int loop_flag, channels;
|
||||
int loop_flag, channels, skip_samples;
|
||||
size_t data_size;
|
||||
|
||||
|
||||
@ -22,6 +22,7 @@ VGMSTREAM* init_vgmstream_naac(STREAMFILE* sf) {
|
||||
start_offset = 0x1000;
|
||||
loop_flag = (read_s32le(0x18,sf) != 0);
|
||||
channels = read_s32le(0x08,sf);
|
||||
skip_samples = 1024; /* raw AAC doesn't set this */
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(channels, loop_flag);
|
||||
@ -42,15 +43,14 @@ VGMSTREAM* init_vgmstream_naac(STREAMFILE* sf) {
|
||||
|
||||
#ifdef VGM_USE_FFMPEG
|
||||
{
|
||||
vgmstream->codec_data = init_ffmpeg_aac(sf, start_offset, data_size);
|
||||
vgmstream->codec_data = init_ffmpeg_aac(sf, start_offset, data_size, skip_samples);
|
||||
if (!vgmstream->codec_data) goto fail;
|
||||
vgmstream->coding_type = coding_FFmpeg;
|
||||
vgmstream->layout_type = layout_none;
|
||||
|
||||
/* observed default, some files start without silence though seems correct when loop_start=0 */
|
||||
ffmpeg_set_skip_samples(vgmstream->codec_data, 1024); /* raw AAC doesn't set this */
|
||||
vgmstream->num_samples -= 1024;
|
||||
vgmstream->loop_end_sample -= 1024;
|
||||
vgmstream->num_samples -= skip_samples;
|
||||
vgmstream->loop_end_sample -= skip_samples;
|
||||
/* for some reason last frame is ignored/bugged in various decoders (gives EOF errors) */
|
||||
}
|
||||
#else
|
||||
|
54
src/meta/piff_tpcm.c
Normal file
54
src/meta/piff_tpcm.c
Normal file
@ -0,0 +1,54 @@
|
||||
#include "meta.h"
|
||||
#include "../coding/coding.h"
|
||||
|
||||
|
||||
/* PIFF TADH - from Tantalus games [House of the Dead (SAT)] */
|
||||
VGMSTREAM* init_vgmstream_piff_tpcm(STREAMFILE* sf) {
|
||||
VGMSTREAM* vgmstream = NULL;
|
||||
off_t start_offset, header_offset, data_size;
|
||||
int loop_flag, channels, sample_rate;
|
||||
|
||||
|
||||
/* checks */
|
||||
/* .tad: from internal filenames */
|
||||
if (!check_extensions(sf, "tad"))
|
||||
goto fail;
|
||||
/* Tantalus also has PIFF without this */
|
||||
if (!is_id32be(0x00,sf, "PIFF") || !is_id32be(0x08,sf, "TPCM") || !is_id32be(0x0c,sf, "TADH"))
|
||||
goto fail;
|
||||
|
||||
header_offset = 0x14;
|
||||
/* 0x00: 1? */
|
||||
/* 0x01: 1? */
|
||||
channels = read_u16le(header_offset + 0x02,sf);
|
||||
sample_rate = read_s32le(header_offset + 0x04,sf);
|
||||
/* 0x08+: ? (mostly fixed, maybe related to ADPCM?) */
|
||||
loop_flag = 0;
|
||||
|
||||
if (!is_id32be(0x38,sf, "BODY"))
|
||||
goto fail;
|
||||
start_offset = 0x40;
|
||||
data_size = read_u32le(0x3c,sf);
|
||||
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(channels, loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
vgmstream->meta_type = meta_PIFF_TPCM;
|
||||
vgmstream->sample_rate = sample_rate;
|
||||
|
||||
vgmstream->coding_type = coding_TANTALUS;
|
||||
vgmstream->layout_type = layout_interleave;
|
||||
vgmstream->interleave_block_size = 0x10;
|
||||
|
||||
vgmstream->num_samples = tantalus_bytes_to_samples(data_size, channels);
|
||||
|
||||
if (!vgmstream_open_stream(vgmstream, sf, start_offset))
|
||||
goto fail;
|
||||
return vgmstream;
|
||||
|
||||
fail:
|
||||
close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
@ -151,7 +151,7 @@ VGMSTREAM * init_vgmstream_rsd(STREAMFILE *sf) {
|
||||
vgmstream->coding_type = coding_FFmpeg;
|
||||
vgmstream->layout_type = layout_none;
|
||||
|
||||
vgmstream->num_samples = (int32_t)ffmpeg_data->totalSamples; /* an estimation, sometimes cuts files a bit early */
|
||||
vgmstream->num_samples = ffmpeg_get_samples(ffmpeg_data); /* an estimation, sometimes cuts files a bit early */
|
||||
//vgmstream->num_samples = read_32bitLE(start_offset + 0x00, sf) / channel_count / 2; /* may be PCM data size, but not exact */
|
||||
vgmstream->sample_rate = read_32bitLE(start_offset + 0x04, sf);
|
||||
break;
|
||||
|
225
src/meta/sdf.c
225
src/meta/sdf.c
@ -1,99 +1,126 @@
|
||||
#include "meta.h"
|
||||
#include "../coding/coding.h"
|
||||
|
||||
/* SDF - from Beyond Reality games */
|
||||
VGMSTREAM * init_vgmstream_sdf(STREAMFILE *streamFile) {
|
||||
VGMSTREAM * vgmstream = NULL;
|
||||
off_t start_offset;
|
||||
size_t data_size;
|
||||
int loop_flag, channel_count, sample_rate, interleave, coefs_offset;
|
||||
|
||||
|
||||
/* checks */
|
||||
if (!check_extensions(streamFile,"sdf"))
|
||||
goto fail;
|
||||
if (read_32bitBE(0x00,streamFile) != 0x53444600) /* "SDF\0" */
|
||||
goto fail;
|
||||
if (read_32bitBE(0x04,streamFile) != 0x03000000) /* version? */
|
||||
goto fail;
|
||||
|
||||
data_size = read_32bitLE(0x08,streamFile);
|
||||
start_offset = get_streamfile_size(streamFile) - data_size;
|
||||
|
||||
switch(start_offset) {
|
||||
case 0x18: /* Agent Hugo - Lemoon Twist (PS2)*/
|
||||
sample_rate = read_32bitLE(0x0c,streamFile);
|
||||
channel_count = read_32bitLE(0x10,streamFile);
|
||||
interleave = read_32bitLE(0x14,streamFile);
|
||||
break;
|
||||
|
||||
case 0x78: /* Gummy Bears Mini Golf (3DS) */
|
||||
sample_rate = read_32bitLE(0x10,streamFile);
|
||||
channel_count = read_32bitLE(0x14,streamFile);
|
||||
interleave = read_32bitLE(0x18,streamFile);
|
||||
coefs_offset = 0x1c;
|
||||
break;
|
||||
|
||||
case 0x84: /* Mr. Bean's Wacky World (Wii) */
|
||||
sample_rate = read_32bitLE(0x10,streamFile);
|
||||
channel_count = read_32bitLE(0x14,streamFile);
|
||||
interleave = read_32bitLE(0x18,streamFile);
|
||||
data_size = read_32bitLE(0x20,streamFile); /* usable size */
|
||||
coefs_offset = 0x28;
|
||||
break;
|
||||
|
||||
default:
|
||||
goto fail;
|
||||
}
|
||||
|
||||
loop_flag = 1;
|
||||
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(channel_count,loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
vgmstream->meta_type = meta_SDF;
|
||||
vgmstream->sample_rate = sample_rate;
|
||||
|
||||
switch(start_offset) {
|
||||
case 0x18:
|
||||
vgmstream->coding_type = coding_PSX;
|
||||
vgmstream->layout_type = layout_interleave;
|
||||
vgmstream->interleave_block_size = interleave;
|
||||
|
||||
vgmstream->num_samples = ps_bytes_to_samples(data_size,channel_count);
|
||||
break;
|
||||
|
||||
case 0x78:
|
||||
case 0x84:
|
||||
vgmstream->coding_type = coding_NGC_DSP;
|
||||
vgmstream->layout_type = layout_interleave;
|
||||
vgmstream->interleave_block_size = interleave;
|
||||
if (vgmstream->interleave_block_size == 0) /* Gummy Bears Mini Golf */
|
||||
vgmstream->interleave_block_size = data_size / channel_count;
|
||||
|
||||
vgmstream->num_samples = dsp_bytes_to_samples(data_size,channel_count);
|
||||
|
||||
dsp_read_coefs_le(vgmstream, streamFile, coefs_offset+0x00,0x2e);
|
||||
dsp_read_hist_le (vgmstream, streamFile, coefs_offset+0x24,0x2e);
|
||||
break;
|
||||
|
||||
default:
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* most songs simply repeat; don't loop if too short (in seconds) */
|
||||
if (vgmstream->num_samples > 10*sample_rate) {
|
||||
vgmstream->loop_start_sample = 0;
|
||||
vgmstream->loop_end_sample = vgmstream->num_samples;
|
||||
}
|
||||
|
||||
if ( !vgmstream_open_stream(vgmstream, streamFile, start_offset) )
|
||||
goto fail;
|
||||
return vgmstream;
|
||||
|
||||
fail:
|
||||
close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
||||
#include "meta.h"
|
||||
#include "../coding/coding.h"
|
||||
|
||||
typedef enum { PSX, PCM, DSP } sdf_codec;
|
||||
|
||||
|
||||
/* SDF - from Beyond Reality games */
|
||||
VGMSTREAM* init_vgmstream_sdf(STREAMFILE* sf) {
|
||||
VGMSTREAM * vgmstream = NULL;
|
||||
off_t start_offset;
|
||||
size_t data_size;
|
||||
int loop_flag, channels, sample_rate, interleave, coefs_offset;
|
||||
sdf_codec codec;
|
||||
|
||||
|
||||
/* checks */
|
||||
if (!check_extensions(sf,"sdf"))
|
||||
goto fail;
|
||||
if (!is_id32be(0x00,sf, "SDF\0"))
|
||||
goto fail;
|
||||
if (read_u32le(0x04,sf) != 3) /* version? */
|
||||
goto fail;
|
||||
|
||||
data_size = read_u32le(0x08,sf);
|
||||
start_offset = get_streamfile_size(sf) - data_size;
|
||||
|
||||
switch(start_offset) {
|
||||
case 0x18:
|
||||
if (read_u32le(0x10,sf) > 6) {
|
||||
/* Hugo Magic in the Troll Woods (NDS) */
|
||||
/* 0x0c: some size? */
|
||||
sample_rate = read_s32le(0x10,sf);
|
||||
channels = read_u8(0x14,sf);
|
||||
/* 0x14: 1? */
|
||||
interleave = read_u16le(0x16,sf);
|
||||
codec = PCM;
|
||||
}
|
||||
else {
|
||||
/* Agent Hugo: Lemoon Twist (PS2) */
|
||||
sample_rate = read_s32le(0x0c,sf);
|
||||
channels = read_u32le(0x10,sf);
|
||||
interleave = read_u32le(0x14,sf);
|
||||
codec = PSX;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 0x78: /* Gummy Bears Mini Golf (3DS) */
|
||||
sample_rate = read_s32le(0x10,sf);
|
||||
channels = read_u32le(0x14,sf);
|
||||
interleave = read_u32le(0x18,sf);
|
||||
coefs_offset = 0x1c;
|
||||
codec = DSP;
|
||||
break;
|
||||
|
||||
case 0x84: /* Mr. Bean's Wacky World (Wii) */
|
||||
sample_rate = read_s32le(0x10,sf);
|
||||
channels = read_u32le(0x14,sf);
|
||||
interleave = read_u32le(0x18,sf);
|
||||
data_size = read_u32le(0x20,sf); /* usable size */
|
||||
coefs_offset = 0x28;
|
||||
codec = DSP;
|
||||
break;
|
||||
|
||||
default:
|
||||
goto fail;
|
||||
}
|
||||
|
||||
loop_flag = 1;
|
||||
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(channels, loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
vgmstream->meta_type = meta_SDF;
|
||||
vgmstream->sample_rate = sample_rate;
|
||||
|
||||
switch(codec) {
|
||||
case PCM:
|
||||
vgmstream->coding_type = coding_PCM16LE;
|
||||
vgmstream->layout_type = layout_interleave;
|
||||
vgmstream->interleave_block_size = interleave;
|
||||
|
||||
vgmstream->num_samples = pcm16_bytes_to_samples(data_size, channels);
|
||||
break;
|
||||
|
||||
case PSX:
|
||||
vgmstream->coding_type = coding_PSX;
|
||||
vgmstream->layout_type = layout_interleave;
|
||||
vgmstream->interleave_block_size = interleave;
|
||||
|
||||
vgmstream->num_samples = ps_bytes_to_samples(data_size, channels);
|
||||
break;
|
||||
|
||||
case DSP:
|
||||
vgmstream->coding_type = coding_NGC_DSP;
|
||||
vgmstream->layout_type = layout_interleave;
|
||||
vgmstream->interleave_block_size = interleave;
|
||||
if (vgmstream->interleave_block_size == 0) /* Gummy Bears Mini Golf */
|
||||
vgmstream->interleave_block_size = data_size / channels;
|
||||
|
||||
vgmstream->num_samples = dsp_bytes_to_samples(data_size, channels);
|
||||
|
||||
dsp_read_coefs_le(vgmstream, sf, coefs_offset+0x00,0x2e);
|
||||
dsp_read_hist_le (vgmstream, sf, coefs_offset+0x24,0x2e);
|
||||
break;
|
||||
|
||||
default:
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* most songs simply repeat; don't loop if too short (in seconds) */
|
||||
if (vgmstream->num_samples > 10*sample_rate) {
|
||||
vgmstream->loop_start_sample = 0;
|
||||
vgmstream->loop_end_sample = vgmstream->num_samples;
|
||||
}
|
||||
|
||||
if (!vgmstream_open_stream(vgmstream, sf, start_offset))
|
||||
goto fail;
|
||||
return vgmstream;
|
||||
|
||||
fail:
|
||||
close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
||||
|
@ -150,8 +150,8 @@ VGMSTREAM* init_vgmstream_str_wav(STREAMFILE* sf) {
|
||||
vgmstream->coding_type = coding_FFmpeg;
|
||||
vgmstream->layout_type = layout_none;
|
||||
|
||||
vgmstream->num_samples = ffmpeg_data->totalSamples;
|
||||
if (vgmstream->channels != ffmpeg_data->channels)
|
||||
vgmstream->num_samples = ffmpeg_get_samples(ffmpeg_data);
|
||||
if (vgmstream->channels != ffmpeg_get_channels(ffmpeg_data))
|
||||
goto fail;
|
||||
|
||||
break;
|
||||
|
@ -4,9 +4,9 @@
|
||||
|
||||
/* .STRM - from Abylight 3DS games [Cursed Castilla (3DS)] */
|
||||
VGMSTREAM* init_vgmstream_strm_abylight(STREAMFILE* sf) {
|
||||
VGMSTREAM * vgmstream = NULL;
|
||||
VGMSTREAM* vgmstream = NULL;
|
||||
off_t start_offset;
|
||||
int loop_flag, channel_count, sample_rate;
|
||||
int loop_flag, channel_count, sample_rate, skip_samples;
|
||||
size_t data_size;
|
||||
|
||||
|
||||
@ -22,6 +22,7 @@ VGMSTREAM* init_vgmstream_strm_abylight(STREAMFILE* sf) {
|
||||
loop_flag = 0;
|
||||
channel_count = 2; /* there are various possible fields but all files are stereo */
|
||||
sample_rate = read_32bitLE(0x08,sf);
|
||||
skip_samples = 1024; /* assumed, maybe a bit more */
|
||||
|
||||
start_offset = 0x1e;
|
||||
data_size = read_32bitLE(0x10,sf);
|
||||
@ -42,14 +43,12 @@ VGMSTREAM* init_vgmstream_strm_abylight(STREAMFILE* sf) {
|
||||
|
||||
#ifdef VGM_USE_FFMPEG
|
||||
{
|
||||
vgmstream->codec_data = init_ffmpeg_aac(sf, start_offset, data_size);
|
||||
vgmstream->codec_data = init_ffmpeg_aac(sf, start_offset, data_size, skip_samples);
|
||||
if (!vgmstream->codec_data) goto fail;
|
||||
vgmstream->coding_type = coding_FFmpeg;
|
||||
vgmstream->layout_type = layout_none;
|
||||
|
||||
/* assumed, maybe a bit more */
|
||||
ffmpeg_set_skip_samples(vgmstream->codec_data, 1024);
|
||||
vgmstream->num_samples -= 1024;
|
||||
vgmstream->num_samples -= skip_samples;
|
||||
}
|
||||
#else
|
||||
goto fail;
|
||||
|
@ -483,10 +483,10 @@ VGMSTREAM* init_vgmstream_txth(STREAMFILE* sf) {
|
||||
if (!ffmpeg_data) goto fail;
|
||||
|
||||
if (vgmstream->num_samples == 0)
|
||||
vgmstream->num_samples = ffmpeg_data->totalSamples; /* sometimes works */
|
||||
vgmstream->num_samples = ffmpeg_get_samples(ffmpeg_data); /* sometimes works */
|
||||
}
|
||||
else if (txth.codec == AAC) {
|
||||
ffmpeg_data = init_ffmpeg_aac(txth.sf_body, txth.start_offset, txth.data_size);
|
||||
ffmpeg_data = init_ffmpeg_aac(txth.sf_body, txth.start_offset, txth.data_size, 0);
|
||||
if (!ffmpeg_data) goto fail;
|
||||
}
|
||||
else {
|
||||
|
@ -412,7 +412,7 @@ VGMSTREAM* init_vgmstream_wwise(STREAMFILE* sf) {
|
||||
|
||||
vgmstream->num_samples = msd.num_samples;
|
||||
if (!vgmstream->num_samples)
|
||||
vgmstream->num_samples = (int32_t)ffmpeg_data->totalSamples; /* very wrong, from avg-br */
|
||||
vgmstream->num_samples = ffmpeg_get_samples(ffmpeg_data); /* very wrong, from avg-br */
|
||||
//num_samples seem to be found in the last "seek" table entry too, as: entry / channels / 2
|
||||
}
|
||||
|
||||
@ -433,7 +433,7 @@ VGMSTREAM* init_vgmstream_wwise(STREAMFILE* sf) {
|
||||
vgmstream->coding_type = coding_FFmpeg;
|
||||
vgmstream->layout_type = layout_none;
|
||||
|
||||
vgmstream->num_samples = (int32_t)ffmpeg_data->totalSamples;
|
||||
vgmstream->num_samples = ffmpeg_get_samples(ffmpeg_data); //todo correct?
|
||||
break;
|
||||
}
|
||||
|
||||
@ -518,7 +518,7 @@ VGMSTREAM* init_vgmstream_wwise(STREAMFILE* sf) {
|
||||
vgmstream->layout_type = layout_none;
|
||||
|
||||
/* FFmpeg's samples seem correct, otherwise see ogg_opus.c for getting samples. */
|
||||
vgmstream->num_samples = (int32_t)((ffmpeg_codec_data*)vgmstream->codec_data)->totalSamples;
|
||||
vgmstream->num_samples = ffmpeg_get_samples(vgmstream->codec_data);
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -530,7 +530,7 @@ VGMSTREAM* init_vgmstream_xwb(STREAMFILE* sf) {
|
||||
|
||||
/* no wma_bytes_to_samples, this should be ok */
|
||||
if (!vgmstream->num_samples)
|
||||
vgmstream->num_samples = (int32_t)ffmpeg_data->totalSamples;
|
||||
vgmstream->num_samples = ffmpeg_get_samples(ffmpeg_data);
|
||||
break;
|
||||
}
|
||||
|
||||
|
174
src/meta/xwma.c
174
src/meta/xwma.c
@ -1,87 +1,87 @@
|
||||
#include "meta.h"
|
||||
#include "../coding/coding.h"
|
||||
|
||||
/* XWMA - Microsoft WMA container [The Elder Scrolls: Skyrim (PC/X360), Hydrophobia (PC)] */
|
||||
VGMSTREAM * init_vgmstream_xwma(STREAMFILE *streamFile) {
|
||||
VGMSTREAM * vgmstream = NULL;
|
||||
off_t fmt_offset, data_offset, first_offset = 0xc;
|
||||
size_t fmt_size, data_size;
|
||||
int loop_flag, channel_count;
|
||||
|
||||
|
||||
/* checks */
|
||||
/* .xwma: standard
|
||||
* .xwm: The Elder Scrolls: Skyrim (PC), Blade Arcus from Shining (PC) */
|
||||
if (!check_extensions(streamFile, "xwma,xwm"))
|
||||
goto fail;
|
||||
if (read_32bitBE(0x00,streamFile) != 0x52494646) /* "RIFF" */
|
||||
goto fail;
|
||||
if (read_32bitBE(0x08,streamFile) != 0x58574D41) /* "XWMA" */
|
||||
goto fail;
|
||||
|
||||
if ( !find_chunk_le(streamFile, 0x666d7420,first_offset,0, &fmt_offset,&fmt_size) ) /* "fmt "*/
|
||||
goto fail;
|
||||
if ( !find_chunk_le(streamFile, 0x64617461,first_offset,0, &data_offset,&data_size) ) /* "data"*/
|
||||
goto fail;
|
||||
|
||||
channel_count = read_16bitLE(fmt_offset+0x02,streamFile);
|
||||
loop_flag = 0;
|
||||
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(channel_count, loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
vgmstream->sample_rate = read_32bitLE(fmt_offset+0x04, streamFile);
|
||||
vgmstream->meta_type = meta_XWMA;
|
||||
|
||||
/* the main purpose of this meta is redoing the XWMA header to:
|
||||
* - redo header to fix XWMA with buggy bit rates so FFmpeg can play them ok
|
||||
* - skip seek table to fix FFmpeg buggy XWMA seeking (see init_seek)
|
||||
* - read num_samples correctly
|
||||
*/
|
||||
|
||||
#ifdef VGM_USE_FFMPEG
|
||||
{
|
||||
uint8_t buf[0x100];
|
||||
int bytes, avg_bps, block_align, wma_codec;
|
||||
|
||||
avg_bps = read_32bitLE(fmt_offset+0x08, streamFile);
|
||||
block_align = (uint16_t)read_16bitLE(fmt_offset+0x0c, streamFile);
|
||||
wma_codec = (uint16_t)read_16bitLE(fmt_offset+0x00, streamFile);
|
||||
|
||||
bytes = ffmpeg_make_riff_xwma(buf,0x100, wma_codec, data_size, vgmstream->channels, vgmstream->sample_rate, avg_bps, block_align);
|
||||
vgmstream->codec_data = init_ffmpeg_header_offset(streamFile, buf,bytes, data_offset,data_size);
|
||||
if (!vgmstream->codec_data) goto fail;
|
||||
vgmstream->coding_type = coding_FFmpeg;
|
||||
vgmstream->layout_type = layout_none;
|
||||
|
||||
/* manually find total samples, why don't they put this in the header is beyond me */
|
||||
{
|
||||
ms_sample_data msd = {0};
|
||||
|
||||
msd.channels = vgmstream->channels;
|
||||
msd.data_offset = data_offset;
|
||||
msd.data_size = data_size;
|
||||
|
||||
if (wma_codec == 0x0162)
|
||||
wmapro_get_samples(&msd, streamFile, block_align, vgmstream->sample_rate,0x00E0);
|
||||
else
|
||||
wma_get_samples(&msd, streamFile, block_align, vgmstream->sample_rate,0x001F);
|
||||
|
||||
vgmstream->num_samples = msd.num_samples;
|
||||
if (vgmstream->num_samples == 0)
|
||||
vgmstream->num_samples = (int32_t)((ffmpeg_codec_data*)vgmstream->codec_data)->totalSamples; /* from avg-br */
|
||||
//num_samples seem to be found in the last "seek" table entry too, as: entry / channels / 2
|
||||
}
|
||||
}
|
||||
#else
|
||||
goto fail;
|
||||
#endif
|
||||
|
||||
return vgmstream;
|
||||
|
||||
fail:
|
||||
close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
||||
#include "meta.h"
|
||||
#include "../coding/coding.h"
|
||||
|
||||
/* XWMA - Microsoft WMA container [The Elder Scrolls: Skyrim (PC/X360), Hydrophobia (PC)] */
|
||||
VGMSTREAM * init_vgmstream_xwma(STREAMFILE *streamFile) {
|
||||
VGMSTREAM * vgmstream = NULL;
|
||||
off_t fmt_offset, data_offset, first_offset = 0xc;
|
||||
size_t fmt_size, data_size;
|
||||
int loop_flag, channel_count;
|
||||
|
||||
|
||||
/* checks */
|
||||
/* .xwma: standard
|
||||
* .xwm: The Elder Scrolls: Skyrim (PC), Blade Arcus from Shining (PC) */
|
||||
if (!check_extensions(streamFile, "xwma,xwm"))
|
||||
goto fail;
|
||||
if (read_32bitBE(0x00,streamFile) != 0x52494646) /* "RIFF" */
|
||||
goto fail;
|
||||
if (read_32bitBE(0x08,streamFile) != 0x58574D41) /* "XWMA" */
|
||||
goto fail;
|
||||
|
||||
if ( !find_chunk_le(streamFile, 0x666d7420,first_offset,0, &fmt_offset,&fmt_size) ) /* "fmt "*/
|
||||
goto fail;
|
||||
if ( !find_chunk_le(streamFile, 0x64617461,first_offset,0, &data_offset,&data_size) ) /* "data"*/
|
||||
goto fail;
|
||||
|
||||
channel_count = read_16bitLE(fmt_offset+0x02,streamFile);
|
||||
loop_flag = 0;
|
||||
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(channel_count, loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
vgmstream->sample_rate = read_32bitLE(fmt_offset+0x04, streamFile);
|
||||
vgmstream->meta_type = meta_XWMA;
|
||||
|
||||
/* the main purpose of this meta is redoing the XWMA header to:
|
||||
* - redo header to fix XWMA with buggy bit rates so FFmpeg can play them ok
|
||||
* - skip seek table to fix FFmpeg buggy XWMA seeking (see init_seek)
|
||||
* - read num_samples correctly
|
||||
*/
|
||||
|
||||
#ifdef VGM_USE_FFMPEG
|
||||
{
|
||||
uint8_t buf[0x100];
|
||||
int bytes, avg_bps, block_align, wma_codec;
|
||||
|
||||
avg_bps = read_32bitLE(fmt_offset+0x08, streamFile);
|
||||
block_align = (uint16_t)read_16bitLE(fmt_offset+0x0c, streamFile);
|
||||
wma_codec = (uint16_t)read_16bitLE(fmt_offset+0x00, streamFile);
|
||||
|
||||
bytes = ffmpeg_make_riff_xwma(buf,0x100, wma_codec, data_size, vgmstream->channels, vgmstream->sample_rate, avg_bps, block_align);
|
||||
vgmstream->codec_data = init_ffmpeg_header_offset(streamFile, buf,bytes, data_offset,data_size);
|
||||
if (!vgmstream->codec_data) goto fail;
|
||||
vgmstream->coding_type = coding_FFmpeg;
|
||||
vgmstream->layout_type = layout_none;
|
||||
|
||||
/* manually find total samples, why don't they put this in the header is beyond me */
|
||||
{
|
||||
ms_sample_data msd = {0};
|
||||
|
||||
msd.channels = vgmstream->channels;
|
||||
msd.data_offset = data_offset;
|
||||
msd.data_size = data_size;
|
||||
|
||||
if (wma_codec == 0x0162)
|
||||
wmapro_get_samples(&msd, streamFile, block_align, vgmstream->sample_rate,0x00E0);
|
||||
else
|
||||
wma_get_samples(&msd, streamFile, block_align, vgmstream->sample_rate,0x001F);
|
||||
|
||||
vgmstream->num_samples = msd.num_samples;
|
||||
if (vgmstream->num_samples == 0)
|
||||
vgmstream->num_samples = ffmpeg_get_samples(vgmstream->codec_data); /* from avg-br */
|
||||
//num_samples seem to be found in the last "seek" table entry too, as: entry / channels / 2
|
||||
}
|
||||
}
|
||||
#else
|
||||
goto fail;
|
||||
#endif
|
||||
|
||||
return vgmstream;
|
||||
|
||||
fail:
|
||||
close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
||||
|
@ -1,86 +1,86 @@
|
||||
#include "meta.h"
|
||||
#include "../coding/coding.h"
|
||||
#include "xwma_konami_streamfile.h"
|
||||
|
||||
|
||||
/* MSFC - Konami (Armature?) variation [Metal Gear Solid 2 HD (X360), Metal Gear Solid 3 HD (X360)] */
|
||||
VGMSTREAM * init_vgmstream_xwma_konami(STREAMFILE *streamFile) {
|
||||
VGMSTREAM * vgmstream = NULL;
|
||||
off_t start_offset;
|
||||
int loop_flag, channel_count, codec, sample_rate;
|
||||
size_t data_size;
|
||||
STREAMFILE *temp_streamFile = NULL;
|
||||
|
||||
|
||||
/* checks */
|
||||
if (!check_extensions(streamFile,"xwma"))
|
||||
goto fail;
|
||||
if (read_32bitBE(0x00,streamFile) != 0x58574D41) /* "XWMA" */
|
||||
goto fail;
|
||||
|
||||
codec = read_32bitBE(0x04,streamFile);
|
||||
channel_count = read_32bitBE(0x08,streamFile);
|
||||
sample_rate = read_32bitBE(0x0c,streamFile);
|
||||
data_size = read_32bitBE(0x10,streamFile); /* data size without padding */
|
||||
loop_flag = 0;
|
||||
start_offset = 0x20;
|
||||
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(channel_count, loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
vgmstream->sample_rate = sample_rate;
|
||||
vgmstream->meta_type = meta_XWMA_KONAMI;
|
||||
|
||||
#ifdef VGM_USE_FFMPEG
|
||||
{
|
||||
uint8_t buf[0x100];
|
||||
int bytes, avg_bps, block_align;
|
||||
|
||||
/* 0x10: related to size? */
|
||||
avg_bps = read_32bitBE(0x14, streamFile);
|
||||
block_align = read_32bitBE(0x18, streamFile);
|
||||
|
||||
/* data has padding (unrelated to KCEJ blocks) */
|
||||
temp_streamFile = setup_xwma_konami_streamfile(streamFile, start_offset, block_align);
|
||||
if (!temp_streamFile) goto fail;
|
||||
|
||||
bytes = ffmpeg_make_riff_xwma(buf,0x100, codec, data_size, channel_count, sample_rate, avg_bps, block_align);
|
||||
vgmstream->codec_data = init_ffmpeg_header_offset(temp_streamFile, buf,bytes, 0x00,data_size);
|
||||
if (!vgmstream->codec_data) goto fail;
|
||||
vgmstream->coding_type = coding_FFmpeg;
|
||||
vgmstream->layout_type = layout_none;
|
||||
|
||||
/* manually find total samples */
|
||||
{
|
||||
ms_sample_data msd = {0};
|
||||
|
||||
msd.channels = vgmstream->channels;
|
||||
msd.data_offset = 0x00;
|
||||
msd.data_size = data_size;
|
||||
|
||||
|
||||
if (codec == 0x0161)
|
||||
wma_get_samples(&msd, temp_streamFile, block_align, vgmstream->sample_rate,0x001F);
|
||||
//else //todo not correct
|
||||
// wmapro_get_samples(&msd, temp_streamFile, block_align, vgmstream->sample_rate,0x00E0);
|
||||
|
||||
vgmstream->num_samples = msd.num_samples;
|
||||
if (vgmstream->num_samples == 0)
|
||||
vgmstream->num_samples = (int32_t)((ffmpeg_codec_data*)vgmstream->codec_data)->totalSamples; /* from avg-br */
|
||||
//num_samples seem to be found in the last "seek" table entry too, as: entry / channels / 2
|
||||
}
|
||||
}
|
||||
#else
|
||||
goto fail;
|
||||
#endif
|
||||
|
||||
close_streamfile(temp_streamFile);
|
||||
return vgmstream;
|
||||
|
||||
fail:
|
||||
close_streamfile(temp_streamFile);
|
||||
close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
||||
#include "meta.h"
|
||||
#include "../coding/coding.h"
|
||||
#include "xwma_konami_streamfile.h"
|
||||
|
||||
|
||||
/* MSFC - Konami (Armature?) variation [Metal Gear Solid 2 HD (X360), Metal Gear Solid 3 HD (X360)] */
|
||||
VGMSTREAM * init_vgmstream_xwma_konami(STREAMFILE *streamFile) {
|
||||
VGMSTREAM * vgmstream = NULL;
|
||||
off_t start_offset;
|
||||
int loop_flag, channel_count, codec, sample_rate;
|
||||
size_t data_size;
|
||||
STREAMFILE *temp_streamFile = NULL;
|
||||
|
||||
|
||||
/* checks */
|
||||
if (!check_extensions(streamFile,"xwma"))
|
||||
goto fail;
|
||||
if (read_32bitBE(0x00,streamFile) != 0x58574D41) /* "XWMA" */
|
||||
goto fail;
|
||||
|
||||
codec = read_32bitBE(0x04,streamFile);
|
||||
channel_count = read_32bitBE(0x08,streamFile);
|
||||
sample_rate = read_32bitBE(0x0c,streamFile);
|
||||
data_size = read_32bitBE(0x10,streamFile); /* data size without padding */
|
||||
loop_flag = 0;
|
||||
start_offset = 0x20;
|
||||
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(channel_count, loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
vgmstream->sample_rate = sample_rate;
|
||||
vgmstream->meta_type = meta_XWMA_KONAMI;
|
||||
|
||||
#ifdef VGM_USE_FFMPEG
|
||||
{
|
||||
uint8_t buf[0x100];
|
||||
int bytes, avg_bps, block_align;
|
||||
|
||||
/* 0x10: related to size? */
|
||||
avg_bps = read_32bitBE(0x14, streamFile);
|
||||
block_align = read_32bitBE(0x18, streamFile);
|
||||
|
||||
/* data has padding (unrelated to KCEJ blocks) */
|
||||
temp_streamFile = setup_xwma_konami_streamfile(streamFile, start_offset, block_align);
|
||||
if (!temp_streamFile) goto fail;
|
||||
|
||||
bytes = ffmpeg_make_riff_xwma(buf,0x100, codec, data_size, channel_count, sample_rate, avg_bps, block_align);
|
||||
vgmstream->codec_data = init_ffmpeg_header_offset(temp_streamFile, buf,bytes, 0x00,data_size);
|
||||
if (!vgmstream->codec_data) goto fail;
|
||||
vgmstream->coding_type = coding_FFmpeg;
|
||||
vgmstream->layout_type = layout_none;
|
||||
|
||||
/* manually find total samples */
|
||||
{
|
||||
ms_sample_data msd = {0};
|
||||
|
||||
msd.channels = vgmstream->channels;
|
||||
msd.data_offset = 0x00;
|
||||
msd.data_size = data_size;
|
||||
|
||||
|
||||
if (codec == 0x0161)
|
||||
wma_get_samples(&msd, temp_streamFile, block_align, vgmstream->sample_rate,0x001F);
|
||||
//else //todo not correct
|
||||
// wmapro_get_samples(&msd, temp_streamFile, block_align, vgmstream->sample_rate,0x00E0);
|
||||
|
||||
vgmstream->num_samples = msd.num_samples;
|
||||
if (vgmstream->num_samples == 0)
|
||||
vgmstream->num_samples = ffmpeg_get_samples(vgmstream->codec_data); /* from avg-br */
|
||||
//num_samples seem to be found in the last "seek" table entry too, as: entry / channels / 2
|
||||
}
|
||||
}
|
||||
#else
|
||||
goto fail;
|
||||
#endif
|
||||
|
||||
close_streamfile(temp_streamFile);
|
||||
return vgmstream;
|
||||
|
||||
fail:
|
||||
close_streamfile(temp_streamFile);
|
||||
close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
||||
|
@ -525,6 +525,7 @@ VGMSTREAM* (*init_vgmstream_functions[])(STREAMFILE* sf) = {
|
||||
init_vgmstream_dsp_kwa,
|
||||
init_vgmstream_ogv_3rdeye,
|
||||
init_vgmstream_sspr,
|
||||
init_vgmstream_piff_tpcm,
|
||||
|
||||
/* lowest priority metas (should go after all metas, and TXTH should go before raw formats) */
|
||||
init_vgmstream_txth, /* proper parsers should supersede TXTH, once added */
|
||||
@ -606,9 +607,9 @@ static VGMSTREAM* init_vgmstream_internal(STREAMFILE* sf) {
|
||||
#ifdef VGM_USE_FFMPEG
|
||||
/* check FFmpeg streams here, for lack of a better place */
|
||||
if (vgmstream->coding_type == coding_FFmpeg) {
|
||||
ffmpeg_codec_data *data = vgmstream->codec_data;
|
||||
if (data && data->streamCount && !vgmstream->num_streams) {
|
||||
vgmstream->num_streams = data->streamCount;
|
||||
int ffmpeg_subsongs = ffmpeg_get_subsong_count(vgmstream->codec_data);
|
||||
if (ffmpeg_subsongs && !vgmstream->num_streams) {
|
||||
vgmstream->num_streams = ffmpeg_subsongs;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@ -1464,7 +1465,21 @@ static int get_vgmstream_file_bitrate_main(VGMSTREAM* vgmstream, bitrate_info_t*
|
||||
}
|
||||
|
||||
if (is_unique) {
|
||||
size_t stream_size;
|
||||
|
||||
if (br->count >= br->count_max) goto fail;
|
||||
|
||||
if (vgmstream->stream_size) {
|
||||
/* stream_size applies to both channels but should add once and detect repeats (for current subsong) */
|
||||
stream_size = get_vgmstream_file_bitrate_from_size(vgmstream->stream_size, vgmstream->sample_rate, vgmstream->num_samples);
|
||||
}
|
||||
else {
|
||||
stream_size = get_vgmstream_file_bitrate_from_streamfile(sf_cur, vgmstream->sample_rate, vgmstream->num_samples);
|
||||
}
|
||||
|
||||
/* possible in cases like using silence codec */
|
||||
if (!stream_size)
|
||||
break;
|
||||
|
||||
br->hash[br->count] = hash_cur;
|
||||
br->subsong[br->count] = subsong_cur;
|
||||
@ -1473,13 +1488,7 @@ static int get_vgmstream_file_bitrate_main(VGMSTREAM* vgmstream, bitrate_info_t*
|
||||
if (p_uniques)
|
||||
(*p_uniques)++;
|
||||
|
||||
if (vgmstream->stream_size) {
|
||||
/* stream_size applies to both channels but should add once and detect repeats (for current subsong) */
|
||||
bitrate += get_vgmstream_file_bitrate_from_size(vgmstream->stream_size, vgmstream->sample_rate, vgmstream->num_samples);
|
||||
}
|
||||
else {
|
||||
bitrate += get_vgmstream_file_bitrate_from_streamfile(sf_cur, vgmstream->sample_rate, vgmstream->num_samples);
|
||||
}
|
||||
bitrate += stream_size;
|
||||
|
||||
break;
|
||||
}
|
||||
|
@ -52,12 +52,6 @@ enum { VGMSTREAM_MAX_NUM_SAMPLES = 1000000000 }; /* no ~5h vgm hopefully */
|
||||
#include <maiatrac3plus.h>
|
||||
#endif
|
||||
|
||||
#ifdef VGM_USE_FFMPEG
|
||||
#include <libavcodec/avcodec.h>
|
||||
#include <libavformat/avformat.h>
|
||||
#include <libswresample/swresample.h>
|
||||
#endif
|
||||
|
||||
#include <clHCA.h>
|
||||
|
||||
#include "coding/g72x_state.h"
|
||||
@ -170,6 +164,7 @@ typedef enum {
|
||||
coding_ASF, /* Argonaut ASF 4-bit ADPCM */
|
||||
coding_DSA, /* Ocean DSA 4-bit ADPCM */
|
||||
coding_XMD, /* Konami XMD 4-bit ADPCM */
|
||||
coding_TANTALUS, /* Tantalus 4-bit ADPCM */
|
||||
coding_PCFX, /* PC-FX 4-bit ADPCM */
|
||||
coding_OKI16, /* OKI 4-bit ADPCM with 16-bit output and modified expand */
|
||||
coding_OKI4S, /* OKI 4-bit ADPCM with 16-bit output and cuadruple step */
|
||||
@ -758,6 +753,7 @@ typedef enum {
|
||||
meta_IDSP_TOSE,
|
||||
meta_DSP_KWA,
|
||||
meta_OGV_3RDEYE,
|
||||
meta_PIFF_TPCM,
|
||||
} meta_t;
|
||||
|
||||
/* standard WAVEFORMATEXTENSIBLE speaker positions */
|
||||
@ -1042,63 +1038,6 @@ typedef struct {
|
||||
} acm_codec_data;
|
||||
|
||||
|
||||
#ifdef VGM_USE_FFMPEG
|
||||
typedef struct {
|
||||
/*** IO internals ***/
|
||||
STREAMFILE* streamfile;
|
||||
|
||||
uint64_t start; // absolute start within the streamfile
|
||||
uint64_t offset; // absolute offset within the streamfile
|
||||
uint64_t size; // max size within the streamfile
|
||||
uint64_t logical_offset; // computed offset FFmpeg sees (including fake header)
|
||||
uint64_t logical_size; // computed size FFmpeg sees (including fake header)
|
||||
|
||||
uint64_t header_size; // fake header (parseable by FFmpeg) prepended on reads
|
||||
uint8_t* header_block; // fake header data (ie. RIFF)
|
||||
|
||||
/*** "public" API (read-only) ***/
|
||||
// stream info
|
||||
int channels;
|
||||
int sampleRate;
|
||||
int bitrate;
|
||||
// extra info: 0 if unknown or not fixed
|
||||
int64_t totalSamples; // estimated count (may not be accurate for some demuxers)
|
||||
int64_t skipSamples; // number of start samples that will be skipped (encoder delay), for looping adjustments
|
||||
int streamCount; // number of FFmpeg audio streams
|
||||
|
||||
/*** internal state ***/
|
||||
// config
|
||||
int channel_remap_set;
|
||||
int channel_remap[32]; /* map of channel > new position */
|
||||
int invert_floats_set;
|
||||
int skip_samples_set; /* flag to know skip samples were manually added from vgmstream */
|
||||
int force_seek; /* flags for special seeking in faulty formats */
|
||||
int bad_init;
|
||||
|
||||
// FFmpeg context used for metadata
|
||||
AVCodec *codec;
|
||||
|
||||
// FFmpeg decoder state
|
||||
unsigned char *buffer;
|
||||
AVIOContext *ioCtx;
|
||||
int streamIndex;
|
||||
AVFormatContext *formatCtx;
|
||||
AVCodecContext *codecCtx;
|
||||
AVFrame *frame; /* last decoded frame */
|
||||
AVPacket *packet; /* last read data packet */
|
||||
|
||||
int read_packet;
|
||||
int end_of_stream;
|
||||
int end_of_audio;
|
||||
|
||||
/* sample state */
|
||||
int32_t samples_discard;
|
||||
int32_t samples_consumed;
|
||||
int32_t samples_filled;
|
||||
|
||||
} ffmpeg_codec_data;
|
||||
#endif
|
||||
|
||||
#ifdef VGM_USE_MP4V2
|
||||
typedef struct {
|
||||
STREAMFILE* streamfile;
|
||||
|
Loading…
x
Reference in New Issue
Block a user