acm that at least builds

git-svn-id: https://vgmstream.svn.sourceforge.net/svnroot/vgmstream@348 51a99a44-fe44-0410-b1ba-c3e57ba2b86b
This commit is contained in:
halleyscometsw 2008-07-20 05:41:41 +00:00
parent 3e8d8edb40
commit a14cd4082d
12 changed files with 1034 additions and 4 deletions

19
COPYING.libacm Normal file
View File

@ -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.

View File

@ -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)

View File

@ -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

782
src/coding/acm_decoder.c Normal file
View File

@ -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 <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) {
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);
}

100
src/coding/acm_decoder.h Normal file
View File

@ -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

View File

@ -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

View File

@ -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

65
src/meta/acm.c Normal file
View File

@ -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;
}

View File

@ -155,4 +155,6 @@ VGMSTREAM * init_vgmstream_kcey(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_ps2_rstm(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_acm(STREAMFILE * streamFile);
#endif

View File

@ -1,6 +1,7 @@
#include "meta.h"
#include "../util.h"
#include <string.h>
#include <ctype.h>
#ifdef WIN32
#define DIRSEP '\\'

View File

@ -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; i<data->file_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");
}

View File

@ -20,6 +20,7 @@
#ifdef VGM_USE_MPEG
#include <mpg123.h>
#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);