/* * 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 #include #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; }