mirror of
https://github.com/vgmstream/vgmstream.git
synced 2025-02-17 19:19:16 +01:00
support for mpeg audio in genh
git-svn-id: https://vgmstream.svn.sourceforge.net/svnroot/vgmstream@292 51a99a44-fe44-0410-b1ba-c3e57ba2b86b
This commit is contained in:
parent
32f6286080
commit
97ebe328c8
@ -88,7 +88,7 @@ File types supported by this version of vgmstream:
|
||||
- .sng, .asf, .str, .eam (EA/XA ADPCM or PSX ADPCM)
|
||||
- .cfn (GC DSP ADPCM)
|
||||
- .vpk (PSX ADPCM)
|
||||
- .genh (PSX ADPCM, XBOX IMA ADPCM, GC DTK ADPCM, 8/16 bit PCM, SDX2, DVI)
|
||||
- .genh (PSX ADPCM, XBOX IMA ADPCM, GC DTK ADPCM, 8/16 bit PCM, SDX2, DVI, MP3)
|
||||
- .ogg, .logg (Ogg Vorbis)
|
||||
- .sad (GC DSP ADPCM)
|
||||
- .bmdx (PSX ADPCM)
|
||||
|
@ -43,8 +43,11 @@ void decode_ws(VGMSTREAM * vgmstream, int channel, sample * outbuf, int channels
|
||||
|
||||
#ifdef VGM_USE_MPEG
|
||||
void decode_fake_mpeg2_l2(VGMSTREAMCHANNEL * stream,
|
||||
fake_mpeg2_l2_codec_data * data,
|
||||
mpeg_codec_data * data,
|
||||
sample * outbuf, int32_t samples_to_do);
|
||||
void decode_mpeg(VGMSTREAMCHANNEL * stream,
|
||||
mpeg_codec_data * data,
|
||||
sample * outbuf, int32_t samples_to_do, int channels);
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
@ -9,7 +9,7 @@
|
||||
/* mono, mpg123 expects frames of 0x414 (160kbps, 22050Hz) but they
|
||||
* actually vary and are much shorter */
|
||||
void decode_fake_mpeg2_l2(VGMSTREAMCHANNEL *stream,
|
||||
fake_mpeg2_l2_codec_data * data,
|
||||
mpeg_codec_data * data,
|
||||
sample * outbuf, int32_t samples_to_do) {
|
||||
int samples_done = 0;
|
||||
|
||||
@ -85,4 +85,45 @@ void decode_fake_mpeg2_l2(VGMSTREAMCHANNEL *stream,
|
||||
}
|
||||
}
|
||||
|
||||
/* decode anything mpg123 can */
|
||||
void decode_mpeg(VGMSTREAMCHANNEL *stream,
|
||||
mpeg_codec_data * data,
|
||||
sample * outbuf, int32_t samples_to_do, int channels) {
|
||||
int samples_done = 0;
|
||||
|
||||
while (samples_done < samples_to_do) {
|
||||
size_t bytes_done;
|
||||
int rc;
|
||||
|
||||
if (!data->buffer_full) {
|
||||
data->bytes_in_buffer = read_streamfile(data->buffer,
|
||||
stream->offset,MPEG_BUFFER_SIZE,stream->streamfile);
|
||||
|
||||
data->buffer_full = 1;
|
||||
data->buffer_used = 0;
|
||||
|
||||
stream->offset += data->bytes_in_buffer;
|
||||
}
|
||||
|
||||
if (!data->buffer_used) {
|
||||
rc = mpg123_decode(data->m,
|
||||
data->buffer,data->bytes_in_buffer,
|
||||
(unsigned char *)(outbuf+samples_done*channels),
|
||||
(samples_to_do-samples_done)*sizeof(sample)*channels,
|
||||
&bytes_done);
|
||||
data->buffer_used = 1;
|
||||
} else {
|
||||
rc = mpg123_decode(data->m,
|
||||
NULL,0,
|
||||
(unsigned char *)(outbuf+samples_done*channels),
|
||||
(samples_to_do-samples_done)*sizeof(sample)*channels,
|
||||
&bytes_done);
|
||||
}
|
||||
|
||||
if (rc == MPG123_NEED_MORE) data->buffer_full = 0;
|
||||
|
||||
samples_done += bytes_done/sizeof(sample)/channels;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -15,7 +15,7 @@ VGMSTREAM * init_vgmstream_ahx(STREAMFILE *streamFile) {
|
||||
char filename[260];
|
||||
int channel_count = 1;
|
||||
int loop_flag = 0;
|
||||
fake_mpeg2_l2_codec_data *data = NULL;
|
||||
mpeg_codec_data *data = NULL;
|
||||
|
||||
/* check extension, case insensitive */
|
||||
streamFile->get_name(streamFile,filename,sizeof(filename));
|
||||
@ -83,7 +83,7 @@ VGMSTREAM * init_vgmstream_ahx(STREAMFILE *streamFile) {
|
||||
/* ooh, fun part, set up mpg123 */
|
||||
{
|
||||
int rc;
|
||||
data = calloc(1,sizeof(fake_mpeg2_l2_codec_data));
|
||||
data = calloc(1,sizeof(mpeg_codec_data));
|
||||
if (!data) goto fail;
|
||||
|
||||
data->m = mpg123_new(NULL,&rc);
|
||||
|
106
src/meta/genh.c
106
src/meta/genh.c
@ -1,6 +1,10 @@
|
||||
#include "../vgmstream.h"
|
||||
#include "meta.h"
|
||||
#include "../layout/layout.h"
|
||||
#include "../util.h"
|
||||
#ifdef VGM_USE_MPEG
|
||||
#include <mpg123.h>
|
||||
#endif
|
||||
|
||||
/* GENH is an artificial "generic" header for headerless streams */
|
||||
|
||||
@ -17,6 +21,9 @@ VGMSTREAM * init_vgmstream_genh(STREAMFILE *streamFile) {
|
||||
int32_t header_size;
|
||||
char filename[260];
|
||||
int coding;
|
||||
#ifdef VGM_USE_MPEG
|
||||
mpeg_codec_data *data = NULL;
|
||||
#endif
|
||||
|
||||
/* check extension, case insensitive */
|
||||
streamFile->get_name(streamFile,filename,sizeof(filename));
|
||||
@ -38,6 +45,7 @@ VGMSTREAM * init_vgmstream_genh(STREAMFILE *streamFile) {
|
||||
/* 5 = 8bit PCM */
|
||||
/* 6 = SDX2 */
|
||||
/* 7 = DVI IMA */
|
||||
/* 8 = MPEG-1 Layer III, possibly also the MPEG-2 and 2.5 extensions */
|
||||
/* ... others to come */
|
||||
switch (read_32bitLE(0x18,streamFile)) {
|
||||
case 0:
|
||||
@ -65,6 +73,12 @@ VGMSTREAM * init_vgmstream_genh(STREAMFILE *streamFile) {
|
||||
case 7:
|
||||
coding = coding_DVI_IMA;
|
||||
break;
|
||||
#ifdef VGM_USE_MPEG
|
||||
case 8:
|
||||
/* we say MPEG-1 L3 here, but later find out exactly which */
|
||||
coding = coding_MPEG1_L3;
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
goto fail;
|
||||
}
|
||||
@ -125,6 +139,11 @@ VGMSTREAM * init_vgmstream_genh(STREAMFILE *streamFile) {
|
||||
case coding_NGC_DTK:
|
||||
vgmstream->layout_type = layout_dtk_interleave;
|
||||
break;
|
||||
#ifdef VGM_USE_MPEG
|
||||
case coding_MPEG1_L3:
|
||||
vgmstream->layout_type = layout_mpeg;
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
|
||||
vgmstream->meta_type = meta_GENH;
|
||||
@ -173,6 +192,13 @@ VGMSTREAM * init_vgmstream_genh(STREAMFILE *streamFile) {
|
||||
chstreamfile =
|
||||
streamFile->open(streamFile,filename,32*0x400);
|
||||
break;
|
||||
#ifdef VGM_USE_MPEG
|
||||
case coding_MPEG1_L3:
|
||||
if (!chstreamfile)
|
||||
chstreamfile =
|
||||
streamFile->open(streamFile,filename,MPEG_BUFFER_SIZE);
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
|
||||
if (!chstreamfile) goto fail;
|
||||
@ -184,9 +210,89 @@ VGMSTREAM * init_vgmstream_genh(STREAMFILE *streamFile) {
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef VGM_USE_MPEG
|
||||
if (coding == coding_MPEG1_L3) {
|
||||
int rc;
|
||||
off_t read_offset;
|
||||
data = calloc(1,sizeof(mpeg_codec_data));
|
||||
if (!data) goto mpeg_fail;
|
||||
|
||||
data->m = mpg123_new(NULL,&rc);
|
||||
if (rc==MPG123_NOT_INITIALIZED) {
|
||||
if (mpg123_init()!=MPG123_OK) goto mpeg_fail;
|
||||
data->m = mpg123_new(NULL,&rc);
|
||||
if (rc!=MPG123_OK) goto mpeg_fail;
|
||||
} else if (rc!=MPG123_OK) {
|
||||
goto mpeg_fail;
|
||||
}
|
||||
|
||||
if (mpg123_open_feed(data->m)!=MPG123_OK) {
|
||||
goto mpeg_fail;
|
||||
}
|
||||
|
||||
/* check format */
|
||||
read_offset=0;
|
||||
do {
|
||||
size_t bytes_done;
|
||||
if (read_streamfile(data->buffer, start_offset+read_offset,
|
||||
MPEG_BUFFER_SIZE,vgmstream->ch[0].streamfile) !=
|
||||
MPEG_BUFFER_SIZE) goto mpeg_fail;
|
||||
read_offset+=1;
|
||||
rc = mpg123_decode(data->m,data->buffer,MPEG_BUFFER_SIZE,
|
||||
NULL,0,&bytes_done);
|
||||
if (rc != MPG123_OK && rc != MPG123_NEW_FORMAT &&
|
||||
rc != MPG123_NEED_MORE) goto mpeg_fail;
|
||||
} while (rc != MPG123_NEW_FORMAT);
|
||||
|
||||
{
|
||||
long rate;
|
||||
int channels,encoding;
|
||||
struct mpg123_frameinfo mi;
|
||||
rc = mpg123_getformat(data->m,&rate,&channels,&encoding);
|
||||
if (rc != MPG123_OK) goto mpeg_fail;
|
||||
if (rate != vgmstream->sample_rate ||
|
||||
channels != vgmstream->channels ||
|
||||
encoding != MPG123_ENC_SIGNED_16) goto mpeg_fail;
|
||||
mpg123_info(data->m,&mi);
|
||||
if (mi.rate != vgmstream->sample_rate) goto mpeg_fail;
|
||||
if (mi.version == MPG123_1_0 && mi.layer == 1)
|
||||
vgmstream->coding_type = coding_MPEG1_L1;
|
||||
else if (mi.version == MPG123_1_0 && mi.layer == 2)
|
||||
vgmstream->coding_type = coding_MPEG1_L2;
|
||||
else if (mi.version == MPG123_1_0 && mi.layer == 3)
|
||||
vgmstream->coding_type = coding_MPEG1_L3;
|
||||
else if (mi.version == MPG123_2_0 && mi.layer == 1)
|
||||
vgmstream->coding_type = coding_MPEG2_L1;
|
||||
else if (mi.version == MPG123_2_0 && mi.layer == 2)
|
||||
vgmstream->coding_type = coding_MPEG2_L2;
|
||||
else if (mi.version == MPG123_2_0 && mi.layer == 3)
|
||||
vgmstream->coding_type = coding_MPEG2_L3;
|
||||
else if (mi.version == MPG123_2_5 && mi.layer == 1)
|
||||
vgmstream->coding_type = coding_MPEG25_L1;
|
||||
else if (mi.version == MPG123_2_5 && mi.layer == 2)
|
||||
vgmstream->coding_type = coding_MPEG25_L2;
|
||||
else if (mi.version == MPG123_2_5 && mi.layer == 3)
|
||||
vgmstream->coding_type = coding_MPEG25_L3;
|
||||
else goto mpeg_fail;
|
||||
}
|
||||
|
||||
/* reinit, to ignore the reading we've done so far */
|
||||
mpg123_open_feed(data->m);
|
||||
|
||||
vgmstream->codec_data = data;
|
||||
}
|
||||
#endif
|
||||
|
||||
return vgmstream;
|
||||
|
||||
/* clean up anything we may have opened */
|
||||
#ifdef VGM_USE_MPEG
|
||||
mpeg_fail:
|
||||
if (data) {
|
||||
mpg123_delete(data->m);
|
||||
free(data);
|
||||
}
|
||||
#endif
|
||||
fail:
|
||||
if (vgmstream) close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
|
@ -150,9 +150,10 @@ void reset_vgmstream(VGMSTREAM * vgmstream) {
|
||||
}
|
||||
#endif
|
||||
#ifdef VGM_USE_MPEG
|
||||
if (vgmstream->coding_type==coding_fake_MPEG2_L2) {
|
||||
if (vgmstream->layout_type==layout_mpeg ||
|
||||
vgmstream->layout_type==layout_fake_mpeg) {
|
||||
off_t input_offset;
|
||||
fake_mpeg2_l2_codec_data *data = vgmstream->codec_data;
|
||||
mpeg_codec_data *data = vgmstream->codec_data;
|
||||
|
||||
/* input_offset is ignored as we can assume it will be 0 for a seek
|
||||
* to sample 0 */
|
||||
@ -260,15 +261,16 @@ void close_vgmstream(VGMSTREAM * vgmstream) {
|
||||
#endif
|
||||
|
||||
#ifdef VGM_USE_MPEG
|
||||
if (vgmstream->coding_type==coding_fake_MPEG2_L2) {
|
||||
fake_mpeg2_l2_codec_data *data = vgmstream->codec_data;
|
||||
if (vgmstream->layout_type==layout_fake_mpeg||
|
||||
vgmstream->layout_type==layout_mpeg) {
|
||||
mpeg_codec_data *data = vgmstream->codec_data;
|
||||
|
||||
if (data) {
|
||||
mpg123_delete(data->m);
|
||||
free(vgmstream->codec_data);
|
||||
vgmstream->codec_data = NULL;
|
||||
/* The astute reader will note that a call to mpg123_exit is never
|
||||
* make. While is is evilly breaking our contract with mpg123, it
|
||||
* made. While is is evilly breaking our contract with mpg123, it
|
||||
* doesn't actually do anything except set the "initialized" flag
|
||||
* to 0. And if we exit we run the risk of turning it off when
|
||||
* someone else in another thread is using it. */
|
||||
@ -296,6 +298,7 @@ void render_vgmstream(sample * buffer, int32_t sample_count, VGMSTREAM * vgmstre
|
||||
#endif
|
||||
#ifdef VGM_USE_MPEG
|
||||
case layout_fake_mpeg:
|
||||
case layout_mpeg:
|
||||
#endif
|
||||
case layout_dtk_interleave:
|
||||
case layout_none:
|
||||
@ -328,6 +331,15 @@ int get_vgmstream_samples_per_frame(VGMSTREAM * vgmstream) {
|
||||
#endif
|
||||
#ifdef VGM_USE_MPEG
|
||||
case coding_fake_MPEG2_L2:
|
||||
case coding_MPEG1_L1:
|
||||
case coding_MPEG1_L2:
|
||||
case coding_MPEG1_L3:
|
||||
case coding_MPEG2_L1:
|
||||
case coding_MPEG2_L2:
|
||||
case coding_MPEG2_L3:
|
||||
case coding_MPEG25_L1:
|
||||
case coding_MPEG25_L2:
|
||||
case coding_MPEG25_L3:
|
||||
#endif
|
||||
case coding_SDX2:
|
||||
return 1;
|
||||
@ -554,12 +566,25 @@ void decode_vgmstream(VGMSTREAM * vgmstream, int samples_written, int samples_to
|
||||
break;
|
||||
#ifdef VGM_USE_MPEG
|
||||
case coding_fake_MPEG2_L2:
|
||||
for (chan=0;chan<vgmstream->channels;chan++) {
|
||||
decode_fake_mpeg2_l2(
|
||||
&vgmstream->ch[chan],
|
||||
vgmstream->codec_data,
|
||||
buffer+samples_written*vgmstream->channels,samples_to_do);
|
||||
}
|
||||
decode_fake_mpeg2_l2(
|
||||
&vgmstream->ch[0],
|
||||
vgmstream->codec_data,
|
||||
buffer+samples_written*vgmstream->channels,samples_to_do);
|
||||
break;
|
||||
case coding_MPEG1_L1:
|
||||
case coding_MPEG1_L2:
|
||||
case coding_MPEG1_L3:
|
||||
case coding_MPEG2_L1:
|
||||
case coding_MPEG2_L2:
|
||||
case coding_MPEG2_L3:
|
||||
case coding_MPEG25_L1:
|
||||
case coding_MPEG25_L2:
|
||||
case coding_MPEG25_L3:
|
||||
decode_mpeg(
|
||||
&vgmstream->ch[0],
|
||||
vgmstream->codec_data,
|
||||
buffer+samples_written*vgmstream->channels,samples_to_do,
|
||||
vgmstream->channels);
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
@ -637,6 +662,19 @@ int vgmstream_do_loop(VGMSTREAM * vgmstream) {
|
||||
|
||||
ov_pcm_seek_lap(ogg_vorbis_file, vgmstream->loop_sample);
|
||||
}
|
||||
#endif
|
||||
#ifdef VGM_USE_MPEG
|
||||
/* won't work for fake MPEG */
|
||||
if (vgmstream->layout_type==layout_mpeg) {
|
||||
off_t input_offset;
|
||||
mpeg_codec_data *data = vgmstream->codec_data;
|
||||
|
||||
mpg123_feedseek(data->m,vgmstream->loop_sample,
|
||||
SEEK_SET,&input_offset);
|
||||
vgmstream->ch[0].offset =
|
||||
vgmstream->ch[0].channel_start_offset + input_offset;
|
||||
data->buffer_full = data->buffer_used = 0;
|
||||
}
|
||||
#endif
|
||||
/* restore! */
|
||||
memcpy(vgmstream->ch,vgmstream->loop_ch,sizeof(VGMSTREAMCHANNEL)*vgmstream->channels);
|
||||
@ -764,6 +802,33 @@ void describe_vgmstream(VGMSTREAM * vgmstream, char * desc, int length) {
|
||||
case coding_fake_MPEG2_L2:
|
||||
snprintf(temp,TEMPSIZE,"MPEG-2 Layer II Audio");
|
||||
break;
|
||||
case coding_MPEG1_L1:
|
||||
snprintf(temp,TEMPSIZE,"MPEG-1 Layer I Audio");
|
||||
break;
|
||||
case coding_MPEG1_L2:
|
||||
snprintf(temp,TEMPSIZE,"MPEG-1 Layer II Audio");
|
||||
break;
|
||||
case coding_MPEG1_L3:
|
||||
snprintf(temp,TEMPSIZE,"MPEG-1 Layer III Audio (MP3)");
|
||||
break;
|
||||
case coding_MPEG2_L1:
|
||||
snprintf(temp,TEMPSIZE,"MPEG-2 Layer I Audio");
|
||||
break;
|
||||
case coding_MPEG2_L2:
|
||||
snprintf(temp,TEMPSIZE,"MPEG-2 Layer II Audio");
|
||||
break;
|
||||
case coding_MPEG2_L3:
|
||||
snprintf(temp,TEMPSIZE,"MPEG-2 Layer III Audio (MP3)");
|
||||
break;
|
||||
case coding_MPEG25_L1:
|
||||
snprintf(temp,TEMPSIZE,"MPEG-2.5 Layer I Audio");
|
||||
break;
|
||||
case coding_MPEG25_L2:
|
||||
snprintf(temp,TEMPSIZE,"MPEG-2.5 Layer II Audio");
|
||||
break;
|
||||
case coding_MPEG25_L3:
|
||||
snprintf(temp,TEMPSIZE,"MPEG-2.5 Layer III Audio (MP3)");
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
snprintf(temp,TEMPSIZE,"CANNOT DECODE");
|
||||
@ -819,6 +884,9 @@ void describe_vgmstream(VGMSTREAM * vgmstream, char * desc, int length) {
|
||||
case layout_fake_mpeg:
|
||||
snprintf(temp,TEMPSIZE,"MPEG Audio stream with incorrect frame headers");
|
||||
break;
|
||||
case layout_mpeg:
|
||||
snprintf(temp,TEMPSIZE,"MPEG Audio stream");
|
||||
break;
|
||||
#endif
|
||||
|
||||
default:
|
||||
|
@ -48,7 +48,17 @@ typedef enum {
|
||||
coding_IMA, /* bare IMA, low nibble first */
|
||||
coding_WS, /* Westwood Studios' custom VBR ADPCM */
|
||||
#ifdef VGM_USE_MPEG
|
||||
coding_fake_MPEG2_L2, /* MPEG-2 Level 2 (AHX), with lying headers */
|
||||
coding_fake_MPEG2_L2, /* MPEG-2 Layer 2 (AHX), with lying headers */
|
||||
/* I don't even know offhand if all these combinations exist... */
|
||||
coding_MPEG1_L1,
|
||||
coding_MPEG1_L2,
|
||||
coding_MPEG1_L3, /* good ol' MPEG-1 Layer 3 (MP3) */
|
||||
coding_MPEG2_L1,
|
||||
coding_MPEG2_L2,
|
||||
coding_MPEG2_L3,
|
||||
coding_MPEG25_L1,
|
||||
coding_MPEG25_L2,
|
||||
coding_MPEG25_L3,
|
||||
#endif
|
||||
} coding_t;
|
||||
|
||||
@ -81,6 +91,7 @@ typedef enum {
|
||||
#endif
|
||||
#ifdef VGM_USE_MPEG
|
||||
layout_fake_mpeg, /* MPEG audio stream with bad frame headers (AHX) */
|
||||
layout_mpeg, /* proper MPEG audio stream */
|
||||
#endif
|
||||
} layout_t;
|
||||
|
||||
@ -281,12 +292,15 @@ typedef struct {
|
||||
|
||||
#ifdef VGM_USE_MPEG
|
||||
#define AHX_EXPECTED_FRAME_SIZE 0x414
|
||||
/* MPEG_BUFFER_SIZE should be >= AHX_EXPECTED_FRAME_SIZE */
|
||||
#define MPEG_BUFFER_SIZE 0x1000
|
||||
typedef struct {
|
||||
uint8_t buffer[AHX_EXPECTED_FRAME_SIZE];
|
||||
uint8_t buffer[MPEG_BUFFER_SIZE];
|
||||
int buffer_used;
|
||||
int buffer_full;
|
||||
size_t bytes_in_buffer;
|
||||
mpg123_handle *m;
|
||||
} fake_mpeg2_l2_codec_data;
|
||||
} mpeg_codec_data;
|
||||
#endif
|
||||
|
||||
/* do format detection, return pointer to a usable VGMSTREAM, or NULL on failure */
|
||||
|
Loading…
x
Reference in New Issue
Block a user