vgmstream/src/coding/atrac9_decoder.c

222 lines
7.7 KiB
C
Raw Normal View History

#include "coding.h"
#ifdef VGM_USE_ATRAC9
#include "libatrac9.h"
atrac9_codec_data *init_atrac9(atrac9_config *cfg) {
int status;
uint8_t config_data[4];
Atrac9CodecInfo info = {0};
atrac9_codec_data *data = NULL;
data = calloc(1, sizeof(atrac9_codec_data));
data->handle = Atrac9GetHandle();
if (!data->handle) goto fail;
put_32bitBE(config_data, cfg->config_data);
status = Atrac9InitDecoder(data->handle, config_data);
if (status < 0) goto fail;
status = Atrac9GetCodecInfo(data->handle, &info);
if (status < 0) goto fail;
//;VGM_LOG("ATRAC9: config=%x, sf-size=%x, sub-frames=%i x %i samples\n", cfg->config_data, info.superframeSize, info.framesInSuperframe, info.frameSamples);
if (cfg->channels && cfg->channels != info.channels) {
VGM_LOG("ATRAC9: channels in header %i vs config %i don't match\n", cfg->channels, info.channels);
goto fail; /* unknown multichannel layout */
}
/* must hold at least one superframe and its samples */
data->data_buffer_size = info.superframeSize;
data->data_buffer = calloc(sizeof(uint8_t), data->data_buffer_size);
data->sample_buffer = calloc(sizeof(sample), info.channels * info.frameSamples * info.framesInSuperframe);
data->samples_to_discard = cfg->encoder_delay;
memcpy(&data->config, cfg, sizeof(atrac9_config));
return data;
fail:
return NULL;
}
void decode_atrac9(VGMSTREAM *vgmstream, sample * outbuf, int32_t samples_to_do, int channels) {
VGMSTREAMCHANNEL *stream = &vgmstream->ch[0];
atrac9_codec_data * data = vgmstream->codec_data;
int samples_done = 0;
while (samples_done < samples_to_do) {
if (data->samples_filled) { /* consume samples */
int samples_to_get = data->samples_filled;
if (data->samples_to_discard) {
/* discard samples for looping */
if (samples_to_get > data->samples_to_discard)
samples_to_get = data->samples_to_discard;
data->samples_to_discard -= samples_to_get;
}
else {
/* get max samples and copy */
if (samples_to_get > samples_to_do - samples_done)
samples_to_get = samples_to_do - samples_done;
memcpy(outbuf + samples_done*channels,
data->sample_buffer + data->samples_used*channels,
samples_to_get*channels * sizeof(sample));
samples_done += samples_to_get;
}
/* mark consumed samples */
data->samples_used += samples_to_get;
data->samples_filled -= samples_to_get;
}
else { /* decode data */
int iframe, status;
int bytes_used = 0;
uint8_t *buffer = data->data_buffer;
size_t bytes;
Atrac9CodecInfo info = {0};
data->samples_used = 0;
/* ATRAC9 is made of decodable superframes with several sub-frames. AT9 config data gives
* superframe size, number of frames and samples (~100-200 bytes and ~256/1024 samples). */
status = Atrac9GetCodecInfo(data->handle, &info);
if (status < 0) goto decode_fail;
/* preadjust */ //todo improve
switch(data->config.type) {
case ATRAC9_XVAG:
/* PS4 (ex. The Last of Us) has a RIFF AT9 (can be ignored) instead of the first superframe.
* As subsongs do too, needs to be skipped here instead of adjusting start_offset */
if (stream->offset == stream->channel_start_offset) {
if (read_32bitBE(stream->offset, stream->streamfile) == 0x00000000 /* padding before RIFF */
&& read_32bitBE(stream->offset + info.superframeSize - 0x08,stream->streamfile) == 0x64617461) { /* RIFF's "data" */
stream->offset += info.superframeSize;
}
}
break;
default:
break;
}
/* read one raw block (superframe) and advance offsets */
bytes = read_streamfile(data->data_buffer,stream->offset, info.superframeSize,stream->streamfile);
if (bytes != data->data_buffer_size) {
VGM_LOG("ATRAC9: read %x vs expected %x bytes at %lx\n", bytes, info.superframeSize, stream->offset);
goto decode_fail;
}
stream->offset += bytes;
/* postadjust */ //todo improve
switch(data->config.type) {
case ATRAC9_XVAG:
case ATRAC9_KMA9:
/* skip other subsong blocks */
if (data->config.interleave_skip && ((stream->offset - stream->channel_start_offset) % data->config.interleave_skip == 0)) {
stream->offset += data->config.interleave_skip * (data->config.subsong_skip - 1);
}
break;
default:
break;
}
/* decode all frames in the superframe block */
for (iframe = 0; iframe < info.framesInSuperframe; iframe++) {
status = Atrac9Decode(data->handle, buffer, data->sample_buffer + data->samples_filled*channels, &bytes_used);
if (status < 0) goto decode_fail;
buffer += bytes_used;
data->samples_filled += info.frameSamples;
}
}
}
return;
decode_fail:
/* on error just put some 0 samples */
VGM_LOG("ATRAC9: decode fail at %lx, missing %i samples\n", stream->offset, (samples_to_do - samples_done));
memset(outbuf + samples_done * channels, 0, (samples_to_do - samples_done) * sizeof(sample) * channels);
}
void reset_atrac9(VGMSTREAM *vgmstream) {
atrac9_codec_data *data = vgmstream->codec_data;
if (!data->handle)
goto fail;
#if 0
/* reopen/flush, not needed as superframes decode separatedly and there is no carried state */
{
int status;
uint8_t config_data[4];
Atrac9ReleaseHandle(data->handle);
data->handle = Atrac9GetHandle();
if (!data->handle) goto fail;
put_32bitBE(config_data, data->config.config_data);
status = Atrac9InitDecoder(data->handle, config_data);
if (status < 0) goto fail;
}
#endif
data->samples_used = 0;
data->samples_filled = 0;
data->samples_to_discard = 0;
return;
fail:
return; /* decode calls should fail... */
}
void seek_atrac9(VGMSTREAM *vgmstream, int32_t num_sample) {
atrac9_codec_data *data = vgmstream->codec_data;
reset_atrac9(vgmstream);
data->samples_to_discard = num_sample;
data->samples_to_discard += data->config.encoder_delay;
/* 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_atrac9(atrac9_codec_data *data) {
if (!data)
return;
if (data->handle) Atrac9ReleaseHandle(data->handle);
free(data->data_buffer);
free(data->sample_buffer);
free(data);
}
size_t atrac9_bytes_to_samples(size_t bytes, atrac9_codec_data *data) {
Atrac9CodecInfo info = {0};
int status;
status = Atrac9GetCodecInfo(data->handle, &info);
if (status < 0) goto fail;
return bytes / info.superframeSize * (info.frameSamples * info.framesInSuperframe);
fail:
return 0;
}
#endif