From a14cd4082d8a65908b6a6345ab00dfaba7f215e4 Mon Sep 17 00:00:00 2001 From: halleyscometsw Date: Sun, 20 Jul 2008 05:41:41 +0000 Subject: [PATCH] acm that at least builds git-svn-id: https://vgmstream.svn.sourceforge.net/svnroot/vgmstream@348 51a99a44-fe44-0410-b1ba-c3e57ba2b86b --- COPYING.libacm | 19 + src/Makefile | 6 +- src/coding/Makefile.unix.am | 2 +- src/coding/acm_decoder.c | 782 ++++++++++++++++++++++++++++++++++++ src/coding/acm_decoder.h | 100 +++++ src/coding/coding.h | 3 + src/meta/Makefile.unix.am | 1 + src/meta/acm.c | 65 +++ src/meta/meta.h | 2 + src/meta/nwa.c | 1 + src/vgmstream.c | 43 +- src/vgmstream.h | 14 + 12 files changed, 1034 insertions(+), 4 deletions(-) create mode 100644 COPYING.libacm create mode 100644 src/coding/acm_decoder.c create mode 100644 src/coding/acm_decoder.h create mode 100644 src/meta/acm.c diff --git a/COPYING.libacm b/COPYING.libacm new file mode 100644 index 00000000..2cceb5ae --- /dev/null +++ b/COPYING.libacm @@ -0,0 +1,19 @@ + +Starting from version 0.9.2, the libacm core code is licensed under +minimal BSD/ISC license. (Core code = everything except plugins.) + +------------------------------------------------------------------------- + +Copyright (c) 2004-2008, Marko Kreen + +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. diff --git a/src/Makefile b/src/Makefile index e4f8a9bf..96498b1c 100644 --- a/src/Makefile +++ b/src/Makefile @@ -11,7 +11,8 @@ CODING_OBJS=coding/adx_decoder.o \ coding/ogg_vorbis_decoder.o \ coding/sdx2_decoder.o \ coding/ws_decoder.o \ - coding/mpeg_decoder.o + coding/mpeg_decoder.o \ + coding/acm_decoder.o LAYOUT_OBJS=layout/ast_blocked.o \ layout/blocked.o \ @@ -92,7 +93,8 @@ META_OBJS=meta/adx_header.o \ meta/sat_dvi.o \ meta/ps2_bg00.o \ meta/dc_kcey.o \ - meta/ps2_rstm.o + meta/ps2_rstm.o \ + meta/acm.o OBJECTS=vgmstream.o streamfile.o util.o $(CODING_OBJS) $(LAYOUT_OBJS) $(META_OBJS) diff --git a/src/coding/Makefile.unix.am b/src/coding/Makefile.unix.am index bbc69e01..aaf63cb1 100644 --- a/src/coding/Makefile.unix.am +++ b/src/coding/Makefile.unix.am @@ -4,6 +4,6 @@ AM_CFLAGS = -Wall @CFLAGS@ -I$(top_builddir) -I$(top_srcdir) AM_MAKEFLAGS=-f Makefile.unix libcoding_la_LDFLAGS = -libcoding_la_SOURCES = adx_decoder.c eaxa_decoder.c g721_decoder.c ima_decoder.c ngc_afc_decoder.c ngc_dsp_decoder.c ngc_dtk_decoder.c pcm_decoder.c psx_decoder.c xa_decoder.c ogg_vorbis_decoder.c sdx2_decoder.c ws_decoder.c mpeg_decoder.c +libcoding_la_SOURCES = adx_decoder.c eaxa_decoder.c g721_decoder.c ima_decoder.c ngc_afc_decoder.c ngc_dsp_decoder.c ngc_dtk_decoder.c pcm_decoder.c psx_decoder.c xa_decoder.c ogg_vorbis_decoder.c sdx2_decoder.c ws_decoder.c mpeg_decoder.c acm_decoder.c EXTRA_DIST = coding.h g72x_state.h diff --git a/src/coding/acm_decoder.c b/src/coding/acm_decoder.c new file mode 100644 index 00000000..963621e0 --- /dev/null +++ b/src/coding/acm_decoder.c @@ -0,0 +1,782 @@ +/* + * 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 +#include +#include + +#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) { + if (data->file_count == 1 && data->current_file == 0) + { + acm_read(data->files[0],(char*)outbuf,samples_to_do*sizeof(sample)* + channelspacing, + 0,2,1); + } +} + +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); +} + diff --git a/src/coding/acm_decoder.h b/src/coding/acm_decoder.h new file mode 100644 index 00000000..2d198301 --- /dev/null +++ b/src/coding/acm_decoder.h @@ -0,0 +1,100 @@ +/* + * libacm - Interplay ACM audio decoder. + * + * Copyright (c) 2004-2008, Marko Kreen + * + * 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. + */ + +#ifndef __LIBACM_H +#define __LIBACM_H + +#include "../streamfile.h" + +#define LIBACM_VERSION "0.9.2" + +#define ACM_ID 0x032897 +#define ACM_WORD 2 + +#define ACM_OK 0 +#define ACM_ERR_OTHER -1 +#define ACM_ERR_OPEN -2 +#define ACM_ERR_NOT_ACM -3 +#define ACM_ERR_READ_ERR -4 +#define ACM_ERR_BADFMT -5 +#define ACM_ERR_CORRUPT -6 +#define ACM_ERR_UNEXPECTED_EOF -7 +#define ACM_ERR_NOT_SEEKABLE -8 + +typedef struct ACMInfo { + int channels; + int rate; + int acm_id; + int acm_version; + int acm_level; + int acm_cols; /* 1 << acm_level */ + int acm_rows; +} ACMInfo; + +struct ACMStream { + ACMInfo info; + int total_values; + + /* acm data stream */ + STREAMFILE *streamfile; + int data_len; + + /* acm stream buffer */ + int bit_avail; + unsigned bit_data; + unsigned buf_start_ofs; + + /* block lengths (in samples) */ + int block_len; + int wrapbuf_len; + /* buffers */ + int *block; + int *wrapbuf; + int *ampbuf; + int *midbuf; /* pointer into ampbuf */ + /* result */ + int block_ready; + int stream_pos; /* in words. absolute */ + int block_pos; /* in words, relative */ +}; +typedef struct ACMStream ACMStream; + +/* decode.c */ +int acm_open_decoder(ACMStream **res, STREAMFILE *facilitator_file, const char *const filename); +int acm_read(ACMStream *acm, char *buf, int nbytes, + int bigendianp, int wordlen, int sgned); +void acm_close(ACMStream *acm); + +/* util.c */ +const ACMInfo *acm_info(ACMStream *acm); +int acm_seekable(ACMStream *acm); +int acm_bitrate(ACMStream *acm); +int acm_raw_total(ACMStream *acm); +int acm_raw_tell(ACMStream *acm); +int acm_pcm_total(ACMStream *acm); +int acm_pcm_tell(ACMStream *acm); +int acm_time_total(ACMStream *acm); +int acm_time_tell(ACMStream *acm); +int acm_read_loop(ACMStream *acm, char *dst, int len, + int bigendianp, int wordlen, int sgned); +int acm_seek_pcm(ACMStream *acm, int pcm_pos); +int acm_seek_time(ACMStream *acm, int pos_ms); +const char *acm_strerror(int err); + +#endif + diff --git a/src/coding/coding.h b/src/coding/coding.h index fbb6b756..8751a073 100644 --- a/src/coding/coding.h +++ b/src/coding/coding.h @@ -58,4 +58,7 @@ void decode_mpeg(VGMSTREAMCHANNEL * stream, sample * outbuf, int32_t samples_to_do, int channels); #endif +void decode_acm(mus_acm_codec_data * data, sample * outbuf, + int32_t samples_to_do, int channelspacing); + #endif diff --git a/src/meta/Makefile.unix.am b/src/meta/Makefile.unix.am index 75bfbefa..a21e55a1 100644 --- a/src/meta/Makefile.unix.am +++ b/src/meta/Makefile.unix.am @@ -71,5 +71,6 @@ libmeta_la_SOURCES += sat_dvi.c libmeta_la_SOURCES += ps2_bg00.c libmeta_la_SOURCES += dc_kcey.c libmeta_la_SOURCES += ps2_rstm.c +libmeta_la_SOURCES += acm.c EXTRA_DIST = meta.h diff --git a/src/meta/acm.c b/src/meta/acm.c new file mode 100644 index 00000000..ecf495ee --- /dev/null +++ b/src/meta/acm.c @@ -0,0 +1,65 @@ +#include "../vgmstream.h" +#include "meta.h" +#include "../util.h" +#include "../coding/acm_decoder.h" + +/* InterPlay ACM */ +/* The real work is done by libacm */ +VGMSTREAM * init_vgmstream_acm(STREAMFILE *streamFile) { + VGMSTREAM * vgmstream = NULL; + ACMStream *acm_stream = NULL; + mus_acm_codec_data *data; + + char filename[260]; + + int loop_flag = 0; + int channel_count; + + /* check extension, case insensitive */ + streamFile->get_name(streamFile,filename,sizeof(filename)); + if (strcasecmp("acm",filename_extension(filename))) goto fail; + + /* check header */ + if (read_32bitBE(0x0,streamFile) != 0x97280301) + goto fail; + + data = calloc(1,sizeof(mus_acm_codec_data)); + if (!data) goto fail; + + data->files = calloc(1,sizeof(ACMStream *)); + if (!data->files) { + free(data); data = NULL; + goto fail; + } + + /* gonna do this a little backwards, open and parse the file + before creating the vgmstream */ + + if (acm_open_decoder(&acm_stream,streamFile,filename) != ACM_OK) { + goto fail; + } + + channel_count = acm_stream->info.channels; + vgmstream = allocate_vgmstream(channel_count,loop_flag); + if (!vgmstream) goto fail; + + vgmstream->channels = channel_count; + vgmstream->sample_rate = acm_stream->info.rate; + vgmstream->coding_type = coding_ACM; + vgmstream->num_samples = acm_stream->total_values / acm_stream->info.channels; + vgmstream->layout_type = layout_acm; + vgmstream->meta_type = meta_ACM; + + data->file_count = 1; + data->current_file = 0; + data->files[0] = acm_stream; + + vgmstream->codec_data = data; + + return vgmstream; + + /* clean up anything we may have opened */ +fail: + if (vgmstream) close_vgmstream(vgmstream); + return NULL; +} diff --git a/src/meta/meta.h b/src/meta/meta.h index 62a6dd69..653b59f5 100644 --- a/src/meta/meta.h +++ b/src/meta/meta.h @@ -155,4 +155,6 @@ VGMSTREAM * init_vgmstream_kcey(STREAMFILE * streamFile); VGMSTREAM * init_vgmstream_ps2_rstm(STREAMFILE * streamFile); +VGMSTREAM * init_vgmstream_acm(STREAMFILE * streamFile); + #endif diff --git a/src/meta/nwa.c b/src/meta/nwa.c index 11c02e9a..e6a3ccad 100644 --- a/src/meta/nwa.c +++ b/src/meta/nwa.c @@ -1,6 +1,7 @@ #include "meta.h" #include "../util.h" #include +#include #ifdef WIN32 #define DIRSEP '\\' diff --git a/src/vgmstream.c b/src/vgmstream.c index ce9acf53..80988679 100644 --- a/src/vgmstream.c +++ b/src/vgmstream.c @@ -93,6 +93,7 @@ VGMSTREAM * (*init_vgmstream_fcns[])(STREAMFILE *streamFile) = { init_vgmstream_dvi, init_vgmstream_kcey, init_vgmstream_ps2_rstm, + init_vgmstream_acm, }; #define INIT_VGMSTREAM_FCNS (sizeof(init_vgmstream_fcns)/sizeof(init_vgmstream_fcns[0])) @@ -185,6 +186,8 @@ void reset_vgmstream(VGMSTREAM * vgmstream) { data->buffer_full = data->buffer_used = 0; } #endif + + /* TODO: reset ACM */ } /* simply allocate memory for the VGMSTREAM and its channels */ @@ -302,6 +305,28 @@ void close_vgmstream(VGMSTREAM * vgmstream) { } #endif + if (vgmstream->coding_type==coding_ACM) { + mus_acm_codec_data *data = vgmstream->codec_data; + + if (data) { + if (data->files) { + int i; + for (i=0; ifile_count; i++) { + /* shouldn't be duplicates */ + if (data->files[i]) { + acm_close(data->files[i]); + data->files[i] = NULL; + } + } + free(data->files); + data->files = NULL; + } + + free(vgmstream->codec_data); + vgmstream->codec_data = NULL; + } + } + free(vgmstream); } @@ -324,6 +349,7 @@ void render_vgmstream(sample * buffer, int32_t sample_count, VGMSTREAM * vgmstre case layout_fake_mpeg: case layout_mpeg: #endif + case layout_acm: case layout_dtk_interleave: case layout_none: render_vgmstream_nolayout(buffer,sample_count,vgmstream); @@ -373,6 +399,7 @@ int get_vgmstream_samples_per_frame(VGMSTREAM * vgmstream) { #endif case coding_SDX2: case coding_SDX2_int: + case coding_ACM: return 1; case coding_NDS_IMA: return (vgmstream->interleave_block_size-4)*2; @@ -681,6 +708,12 @@ void decode_vgmstream(VGMSTREAM * vgmstream, int samples_written, int samples_to buffer+samples_written*vgmstream->channels,samples_to_do, vgmstream->channels); break; + case coding_ACM: + decode_acm( + vgmstream->codec_data, + buffer+samples_written*vgmstream->channels,samples_to_do, + vgmstream->channels); + break; #endif } } @@ -943,6 +976,9 @@ void describe_vgmstream(VGMSTREAM * vgmstream, char * desc, int length) { snprintf(temp,TEMPSIZE,"MPEG-2.5 Layer III Audio (MP3)"); break; #endif + case coding_ACM: + snprintf(temp,TEMPSIZE,"InterPlay ACM"); + break; default: snprintf(temp,TEMPSIZE,"CANNOT DECODE"); } @@ -1007,7 +1043,9 @@ void describe_vgmstream(VGMSTREAM * vgmstream, char * desc, int length) { snprintf(temp,TEMPSIZE,"MPEG Audio stream"); break; #endif - + case layout_acm: + snprintf(temp,TEMPSIZE,"However ACM Does That"); + break; default: snprintf(temp,TEMPSIZE,"INCONCEIVABLE"); } @@ -1318,6 +1356,9 @@ void describe_vgmstream(VGMSTREAM * vgmstream, char * desc, int length) { case meta_PS2_RSTM: snprintf(temp,TEMPSIZE,"Rockstar Games RSTM Header"); break; + case meta_ACM: + snprintf(temp,TEMPSIZE,"InterPlay ACM Header"); + break; default: snprintf(temp,TEMPSIZE,"THEY SHOULD HAVE SENT A POET"); } diff --git a/src/vgmstream.h b/src/vgmstream.h index 59a0b0e4..7f0b42f6 100644 --- a/src/vgmstream.h +++ b/src/vgmstream.h @@ -20,6 +20,7 @@ #ifdef VGM_USE_MPEG #include #endif +#include "coding/acm_decoder.h" /* The encoding type specifies the format the sound data itself takes */ typedef enum { @@ -73,6 +74,8 @@ typedef enum { coding_MPEG25_L2, coding_MPEG25_L3, #endif + + coding_ACM, /* InterPlay ACM */ } coding_t; /* The layout type specifies how the sound data is laid out in the file */ @@ -107,6 +110,7 @@ typedef enum { layout_fake_mpeg, /* MPEG audio stream with bad frame headers (AHX) */ layout_mpeg, /* proper MPEG audio stream */ #endif + layout_acm, /* dummy, let libacm handle layout */ } layout_t; /* The meta type specifies how we know what we know about the file. We may know because of a header we read, some of it may have been guessed from filenames, etc. */ @@ -225,6 +229,8 @@ typedef enum { meta_NWA_GAMEEXEINI, /* NWA w/ Gameexe.ini for looping */ meta_DVI, /* DVI Interleaved */ meta_KCEY, /* KCEYCOMP */ + meta_ACM, /* InterPlay ACM header */ + meta_MUS_ACM, /* MUS playlist of InterPlay ACM files */ } meta_t; typedef struct { @@ -349,6 +355,14 @@ typedef struct { } mpeg_codec_data; #endif +/* with one file this is also used for just + ACM */ +typedef struct { + int file_count; + int current_file; + ACMStream **files; +} mus_acm_codec_data; + /* do format detection, return pointer to a usable VGMSTREAM, or NULL on failure */ VGMSTREAM * init_vgmstream(const char * const filename);