Updated VGMStream.

CQTexperiment
Christopher Snowhill 2016-11-20 11:49:08 -08:00
parent 03dab36e26
commit 3453e6a3c7
7 changed files with 588 additions and 497 deletions

View File

@ -135,4 +135,6 @@ void decode_lsf(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing,
void decode_mtaf(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel, int channels);
void decode_hca(hca_codec_data * data, sample * outbuf, int32_t samples_to_do, int channels);
#endif

View File

@ -117,6 +117,7 @@ void decode_ffmpeg(VGMSTREAM *vgmstream,
endOfStream = data->endOfStream;
endOfAudio = data->endOfAudio;
/* keep reading and decoding packets until the requested number of samples (in bytes) */
while (bytesRead < bytesToRead) {
int planeSize;
int planar = av_sample_fmt_is_planar(codecCtx->sample_fmt);
@ -127,6 +128,7 @@ void decode_ffmpeg(VGMSTREAM *vgmstream,
if (dataSize < 0)
dataSize = 0;
/* read packet */
while (readNextPacket && !endOfAudio) {
if (!endOfStream) {
av_packet_unref(lastReadPacket);
@ -138,7 +140,7 @@ void decode_ffmpeg(VGMSTREAM *vgmstream,
break;
}
if (lastReadPacket->stream_index != streamIndex)
continue;
continue; /* ignore non audio streams */
}
if ((errcode = avcodec_send_packet(codecCtx, endOfStream ? NULL : lastReadPacket)) < 0) {
@ -150,6 +152,7 @@ void decode_ffmpeg(VGMSTREAM *vgmstream,
readNextPacket = 0;
}
/* decode packet */
if (dataSize <= bytesConsumedFromDecodedFrame) {
if (endOfStream && endOfAudio)
break;
@ -178,21 +181,32 @@ void decode_ffmpeg(VGMSTREAM *vgmstream,
toConsume = FFMIN((dataSize - bytesConsumedFromDecodedFrame), (bytesToRead - bytesRead));
/* discard packet if needed (fully or partially) */
if (data->samplesToDiscard) {
int samplesToConsume;
int bytesPerFrame = ((data->bitsPerSample / 8) * channels);
int samplesToConsume = toConsume / bytesPerFrame;
if (data->samplesToDiscard >= samplesToConsume) {
/* discard all if there are more samples to do than the packet's samples */
if (data->samplesToDiscard >= dataSize / bytesPerFrame) {
samplesToConsume = dataSize / bytesPerFrame;
}
else {
samplesToConsume = toConsume / bytesPerFrame;
}
if (data->samplesToDiscard >= samplesToConsume) { /* full discard: skip to next */
data->samplesToDiscard -= samplesToConsume;
bytesConsumedFromDecodedFrame = dataSize;
continue;
}
else {
else { /* partial discard: copy below */
bytesConsumedFromDecodedFrame += data->samplesToDiscard * bytesPerFrame;
toConsume -= data->samplesToDiscard * bytesPerFrame;
data->samplesToDiscard = 0;
}
}
/* copy packet to buffer (mux channels if needed) */
if (!planar || channels == 1) {
memmove(targetBuf + bytesRead, (lastDecodedFrame->data[0] + bytesConsumedFromDecodedFrame), toConsume);
}
@ -210,6 +224,7 @@ void decode_ffmpeg(VGMSTREAM *vgmstream,
}
}
/* consume */
bytesConsumedFromDecodedFrame += toConsume;
bytesRead += toConsume;
}

View File

@ -43,7 +43,7 @@ VGMSTREAM * init_vgmstream_adx(STREAMFILE *streamFile) {
if (xb3d_flag) {
channel_count = read_32bitLE(0, streamFile);
loop_flag = read_16bitLE(0x6e, streamFile);
channel_header_spacing = 0x34;
channel_header_spacing = 0x34;
}
else {
/* get stream offset, check for CRI signature just before */
@ -109,6 +109,16 @@ VGMSTREAM * init_vgmstream_adx(STREAMFILE *streamFile) {
loop_end_sample = read_32bitBE(0x30,streamFile);
//loop_end_offset = read_32bitBE(0x34,streamFile);
}
/* AINF header can also start after the loop points
* (may be inserted by CRI's tools but is rarely used) */
/* ainf_magic = read_32bitBE(0x38,streamFile); */ /* 0x41494E46 */
/* ainf_length = read_32bitBE(0x3c,streamFile); */
/* ainf_str_id = read_string(0x40,streamFile); */ /* max size 0x10 */
/* ainf_volume = read_16bitBE(0x50,streamFile); */ /* 0=base/max?, negative=reduce */
/* ainf_pan_l = read_16bitBE(0x54,streamFile); */ /* 0=base, max +-128 */
/* ainf_pan_r = read_16bitBE(0x56,streamFile); */
} else if (version_signature == 0x0500) { /* found in some SFD : Buggy Heat, appears to have no loop */
header_type = meta_ADX_05;
} else goto fail; /* not a known/supported version signature */
@ -150,7 +160,7 @@ VGMSTREAM * init_vgmstream_adx(STREAMFILE *streamFile) {
= read_32bitLE(0x34+i*channel_header_spacing, streamFile);
if (!vgmstream->ch[i].streamfile) goto fail;
}
}
else {
vgmstream->num_samples = read_32bitBE(0xc,streamFile);

View File

@ -1,448 +1,455 @@
#include "../vgmstream.h"
#ifdef VGM_USE_VORBIS
#include <stdio.h>
#include <string.h>
#include "meta.h"
#include "../util.h"
#include <vorbis/vorbisfile.h>
#define DEFAULT_BITSTREAM 0
static size_t read_func(void *ptr, size_t size, size_t nmemb, void * datasource)
{
ogg_vorbis_streamfile * const ov_streamfile = datasource;
size_t items_read;
size_t bytes_read;
bytes_read = read_streamfile(ptr, ov_streamfile->offset + ov_streamfile->other_header_bytes, size * nmemb,
ov_streamfile->streamfile);
items_read = bytes_read / size;
ov_streamfile->offset += items_read * size;
return items_read;
}
static size_t read_func_um3(void *ptr, size_t size, size_t nmemb, void * datasource)
{
ogg_vorbis_streamfile * const ov_streamfile = datasource;
size_t items_read;
size_t bytes_read;
bytes_read = read_streamfile(ptr, ov_streamfile->offset + ov_streamfile->other_header_bytes, size * nmemb,
ov_streamfile->streamfile);
items_read = bytes_read / size;
/* first 0x800 bytes of um3 are xor'd with 0xff */
if (ov_streamfile->offset < 0x800) {
int num_crypt = 0x800-ov_streamfile->offset;
int i;
if (num_crypt > bytes_read) num_crypt=bytes_read;
for (i=0;i<num_crypt;i++)
((uint8_t*)ptr)[i] ^= 0xff;
}
ov_streamfile->offset += items_read * size;
return items_read;
}
static size_t read_func_kovs(void *ptr, size_t size, size_t nmemb, void * datasource)
{
ogg_vorbis_streamfile * const ov_streamfile = datasource;
size_t items_read;
size_t bytes_read;
bytes_read = read_streamfile(ptr, ov_streamfile->offset+ov_streamfile->other_header_bytes, size * nmemb,
ov_streamfile->streamfile);
items_read = bytes_read / size;
/* first 0x100 bytes of KOVS are xor'd with offset */
if (ov_streamfile->offset < 0x100) {
int i;
for (i=ov_streamfile->offset;i<0x100;i++)
((uint8_t*)ptr)[i-ov_streamfile->offset] ^= i;
}
ov_streamfile->offset += items_read * size;
return items_read;
}
static size_t read_func_scd(void *ptr, size_t size, size_t nmemb, void * datasource)
{
ogg_vorbis_streamfile * const ov_streamfile = datasource;
size_t items_read;
size_t bytes_read;
bytes_read = read_streamfile(ptr, ov_streamfile->offset + ov_streamfile->other_header_bytes, size * nmemb,
ov_streamfile->streamfile);
items_read = bytes_read / size;
/* first bytes are xor'd with a constant byte */
if (ov_streamfile->offset < ov_streamfile->scd_xor_len) {
int num_crypt = ov_streamfile->scd_xor_len-ov_streamfile->offset;
int i;
if (num_crypt > bytes_read) num_crypt=bytes_read;
for (i=0;i<num_crypt;i++)
((uint8_t*)ptr)[i] ^= ov_streamfile->scd_xor;
}
ov_streamfile->offset += items_read * size;
return items_read;
}
static size_t read_func_psych(void *ptr, size_t size, size_t nmemb, void * datasource)
{
ogg_vorbis_streamfile * const ov_streamfile = datasource;
size_t items_read;
size_t bytes_read;
size_t i;
bytes_read = read_streamfile(ptr, ov_streamfile->offset + ov_streamfile->other_header_bytes, size * nmemb,
ov_streamfile->streamfile);
/* add 0x23 ('#') */
for (i=0;i<bytes_read;i++)
((uint8_t*)ptr)[i] += 0x23;
items_read = bytes_read / size;
ov_streamfile->offset += items_read * size;
return items_read;
}
static int seek_func(void *datasource, ogg_int64_t offset, int whence) {
ogg_vorbis_streamfile * const ov_streamfile = datasource;
ogg_int64_t base_offset;
ogg_int64_t new_offset;
switch (whence) {
case SEEK_SET:
base_offset = 0;
break;
case SEEK_CUR:
base_offset = ov_streamfile->offset;
break;
case SEEK_END:
base_offset = ov_streamfile->size - ov_streamfile->other_header_bytes;
break;
default:
return -1;
break;
}
new_offset = base_offset + offset;
if (new_offset < 0 || new_offset > (ov_streamfile->size - ov_streamfile->other_header_bytes)) {
return -1;
} else {
ov_streamfile->offset = new_offset;
return 0;
}
}
static long tell_func(void * datasource) {
ogg_vorbis_streamfile * const ov_streamfile = datasource;
return ov_streamfile->offset;
}
/* setting close_func in ov_callbacks to NULL doesn't seem to work */
static int close_func(void * datasource) {
return 0;
}
/* Ogg Vorbis, by way of libvorbisfile */
VGMSTREAM * init_vgmstream_ogg_vorbis(STREAMFILE *streamFile) {
char filename[PATH_LIMIT];
ov_callbacks callbacks;
off_t other_header_bytes = 0;
int um3_ogg = 0;
int kovs_ogg = 0;
int psych_ogg = 0;
vgm_vorbis_info_t inf;
memset(&inf, 0, sizeof(inf));
/* check extension, case insensitive */
streamFile->get_name(streamFile,filename,sizeof(filename));
/* It is only interesting to use oggs with vgmstream if they are looped.
To prevent such files from being played by other plugins and such they
may be renamed to .logg. This meta reader should still support .ogg,
though. */
if (strcasecmp("logg",filename_extension(filename)) &&
strcasecmp("ogg",filename_extension(filename))) {
if (!strcasecmp("um3",filename_extension(filename))) {
um3_ogg = 1;
} else if (!strcasecmp("kovs",filename_extension(filename))) {
kovs_ogg = 1;
} else {
goto fail;
}
}
/* not all um3-ogg are crypted */
if (um3_ogg && read_32bitBE(0x0,streamFile)==0x4f676753) {
um3_ogg = 0;
}
/* use KOVS header */
if (kovs_ogg) {
if (read_32bitBE(0x0,streamFile)!=0x4b4f5653) { /* "KOVS" */
goto fail;
}
if (read_32bitLE(0x8,streamFile)!=0) {
inf.loop_start = read_32bitLE(0x8,streamFile);
inf.loop_flag = 1;
}
other_header_bytes = 0x20;
}
/* detect Psychic Software obfuscation (as seen in "Darkwind") */
if (read_32bitBE(0x0,streamFile)==0x2c444430) {
psych_ogg = 1;
}
if (um3_ogg) {
callbacks.read_func = read_func_um3;
} else if (kovs_ogg) {
callbacks.read_func = read_func_kovs;
} else if (psych_ogg) {
callbacks.read_func = read_func_psych;
} else {
callbacks.read_func = read_func;
}
callbacks.seek_func = seek_func;
callbacks.close_func = close_func;
callbacks.tell_func = tell_func;
if (um3_ogg) {
inf.meta_type = meta_um3_ogg;
} else if (kovs_ogg) {
inf.meta_type = meta_KOVS_ogg;
} else if (psych_ogg) {
inf.meta_type = meta_psych_ogg;
} else {
inf.meta_type = meta_ogg_vorbis;
}
inf.layout_type = layout_ogg_vorbis;
return init_vgmstream_ogg_vorbis_callbacks(streamFile, filename, &callbacks, other_header_bytes, &inf);
fail:
return NULL;
}
VGMSTREAM * init_vgmstream_ogg_vorbis_callbacks(STREAMFILE *streamFile, const char * filename, ov_callbacks *callbacks_p, off_t other_header_bytes, const vgm_vorbis_info_t *vgm_inf) {
VGMSTREAM * vgmstream = NULL;
OggVorbis_File temp_ovf;
ogg_vorbis_streamfile temp_streamfile;
ogg_vorbis_codec_data * data = NULL;
OggVorbis_File *ovf;
int inited_ovf = 0;
vorbis_info *info;
int loop_flag = vgm_inf->loop_flag;
int32_t loop_start = vgm_inf->loop_start;
int loop_length_found = vgm_inf->loop_length_found;
int32_t loop_length = vgm_inf->loop_length;
int loop_end_found = vgm_inf->loop_end_found;
int32_t loop_end = vgm_inf->loop_end;
ov_callbacks default_callbacks;
if (!callbacks_p) {
default_callbacks.read_func = read_func;
default_callbacks.seek_func = seek_func;
default_callbacks.close_func = close_func;
default_callbacks.tell_func = tell_func;
if (vgm_inf->scd_xor != 0) {
default_callbacks.read_func = read_func_scd;
}
callbacks_p = &default_callbacks;
}
temp_streamfile.streamfile = streamFile;
temp_streamfile.offset = 0;
temp_streamfile.size = get_streamfile_size(temp_streamfile.streamfile);
temp_streamfile.other_header_bytes = other_header_bytes;
temp_streamfile.scd_xor = vgm_inf->scd_xor;
temp_streamfile.scd_xor_len = vgm_inf->scd_xor_len;
/* can we open this as a proper ogg vorbis file? */
memset(&temp_ovf, 0, sizeof(temp_ovf));
if (ov_test_callbacks(&temp_streamfile, &temp_ovf, NULL,
0, *callbacks_p)) goto fail;
/* we have to close this as it has the init_vgmstream meta-reading
STREAMFILE */
ov_clear(&temp_ovf);
/* proceed to open a STREAMFILE just for this stream */
data = calloc(1,sizeof(ogg_vorbis_codec_data));
if (!data) goto fail;
data->ov_streamfile.streamfile = streamFile->open(streamFile,filename,
STREAMFILE_DEFAULT_BUFFER_SIZE);
if (!data->ov_streamfile.streamfile) goto fail;
data->ov_streamfile.offset = 0;
data->ov_streamfile.size = get_streamfile_size(data->ov_streamfile.streamfile);
data->ov_streamfile.other_header_bytes = other_header_bytes;
data->ov_streamfile.scd_xor = vgm_inf->scd_xor;
data->ov_streamfile.scd_xor_len = vgm_inf->scd_xor_len;
/* open the ogg vorbis file for real */
if (ov_open_callbacks(&data->ov_streamfile, &data->ogg_vorbis_file, NULL,
0, *callbacks_p)) goto fail;
ovf = &data->ogg_vorbis_file;
inited_ovf = 1;
data->bitstream = DEFAULT_BITSTREAM;
info = ov_info(ovf,DEFAULT_BITSTREAM);
/* grab the comments */
{
int i;
vorbis_comment *comment;
comment = ov_comment(ovf,DEFAULT_BITSTREAM);
/* search for a "loop_start" comment */
for (i=0;i<comment->comments;i++) {
if (strstr(comment->user_comments[i],"loop_start=")==
comment->user_comments[i] ||
strstr(comment->user_comments[i],"LOOP_START=")==
comment->user_comments[i] ||
strstr(comment->user_comments[i],"COMMENT=LOOPPOINT=")==
comment->user_comments[i] ||
strstr(comment->user_comments[i],"LOOPSTART=")==
comment->user_comments[i] ||
strstr(comment->user_comments[i],"um3.stream.looppoint.start=")==
comment->user_comments[i] ||
strstr(comment->user_comments[i],"LOOP_BEGIN=")==
comment->user_comments[i] ||
strstr(comment->user_comments[i],"LoopStart=")==
comment->user_comments[i]
) {
loop_start=atol(strrchr(comment->user_comments[i],'=')+1);
if (loop_start >= 0)
loop_flag=1;
}
else if (strstr(comment->user_comments[i],"LOOPLENGTH=")==
comment->user_comments[i]) {
loop_length=atol(strrchr(comment->user_comments[i],'=')+1);
loop_length_found=1;
}
else if (strstr(comment->user_comments[i],"title=-lps")==
comment->user_comments[i]) {
loop_start=atol(comment->user_comments[i]+10);
if (loop_start >= 0)
loop_flag=1;
}
else if (strstr(comment->user_comments[i],"album=-lpe")==
comment->user_comments[i]) {
loop_end=atol(comment->user_comments[i]+10);
loop_flag=1;
loop_end_found=1;
}
else if (strstr(comment->user_comments[i],"LoopEnd=")==
comment->user_comments[i]) {
if(loop_flag) {
loop_length=atol(strrchr(comment->user_comments[i],'=')+1)-loop_start;
loop_length_found=1;
}
}
else if (strstr(comment->user_comments[i],"LOOP_END=")==
comment->user_comments[i]) {
if(loop_flag) {
loop_length=atol(strrchr(comment->user_comments[i],'=')+1)-loop_start;
loop_length_found=1;
}
}
else if (strstr(comment->user_comments[i],"lp=")==
comment->user_comments[i]) {
sscanf(strrchr(comment->user_comments[i],'=')+1,"%d,%d",
&loop_start,&loop_end);
loop_flag=1;
loop_end_found=1;
}
}
}
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(info->channels,loop_flag);
if (!vgmstream) goto fail;
/* store our fun extra datas */
vgmstream->codec_data = data;
/* fill in the vital statistics */
vgmstream->channels = info->channels;
vgmstream->sample_rate = info->rate;
/* let's play the whole file */
vgmstream->num_samples = ov_pcm_total(ovf,-1);
if (loop_flag) {
vgmstream->loop_start_sample = loop_start;
if (loop_length_found)
vgmstream->loop_end_sample = loop_start+loop_length;
else if (loop_end_found)
vgmstream->loop_end_sample = loop_end;
else
vgmstream->loop_end_sample = vgmstream->num_samples;
vgmstream->loop_flag = loop_flag;
if (vgmstream->loop_end_sample > vgmstream->num_samples)
vgmstream->loop_end_sample = vgmstream->num_samples;
}
vgmstream->coding_type = coding_ogg_vorbis;
vgmstream->layout_type = vgm_inf->layout_type;
vgmstream->meta_type = vgm_inf->meta_type;
return vgmstream;
/* clean up anything we may have opened */
fail:
if (data) {
if (inited_ovf)
ov_clear(&data->ogg_vorbis_file);
if (data->ov_streamfile.streamfile)
close_streamfile(data->ov_streamfile.streamfile);
free(data);
}
if (vgmstream) {
vgmstream->codec_data = NULL;
close_vgmstream(vgmstream);
}
return NULL;
}
#endif
#include "../vgmstream.h"
#ifdef VGM_USE_VORBIS
#include <stdio.h>
#include <string.h>
#include "meta.h"
#include "../util.h"
#include <vorbis/vorbisfile.h>
#define DEFAULT_BITSTREAM 0
static size_t read_func(void *ptr, size_t size, size_t nmemb, void * datasource)
{
ogg_vorbis_streamfile * const ov_streamfile = datasource;
size_t items_read;
size_t bytes_read;
bytes_read = read_streamfile(ptr, ov_streamfile->offset + ov_streamfile->other_header_bytes, size * nmemb,
ov_streamfile->streamfile);
items_read = bytes_read / size;
ov_streamfile->offset += items_read * size;
return items_read;
}
static size_t read_func_um3(void *ptr, size_t size, size_t nmemb, void * datasource)
{
ogg_vorbis_streamfile * const ov_streamfile = datasource;
size_t items_read;
size_t bytes_read;
bytes_read = read_streamfile(ptr, ov_streamfile->offset + ov_streamfile->other_header_bytes, size * nmemb,
ov_streamfile->streamfile);
items_read = bytes_read / size;
/* first 0x800 bytes of um3 are xor'd with 0xff */
if (ov_streamfile->offset < 0x800) {
int num_crypt = 0x800-ov_streamfile->offset;
int i;
if (num_crypt > bytes_read) num_crypt=bytes_read;
for (i=0;i<num_crypt;i++)
((uint8_t*)ptr)[i] ^= 0xff;
}
ov_streamfile->offset += items_read * size;
return items_read;
}
static size_t read_func_kovs(void *ptr, size_t size, size_t nmemb, void * datasource)
{
ogg_vorbis_streamfile * const ov_streamfile = datasource;
size_t items_read;
size_t bytes_read;
bytes_read = read_streamfile(ptr, ov_streamfile->offset+ov_streamfile->other_header_bytes, size * nmemb,
ov_streamfile->streamfile);
items_read = bytes_read / size;
/* first 0x100 bytes of KOVS are xor'd with offset */
if (ov_streamfile->offset < 0x100) {
int i;
for (i=ov_streamfile->offset;i<0x100;i++)
((uint8_t*)ptr)[i-ov_streamfile->offset] ^= i;
}
ov_streamfile->offset += items_read * size;
return items_read;
}
static size_t read_func_scd(void *ptr, size_t size, size_t nmemb, void * datasource)
{
ogg_vorbis_streamfile * const ov_streamfile = datasource;
size_t items_read;
size_t bytes_read;
bytes_read = read_streamfile(ptr, ov_streamfile->offset + ov_streamfile->other_header_bytes, size * nmemb,
ov_streamfile->streamfile);
items_read = bytes_read / size;
/* first bytes are xor'd with a constant byte */
if (ov_streamfile->offset < ov_streamfile->scd_xor_len) {
int num_crypt = ov_streamfile->scd_xor_len-ov_streamfile->offset;
int i;
if (num_crypt > bytes_read) num_crypt=bytes_read;
for (i=0;i<num_crypt;i++)
((uint8_t*)ptr)[i] ^= ov_streamfile->scd_xor;
}
ov_streamfile->offset += items_read * size;
return items_read;
}
static size_t read_func_psych(void *ptr, size_t size, size_t nmemb, void * datasource)
{
ogg_vorbis_streamfile * const ov_streamfile = datasource;
size_t items_read;
size_t bytes_read;
size_t i;
bytes_read = read_streamfile(ptr, ov_streamfile->offset + ov_streamfile->other_header_bytes, size * nmemb,
ov_streamfile->streamfile);
/* add 0x23 ('#') */
for (i=0;i<bytes_read;i++)
((uint8_t*)ptr)[i] += 0x23;
items_read = bytes_read / size;
ov_streamfile->offset += items_read * size;
return items_read;
}
static int seek_func(void *datasource, ogg_int64_t offset, int whence) {
ogg_vorbis_streamfile * const ov_streamfile = datasource;
ogg_int64_t base_offset;
ogg_int64_t new_offset;
switch (whence) {
case SEEK_SET:
base_offset = 0;
break;
case SEEK_CUR:
base_offset = ov_streamfile->offset;
break;
case SEEK_END:
base_offset = ov_streamfile->size - ov_streamfile->other_header_bytes;
break;
default:
return -1;
break;
}
new_offset = base_offset + offset;
if (new_offset < 0 || new_offset > (ov_streamfile->size - ov_streamfile->other_header_bytes)) {
return -1;
} else {
ov_streamfile->offset = new_offset;
return 0;
}
}
static long tell_func(void * datasource) {
ogg_vorbis_streamfile * const ov_streamfile = datasource;
return ov_streamfile->offset;
}
/* setting close_func in ov_callbacks to NULL doesn't seem to work */
static int close_func(void * datasource) {
return 0;
}
/* Ogg Vorbis, by way of libvorbisfile */
VGMSTREAM * init_vgmstream_ogg_vorbis(STREAMFILE *streamFile) {
char filename[PATH_LIMIT];
ov_callbacks callbacks;
off_t other_header_bytes = 0;
int um3_ogg = 0;
int kovs_ogg = 0;
int psych_ogg = 0;
vgm_vorbis_info_t inf;
memset(&inf, 0, sizeof(inf));
/* check extension, case insensitive */
streamFile->get_name(streamFile,filename,sizeof(filename));
/* It is only interesting to use oggs with vgmstream if they are looped.
To prevent such files from being played by other plugins and such they
may be renamed to .logg. This meta reader should still support .ogg,
though. */
if (strcasecmp("logg",filename_extension(filename)) &&
strcasecmp("ogg",filename_extension(filename))) {
if (!strcasecmp("um3",filename_extension(filename))) {
um3_ogg = 1;
} else if (!strcasecmp("kovs",filename_extension(filename))) {
kovs_ogg = 1;
} else {
goto fail;
}
}
/* not all um3-ogg are crypted */
if (um3_ogg && read_32bitBE(0x0,streamFile)==0x4f676753) {
um3_ogg = 0;
}
/* use KOVS header */
if (kovs_ogg) {
if (read_32bitBE(0x0,streamFile)!=0x4b4f5653) { /* "KOVS" */
goto fail;
}
if (read_32bitLE(0x8,streamFile)!=0) {
inf.loop_start = read_32bitLE(0x8,streamFile);
inf.loop_flag = 1;
}
other_header_bytes = 0x20;
}
/* detect Psychic Software obfuscation (as seen in "Darkwind") */
if (read_32bitBE(0x0,streamFile)==0x2c444430) {
psych_ogg = 1;
}
if (um3_ogg) {
callbacks.read_func = read_func_um3;
} else if (kovs_ogg) {
callbacks.read_func = read_func_kovs;
} else if (psych_ogg) {
callbacks.read_func = read_func_psych;
} else {
callbacks.read_func = read_func;
}
callbacks.seek_func = seek_func;
callbacks.close_func = close_func;
callbacks.tell_func = tell_func;
if (um3_ogg) {
inf.meta_type = meta_um3_ogg;
} else if (kovs_ogg) {
inf.meta_type = meta_KOVS_ogg;
} else if (psych_ogg) {
inf.meta_type = meta_psych_ogg;
} else {
inf.meta_type = meta_ogg_vorbis;
}
inf.layout_type = layout_ogg_vorbis;
return init_vgmstream_ogg_vorbis_callbacks(streamFile, filename, &callbacks, other_header_bytes, &inf);
fail:
return NULL;
}
VGMSTREAM * init_vgmstream_ogg_vorbis_callbacks(STREAMFILE *streamFile, const char * filename, ov_callbacks *callbacks_p, off_t other_header_bytes, const vgm_vorbis_info_t *vgm_inf) {
VGMSTREAM * vgmstream = NULL;
OggVorbis_File temp_ovf;
ogg_vorbis_streamfile temp_streamfile;
ogg_vorbis_codec_data * data = NULL;
OggVorbis_File *ovf;
int inited_ovf = 0;
vorbis_info *info;
int loop_flag = vgm_inf->loop_flag;
int32_t loop_start = vgm_inf->loop_start;
int loop_length_found = vgm_inf->loop_length_found;
int32_t loop_length = vgm_inf->loop_length;
int loop_end_found = vgm_inf->loop_end_found;
int32_t loop_end = vgm_inf->loop_end;
ov_callbacks default_callbacks;
if (!callbacks_p) {
default_callbacks.read_func = read_func;
default_callbacks.seek_func = seek_func;
default_callbacks.close_func = close_func;
default_callbacks.tell_func = tell_func;
if (vgm_inf->scd_xor != 0) {
default_callbacks.read_func = read_func_scd;
}
callbacks_p = &default_callbacks;
}
temp_streamfile.streamfile = streamFile;
temp_streamfile.offset = 0;
temp_streamfile.size = get_streamfile_size(temp_streamfile.streamfile);
temp_streamfile.other_header_bytes = other_header_bytes;
temp_streamfile.scd_xor = vgm_inf->scd_xor;
temp_streamfile.scd_xor_len = vgm_inf->scd_xor_len;
/* can we open this as a proper ogg vorbis file? */
memset(&temp_ovf, 0, sizeof(temp_ovf));
if (ov_test_callbacks(&temp_streamfile, &temp_ovf, NULL,
0, *callbacks_p)) goto fail;
/* we have to close this as it has the init_vgmstream meta-reading
STREAMFILE */
ov_clear(&temp_ovf);
/* proceed to open a STREAMFILE just for this stream */
data = calloc(1,sizeof(ogg_vorbis_codec_data));
if (!data) goto fail;
data->ov_streamfile.streamfile = streamFile->open(streamFile,filename,
STREAMFILE_DEFAULT_BUFFER_SIZE);
if (!data->ov_streamfile.streamfile) goto fail;
data->ov_streamfile.offset = 0;
data->ov_streamfile.size = get_streamfile_size(data->ov_streamfile.streamfile);
data->ov_streamfile.other_header_bytes = other_header_bytes;
data->ov_streamfile.scd_xor = vgm_inf->scd_xor;
data->ov_streamfile.scd_xor_len = vgm_inf->scd_xor_len;
/* open the ogg vorbis file for real */
if (ov_open_callbacks(&data->ov_streamfile, &data->ogg_vorbis_file, NULL,
0, *callbacks_p)) goto fail;
ovf = &data->ogg_vorbis_file;
inited_ovf = 1;
data->bitstream = DEFAULT_BITSTREAM;
info = ov_info(ovf,DEFAULT_BITSTREAM);
/* grab the comments */
{
int i;
vorbis_comment *comment;
comment = ov_comment(ovf,DEFAULT_BITSTREAM);
/* search for a "loop_start" comment */
for (i=0;i<comment->comments;i++) {
if (strstr(comment->user_comments[i],"loop_start=")==
comment->user_comments[i] ||
strstr(comment->user_comments[i],"LOOP_START=")==
comment->user_comments[i] ||
strstr(comment->user_comments[i],"COMMENT=LOOPPOINT=")==
comment->user_comments[i] ||
strstr(comment->user_comments[i],"LOOPSTART=")==
comment->user_comments[i] ||
strstr(comment->user_comments[i],"um3.stream.looppoint.start=")==
comment->user_comments[i] ||
strstr(comment->user_comments[i],"LOOP_BEGIN=")==
comment->user_comments[i] ||
strstr(comment->user_comments[i],"LoopStart=")==
comment->user_comments[i]
) {
loop_start=atol(strrchr(comment->user_comments[i],'=')+1);
if (loop_start >= 0)
loop_flag=1;
}
else if (strstr(comment->user_comments[i],"LOOPLENGTH=")==
comment->user_comments[i]) {
loop_length=atol(strrchr(comment->user_comments[i],'=')+1);
loop_length_found=1;
}
else if (strstr(comment->user_comments[i],"title=-lps")==
comment->user_comments[i]) {
loop_start=atol(comment->user_comments[i]+10);
if (loop_start >= 0)
loop_flag=1;
}
else if (strstr(comment->user_comments[i],"album=-lpe")==
comment->user_comments[i]) {
loop_end=atol(comment->user_comments[i]+10);
loop_flag=1;
loop_end_found=1;
}
else if (strstr(comment->user_comments[i],"LoopEnd=")==
comment->user_comments[i]) {
if(loop_flag) {
loop_length=atol(strrchr(comment->user_comments[i],'=')+1)-loop_start;
loop_length_found=1;
}
}
else if (strstr(comment->user_comments[i],"LOOP_END=")==
comment->user_comments[i]) {
if(loop_flag) {
loop_length=atol(strrchr(comment->user_comments[i],'=')+1)-loop_start;
loop_length_found=1;
}
}
else if (strstr(comment->user_comments[i],"lp=")==
comment->user_comments[i]) {
sscanf(strrchr(comment->user_comments[i],'=')+1,"%d,%d",
&loop_start,&loop_end);
loop_flag=1;
loop_end_found=1;
}
else if (strstr(comment->user_comments[i],"COMMENT=loop(")==
comment->user_comments[i]) {
sscanf(strrchr(comment->user_comments[i],'(')+1,"%d,%d",
&loop_start,&loop_end);
loop_flag=1;
loop_end_found=1;
}
}
}
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(info->channels,loop_flag);
if (!vgmstream) goto fail;
/* store our fun extra datas */
vgmstream->codec_data = data;
/* fill in the vital statistics */
vgmstream->channels = info->channels;
vgmstream->sample_rate = info->rate;
/* let's play the whole file */
vgmstream->num_samples = ov_pcm_total(ovf,-1);
if (loop_flag) {
vgmstream->loop_start_sample = loop_start;
if (loop_length_found)
vgmstream->loop_end_sample = loop_start+loop_length;
else if (loop_end_found)
vgmstream->loop_end_sample = loop_end;
else
vgmstream->loop_end_sample = vgmstream->num_samples;
vgmstream->loop_flag = loop_flag;
if (vgmstream->loop_end_sample > vgmstream->num_samples)
vgmstream->loop_end_sample = vgmstream->num_samples;
}
vgmstream->coding_type = coding_ogg_vorbis;
vgmstream->layout_type = vgm_inf->layout_type;
vgmstream->meta_type = vgm_inf->meta_type;
return vgmstream;
/* clean up anything we may have opened */
fail:
if (data) {
if (inited_ovf)
ov_clear(&data->ogg_vorbis_file);
if (data->ov_streamfile.streamfile)
close_streamfile(data->ov_streamfile.streamfile);
free(data);
}
if (vgmstream) {
vgmstream->codec_data = NULL;
close_vgmstream(vgmstream);
}
return NULL;
}
#endif

View File

@ -1,63 +1,96 @@
#include "meta.h"
#include "../util.h"
/* 2PFS
- Mahoromatic: Moetto - KiraKira Maid-San (PS2)
/* 2PFS (Konami)
- Mahoromatic: Moetto - KiraKira Maid-San (PS2) [.2pfs (V1, 2003)]
- GANTZ The Game (PS2) [.sap (V2, 2005)]
There are two versions of the format, though they use different extensions.
Implemented both versions here in case there are .2pfs with the V2 header out there.
Both loop correctly AFAIK (there is a truncated Mahoromatic rip around, beware).
*/
VGMSTREAM * init_vgmstream_ps2_2pfs(STREAMFILE *streamFile)
{
VGMSTREAM * vgmstream = NULL;
char filename[PATH_LIMIT];
off_t start_offset;
off_t start_offset = 0x800;
int interleave = 0x1000;
int loop_flag = 0;
int loop_flag;
int channel_count;
int version; /* v1=1, v2=2 */
int loop_start_block; /* block number where the loop starts */
int loop_end_block; /* usually the last block */
int loop_start_sample_adjust; /* loops start/end a few samples into the start/end block */
int loop_end_sample_adjust;
/* check extension, case insensitive */
streamFile->get_name(streamFile,filename,sizeof(filename));
if (strcasecmp("2pfs",filename_extension(filename))) goto fail;
/* check header */
if (read_32bitBE(0x00,streamFile) != 0x32504653)
if ( strcasecmp("2pfs",filename_extension(filename))
&& strcasecmp("sap",filename_extension(filename)) )
goto fail;
// channel count
channel_count = read_8bit(0x40,streamFile);
/* check header ("2PFS") */
if (read_32bitBE(0x00,streamFile) != 0x32504653)
goto fail;
// header size
start_offset = 0x800;
// loop flag
//if ((read_32bitLE(0x38, streamFile) != 0 ||
// (read_32bitLE(0x34, streamFile) != 0)))
//{
// loop_flag = 1;
//}
version = read_16bitLE(0x04,streamFile);
if ( version!=0x01 && version!=0x02 )
goto fail;
// Loop info unknown right now
//if (loop_flag)
//{
// vgmstream->loop_start_sample = read_32bitLE(0x38,streamFile)*28/16/channel_count;
// vgmstream->loop_end_sample = read_32bitLE(0x34,streamFile)*28/16/channel_count;
//}
/* build the VGMSTREAM */
channel_count = read_8bit(0x40,streamFile);
loop_flag = read_8bit(0x41,streamFile);
/* other header values
* 0x06 (4): unknown, v1=0x0004 v2=0x0001
* 0x08 (32): unique file id
* 0x0c (32): base header size (v1=0x50, v2=0x60) + datasize (without the 0x800 full header size)
* 0x10-0x30: unknown (v1 differs from v2)
* 0x38-0x40: unknown (v1 same as v2)
* 0x4c (32) in V2: unknown, some kind of total samples?
*/
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count,loop_flag);
if (!vgmstream) goto fail;
/* fill in the vital statistics */
vgmstream->channels = channel_count;
vgmstream->sample_rate = read_32bitLE(0x44,streamFile);
/* fill in the vital statistics */
vgmstream->channels = channel_count;
vgmstream->coding_type = coding_PSX;
vgmstream->num_samples = read_32bitLE(0x0C,streamFile)*28/16/channel_count;
vgmstream->num_samples = read_32bitLE(0x34,streamFile) * 28 / 16 / channel_count;
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = 0x1000;
vgmstream->interleave_block_size = interleave;
vgmstream->meta_type = meta_PS2_2PFS;
if ( version==0x01 ) {
vgmstream->sample_rate = read_32bitLE(0x44,streamFile);
loop_start_sample_adjust = read_16bitLE(0x42,streamFile);
loop_start_block = read_32bitLE(0x48,streamFile);
loop_end_block = read_32bitLE(0x4c,streamFile);
} else {
vgmstream->sample_rate = read_32bitLE(0x48,streamFile);
loop_start_sample_adjust = read_32bitLE(0x44,streamFile);
loop_start_block = read_32bitLE(0x50,streamFile);
loop_end_block = read_32bitLE(0x54,streamFile);
}
loop_end_sample_adjust = interleave; /* loops end after all samples in the end_block AFAIK */
if ( loop_flag ) {
/* block to offset > offset to sample + adjust (number of samples into the block) */
vgmstream->loop_start_sample = ((loop_start_block * channel_count * interleave)
* 28 / 16 / channel_count)
+ (loop_start_sample_adjust * 28 / 16);
vgmstream->loop_end_sample = ((loop_end_block * channel_count * interleave)
* 28 / 16 / channel_count)
+ (loop_end_sample_adjust * 28 / 16);
}
/* open the file for reading */
{
int i;
@ -69,11 +102,10 @@ VGMSTREAM * init_vgmstream_ps2_2pfs(STREAMFILE *streamFile)
{
vgmstream->ch[i].streamfile = file;
vgmstream->ch[i].channel_start_offset=
vgmstream->ch[i].offset=start_offset + (vgmstream->interleave_block_size * i);
vgmstream->ch[i].channel_start_offset =
vgmstream->ch[i].offset =
start_offset + (vgmstream->interleave_block_size * i);
}
}
return vgmstream;

View File

@ -173,15 +173,17 @@ int read_fmt(int big_endian,
fmt->interleave = 8;
break;
#ifdef VGM_USE_FFMPEG
case 0x270:
case 0xFFFE:
case 0x270: /* ATRAC3 */
#if defined(VGM_USE_FFMPEG) && !defined(VGM_USE_MAIATRAC3PLUS)
case 0xFFFE: /* WAVEFORMATEXTENSIBLE / ATRAC3plus */
#endif /* defined */
fmt->coding_type = coding_FFmpeg;
fmt->block_size = 2048;
fmt->interleave = 0;
break;
#endif
#endif /* VGM_USE_FFMPEG */
#ifdef VGM_USE_MAIATRAC3PLUS
case 0xFFFE: /* WAVEFORMATEXTENSIBLE */
case 0xFFFE: /* WAVEFORMATEXTENSIBLE / ATRAC3plus */
if (read_32bit(current_chunk+0x20,streamFile) == 0xE923AABF &&
read_16bit(current_chunk+0x24,streamFile) == (int16_t)0xCB58 &&
read_16bit(current_chunk+0x26,streamFile) == 0x4471 &&

View File

@ -359,6 +359,13 @@ VGMSTREAM * init_vgmstream_internal(STREAMFILE *streamFile, int do_dfs) {
if (vgmstream) {
/* these are little hacky checks */
/* fail if there is nothing to play
* (without this check vgmstream can generate empty files) */
if ( vgmstream->num_samples==0 ) {
close_vgmstream(vgmstream);
continue;
}
/* everything should have a reasonable sample rate
* (a verification of the metadata) */
if (!check_sample_rate(vgmstream->sample_rate)) {
@ -1783,6 +1790,10 @@ int vgmstream_do_loop(VGMSTREAM * vgmstream) {
if (vgmstream->coding_type==coding_FFmpeg) {
ffmpeg_codec_data *data = (ffmpeg_codec_data *)(vgmstream->codec_data);
int64_t ts;
/* Seek to loop start by timestamp (closest frame) + adjust skipping some samples */
/* FFmpeg seeks by ts by design (since not all containers can accurately skip to a frame). */
/* TODO: this seems to be off by +-1 frames in some cases */
ts = vgmstream->loop_start_sample;
if (ts >= data->sampleRate * 2) {
data->samplesToDiscard = data->sampleRate * 2;
@ -1794,14 +1805,26 @@ int vgmstream_do_loop(VGMSTREAM * vgmstream) {
}
data->framesRead = (int)ts;
ts = data->framesRead * (data->formatCtx->duration) / data->totalFrames;
#ifdef VGM_USE_FFMPEG_ACCURATE_LOOPING
/* Start from 0 and discard samples until loop_start for accurate looping (slower but not too noticeable) */
/* We could also seek by offset (AVSEEK_FLAG_BYTE) to the frame closest to the loop then discard
* some samples, which is fast but would need calculations per format / when frame size is not constant */
data->samplesToDiscard = vgmstream->loop_start_sample;
data->framesRead = 0;
ts = 0;
#endif /* VGM_USE_FFMPEG_ACCURATE_LOOPING */
avformat_seek_file(data->formatCtx, -1, ts - 1000, ts, ts, AVSEEK_FLAG_ANY);
avcodec_flush_buffers(data->codecCtx);
data->readNextPacket = 1;
data->bytesConsumedFromDecodedFrame = INT_MAX;
data->endOfStream = 0;
data->endOfAudio = 0;
}
#endif
#endif /* VGM_USE_FFMPEG */
#if defined(VGM_USE_MP4V2) && defined(VGM_USE_FDKAAC)
if (vgmstream->coding_type==coding_MP4_AAC) {
mp4_aac_codec_data *data = (mp4_aac_codec_data *)(vgmstream->codec_data);