2018-01-04 17:46:38 +01:00
|
|
|
#include "coding.h"
|
|
|
|
|
|
|
|
#ifdef VGM_USE_ATRAC9
|
|
|
|
#include "libatrac9.h"
|
|
|
|
|
|
|
|
|
2018-08-25 17:48:01 +02:00
|
|
|
/* opaque struct */
|
|
|
|
struct atrac9_codec_data {
|
|
|
|
uint8_t *data_buffer;
|
|
|
|
size_t data_buffer_size;
|
|
|
|
|
|
|
|
sample *sample_buffer;
|
|
|
|
size_t samples_filled; /* number of samples in the buffer */
|
|
|
|
size_t samples_used; /* number of samples extracted from the buffer */
|
|
|
|
|
|
|
|
int samples_to_discard;
|
|
|
|
|
|
|
|
atrac9_config config;
|
2018-09-02 21:02:11 +02:00
|
|
|
|
2018-08-25 17:48:01 +02:00
|
|
|
void *handle; /* decoder handle */
|
2018-09-02 21:02:11 +02:00
|
|
|
Atrac9CodecInfo info; /* decoder info */
|
2018-08-25 17:48:01 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
2018-01-04 17:46:38 +01:00
|
|
|
atrac9_codec_data *init_atrac9(atrac9_config *cfg) {
|
|
|
|
int status;
|
|
|
|
uint8_t config_data[4];
|
|
|
|
atrac9_codec_data *data = NULL;
|
|
|
|
|
|
|
|
data = calloc(1, sizeof(atrac9_codec_data));
|
2018-08-11 17:54:08 +02:00
|
|
|
if (!data) goto fail;
|
2018-01-04 17:46:38 +01:00
|
|
|
|
|
|
|
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;
|
|
|
|
|
2018-09-02 21:02:11 +02:00
|
|
|
status = Atrac9GetCodecInfo(data->handle, &data->info);
|
2018-01-04 17:46:38 +01:00
|
|
|
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);
|
|
|
|
|
2018-09-02 21:02:11 +02:00
|
|
|
if (cfg->channels && cfg->channels != data->info.channels) {
|
|
|
|
VGM_LOG("ATRAC9: channels in header %i vs config %i don't match\n", cfg->channels, data->info.channels);
|
2018-01-04 17:46:38 +01:00
|
|
|
goto fail; /* unknown multichannel layout */
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* must hold at least one superframe and its samples */
|
2018-09-02 21:02:11 +02:00
|
|
|
data->data_buffer_size = data->info.superframeSize;
|
2018-01-04 17:46:38 +01:00
|
|
|
data->data_buffer = calloc(sizeof(uint8_t), data->data_buffer_size);
|
2018-09-02 21:02:11 +02:00
|
|
|
data->sample_buffer = calloc(sizeof(sample), data->info.channels * data->info.frameSamples * data->info.framesInSuperframe);
|
2018-01-04 17:46:38 +01:00
|
|
|
|
|
|
|
data->samples_to_discard = cfg->encoder_delay;
|
|
|
|
|
|
|
|
memcpy(&data->config, cfg, sizeof(atrac9_config));
|
|
|
|
|
|
|
|
return data;
|
|
|
|
|
|
|
|
fail:
|
2018-08-11 17:54:08 +02:00
|
|
|
free_atrac9(data);
|
2018-01-04 17:46:38 +01:00
|
|
|
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;
|
|
|
|
|
|
|
|
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). */
|
|
|
|
|
|
|
|
/* read one raw block (superframe) and advance offsets */
|
2018-09-02 21:02:11 +02:00
|
|
|
bytes = read_streamfile(data->data_buffer,stream->offset, data->info.superframeSize,stream->streamfile);
|
2018-09-23 03:01:13 +02:00
|
|
|
if (bytes != data->data_buffer_size) goto decode_fail;
|
2018-01-04 17:46:38 +01:00
|
|
|
|
|
|
|
stream->offset += bytes;
|
|
|
|
|
|
|
|
/* decode all frames in the superframe block */
|
2018-09-02 21:02:11 +02:00
|
|
|
for (iframe = 0; iframe < data->info.framesInSuperframe; iframe++) {
|
2018-01-04 17:46:38 +01:00
|
|
|
status = Atrac9Decode(data->handle, buffer, data->sample_buffer + data->samples_filled*channels, &bytes_used);
|
|
|
|
if (status < 0) goto decode_fail;
|
|
|
|
|
|
|
|
buffer += bytes_used;
|
2018-09-02 21:02:11 +02:00
|
|
|
data->samples_filled += data->info.frameSamples;
|
2018-01-04 17:46:38 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
decode_fail:
|
|
|
|
/* on error just put some 0 samples */
|
2018-09-23 03:01:13 +02:00
|
|
|
VGM_LOG("ATRAC9: decode fail at %"PRIx64", missing %i samples\n", (off64_t)stream->offset, (samples_to_do - samples_done));
|
2018-01-04 17:46:38 +01:00
|
|
|
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;
|
2018-03-10 16:59:00 +01:00
|
|
|
if (!data) return;
|
2018-01-04 17:46:38 +01:00
|
|
|
|
|
|
|
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;
|
2018-09-02 21:02:11 +02:00
|
|
|
data->samples_to_discard = data->config.encoder_delay;
|
2018-01-04 17:46:38 +01:00
|
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
fail:
|
|
|
|
return; /* decode calls should fail... */
|
|
|
|
}
|
|
|
|
|
|
|
|
void seek_atrac9(VGMSTREAM *vgmstream, int32_t num_sample) {
|
|
|
|
atrac9_codec_data *data = vgmstream->codec_data;
|
2018-03-10 16:59:00 +01:00
|
|
|
if (!data) return;
|
2018-01-04 17:46:38 +01:00
|
|
|
|
|
|
|
reset_atrac9(vgmstream);
|
|
|
|
|
2018-09-02 21:02:11 +02:00
|
|
|
/* find closest offset to desired sample, and samples to discard after that offset to reach loop */
|
|
|
|
{
|
|
|
|
int32_t seek_sample = data->config.encoder_delay + num_sample;
|
|
|
|
off_t seek_offset;
|
|
|
|
int32_t seek_discard;
|
|
|
|
int32_t superframe_samples = data->info.frameSamples * data->info.framesInSuperframe;
|
|
|
|
size_t superframe_number, superframe_back;
|
|
|
|
|
|
|
|
superframe_number = (seek_sample / superframe_samples); /* closest */
|
|
|
|
|
|
|
|
/* decoded frames affect each other slightly, so move offset back to make PCM stable
|
|
|
|
* and equivalent to a full discard loop */
|
|
|
|
superframe_back = 1; /* 1 seems enough (even when only 1 subframe in superframe) */
|
|
|
|
if (superframe_back > superframe_number)
|
|
|
|
superframe_back = superframe_number;
|
|
|
|
|
|
|
|
seek_discard = (seek_sample % superframe_samples) + (superframe_back * superframe_samples);
|
|
|
|
seek_offset = (superframe_number - superframe_back) * data->info.superframeSize;
|
|
|
|
|
|
|
|
data->samples_to_discard = seek_discard; /* already includes encoder delay */
|
|
|
|
|
|
|
|
if (vgmstream->loop_ch)
|
|
|
|
vgmstream->loop_ch[0].offset = vgmstream->loop_ch[0].channel_start_offset + seek_offset;
|
|
|
|
}
|
|
|
|
|
|
|
|
#if 0
|
|
|
|
//old full discard loop
|
|
|
|
{
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
#endif
|
2018-01-04 17:46:38 +01:00
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
void free_atrac9(atrac9_codec_data *data) {
|
2018-03-10 16:59:00 +01:00
|
|
|
if (!data) return;
|
2018-01-04 17:46:38 +01:00
|
|
|
|
|
|
|
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) {
|
2018-09-02 21:02:11 +02:00
|
|
|
return bytes / data->info.superframeSize * (data->info.frameSamples * data->info.framesInSuperframe);
|
2018-01-04 17:46:38 +01:00
|
|
|
}
|
2018-08-15 16:42:34 +02:00
|
|
|
|
2018-08-25 18:03:58 +02:00
|
|
|
#if 0 //not needed (for now)
|
2018-08-15 16:42:34 +02:00
|
|
|
int atrac9_parse_config(uint32_t atrac9_config, int *out_sample_rate, int *out_channels, size_t *out_frame_size) {
|
|
|
|
static const int sample_rate_table[16] = {
|
|
|
|
11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000,
|
|
|
|
44100, 48000, 64000, 88200, 96000,128000,176400,192000
|
|
|
|
};
|
|
|
|
static const int channel_table[8] = {
|
|
|
|
1, 2, 2, 6, 8, 4, 0, 0
|
|
|
|
};
|
|
|
|
|
|
|
|
uint32_t sync = (atrac9_config >> 24) & 0xff; /* 8b */
|
|
|
|
uint8_t sample_rate_index = (atrac9_config >> 20) & 0x0f; /* 4b */
|
|
|
|
uint8_t channels_index = (atrac9_config >> 17) & 0x07; /* 3b */
|
|
|
|
/* uint8_t validation bit = (atrac9_config >> 16) & 0x01; */ /* 1b */
|
|
|
|
size_t frame_size = (atrac9_config >> 5) & 0x7FF; /* 11b */
|
|
|
|
size_t superframe_index = (atrac9_config >> 3) & 0x3; /* 2b */
|
|
|
|
/* uint8_t unused = (atrac9_config >> 0) & 0x7);*/ /* 3b */
|
|
|
|
|
|
|
|
if (sync != 0xFE)
|
|
|
|
goto fail;
|
|
|
|
if (out_sample_rate)
|
|
|
|
*out_sample_rate = sample_rate_table[sample_rate_index];
|
|
|
|
if (out_channels)
|
|
|
|
*out_channels = channel_table[channels_index];
|
|
|
|
if (out_frame_size)
|
|
|
|
*out_frame_size = (frame_size+1) * (1 << superframe_index);
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
fail:
|
|
|
|
return 0;
|
|
|
|
}
|
2018-08-25 18:03:58 +02:00
|
|
|
#endif
|
2018-01-04 17:46:38 +01:00
|
|
|
#endif
|