a4cad3d926
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@27404 a1c6a512-1295-4272-9138-f99709370657
580 lines
17 KiB
C
580 lines
17 KiB
C
/*
|
|
* ttadec.c
|
|
*
|
|
* Description: TTAv1 decoder library for HW players
|
|
* Developed by: Alexander Djourik <ald@true-audio.com>
|
|
* Pavel Zhilin <pzh@true-audio.com>
|
|
*
|
|
* Copyright (c) 2004 True Audio Software. All rights reserved.
|
|
*
|
|
*/
|
|
|
|
/*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
*
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
* 3. Neither the name of the True Audio Software nor the names of its
|
|
* contributors may be used to endorse or promote products derived
|
|
* from this software without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
#include "codeclib.h"
|
|
|
|
#include "ttalib.h"
|
|
#include "ttadec.h"
|
|
#include "filter.h"
|
|
|
|
/******************* static variables and structures *******************/
|
|
|
|
static unsigned char isobuffers[ISO_BUFFERS_SIZE + 4] IBSS_ATTR;
|
|
static unsigned char * const iso_buffers_end ICONST_ATTR = isobuffers + ISO_BUFFERS_SIZE;
|
|
static unsigned int pcm_buffer_size IBSS_ATTR;
|
|
|
|
static decoder tta[MAX_NCH] IBSS_ATTR; /* decoder state */
|
|
/* Rockbox speciffic: cache is defined in get_samples() (non static value) */
|
|
/* static int cache[MAX_NCH]; // decoder cache */
|
|
|
|
tta_info *ttainfo IBSS_ATTR; /* currently playing file info */
|
|
|
|
static unsigned int fframes IBSS_ATTR; /* number of frames in file */
|
|
static unsigned int framelen IBSS_ATTR; /* the frame length in samples */
|
|
static unsigned int lastlen IBSS_ATTR; /* the length of the last frame in samples */
|
|
static unsigned int data_pos IBSS_ATTR; /* currently playing frame index */
|
|
static unsigned int data_cur IBSS_ATTR; /* the playing position in frame */
|
|
|
|
static int maxvalue IBSS_ATTR; /* output data max value */
|
|
|
|
/* Rockbox speciffic: seek_table is static size */
|
|
static unsigned int seek_table[MAX_SEEK_TABLE_SIZE]; /* the playing position table */
|
|
static unsigned int st_state; /* seek table status */
|
|
|
|
static unsigned int frame_crc32 IBSS_ATTR;
|
|
static unsigned int bit_count IBSS_ATTR;
|
|
static unsigned int bit_cache IBSS_ATTR;
|
|
static unsigned char *bitpos IBSS_ATTR;
|
|
|
|
/* Rockbox speciffic: deletes read_id3_tags(). */
|
|
/* static int read_id3_tags (tta_info *info); */
|
|
|
|
/********************* rockbox helper functions *************************/
|
|
|
|
/* emulate stdio functions */
|
|
static int fread(void *ptr, size_t size, size_t nobj)
|
|
{
|
|
size_t read_size;
|
|
unsigned char *buffer = ci->request_buffer(&read_size, size * nobj);
|
|
|
|
if (read_size > 0)
|
|
{
|
|
ci->memcpy(ptr, buffer, read_size);
|
|
ci->advance_buffer(read_size);
|
|
}
|
|
return read_size;
|
|
}
|
|
|
|
static int fseek(long offset, int origin)
|
|
{
|
|
switch (origin)
|
|
{
|
|
case SEEK_CUR:
|
|
ci->advance_buffer(offset);
|
|
break;
|
|
case SEEK_SET:
|
|
ci->seek_buffer(offset);
|
|
break;
|
|
case SEEK_END:
|
|
ci->seek_buffer(offset + ci->id3->filesize);
|
|
break;
|
|
default:
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/************************* crc32 functions *****************************/
|
|
|
|
#define UPDATE_CRC32(x, crc) crc = \
|
|
(((crc>>8) & 0x00FFFFFF) ^ crc32_table[(crc^x) & 0xFF])
|
|
|
|
static unsigned int
|
|
crc32 (unsigned char *buffer, unsigned int len) {
|
|
unsigned int i;
|
|
unsigned int crc = 0xFFFFFFFF;
|
|
|
|
for (i = 0; i < len; i++) UPDATE_CRC32(buffer[i], crc);
|
|
|
|
return (crc ^ 0xFFFFFFFF);
|
|
}
|
|
|
|
/************************* bit operations ******************************/
|
|
|
|
#define GET_BINARY(value, bits) \
|
|
while (bit_count < bits) { \
|
|
if (bitpos == iso_buffers_end) { \
|
|
if (!fread(isobuffers, 1, ISO_BUFFERS_SIZE)) { \
|
|
ttainfo->STATE = READ_ERROR; \
|
|
return -1; \
|
|
} \
|
|
bitpos = isobuffers; \
|
|
} \
|
|
UPDATE_CRC32(*bitpos, frame_crc32); \
|
|
bit_cache |= *bitpos << bit_count; \
|
|
bit_count += 8; \
|
|
bitpos++; \
|
|
} \
|
|
value = bit_cache & bit_mask[bits]; \
|
|
bit_cache >>= bits; \
|
|
bit_count -= bits; \
|
|
bit_cache &= bit_mask[bit_count];
|
|
|
|
#define GET_UNARY(value) \
|
|
value = 0; \
|
|
while (!(bit_cache ^ bit_mask[bit_count])) { \
|
|
if (bitpos == iso_buffers_end) { \
|
|
if (!fread(isobuffers, 1, ISO_BUFFERS_SIZE)) { \
|
|
ttainfo->STATE = READ_ERROR; \
|
|
return -1; \
|
|
} \
|
|
bitpos = isobuffers; \
|
|
} \
|
|
value += bit_count; \
|
|
bit_cache = *bitpos++; \
|
|
UPDATE_CRC32(bit_cache, frame_crc32); \
|
|
bit_count = 8; \
|
|
} \
|
|
while (bit_cache & 1) { \
|
|
value++; \
|
|
bit_cache >>= 1; \
|
|
bit_count--; \
|
|
} \
|
|
bit_cache >>= 1; \
|
|
bit_count--;
|
|
|
|
/************************* rice operations ******************************/
|
|
|
|
static inline int update_rice(int value, adapt *rice, int depth,
|
|
const unsigned int *shift_table)
|
|
{
|
|
if (depth > 0)
|
|
{
|
|
rice->sum1 += value - (rice->sum1 >> 4);
|
|
if (rice->k1 > 0 && rice->sum1 < shift_table[rice->k1])
|
|
rice->k1--;
|
|
else if (rice->sum1 > shift_table[rice->k1 + 1])
|
|
rice->k1++;
|
|
value += *(shift_table + rice->k0 - 4);
|
|
}
|
|
rice->sum0 += value - (rice->sum0 >> 4);
|
|
if (rice->k0 > 0 && rice->sum0 < shift_table[rice->k0])
|
|
rice->k0--;
|
|
else if (rice->sum0 > shift_table[rice->k0 + 1])
|
|
rice->k0++;
|
|
|
|
return DEC(value);
|
|
}
|
|
|
|
/************************* buffer functions ******************************/
|
|
|
|
static void init_buffer_read(void) {
|
|
frame_crc32 = 0xFFFFFFFFUL;
|
|
bit_count = bit_cache = 0;
|
|
bitpos = iso_buffers_end;
|
|
}
|
|
|
|
static int done_buffer_read(void) {
|
|
unsigned int crc32, rbytes;
|
|
|
|
frame_crc32 ^= 0xFFFFFFFFUL;
|
|
rbytes = iso_buffers_end - bitpos;
|
|
|
|
if (rbytes < sizeof(int)) {
|
|
ci->memcpy(isobuffers, bitpos, 4);
|
|
if (!fread(isobuffers + rbytes, 1, ISO_BUFFERS_SIZE - rbytes))
|
|
return -1;
|
|
bitpos = isobuffers;
|
|
}
|
|
|
|
ci->memcpy(&crc32, bitpos, 4);
|
|
crc32 = ENDSWAP_INT32(crc32);
|
|
bitpos += sizeof(int);
|
|
|
|
if (crc32 != frame_crc32) return -1;
|
|
|
|
bit_cache = bit_count = 0;
|
|
frame_crc32 = 0xFFFFFFFFUL;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/************************* decoder functions ****************************/
|
|
|
|
const char *get_error_str (int error) {
|
|
switch (error) {
|
|
case NO_ERROR: return "No errors found";
|
|
case OPEN_ERROR: return "Can't open file";
|
|
case FORMAT_ERROR: return "Not supported file format";
|
|
case FILE_ERROR: return "File is corrupted";
|
|
case READ_ERROR: return "Can't read from file";
|
|
case MEMORY_ERROR: return "Insufficient memory available";
|
|
default: return "Unknown error code";
|
|
}
|
|
}
|
|
|
|
int set_tta_info (tta_info *info)
|
|
{
|
|
unsigned int checksum;
|
|
unsigned int datasize;
|
|
unsigned int origsize;
|
|
tta_hdr ttahdr;
|
|
|
|
/* clear the memory */
|
|
ci->memset (info, 0, sizeof(tta_info));
|
|
|
|
/* skip id3v2 tags */
|
|
fseek(ci->id3->id3v2len, SEEK_SET);
|
|
|
|
/* read TTA header */
|
|
if (fread (&ttahdr, 1, sizeof (ttahdr)) == 0) {
|
|
info->STATE = READ_ERROR;
|
|
return -1;
|
|
}
|
|
|
|
/* check for TTA3 signature */
|
|
if (ENDSWAP_INT32(ttahdr.TTAid) != TTA1_SIGN) {
|
|
DEBUGF("ID error: %x\n", ENDSWAP_INT32(ttahdr.TTAid));
|
|
info->STATE = FORMAT_ERROR;
|
|
return -1;
|
|
}
|
|
|
|
ttahdr.CRC32 = ENDSWAP_INT32(ttahdr.CRC32);
|
|
checksum = crc32((unsigned char *) &ttahdr,
|
|
sizeof(tta_hdr) - sizeof(int));
|
|
if (checksum != ttahdr.CRC32) {
|
|
DEBUGF("CRC error: %x != %x\n", ttahdr.CRC32, checksum);
|
|
info->STATE = FILE_ERROR;
|
|
return -1;
|
|
}
|
|
|
|
ttahdr.AudioFormat = ENDSWAP_INT16(ttahdr.AudioFormat);
|
|
ttahdr.NumChannels = ENDSWAP_INT16(ttahdr.NumChannels);
|
|
ttahdr.BitsPerSample = ENDSWAP_INT16(ttahdr.BitsPerSample);
|
|
ttahdr.SampleRate = ENDSWAP_INT32(ttahdr.SampleRate);
|
|
ttahdr.DataLength = ENDSWAP_INT32(ttahdr.DataLength);
|
|
|
|
/* check for player supported formats */
|
|
if (ttahdr.AudioFormat != WAVE_FORMAT_PCM ||
|
|
ttahdr.NumChannels > MAX_NCH ||
|
|
ttahdr.BitsPerSample > MAX_BPS ||(
|
|
ttahdr.SampleRate != 16000 &&
|
|
ttahdr.SampleRate != 22050 &&
|
|
ttahdr.SampleRate != 24000 &&
|
|
ttahdr.SampleRate != 32000 &&
|
|
ttahdr.SampleRate != 44100 &&
|
|
ttahdr.SampleRate != 48000 &&
|
|
ttahdr.SampleRate != 64000 &&
|
|
ttahdr.SampleRate != 88200 &&
|
|
ttahdr.SampleRate != 96000)) {
|
|
info->STATE = FORMAT_ERROR;
|
|
DEBUGF("illegal audio format: %d channels: %d samplerate: %d\n",
|
|
ttahdr.AudioFormat, ttahdr.NumChannels, ttahdr.SampleRate);
|
|
return -1;
|
|
}
|
|
|
|
/* fill the File Info */
|
|
info->NCH = ttahdr.NumChannels;
|
|
info->BPS = ttahdr.BitsPerSample;
|
|
info->BSIZE = (ttahdr.BitsPerSample + 7)/8;
|
|
info->FORMAT = ttahdr.AudioFormat;
|
|
info->SAMPLERATE = ttahdr.SampleRate;
|
|
info->DATALENGTH = ttahdr.DataLength;
|
|
info->FRAMELEN = (int) MULTIPLY_FRAME_TIME(ttahdr.SampleRate);
|
|
info->LENGTH = ttahdr.DataLength / ttahdr.SampleRate;
|
|
info->DATAPOS = ci->id3->id3v2len;
|
|
info->FILESIZE = ci->id3->filesize;
|
|
|
|
datasize = info->FILESIZE - info->DATAPOS;
|
|
origsize = info->DATALENGTH * info->BSIZE * info->NCH;
|
|
|
|
/* info->COMPRESS = (double) datasize / origsize; */
|
|
info->BITRATE = (int) ((uint64_t) datasize * info->SAMPLERATE * info->NCH * info->BPS
|
|
/ (origsize * 1000LL));
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void rice_init(adapt *rice, unsigned int k0, unsigned int k1) {
|
|
rice->k0 = k0;
|
|
rice->k1 = k1;
|
|
rice->sum0 = shift_16[k0];
|
|
rice->sum1 = shift_16[k1];
|
|
}
|
|
|
|
static void decoder_init(decoder *tta, int nch, int byte_size) {
|
|
int shift = flt_set[byte_size - 1];
|
|
int i;
|
|
|
|
for (i = 0; i < nch; i++) {
|
|
filter_init(&tta[i].fst, shift);
|
|
rice_init(&tta[i].rice, 10, 10);
|
|
tta[i].last = 0;
|
|
}
|
|
}
|
|
|
|
static void seek_table_init (unsigned int *seek_table,
|
|
unsigned int len, unsigned int data_offset) {
|
|
unsigned int *st, frame_len;
|
|
|
|
for (st = seek_table; st < (seek_table + len); st++) {
|
|
frame_len = ENDSWAP_INT32(*st);
|
|
*st = data_offset;
|
|
data_offset += frame_len;
|
|
}
|
|
}
|
|
|
|
int set_position (unsigned int pos, enum tta_seek_type type)
|
|
{
|
|
unsigned int i;
|
|
unsigned int seek_pos;
|
|
|
|
if (type == TTA_SEEK_TIME)
|
|
{
|
|
if (pos >= fframes)
|
|
pos = fframes -1;
|
|
}
|
|
else
|
|
{
|
|
pos -= ttainfo->DATAPOS;
|
|
for (i = 1; i < fframes; i++)
|
|
{
|
|
if (seek_table[i] > pos)
|
|
break;
|
|
}
|
|
pos = i - 1;
|
|
}
|
|
if (!st_state) {
|
|
ttainfo->STATE = FILE_ERROR;
|
|
return -1;
|
|
}
|
|
seek_pos = ttainfo->DATAPOS + seek_table[data_pos = pos];
|
|
if (fseek(seek_pos, SEEK_SET) < 0) {
|
|
ttainfo->STATE = READ_ERROR;
|
|
return -1;
|
|
}
|
|
|
|
data_cur = 0;
|
|
framelen = 0;
|
|
|
|
/* init bit reader */
|
|
init_buffer_read();
|
|
return data_pos * ttainfo->FRAMELEN;
|
|
}
|
|
|
|
int player_init (tta_info *info) {
|
|
unsigned int checksum;
|
|
unsigned int data_offset;
|
|
unsigned int st_size;
|
|
|
|
#ifdef CPU_COLDFIRE
|
|
coldfire_set_macsr(0); /* signed integer mode */
|
|
#endif
|
|
|
|
ttainfo = info;
|
|
|
|
framelen = 0;
|
|
data_pos = 0;
|
|
data_cur = 0;
|
|
|
|
lastlen = ttainfo->DATALENGTH % ttainfo->FRAMELEN;
|
|
fframes = ttainfo->DATALENGTH / ttainfo->FRAMELEN + (lastlen ? 1 : 0);
|
|
st_size = (fframes + 1) * sizeof(int);
|
|
|
|
/*
|
|
* Rockbox speciffic
|
|
* playable tta file is to MAX_SEEK_TABLE_SIZE frames
|
|
* about 1:08:15 (frequency 44.1 kHz)
|
|
*/
|
|
if (fframes > MAX_SEEK_TABLE_SIZE)
|
|
{
|
|
LOGF("frame is too many: %d > %d", fframes, MAX_SEEK_TABLE_SIZE);
|
|
return -1;
|
|
}
|
|
|
|
/* read seek table */
|
|
if (!fread(seek_table, st_size, 1)) {
|
|
ttainfo->STATE = READ_ERROR;
|
|
return -1;
|
|
}
|
|
|
|
checksum = crc32((unsigned char *) seek_table, st_size - sizeof(int));
|
|
st_state = (checksum == ENDSWAP_INT32(seek_table[fframes]));
|
|
data_offset = sizeof(tta_hdr) + st_size;
|
|
|
|
/* init seek table */
|
|
seek_table_init(seek_table, fframes, data_offset);
|
|
|
|
/* init bit reader */
|
|
init_buffer_read();
|
|
|
|
/*
|
|
* Rockbox speciffic
|
|
* because pcm data is int32_t, does not multiply ttainfo->BSIZE.
|
|
*/
|
|
pcm_buffer_size = PCM_BUFFER_LENGTH * ttainfo->NCH;
|
|
maxvalue = (1UL << ttainfo->BPS) - 1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Rockbox specffic
|
|
* because the seek table is static size buffer, player_stop() is nooperation function.
|
|
*/
|
|
void player_stop (void) {
|
|
/*
|
|
if (seek_table) {
|
|
free(seek_table);
|
|
seek_table = NULL;
|
|
}
|
|
*/
|
|
}
|
|
|
|
/*
|
|
* Rockbox speciffic
|
|
* with the optimization, the decoding logic is modify a little.
|
|
*/
|
|
int get_samples (int32_t *buffer) {
|
|
unsigned int k, depth, unary, binary;
|
|
int32_t *p = buffer;
|
|
decoder *dec = tta;
|
|
int value, res;
|
|
int cur_pos = pcm_buffer_size;
|
|
int pcm_shift_bits = TTA_OUTPUT_DEPTH - ttainfo->BPS;
|
|
int pr_bits = (ttainfo->BSIZE == 1)? 4 : 5;
|
|
int cache = 0; /* decoder cache */
|
|
|
|
fltst *fst;
|
|
adapt *rice;
|
|
|
|
for (res = 0; --cur_pos >= 0;) {
|
|
fst = &dec->fst;
|
|
rice = &dec->rice;
|
|
|
|
if (data_cur == framelen) {
|
|
if (data_pos == fframes) break;
|
|
if (framelen && done_buffer_read()) {
|
|
if (set_position(data_pos, TTA_SEEK_TIME) < 0) return -1;
|
|
if (res) break;
|
|
}
|
|
|
|
if (data_pos == fframes - 1 && lastlen)
|
|
framelen = lastlen;
|
|
else framelen = ttainfo->FRAMELEN;
|
|
|
|
decoder_init(tta, ttainfo->NCH, ttainfo->BSIZE);
|
|
data_pos++; data_cur = 0;
|
|
}
|
|
|
|
/* decode Rice unsigned */
|
|
GET_UNARY(unary);
|
|
|
|
switch (unary) {
|
|
case 0: depth = 0; k = rice->k0; break;
|
|
default:
|
|
depth = 1; k = rice->k1;
|
|
unary--;
|
|
}
|
|
|
|
if (k) {
|
|
GET_BINARY(binary, k);
|
|
value = (unary << k) + binary;
|
|
} else value = unary;
|
|
|
|
value = update_rice(value, rice, depth, shift_16);
|
|
|
|
/* Rockbox specific: the following logic move to update_rice() */
|
|
#if 0
|
|
if (depth > 0)
|
|
{
|
|
rice->sum1 += value - (rice->sum1 >> 4);
|
|
if (rice->k1 > 0 && rice->sum1 < shift_16[rice->k1])
|
|
rice->k1--;
|
|
else if (rice->sum1 > shift_16[rice->k1 + 1])
|
|
rice->k1++;
|
|
value += bit_shift[rice->k0];
|
|
}
|
|
rice->sum0 += value - (rice->sum0 >> 4);
|
|
if (rice->k0 > 0 && rice->sum0 < shift_16[rice->k0])
|
|
rice->k0--;
|
|
else if (rice->sum0 > shift_16[rice->k0 + 1])
|
|
rice->k0++;
|
|
|
|
value = DEC(value);
|
|
#endif
|
|
|
|
/* decompress stage 1: adaptive hybrid filter */
|
|
hybrid_filter(fst, &value);
|
|
|
|
/* decompress stage 2: fixed order 1 prediction */
|
|
value += PREDICTOR1(dec->last, pr_bits);
|
|
dec->last = value;
|
|
|
|
/* check for errors */
|
|
if (abs(value) > maxvalue) {
|
|
unsigned int tail =
|
|
pcm_buffer_size / (ttainfo->BSIZE * ttainfo->NCH) - res;
|
|
ci->memset(buffer, 0, pcm_buffer_size * sizeof(int32_t));
|
|
data_cur += tail; res += tail;
|
|
break;
|
|
}
|
|
|
|
/* Rockbox speciffic: Rockbox supports max 2channels */
|
|
if (ttainfo->NCH == 1)
|
|
{
|
|
*p++ = value << pcm_shift_bits;
|
|
data_cur++;
|
|
res++;
|
|
}
|
|
else
|
|
{
|
|
if (dec == tta)
|
|
{
|
|
cache = value;
|
|
dec++;
|
|
}
|
|
else
|
|
{
|
|
value += cache / 2;
|
|
cache = value - cache;
|
|
dec = tta;
|
|
*p++ = cache << pcm_shift_bits;
|
|
*p++ = value << pcm_shift_bits;
|
|
data_cur++;
|
|
res++;
|
|
}
|
|
}
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
/* Rockbox speciffic: id3 tags functions delete. */
|
|
|
|
/* eof */
|