vgmstream/src/coding/acm_decoder_util.c

279 lines
5.5 KiB
C

/*
* Utility functions for libacm.
*
* Copyright (c) 2004-2010, Marko Kreen
*
* Permission to use, copy, modify, and/or 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 <string.h>
#include "acm_decoder_libacm.h" //"libacm.h"//vgmstream mod
#define WAVC_HEADER_LEN 28
#define ACM_HEADER_LEN 14
/*
* error strings
*/
static const char *_errlist[] = {
"No error",
"ACM error",
"Cannot open file",
"Not an ACM file",
"Read error",
"Bad format",
"Corrupt file",
"Unexcpected EOF",
"Stream not seekable"
};
const char *acm_strerror(int err)
{
int nerr = sizeof(_errlist) / sizeof(char *);
if ((-err) < 0 || (-err) >= nerr)
return "Unknown error";
return _errlist[-err];
}
/*
* File IO using stdio
*/
static int _read_file(void *ptr, int size, int n, void *arg)
{
FILE *f = (FILE *)arg;
return fread(ptr, size, n, f);
}
static int _close_file(void *arg)
{
FILE *f = (FILE *)arg;
return fclose(f);
}
static int _seek_file(void *arg, int offset, int whence)
{
FILE *f = (FILE *)arg;
return fseek(f, offset, whence);
}
static int _get_length_file(void *arg)
{
FILE *f = (FILE *)arg;
int res, pos, len = -1;
pos = ftell(f);
if (pos < 0)
return -1;
res = fseek(f, 0, SEEK_END);
if (res >= 0) {
len = ftell(f);
fseek(f, pos, SEEK_SET);
}
return len;
}
int acm_open_file(ACMStream **res, const char *filename, int force_chans)
{
int err;
FILE *f;
acm_io_callbacks io;
ACMStream *acm;
if ((f = fopen(filename, "rb")) == NULL)
return ACM_ERR_OPEN;
memset(&io, 0, sizeof(io));
io.read_func = _read_file;
io.seek_func = _seek_file;
io.close_func = _close_file;
io.get_length_func = _get_length_file;
if ((err = acm_open_decoder(&acm, f, io, force_chans)) < 0) {
fclose(f);
return err;
}
*res = acm;
return 0;
}
/*
* utility functions
*/
static unsigned pcm2time(ACMStream *acm, unsigned long long pcm)
{
return pcm * 1000 / acm->info.rate;
/* return ((10 * pcm) / acm->info.rate) * 100; */
}
static unsigned time2pcm(ACMStream *acm, unsigned long long time_ms)
{
return time_ms * acm->info.rate / 1000;
/* return (time_ms / 100) * (acm->info.rate / 10); */
}
/*
* info functions
*/
const ACMInfo *acm_info(ACMStream *acm)
{
return &acm->info;
}
unsigned acm_rate(ACMStream *acm)
{
return acm->info.rate;
}
unsigned acm_channels(ACMStream *acm)
{
return acm->info.channels;
}
int acm_seekable(ACMStream *acm)
{
return acm->data_len > 0;
}
unsigned acm_bitrate(ACMStream *acm)
{
unsigned long long bits, time, bitrate = 0;
if (acm_raw_total(acm) == 0)
return 13000;
time = acm_time_total(acm);
if (time > 0) {
bits = 8 * acm_raw_total(acm);
bitrate = 1000 * bits / time;
}
return bitrate;
}
unsigned acm_pcm_tell(ACMStream *acm)
{
return acm->stream_pos / acm->info.channels;
}
unsigned acm_pcm_total(ACMStream *acm)
{
return acm->total_values / acm->info.channels;
}
unsigned acm_time_tell(ACMStream *acm)
{
return pcm2time(acm, acm_pcm_tell(acm));
}
unsigned acm_time_total(ACMStream *acm)
{
return pcm2time(acm, acm_pcm_total(acm));
}
unsigned acm_raw_tell(ACMStream *acm)
{
return acm->buf_start_ofs + acm->buf_pos;
}
unsigned acm_raw_total(ACMStream *acm)
{
return acm->data_len;
}
/*
* seeking
*/
int acm_seek_time(ACMStream *acm, unsigned time_ms)
{
int res = acm_seek_pcm(acm, time2pcm(acm, time_ms));
if (res <= 0)
return res;
return pcm2time(acm, res);
}
int acm_seek_pcm(ACMStream *acm, unsigned pcm_pos)
{
unsigned word_pos = pcm_pos * acm->info.channels;
unsigned start_ofs;
if (word_pos < acm->stream_pos) {
if (acm->io.seek_func == NULL)
return ACM_ERR_NOT_SEEKABLE;
start_ofs = ACM_HEADER_LEN;
if (acm->wavc_file)
start_ofs += WAVC_HEADER_LEN;
if (acm->io.seek_func(acm->io_arg, start_ofs, SEEK_SET) < 0)
return ACM_ERR_NOT_SEEKABLE;
acm->file_eof = 0;
acm->buf_pos = 0;
acm->buf_size = 0;
acm->bit_avail = 0;
acm->bit_data = 0;
acm->stream_pos = 0;
acm->block_pos = 0;
acm->block_ready = 0;
acm->buf_start_ofs = ACM_HEADER_LEN;
memset(acm->wrapbuf, 0, acm->wrapbuf_len * sizeof(int));
}
while (acm->stream_pos < word_pos) {
int step = 2048, res;
if (acm->stream_pos + step > word_pos)
step = word_pos - acm->stream_pos;
res = acm_read(acm, NULL, step*2, 0,2,1);
if (res < 1)
break;
}
return acm->stream_pos / acm->info.channels;
}
/*
* read loop - full block reading
*/
int acm_read_loop(ACMStream *acm, void *dst, unsigned bytes,
int bigendianp, int wordlen, int sgned)
{
unsigned char *dstp = dst;
int res, got = 0;
while (bytes > 0) {
res = acm_read(acm, dstp, bytes, bigendianp, wordlen, sgned);
if (res > 0) {
if (dstp)
dstp += res;
got += res;
bytes -= res;
} else {
if (res < 0 && got == 0)
return res;
break;
}
}
return got;
}