mirror of
https://github.com/vgmstream/vgmstream.git
synced 2024-12-20 10:25:53 +01:00
637 lines
23 KiB
C
637 lines
23 KiB
C
#include "coding.h"
|
|
#include "../util.h"
|
|
#include "../vgmstream.h"
|
|
|
|
#ifdef VGM_USE_MPEG
|
|
#include <mpg123.h>
|
|
#include "mpeg_decoder.h"
|
|
|
|
|
|
#define MPEG_DATA_BUFFER_SIZE 0x1000 /* at least one MPEG frame (max ~0x5A1 plus some more in case of free bitrate) */
|
|
|
|
static mpg123_handle* init_mpg123_handle();
|
|
static void decode_mpeg_standard(VGMSTREAMCHANNEL* stream, mpeg_codec_data* data, sample_t* outbuf, int32_t samples_to_do, int channels);
|
|
static void decode_mpeg_custom(VGMSTREAM* vgmstream, mpeg_codec_data* data, sample_t* outbuf, int32_t samples_to_do, int channels);
|
|
static void decode_mpeg_custom_stream(VGMSTREAMCHANNEL *stream, mpeg_codec_data* data, int num_stream);
|
|
|
|
|
|
/* Inits regular MPEG */
|
|
mpeg_codec_data* init_mpeg(STREAMFILE* sf, off_t start_offset, coding_t* coding_type, int channels) {
|
|
mpeg_codec_data* data = NULL;
|
|
|
|
/* init codec */
|
|
data = calloc(1, sizeof(mpeg_codec_data));
|
|
if (!data) goto fail;
|
|
|
|
data->buffer_size = MPEG_DATA_BUFFER_SIZE;
|
|
data->buffer = calloc(sizeof(uint8_t), data->buffer_size);
|
|
if (!data->buffer) goto fail;
|
|
|
|
data->m = init_mpg123_handle();
|
|
if (!data->m) goto fail;
|
|
|
|
/* check format */
|
|
{
|
|
mpg123_handle *main_m = data->m;
|
|
off_t read_offset = 0;
|
|
int rc;
|
|
|
|
long sample_rate_per_frame;
|
|
int channels_per_frame, encoding;
|
|
size_t samples_per_frame;
|
|
struct mpg123_frameinfo mi;
|
|
|
|
//todo read single big buffer then add +1 up to a few max bytes, or just read once only
|
|
/* read first frame(s) */
|
|
do {
|
|
size_t bytes_read, bytes_done;
|
|
|
|
/* don't check max as sfx can be smaller than buffer */
|
|
bytes_read = read_streamfile(data->buffer, start_offset + read_offset, data->buffer_size, sf);
|
|
read_offset += 1;
|
|
|
|
rc = mpg123_decode(main_m, data->buffer, bytes_read, NULL,0, &bytes_done);
|
|
if (rc != MPG123_OK && rc != MPG123_NEW_FORMAT && rc != MPG123_NEED_MORE) {
|
|
VGM_LOG("MPEG: unable to set up mpg123 at start offset\n");
|
|
goto fail; //handle MPG123_DONE?
|
|
}
|
|
if (read_offset > 0x5000) { /* don't hang in some incorrectly detected formats */
|
|
VGM_LOG("MPEG: unable to find mpeg data at start offset\n");
|
|
goto fail;
|
|
}
|
|
|
|
} while (rc != MPG123_NEW_FORMAT);
|
|
|
|
/* check first frame header and validate */
|
|
rc = mpg123_getformat(main_m,&sample_rate_per_frame,&channels_per_frame,&encoding);
|
|
if (rc != MPG123_OK) goto fail;
|
|
|
|
mpg123_info(main_m,&mi);
|
|
|
|
if (encoding != MPG123_ENC_SIGNED_16)
|
|
goto fail;
|
|
if (sample_rate_per_frame != mi.rate)
|
|
goto fail;
|
|
if ((channels != -1 && channels_per_frame != channels))
|
|
goto fail;
|
|
|
|
switch(mi.layer) {
|
|
case 1: *coding_type = coding_MPEG_layer1; break;
|
|
case 2: *coding_type = coding_MPEG_layer2; break;
|
|
case 3: *coding_type = coding_MPEG_layer3; break;
|
|
default: goto fail;
|
|
}
|
|
|
|
if (mi.layer == 1)
|
|
samples_per_frame = 384;
|
|
else if (mi.layer == 2)
|
|
samples_per_frame = 1152;
|
|
else if (mi.layer == 3 && mi.version == MPG123_1_0) //MP3
|
|
samples_per_frame = 1152;
|
|
else if (mi.layer == 3)
|
|
samples_per_frame = 576;
|
|
else goto fail;
|
|
|
|
data->channels_per_frame = channels_per_frame;
|
|
data->samples_per_frame = samples_per_frame;
|
|
if (channels_per_frame != channels)
|
|
goto fail;
|
|
|
|
/* copy current as open_feed may invalidate until data is fed */
|
|
memcpy(&data->mi, &mi, sizeof(struct mpg123_frameinfo));
|
|
|
|
/* reinit, to ignore the reading done */
|
|
mpg123_open_feed(main_m);
|
|
}
|
|
|
|
return data;
|
|
|
|
fail:
|
|
free_mpeg(data);
|
|
return NULL;
|
|
}
|
|
|
|
|
|
/* Init custom MPEG, with given type and config */
|
|
mpeg_codec_data* init_mpeg_custom(STREAMFILE* sf, off_t start_offset, coding_t* coding_type, int channels, mpeg_custom_t type, mpeg_custom_config* config) {
|
|
mpeg_codec_data* data = NULL;
|
|
int i, ok;
|
|
|
|
/* init codec */
|
|
data = calloc(1, sizeof(mpeg_codec_data));
|
|
if (!data) goto fail;
|
|
|
|
/* keep around to decode */
|
|
data->custom = 1;
|
|
data->type = type;
|
|
memcpy(&data->config, config, sizeof(mpeg_custom_config));
|
|
data->config.channels = channels;
|
|
|
|
data->default_buffer_size = MPEG_DATA_BUFFER_SIZE;
|
|
|
|
/* init per subtype */
|
|
switch(data->type) {
|
|
case MPEG_EAL31:
|
|
case MPEG_EAL31b:
|
|
case MPEG_EAL32P:
|
|
case MPEG_EAL32S: ok = mpeg_custom_setup_init_ealayer3(sf, start_offset, data, coding_type); break;
|
|
case MPEG_AWC: ok = mpeg_custom_setup_init_awc(sf, start_offset, data, coding_type); break;
|
|
case MPEG_EAMP3: ok = mpeg_custom_setup_init_eamp3(sf, start_offset, data, coding_type); break;
|
|
default: ok = mpeg_custom_setup_init_default(sf, start_offset, data, coding_type); break;
|
|
}
|
|
if (!ok)
|
|
goto fail;
|
|
|
|
if (channels <= 0 || channels > 16) goto fail; /* arbitrary max */
|
|
if (channels < data->channels_per_frame) goto fail;
|
|
//todo simplify/unify XVAG/P3D/SCD/LYN and just feed arbitrary chunks to the decoder
|
|
if (data->default_buffer_size > 0x10000) goto fail; /* max for some Ubi Lyn */
|
|
|
|
|
|
/* init streams */
|
|
data->streams_size = channels / data->channels_per_frame;
|
|
|
|
/* 2ch streams + odd channels = last stream must be 1ch */
|
|
/* (known channels combos are 2ch+..+2ch, 1ch+..+1ch, or rarely 2ch+..+2ch+1ch in EALayer3) */
|
|
if (data->channels_per_frame == 2 && channels % 2)
|
|
data->streams_size += 1;
|
|
|
|
data->streams = calloc(data->streams_size, sizeof(mpeg_custom_stream*));
|
|
for (i = 0; i < data->streams_size; i++) {
|
|
data->streams[i] = calloc(1, sizeof(mpeg_custom_stream));
|
|
data->streams[i]->m = init_mpg123_handle(); /* decoder not shared as may need several frames to decode)*/
|
|
if (!data->streams[i]->m) goto fail;
|
|
|
|
/* size could be any value */
|
|
data->streams[i]->output_buffer_size = sizeof(sample) * data->channels_per_frame * data->samples_per_frame;
|
|
data->streams[i]->output_buffer = calloc(data->streams[i]->output_buffer_size, sizeof(uint8_t));
|
|
if (!data->streams[i]->output_buffer) goto fail;
|
|
|
|
/* one per stream as sometimes mpg123 can't read the whole buffer in one pass */
|
|
data->streams[i]->buffer_size = data->default_buffer_size;
|
|
data->streams[i]->buffer = calloc(sizeof(uint8_t), data->streams[i]->buffer_size);
|
|
if (!data->streams[i]->buffer) goto fail;
|
|
|
|
data->streams[i]->channels_per_frame = data->channels_per_frame;
|
|
if (i + 1 == data->streams_size && data->channels_per_frame == 2 && channels % 2)
|
|
data->streams[i]->channels_per_frame = 1;
|
|
}
|
|
|
|
return data;
|
|
|
|
fail:
|
|
free_mpeg(data);
|
|
return NULL;
|
|
}
|
|
|
|
|
|
static mpg123_handle* init_mpg123_handle() {
|
|
mpg123_handle* m = NULL;
|
|
int rc;
|
|
|
|
/* inits a new mpg123 handle */
|
|
m = mpg123_new(NULL, &rc);
|
|
if (rc == MPG123_NOT_INITIALIZED) {
|
|
/* inits the library if needed */
|
|
if (mpg123_init() != MPG123_OK)
|
|
goto fail;
|
|
m = mpg123_new(NULL,&rc);
|
|
if (rc != MPG123_OK) goto fail;
|
|
} else if (rc != MPG123_OK) {
|
|
goto fail;
|
|
}
|
|
|
|
mpg123_param(m,MPG123_REMOVE_FLAGS,MPG123_GAPLESS,0.0); /* wonky support */
|
|
mpg123_param(m,MPG123_RESYNC_LIMIT, -1, 0x2000); /* just in case, games shouldn't ever need this */
|
|
|
|
if (mpg123_open_feed(m) != MPG123_OK) {
|
|
goto fail;
|
|
}
|
|
|
|
return m;
|
|
|
|
fail:
|
|
mpg123_delete(m);
|
|
return NULL;
|
|
}
|
|
|
|
|
|
/************/
|
|
/* DECODERS */
|
|
/************/
|
|
|
|
void decode_mpeg(VGMSTREAM* vgmstream, sample_t* outbuf, int32_t samples_to_do, int channels) {
|
|
mpeg_codec_data* data = vgmstream->codec_data;
|
|
|
|
if (!data->custom) {
|
|
decode_mpeg_standard(&vgmstream->ch[0], data, outbuf, samples_to_do, channels);
|
|
} else {
|
|
decode_mpeg_custom(vgmstream, data, outbuf, samples_to_do, channels);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Decode anything mpg123 can.
|
|
* Feeds raw data and extracts decoded samples as needed.
|
|
*/
|
|
static void decode_mpeg_standard(VGMSTREAMCHANNEL* stream, mpeg_codec_data* data, sample_t* outbuf, int32_t samples_to_do, int channels) {
|
|
int samples_done = 0;
|
|
unsigned char *outbytes = (unsigned char *)outbuf;
|
|
|
|
while (samples_done < samples_to_do) {
|
|
size_t bytes_done;
|
|
int rc, bytes_to_do;
|
|
|
|
/* read more raw data */
|
|
if (!data->buffer_full) {
|
|
data->bytes_in_buffer = read_streamfile(data->buffer,stream->offset,data->buffer_size,stream->streamfile);
|
|
|
|
/* end of stream, fill rest with 0s */
|
|
if (data->bytes_in_buffer <= 0) {
|
|
VGM_ASSERT(samples_to_do < samples_done, "MPEG: end of stream, filling %i\n", (samples_to_do - samples_done));
|
|
memset(outbuf + samples_done * channels, 0, (samples_to_do - samples_done) * channels * sizeof(sample));
|
|
break;
|
|
}
|
|
|
|
data->buffer_full = 1;
|
|
data->buffer_used = 0;
|
|
|
|
stream->offset += data->bytes_in_buffer;
|
|
}
|
|
|
|
bytes_to_do = (samples_to_do-samples_done)*sizeof(sample)*channels;
|
|
|
|
/* feed new raw data to the decoder if needed, copy decoded results to output */
|
|
if (!data->buffer_used) {
|
|
rc = mpg123_decode(data->m, data->buffer,data->bytes_in_buffer, outbytes, bytes_to_do, &bytes_done);
|
|
data->buffer_used = 1;
|
|
}
|
|
else {
|
|
rc = mpg123_decode(data->m, NULL,0, outbytes, bytes_to_do, &bytes_done);
|
|
}
|
|
|
|
/* not enough raw data, request more */
|
|
if (rc == MPG123_NEED_MORE) {
|
|
data->buffer_full = 0;
|
|
}
|
|
VGM_ASSERT(rc != MPG123_NEED_MORE && rc != MPG123_OK, "MPEG: error %i\n", rc);
|
|
|
|
/* update copied samples */
|
|
samples_done += bytes_done/sizeof(sample)/channels;
|
|
outbytes += bytes_done;
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Decode custom MPEG, for: single frames, mutant frames, interleave/multiple streams (Nch = 2ch*N/2 or 1ch*N), etc.
|
|
*
|
|
* Copies to outbuf when there are samples in all streams and calls decode_mpeg_custom_stream to decode.
|
|
. Depletes the stream's sample buffers before decoding more, so it doesn't run out of buffer space.
|
|
*/
|
|
static void decode_mpeg_custom(VGMSTREAM* vgmstream, mpeg_codec_data* data, sample_t* outbuf, int32_t samples_to_do, int channels) {
|
|
int i, samples_done = 0;
|
|
|
|
while (samples_done < samples_to_do) {
|
|
int samples_to_copy = -1;
|
|
|
|
/* find max to copy from all streams (equal for all channels) */
|
|
for (i = 0; i < data->streams_size; i++) {
|
|
size_t samples_in_stream = data->streams[i]->samples_filled - data->streams[i]->samples_used;
|
|
if (samples_to_copy < 0 || samples_in_stream < samples_to_copy)
|
|
samples_to_copy = samples_in_stream;
|
|
}
|
|
|
|
|
|
/* discard if needed (for looping) */
|
|
if (data->samples_to_discard) {
|
|
int samples_to_discard = samples_to_copy;
|
|
if (samples_to_discard > data->samples_to_discard)
|
|
samples_to_discard = data->samples_to_discard;
|
|
|
|
for (i = 0; i < data->streams_size; i++) {
|
|
data->streams[i]->samples_used += samples_to_discard;
|
|
}
|
|
data->samples_to_discard -= samples_to_discard;
|
|
samples_to_copy -= samples_to_discard;
|
|
}
|
|
|
|
/* mux streams channels (1/2ch combos) to outbuf (Nch) */
|
|
if (samples_to_copy > 0) {
|
|
int ch, stream;
|
|
|
|
if (samples_to_copy > samples_to_do - samples_done)
|
|
samples_to_copy = samples_to_do - samples_done;
|
|
|
|
ch = 0;
|
|
for (stream = 0; stream < data->streams_size; stream++) {
|
|
mpeg_custom_stream *ms = data->streams[stream];
|
|
sample_t *inbuf = (sample_t *)ms->output_buffer;
|
|
int stream_channels = ms->channels_per_frame;
|
|
int stream_ch, s;
|
|
|
|
for (stream_ch = 0; stream_ch < stream_channels; stream_ch++) {
|
|
for (s = 0; s < samples_to_copy; s++) {
|
|
size_t stream_sample = (ms->samples_used+s)*stream_channels + stream_ch;
|
|
size_t buffer_sample = (samples_done+s)*channels + ch;
|
|
|
|
outbuf[buffer_sample] = inbuf[stream_sample];
|
|
}
|
|
ch++;
|
|
}
|
|
|
|
ms->samples_used += samples_to_copy;
|
|
}
|
|
|
|
samples_done += samples_to_copy;
|
|
}
|
|
else {
|
|
/* decode more into stream sample buffers */
|
|
|
|
/* Handle offsets depending on the data layout (may only use half VGMSTREAMCHANNELs with 2ch streams)
|
|
* With multiple offsets they should already start in the first frame of each stream. */
|
|
for (i=0; i < data->streams_size; i++) {
|
|
switch(data->type) {
|
|
//case MPEG_FSB:
|
|
/* same offset: alternate frames between streams (maybe needed for weird layouts?) */
|
|
//decode_mpeg_custom_stream(&vgmstream->ch[0], data, i);
|
|
|
|
default:
|
|
/* offset per stream: absolute offsets, fixed interleave (skips other streams/interleave) */
|
|
decode_mpeg_custom_stream(&vgmstream->ch[i], data, i);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Decodes frames from a stream into the stream's sample buffer, feeding mpg123 buffer data.
|
|
* If not enough data to decode (as N data-frames = 1 full-frame) this will exit but be called again. */
|
|
static void decode_mpeg_custom_stream(VGMSTREAMCHANNEL* stream, mpeg_codec_data* data, int num_stream) {
|
|
size_t bytes_done = 0, bytes_filled, samples_filled;
|
|
size_t stream_size = get_streamfile_size(stream->streamfile);
|
|
int rc, ok;
|
|
mpeg_custom_stream *ms = data->streams[num_stream];
|
|
int channels_per_frame = ms->channels_per_frame;
|
|
|
|
//;VGM_LOG("MPEG: decode stream%i @ 0x%08lx (filled=%i, used=%i, buffer_full=%i)\n", num_stream, stream->offset, ms->samples_filled, ms->samples_used, ms->buffer_full);
|
|
|
|
/* wait until samples are depleted, so buffers don't grow too big */
|
|
if (ms->samples_filled - ms->samples_used > 0) {
|
|
return; /* common with multi-streams, as they decode at different rates) */
|
|
}
|
|
|
|
/* no samples = reset the counters */
|
|
ms->samples_filled = 0;
|
|
ms->samples_used = 0;
|
|
|
|
/* extra EOF check for edge cases when the caller tries to read more samples than possible */
|
|
if (!ms->buffer_full && stream->offset >= stream_size) {
|
|
VGM_LOG("MPEG: EOF found but more data is requested in stream %i\n", num_stream);
|
|
goto decode_fail;
|
|
}
|
|
|
|
|
|
/* read more raw data (could fill the sample buffer too in some cases, namely EALayer3) */
|
|
if (!ms->buffer_full) {
|
|
//;VGM_LOG("MPEG: reading more raw data\n");
|
|
switch(data->type) {
|
|
case MPEG_EAL31:
|
|
case MPEG_EAL31b:
|
|
case MPEG_EAL32P:
|
|
case MPEG_EAL32S: ok = mpeg_custom_parse_frame_ealayer3(stream, data, num_stream); break;
|
|
case MPEG_AHX: ok = mpeg_custom_parse_frame_ahx(stream, data, num_stream); break;
|
|
case MPEG_AWC: ok = mpeg_custom_parse_frame_awc(stream, data, num_stream); break;
|
|
case MPEG_EAMP3: ok = mpeg_custom_parse_frame_eamp3(stream, data, num_stream); break;
|
|
default: ok = mpeg_custom_parse_frame_default(stream, data, num_stream); break;
|
|
}
|
|
if (!ok) {
|
|
VGM_LOG("MPEG: cannot parse frame @ around %x\n",(uint32_t)stream->offset);
|
|
goto decode_fail; /* mpg123 could resync but custom MPEGs wouldn't need that */
|
|
}
|
|
//;VGM_LOG("MPEG: read results: bytes_in_buffer=0x%x, new offset=%lx\n", ms->bytes_in_buffer, stream->offset);
|
|
|
|
/* parse frame may not touch the buffer (only move offset, or fill the sample buffer) */
|
|
if (ms->bytes_in_buffer) {
|
|
ms->buffer_full = 1;
|
|
ms->buffer_used = 0;
|
|
}
|
|
}
|
|
|
|
|
|
bytes_filled = sizeof(sample) * ms->samples_filled * channels_per_frame;
|
|
/* feed new raw data to the decoder if needed, copy decoded results to frame buffer output */
|
|
if (!ms->buffer_used) {
|
|
//;VGM_LOG("MPEG: feed new data and get samples\n");
|
|
rc = mpg123_decode(ms->m,
|
|
ms->buffer, ms->bytes_in_buffer,
|
|
(unsigned char*)ms->output_buffer + bytes_filled, ms->output_buffer_size - bytes_filled,
|
|
&bytes_done);
|
|
ms->buffer_used = 1;
|
|
}
|
|
else {
|
|
//;VGM_LOG("MPEG: get samples from old data\n");
|
|
rc = mpg123_decode(ms->m,
|
|
NULL, 0,
|
|
(unsigned char*)ms->output_buffer + bytes_filled, ms->output_buffer_size - bytes_filled,
|
|
&bytes_done);
|
|
}
|
|
samples_filled = (bytes_done / sizeof(sample) / channels_per_frame);
|
|
|
|
/* discard for weird features (EALayer3 and PCM blocks, AWC and repeated frames) */
|
|
if (ms->decode_to_discard) {
|
|
size_t bytes_to_discard = 0;
|
|
size_t decode_to_discard = ms->decode_to_discard;
|
|
if (decode_to_discard > samples_filled)
|
|
decode_to_discard = samples_filled;
|
|
bytes_to_discard = sizeof(sample) * decode_to_discard * channels_per_frame;
|
|
|
|
bytes_done -= bytes_to_discard;
|
|
ms->decode_to_discard -= decode_to_discard;
|
|
ms->samples_used += decode_to_discard;
|
|
}
|
|
|
|
/* if no decoding was done bytes_done will be zero */
|
|
ms->samples_filled += samples_filled;
|
|
|
|
/* not enough raw data, set flag to request more next time
|
|
* (but only with empty mpg123 buffer, EA blocks wait for all samples decoded before advancing blocks) */
|
|
if (!bytes_done && rc == MPG123_NEED_MORE) {
|
|
//;VGM_LOG("MPEG: need more raw data to get samples (bytes_done=%x)\n", bytes_done);
|
|
ms->buffer_full = 0;
|
|
}
|
|
|
|
|
|
//;VGM_LOG("MPEG: stream samples now=%i, filled=%i)\n\n", ms->samples_filled, samples_filled);
|
|
return;
|
|
|
|
decode_fail:
|
|
/* 0-fill but continue with other streams */
|
|
bytes_filled = ms->samples_filled * channels_per_frame * sizeof(sample);
|
|
memset(ms->output_buffer + bytes_filled, 0, ms->output_buffer_size - bytes_filled);
|
|
ms->samples_filled = (ms->output_buffer_size / channels_per_frame / sizeof(sample));
|
|
}
|
|
|
|
|
|
/*********/
|
|
/* UTILS */
|
|
/*********/
|
|
|
|
void free_mpeg(mpeg_codec_data* data) {
|
|
if (!data)
|
|
return;
|
|
|
|
if (!data->custom) {
|
|
mpg123_delete(data->m);
|
|
}
|
|
else {
|
|
int i;
|
|
for (i=0; i < data->streams_size; i++) {
|
|
mpg123_delete(data->streams[i]->m);
|
|
free(data->streams[i]->buffer);
|
|
free(data->streams[i]->output_buffer);
|
|
free(data->streams[i]);
|
|
}
|
|
free(data->streams);
|
|
}
|
|
|
|
free(data->buffer);
|
|
free(data);
|
|
|
|
/* The astute reader will note that a call to mpg123_exit is never
|
|
* 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. */
|
|
}
|
|
|
|
/* seeks stream to 0 */
|
|
void reset_mpeg(mpeg_codec_data* data) {
|
|
if (!data) return;
|
|
|
|
flush_mpeg(data);
|
|
|
|
#if 0
|
|
/* flush_mpeg properly resets mpg123 with mpg123_open_feed, and
|
|
* offsets are reset in the VGMSTREAM externally, but for posterity: */
|
|
if (!data->custom) {
|
|
off_t input_offset = 0;
|
|
mpg123_feedseek(data->m,0,SEEK_SET,&input_offset);
|
|
}
|
|
else {
|
|
off_t input_offset = 0;
|
|
int i;
|
|
for (i = 0; i < data->streams_size; i++) {
|
|
mpg123_feedseek(data->streams[i]->m,0,SEEK_SET,&input_offset);
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/* seeks to a point */
|
|
void seek_mpeg(VGMSTREAM* vgmstream, int32_t num_sample) {
|
|
mpeg_codec_data* data = vgmstream->codec_data;
|
|
if (!data) return;
|
|
|
|
|
|
if (!data->custom) {
|
|
off_t input_offset = 0;
|
|
|
|
mpg123_feedseek(data->m, num_sample,SEEK_SET,&input_offset);
|
|
|
|
/* adjust loop with mpg123's offset (useful?) */
|
|
if (vgmstream->loop_ch)
|
|
vgmstream->loop_ch[0].offset = vgmstream->loop_ch[0].channel_start_offset + input_offset;
|
|
}
|
|
else {
|
|
int i;
|
|
|
|
flush_mpeg(data);
|
|
|
|
/* restart from 0 and manually discard samples, since we don't really know the correct offset */
|
|
for (i = 0; i < data->streams_size; i++) {
|
|
//mpg123_feedseek(data->streams[i]->m,0,SEEK_SET,&input_offset); /* already reset */
|
|
|
|
/* force first offset as discard-looping needs to start from the beginning */
|
|
if (vgmstream->loop_ch)
|
|
vgmstream->loop_ch[i].offset = vgmstream->loop_ch[i].channel_start_offset;
|
|
}
|
|
|
|
data->samples_to_discard += num_sample;
|
|
}
|
|
}
|
|
|
|
/* resets mpg123 decoder and its internals without seeking, useful when a new MPEG substream starts */
|
|
void flush_mpeg(mpeg_codec_data* data) {
|
|
if (!data)
|
|
return;
|
|
|
|
if (!data->custom) {
|
|
/* input_offset is ignored as we can assume it will be 0 for a seek to sample 0 */
|
|
mpg123_open_feed(data->m); /* mpg123_feedseek won't work */
|
|
}
|
|
else {
|
|
int i;
|
|
/* re-start from 0 */
|
|
for (i=0; i < data->streams_size; i++) {
|
|
mpg123_open_feed(data->streams[i]->m);
|
|
data->streams[i]->bytes_in_buffer = 0;
|
|
data->streams[i]->buffer_full = 0;
|
|
data->streams[i]->buffer_used = 0;
|
|
data->streams[i]->samples_filled = 0;
|
|
data->streams[i]->samples_used = 0;
|
|
data->streams[i]->current_size_count = 0;
|
|
data->streams[i]->current_size_target = 0;
|
|
data->streams[i]->decode_to_discard = 0;
|
|
}
|
|
|
|
data->samples_to_discard = data->skip_samples;
|
|
}
|
|
|
|
data->bytes_in_buffer = 0;
|
|
data->buffer_full = 0;
|
|
data->buffer_used = 0;
|
|
}
|
|
|
|
int mpeg_get_sample_rate(mpeg_codec_data* data) {
|
|
return data->sample_rate_per_frame;
|
|
}
|
|
|
|
long mpeg_bytes_to_samples(long bytes, const mpeg_codec_data* data) {
|
|
/* if not found just return 0 and expect to fail (if used for num_samples) */
|
|
if (!data->custom) {
|
|
/* We would need to read all VBR frames headers to count samples */
|
|
if (data->mi.vbr != MPG123_CBR) { //maybe abr_rate could be used to get an approx
|
|
VGM_LOG("MPEG: vbr mp3 can't do bytes_to_samples\n");
|
|
return 0;
|
|
}
|
|
|
|
return (int64_t)bytes * data->mi.rate * 8 / (data->mi.bitrate * 1000);
|
|
}
|
|
else {
|
|
/* needed for SCD */
|
|
if (data->streams_size && data->bitrate_per_frame) {
|
|
return (int64_t)(bytes / data->streams_size) * data->sample_rate_per_frame * 8 / (data->bitrate_per_frame * 1000);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
#if 0
|
|
/* disables/enables stderr output, for MPEG known to contain recoverable errors */
|
|
void mpeg_set_error_logging(mpeg_codec_data* data, int enable) {
|
|
if (!data->custom) {
|
|
mpg123_param(data->m, MPG123_ADD_FLAGS, MPG123_QUIET, !enable);
|
|
}
|
|
else {
|
|
int i;
|
|
for (i=0; i < data->streams_size; i++) {
|
|
mpg123_param(data->streams[i]->m, MPG123_ADD_FLAGS, MPG123_QUIET, !enable);
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
#endif
|