vgmstream/src/coding/speex_decoder.c

189 lines
4.8 KiB
C
Raw Normal View History

2021-01-12 15:11:24 +01:00
#include "coding.h"
#include "coding_utils_samples.h"
#ifdef VGM_USE_SPEEX
#include "speex/speex.h"
#define SPEEX_MAX_FRAME_SIZE 0x100 /* frame sizes are stored in a byte */
#define SPEEX_MAX_FRAME_SAMPLES 640 /* nb=160, wb/uwb=320*2 */
#define SPEEX_CTL_OK 0 /* -1=request unknown, -2=invalid param */
#define SPEEX_DECODE_OK 0 /* -1 for end of stream, -2 corrupt stream */
/* opaque struct */
struct speex_codec_data {
/* config */
int channels;
int samples_discard;
int encoder_delay;
uint8_t buf[SPEEX_MAX_FRAME_SIZE];
uint8_t frame_size;
int16_t* samples;
int frame_samples;
/* frame state */
s16buf_t sbuf;
void* state;
SpeexBits bits;
};
/* raw SPEEX */
speex_codec_data* init_speex_ea(int channels) {
int res, sample_rate;
speex_codec_data* data = NULL;
data = calloc(1, sizeof(speex_codec_data));
if (!data) goto fail;
//TODO: EA uses N decoders, unknown layout (known samples are mono)
data->channels = channels;
if (channels != 1)
goto fail;
/* Modes: narrowband=nb, wideband=wb, ultrawideband=uwb modes.
* EASpeex seem to always use uwb so use that for now until config is needed.
* Examples normally use &speex_*_mode but export seem problematic? */
data->state = speex_decoder_init(speex_lib_get_mode(SPEEX_MODEID_UWB));
if (!data->state) goto fail;
speex_bits_init(&data->bits);
res = speex_decoder_ctl(data->state, SPEEX_GET_FRAME_SIZE, &data->frame_samples);
if (res != SPEEX_CTL_OK) goto fail;
if (data->frame_samples > SPEEX_MAX_FRAME_SAMPLES)
goto fail;
/* forced in EA's code, doesn't seem to affect decoding (all EAAC headers use this rate too) */
sample_rate = 32000;
res = speex_decoder_ctl(data->state, SPEEX_SET_SAMPLING_RATE, &sample_rate);
if (res != SPEEX_CTL_OK) goto fail;
/* default "latency" for EASpeex */
data->encoder_delay = 509;
data->samples_discard = data->encoder_delay;
data->samples = malloc(channels * data->frame_samples * sizeof(int16_t));
if (!data->samples) goto fail;
return data;
fail:
free_speex(data);
return NULL;
}
static int decode_frame(speex_codec_data* data) {
int res;
data->sbuf.samples = data->samples;
data->sbuf.channels = 1;
data->sbuf.filled = 0;
speex_bits_read_from(&data->bits, (const char*)data->buf, data->frame_size);
res = speex_decode_int(data->state, &data->bits, data->sbuf.samples);
if (res != SPEEX_DECODE_OK) goto fail;
data->sbuf.filled = data->frame_samples;
return 1;
fail:
return 0;
}
/* for simple style speex (seen in EA-Speex and libspeex's sampledec.c) */
static int read_frame(speex_codec_data* data, VGMSTREAMCHANNEL* stream) {
uint8_t bytes;
data->frame_size = read_u8(stream->offset, stream->streamfile);
stream->offset += 0x01;
if (data->frame_size == 0) goto fail;
bytes = read_streamfile(data->buf, stream->offset, data->frame_size, stream->streamfile);
stream->offset += data->frame_size;
if (bytes != data->frame_size) goto fail;
return 1;
fail:
return 0;
}
void decode_speex(VGMSTREAM* vgmstream, sample_t* outbuf, int32_t samples_to_do) {
VGMSTREAMCHANNEL* stream = &vgmstream->ch[0];
speex_codec_data* data = vgmstream->codec_data;
int ok;
while (samples_to_do > 0) {
s16buf_t* sbuf = &data->sbuf;
if (sbuf->filled <= 0) {
ok = read_frame(data, stream);
if (!ok) goto fail;
ok = decode_frame(data);
if (!ok) goto fail;
}
if (data->samples_discard)
s16buf_discard(&outbuf, sbuf, &data->samples_discard);
else
s16buf_consume(&outbuf, sbuf, &samples_to_do);
}
return;
fail:
/* on error just put some 0 samples */
VGM_LOG("SPEEX: decode fail at %x, missing %i samples\n", (uint32_t)stream->offset, samples_to_do);
s16buf_silence(&outbuf, &samples_to_do, data->channels);
}
void reset_speex(speex_codec_data* data) {
int res;
if (!data) return;
res = speex_decoder_ctl(data->state, SPEEX_RESET_STATE, NULL);
if (res != SPEEX_CTL_OK) goto fail;
data->sbuf.filled = 0;
data->samples_discard = data->encoder_delay;
return;
fail:
return; /* ? */
}
void seek_speex(VGMSTREAM* vgmstream, int32_t num_sample) {
speex_codec_data* data = vgmstream->codec_data;
if (!data) return;
reset_speex(data);
data->samples_discard += num_sample;
/* loop offsets are set during decode; force them to stream start so discard works */
if (vgmstream->loop_ch)
vgmstream->loop_ch[0].offset = vgmstream->loop_ch[0].channel_start_offset;
}
void free_speex(speex_codec_data* data) {
if (!data)
return;
speex_decoder_destroy(data->state);
speex_bits_destroy(&data->bits);
free(data->samples);
free(data);
}
#endif