vgmstream/src/coding/acm_decoder.c

796 lines
17 KiB
C
Raw Normal View History

/*
* ACM decoder.
*
* Copyright (c) 2004-2008, Marko Kreen
* Copyright (c) 2008, Adam Gashlin
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "../vgmstream.h"
#include "../streamtypes.h"
#include "../streamfile.h"
#include "acm_decoder.h"
#define ACM_BUFLEN (64*1024)
typedef int (*filler_t)(ACMStream *acm, int ind, int n_col);
/**************************************
* Stream processing
**************************************/
/* NB: bits <= 31! Thus less checks in code. */
static int get_bits_reload(ACMStream *acm, int bits)
{
int got;
unsigned data, b_data, b_avail;
data = acm->bit_data;
got = acm->bit_avail;
bits -= got;
switch (acm->data_len - acm->buf_start_ofs) {
case 0:
return ACM_ERR_UNEXPECTED_EOF;
case 1:
b_data = read_8bit(acm->buf_start_ofs,acm->streamfile);
b_avail = 8;
acm->buf_start_ofs += 1;
break;
case 2:
b_data = read_16bitLE(acm->buf_start_ofs,acm->streamfile);
b_avail = 16;
acm->buf_start_ofs += 2;
break;
case 3:
b_data = read_8bit(acm->buf_start_ofs,acm->streamfile);
b_data |= (int32_t)read_16bitLE(acm->buf_start_ofs+1,acm->streamfile)<<8;
b_avail = 24;
acm->buf_start_ofs += 3;
case 4:
default:
/* shouldn't happen */
if (acm->data_len - acm->buf_start_ofs < 0)
return ACM_ERR_UNEXPECTED_EOF;
b_data = read_32bitLE(acm->buf_start_ofs,acm->streamfile);
b_avail = 32;
acm->buf_start_ofs += 4;
}
data |= (b_data & ((1 << bits) - 1)) << got;
acm->bit_data = b_data >> bits;
acm->bit_avail = b_avail - bits;
return data;
}
#define GET_BITS_NOERR(res, acm, bits) do { \
if (acm->bit_avail >= bits) { \
res = acm->bit_data & ((1 << bits) - 1); \
acm->bit_data >>= bits; \
acm->bit_avail -= bits; \
} else \
res = get_bits_reload(acm, bits); \
} while (0)
#define GET_BITS(res, acm, bits) do { \
GET_BITS_NOERR(res, acm, bits); \
if (res < 0) \
return res; \
} while (0)
#define GET_BITS_ERR_OUT(res, acm, bits) do { \
GET_BITS_NOERR(res, acm, bits); \
if (res < 0) \
goto err_out; \
} while (0)
/*************************************************
* Table filling
*************************************************/
static const int map_1bit[] = { -1, +1 };
static const int map_2bit_near[] = { -2, -1, +1, +2 };
static const int map_2bit_far[] = { -3, -2, +2, +3 };
static const int map_3bit[] = { -4, -3, -2, -1, +1, +2, +3, +4 };
static int mul_3x3[3*3*3];
static int mul_3x5[5*5*5];
static int mul_2x11[11*11];
static int tables_generated;
static void generate_tables()
{
int x1, x2, x3;
if (tables_generated)
return;
for (x3 = 0; x3 < 3; x3++)
for (x2 = 0; x2 < 3; x2++)
for (x1 = 0; x1 < 3; x1++)
mul_3x3[x1 + x2*3 + x3*3*3] =
x1 + (x2 << 4) + (x3 << 8);
for (x3 = 0; x3 < 5; x3++)
for (x2 = 0; x2 < 5; x2++)
for (x1 = 0; x1 < 5; x1++)
mul_3x5[x1 + x2*5 + x3*5*5] =
x1 + (x2 << 4) + (x3 << 8);
for (x2 = 0; x2 < 11; x2++)
for (x1 = 0; x1 < 11; x1++)
mul_2x11[x1 + x2*11] = x1 + (x2 << 4);
tables_generated = 1;
}
/* IOW: (r * acm->subblock_len) + c */
#define set_pos(acm, r, c, idx) do { \
int pos = (r << acm->info.acm_level) + c; \
acm->block[pos] = acm->midbuf[idx]; \
} while (0)
/************ Fillers **********/
static int f_zero(ACMStream *acm, int ind, int col)
{
int i;
for (i = 0; i < acm->info.acm_rows; i++)
set_pos(acm, i, col, 0);
return 1;
}
static int f_bad(ACMStream *acm, int ind, int col)
{
/* corrupt block? */
return ACM_ERR_CORRUPT;
}
static int f_linear(ACMStream *acm, int ind, int col)
{
int i, b;
int middle = 1 << (ind - 1);
for (i = 0; i < acm->info.acm_rows; i++) {
GET_BITS(b, acm, ind);
set_pos(acm, i, col, b - middle);
}
return 1;
}
static int f_k13(ACMStream *acm, int ind, int col)
{
int i, b;
for (i = 0; i < acm->info.acm_rows; i++) {
GET_BITS(b, acm, 1);
if (b == 0) {
/* 0 */
set_pos(acm, i++, col, 0);
if (i >= acm->info.acm_rows)
break;
set_pos(acm, i, col, 0);
continue;
}
GET_BITS(b, acm, 1);
if (b == 0) {
/* 1, 0 */
set_pos(acm, i, col, 0);
continue;
}
/* 1, 1, ? */
GET_BITS(b, acm, 1);
set_pos(acm, i, col, map_1bit[b]);
}
return 1;
}
static int f_k12(ACMStream *acm, int ind, int col)
{
int i, b;
for (i = 0; i < acm->info.acm_rows; i++) {
GET_BITS(b, acm, 1);
if (b == 0) {
/* 0 */
set_pos(acm, i, col, 0);
continue;
}
/* 1, ? */
GET_BITS(b, acm, 1);
set_pos(acm, i, col, map_1bit[b]);
}
return 1;
}
static int f_k24(ACMStream *acm, int ind, int col)
{
int i, b;
for (i = 0; i < acm->info.acm_rows; i++) {
GET_BITS(b, acm, 1);
if (b == 0) {
/* 0 */
set_pos(acm, i++, col, 0);
if (i >= acm->info.acm_rows) break;
set_pos(acm, i, col, 0);
continue;
}
GET_BITS(b, acm, 1);
if (b == 0) {
/* 1, 0 */
set_pos(acm, i, col, 0);
continue;
}
/* 1, 1, ?, ? */
GET_BITS(b, acm, 2);
set_pos(acm, i, col, map_2bit_near[b]);
}
return 1;
}
static int f_k23(ACMStream *acm, int ind, int col)
{
int i, b;
for (i = 0; i < acm->info.acm_rows; i++) {
GET_BITS(b, acm, 1);
if (b == 0) {
/* 0 */
set_pos(acm, i, col, 0);
continue;
}
/* 1, ?, ? */
GET_BITS(b, acm, 2);
set_pos(acm, i, col, map_2bit_near[b]);
}
return 1;
}
static int f_k35(ACMStream *acm, int ind, int col)
{
int i, b;
for (i = 0; i < acm->info.acm_rows; i++) {
GET_BITS(b, acm, 1);
if (b == 0) {
/* 0 */
set_pos(acm, i++, col, 0);
if (i >= acm->info.acm_rows)
break;
set_pos(acm, i, col, 0);
continue;
}
GET_BITS(b, acm, 1);
if (b == 0) {
/* 1, 0 */
set_pos(acm, i, col, 0);
continue;
}
GET_BITS(b, acm, 1);
if (b == 0) {
/* 1, 1, 0, ? */
GET_BITS(b, acm, 1);
set_pos(acm, i, col, map_1bit[b]);
continue;
}
/* 1, 1, 1, ?, ? */
GET_BITS(b, acm, 2);
set_pos(acm, i, col, map_2bit_far[b]);
}
return 1;
}
static int f_k34(ACMStream *acm, int ind, int col)
{
int i, b;
for (i = 0; i < acm->info.acm_rows; i++) {
GET_BITS(b, acm, 1);
if (b == 0) {
/* 0 */
set_pos(acm, i, col, 0);
continue;
}
GET_BITS(b, acm, 1);
if (b == 0) {
/* 1, 0, ? */
GET_BITS(b, acm, 1);
set_pos(acm, i, col, map_1bit[b]);
continue;
}
/* 1, 1, ?, ? */
GET_BITS(b, acm, 2);
set_pos(acm, i, col, map_2bit_far[b]);
}
return 1;
}
static int f_k45(ACMStream *acm, int ind, int col)
{
int i, b;
for (i = 0; i < acm->info.acm_rows; i++) {
GET_BITS(b, acm, 1);
if (b == 0) {
/* 0 */
set_pos(acm, i, col, 0); i++;
if (i >= acm->info.acm_rows)
break;
set_pos(acm, i, col, 0);
continue;
}
GET_BITS(b, acm, 1);
if (b == 0) {
/* 1, 0 */
set_pos(acm, i, col, 0);
continue;
}
/* 1, 1, ?, ?, ? */
GET_BITS(b, acm, 3);
set_pos(acm, i, col, map_3bit[b]);
}
return 1;
}
static int f_k44(ACMStream *acm, int ind, int col)
{
int i, b;
for (i = 0; i < acm->info.acm_rows; i++) {
GET_BITS(b, acm, 1);
if (b == 0) {
/* 0 */
set_pos(acm, i, col, 0);
continue;
}
/* 1, ?, ?, ? */
GET_BITS(b, acm, 3);
set_pos(acm, i, col, map_3bit[b]);
}
return 1;
}
static int f_t15(ACMStream *acm, int ind, int col)
{
int i, b, n1, n2, n3;
for (i = 0; i < acm->info.acm_rows; i++) {
/* b = (x1) + (x2 * 3) + (x3 * 9) */
GET_BITS(b, acm, 5);
n1 = (mul_3x3[b] & 0x0F) - 1;
n2 = ((mul_3x3[b] >> 4) & 0x0F) - 1;
n3 = ((mul_3x3[b] >> 8) & 0x0F) - 1;
set_pos(acm, i++, col, n1);
if (i >= acm->info.acm_rows)
break;
set_pos(acm, i++, col, n2);
if (i >= acm->info.acm_rows)
break;
set_pos(acm, i, col, n3);
}
return 1;
}
static int f_t27(ACMStream *acm, int ind, int col)
{
int i, b, n1, n2, n3;
for (i = 0; i < acm->info.acm_rows; i++) {
/* b = (x1) + (x2 * 5) + (x3 * 25) */
GET_BITS(b, acm, 7);
n1 = (mul_3x5[b] & 0x0F) - 2;
n2 = ((mul_3x5[b] >> 4) & 0x0F) - 2;
n3 = ((mul_3x5[b] >> 8) & 0x0F) - 2;
set_pos(acm, i++, col, n1);
if (i >= acm->info.acm_rows)
break;
set_pos(acm, i++, col, n2);
if (i >= acm->info.acm_rows)
break;
set_pos(acm, i, col, n3);
}
return 1;
}
static int f_t37(ACMStream *acm, int ind, int col)
{
int i, b, n1, n2;
for (i = 0; i < acm->info.acm_rows; i++) {
/* b = (x1) + (x2 * 11) */
GET_BITS(b, acm, 7);
n1 = (mul_2x11[b] & 0x0F) - 5;
n2 = ((mul_2x11[b] >> 4) & 0x0F) - 5;
set_pos(acm, i++, col, n1);
if (i >= acm->info.acm_rows)
break;
set_pos(acm, i, col, n2);
}
return 1;
}
/****************/
static filler_t filler_list[] = {
f_zero, f_bad, f_bad, f_linear,
f_linear, f_linear, f_linear, f_linear,
f_linear, f_linear, f_linear, f_linear,
f_linear, f_linear, f_linear, f_linear,
f_linear, f_k13, f_k12, f_t15,
f_k24, f_k23, f_t27, f_k35,
f_k34, f_bad, f_k45, f_k44,
f_bad, f_t37, f_bad, f_bad
};
static int fill_block(ACMStream *acm)
{
int i, err, ind;
for (i = 0; i < acm->info.acm_cols; i++) {
/* here eof => acm2wav */
GET_BITS_NOERR(ind, acm, 5);
if (ind < 0)
return -2; /* expected eof? */
err = filler_list[ind](acm, ind, i);
if (err < 0)
return err;
}
return 1;
}
/**********************************************
* Decompress code
**********************************************/
static void juggle(int *wrap_p, int *block_p, int sub_len, int sub_count)
{
int i, j, *p, r0, r1, r2, r3;
for (i = 0; i < sub_len; i++) {
p = block_p;
r0 = wrap_p[0];
r1 = wrap_p[1];
for (j = 0; j < sub_count/2; j++) {
r2 = *p; *p = r1*2 + (r0 + r2); p += sub_len;
r3 = *p; *p = r2*2 - (r1 + r3); p += sub_len;
r0 = r2; r1 = r3;
}
*wrap_p++ = r0;
*wrap_p++ = r1;
block_p++;
}
}
static void juggle_block(ACMStream *acm)
{
int sub_count, sub_len, todo_count, i;
int *wrap_p, *block_p, *p;
int step_subcount;
/* juggle only if subblock_len > 1 */
if (acm->info.acm_level == 0)
return;
/* 2048 / subblock_len */
step_subcount = (2048 >> acm->info.acm_level) - 2;
if (step_subcount < 1)
step_subcount = 1;
/* Apply juggle() (rows)x(cols)
* from (step_subcount * 2) x (subblock_len/2)
* to (step_subcount * subblock_len) x (1)
*/
todo_count = acm->info.acm_rows;
block_p = acm->block;
while (todo_count > 0) {
wrap_p = acm->wrapbuf;
sub_count = step_subcount;
if (sub_count > todo_count)
sub_count = todo_count;
sub_len = acm->info.acm_cols / 2;
sub_count *= 2;
juggle(wrap_p, block_p, sub_len, sub_count);
wrap_p += sub_len*2;
for (i = 0, p = block_p; i < sub_count; i++) {
p[0]++;
p += sub_len;
}
while (sub_len > 1) {
sub_len /= 2;
sub_count *= 2;
juggle(wrap_p, block_p, sub_len, sub_count);
wrap_p += sub_len*2;
}
todo_count -= step_subcount;
block_p += step_subcount << acm->info.acm_level;
}
}
/***************************************************************/
static int decode_block(ACMStream *acm)
{
int pwr, count, val, i, x, err;
acm->block_ready = 0;
acm->block_pos = 0;
/* read header */
GET_BITS(pwr, acm, 4); /* to_check? -> ret val */
GET_BITS(val, acm, 16);
count = 1 << pwr;
/* generate tables */
for (i = 0, x = 0; i < count; i++) {
acm->midbuf[i] = x;
x += val;
}
for (i = 1, x = -val; i <= count; i++) {
acm->midbuf[-i] = x;
x -= val;
}
/* to_check? */
if ((err = fill_block(acm)) <= 0)
return err;
juggle_block(acm);
acm->block_ready = 1;
return 1;
}
/******************************
* Output formats
******************************/
static char *out_s16le(int *src, char *dst, int n, int shift)
{
while (n--) {
int val = *src++ >> shift;
*dst++ = val & 0xFF;
*dst++ = (val >> 8) & 0xFF;
}
return dst;
}
static char *out_s16be(int *src, char *dst, int n, int shift)
{
while (n--) {
int val = *src++ >> shift;
*dst++ = (val >> 8) & 0xFF;
*dst++ = val & 0xFF;
}
return dst;
}
static char *out_u16le(int *src, char *dst, int n, int shift)
{
while (n--) {
int val = (*src++ >> shift) + 0x8000;
*dst++ = val & 0xFF;
*dst++ = (val >> 8) & 0xFF;
}
return dst;
}
static char *out_u16be(int *src, char *dst, int n, int shift)
{
while (n--) {
int val = (*src++ >> shift) + 0x8000;
*dst++ = (val >> 8) & 0xFF;
*dst++ = val & 0xFF;
}
return dst;
}
static int output_values(int *src, char *dst, int n,
int acm_level, int bigendianp, int wordlen, int sgned)
{
char *res = NULL;
if (wordlen == 2) {
if (bigendianp == 0) {
if (sgned)
res = out_s16le(src, dst, n, acm_level);
else
res = out_u16le(src, dst, n, acm_level);
} else {
if (sgned)
res = out_s16be(src, dst, n, acm_level);
else
res = out_u16be(src, dst, n, acm_level);
}
}
if (res != NULL)
return res - dst;
return ACM_ERR_BADFMT;
}
/***********************************************
* Public functions
***********************************************/
int acm_open_decoder(ACMStream **res, STREAMFILE *facilitator_file,
const char *const filename)
{
int err = ACM_ERR_OTHER, tmp;
ACMStream *acm;
acm = malloc(sizeof(*acm));
if (!acm)
return err;
memset(acm, 0, sizeof(*acm));
acm->streamfile = facilitator_file->open(facilitator_file,filename,
STREAMFILE_DEFAULT_BUFFER_SIZE);
if (!acm->streamfile)
{
err = ACM_ERR_OPEN;
goto err_out;
}
acm->data_len = get_streamfile_size(acm->streamfile);
/* read header */
err = ACM_ERR_NOT_ACM;
GET_BITS_ERR_OUT(acm->info.acm_id, acm, 24);
if (acm->info.acm_id != ACM_ID)
goto err_out;
GET_BITS_ERR_OUT(acm->info.acm_version, acm, 8);
if (acm->info.acm_version != 1)
goto err_out;
GET_BITS_ERR_OUT(acm->total_values, acm, 16);
GET_BITS_ERR_OUT(tmp, acm, 16);
acm->total_values += tmp << 16;
if (acm->total_values == 0)
goto err_out;
GET_BITS_ERR_OUT(acm->info.channels, acm, 16);
if (acm->info.channels < 1)
goto err_out;
GET_BITS_ERR_OUT(acm->info.rate, acm, 16);
GET_BITS_ERR_OUT(acm->info.acm_level, acm, 4);
GET_BITS_ERR_OUT(acm->info.acm_rows, acm, 12);
acm->info.acm_cols = 1 << acm->info.acm_level;
/* calculate blocks */
acm->wrapbuf_len = 2 * acm->info.acm_cols - 2;
acm->block_len = acm->info.acm_rows * acm->info.acm_cols;
/* allocate */
acm->block = malloc(acm->block_len * sizeof(int));
acm->wrapbuf = malloc(acm->wrapbuf_len * sizeof(int));
acm->ampbuf = malloc(0x10000 * sizeof(int));
acm->midbuf = acm->ampbuf + 0x8000;
memset(acm->wrapbuf, 0, acm->wrapbuf_len * sizeof(int));
generate_tables();
*res = acm;
return ACM_OK;
err_out:
/* dont let it close here */
acm_close(acm);
return err;
}
int acm_read(ACMStream *acm, char *dst, int numbytes,
int bigendianp, int wordlen, int sgned)
{
int avail, gotbytes = 0, err;
int *src, numwords;
if (wordlen == 2)
numwords = numbytes / 2;
else
return ACM_ERR_BADFMT;
if (acm->stream_pos >= acm->total_values)
return 0;
if (!acm->block_ready) {
err = decode_block(acm);
if (err == -2)
return 0;
if (err < 0)
return err;
}
/* check how many words can be read */
avail = acm->block_len - acm->block_pos;
if (avail < numwords)
numwords = avail;
if (acm->stream_pos + numwords > acm->total_values)
numwords = acm->total_values - acm->stream_pos;
if (acm->info.channels > 1)
numwords -= numwords % acm->info.channels;
/* convert, but if dst == NULL, simulate */
if (dst != NULL) {
src = acm->block + acm->block_pos;
gotbytes = output_values(src, dst, numwords,
acm->info.acm_level,
bigendianp, wordlen, sgned);
} else
gotbytes = numwords * wordlen;
if (gotbytes >= 0) {
acm->stream_pos += numwords;
acm->block_pos += numwords;
if (acm->block_pos == acm->block_len)
acm->block_ready = 0;
}
return gotbytes;
}
void decode_acm(mus_acm_codec_data * data, sample * outbuf,
int32_t samples_to_do, int channelspacing) {
int32_t samples_read = 0;
if (data->file_count == 1 && data->current_file == 0)
{
while (samples_read < samples_to_do) {
int32_t bytes_read_just_now;
bytes_read_just_now =
acm_read(data->files[0],(char*)(
outbuf+samples_read*channelspacing),
(samples_to_do-samples_read)*sizeof(sample)*
channelspacing,0,2,1);
if (bytes_read_just_now > 0) {
samples_read +=
bytes_read_just_now/sizeof(sample)/channelspacing;
} else {
return;
}
}
}
}
void acm_close(ACMStream *acm)
{
if (acm == NULL)
return;
if (acm->streamfile) {
close_streamfile(acm->streamfile);
}
if (acm->block)
free(acm->block);
if (acm->wrapbuf)
free(acm->wrapbuf);
if (acm->ampbuf)
free(acm->ampbuf);
free(acm);
}