From 655e7857aac3ab7377a062d7ee56d2280622224c Mon Sep 17 00:00:00 2001 From: uchida Date: Thu, 13 May 2010 12:40:09 +0000 Subject: [PATCH] add True Audio (TTA) codec decoding speed iPod video ~153% But in some players, the decoding speed is not enough. (e.g., H180 52.4% (thanks amiconn), H300 55.09% (thanks n1s)) git-svn-id: svn://svn.rockbox.org/rockbox/trunk@25994 a1c6a512-1295-4272-9138-f99709370657 --- apps/SOURCES | 1 + apps/codecs/SOURCES | 1 + apps/codecs/codecs.make | 2 + apps/codecs/libtta/README | 72 +++++ apps/codecs/libtta/README.rockbox | 34 +++ apps/codecs/libtta/SOURCES | 4 + apps/codecs/libtta/filter.h | 136 +++++++++ apps/codecs/libtta/filter_arm.S | 203 ++++++++++++++ apps/codecs/libtta/libtta.make | 29 ++ apps/codecs/libtta/ttadec.c | 576 ++++++++++++++++++++++++++++++++++++++ apps/codecs/libtta/ttadec.h | 203 ++++++++++++++ apps/codecs/libtta/ttalib.h | 157 +++++++++++ apps/codecs/tta.c | 132 +++++++++ apps/filetypes.c | 1 + apps/metadata.c | 11 + apps/metadata.h | 1 + apps/metadata/metadata_parsers.h | 2 + apps/metadata/mp3.c | 2 +- apps/metadata/tta.c | 123 ++++++++ 19 files changed, 1689 insertions(+), 1 deletion(-) create mode 100644 apps/codecs/libtta/README create mode 100644 apps/codecs/libtta/README.rockbox create mode 100644 apps/codecs/libtta/SOURCES create mode 100644 apps/codecs/libtta/filter.h create mode 100644 apps/codecs/libtta/filter_arm.S create mode 100644 apps/codecs/libtta/libtta.make create mode 100644 apps/codecs/libtta/ttadec.c create mode 100644 apps/codecs/libtta/ttadec.h create mode 100644 apps/codecs/libtta/ttalib.h create mode 100644 apps/codecs/tta.c create mode 100644 apps/metadata/tta.c diff --git a/apps/SOURCES b/apps/SOURCES index ef81f0cfd..8151c299b 100644 --- a/apps/SOURCES +++ b/apps/SOURCES @@ -197,6 +197,7 @@ metadata/oma.c metadata/smaf.c metadata/au.c metadata/vox.c +metadata/tta.c #endif #ifdef HAVE_TAGCACHE tagcache.c diff --git a/apps/codecs/SOURCES b/apps/codecs/SOURCES index e2ee53e7e..160b8aa58 100644 --- a/apps/codecs/SOURCES +++ b/apps/codecs/SOURCES @@ -32,6 +32,7 @@ smaf.c au.c vox.c wav64.c +tta.c #if defined(HAVE_RECORDING) && !defined(SIMULATOR) /* encoders */ aiff_enc.c diff --git a/apps/codecs/codecs.make b/apps/codecs/codecs.make index 7a83e5d1d..e671932b4 100644 --- a/apps/codecs/codecs.make +++ b/apps/codecs/codecs.make @@ -41,6 +41,7 @@ include $(APPSDIR)/codecs/libcook/libcook.make include $(APPSDIR)/codecs/librm/librm.make include $(APPSDIR)/codecs/libatrac/libatrac.make include $(APPSDIR)/codecs/libpcm/libpcm.make +include $(APPSDIR)/codecs/libtta/libtta.make # compile flags for codecs CODECFLAGS = $(filter-out -fno-strict-aliasing,$(CFLAGS)) -fstrict-aliasing \ @@ -96,6 +97,7 @@ $(CODECDIR)/smaf.codec : $(CODECDIR)/libpcm.a $(CODECDIR)/au.codec : $(CODECDIR)/libpcm.a $(CODECDIR)/vox.codec : $(CODECDIR)/libpcm.a $(CODECDIR)/wav64.codec : $(CODECDIR)/libpcm.a +$(CODECDIR)/tta.codec : $(CODECDIR)/libtta.a $(CODECS): $(CODECLIB) # this must be last in codec dependency list diff --git a/apps/codecs/libtta/README b/apps/codecs/libtta/README new file mode 100644 index 000000000..8b1a1b23c --- /dev/null +++ b/apps/codecs/libtta/README @@ -0,0 +1,72 @@ +TTA Hardware Players Library +============================ + +Version 1.2, (c) 2004 Alexander Djourik. All rights reserved. + +* Introduction + +This library provides to decode a multichannel 8,16 and 24 +bits TTA audio files. TTA is a lossless audio format. Being +"lossless" means that no data/quality is lost in the compression +- when uncompressed, the data will be identical to the original. +The compression ratios of TTA depend on the type of music file +being compressed, but the compression size will generally range +between 30% - 70% of the original. + +TTA format supports both of ID3v1/v2 tags. Detailed format +description is available at http://www.true-audio.com. + +The decoder process has a minimal system requirements and does +not required to create a big additional memory pools. As the +TTA algorithms has a same system requirements both for decoding +and for encoding processes - the TTA recorder can be easily +realized also. + +* Changes + + 14/04/2004 1.0 Initial release + 16/04/2004 1.1 Code optimization + Code clean-up + 29/10/2004 1.2 ID3 tags support + Code clean-up + +* To Do + + - TTA recorder functions. + +* Developers + + Alexander Djourik + Pavel Zhilin + +* Copying + +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. + +* See also + +Please visit the TTA homepage at http://tta.sourceforge.net for the +latest in news and downloads. diff --git a/apps/codecs/libtta/README.rockbox b/apps/codecs/libtta/README.rockbox new file mode 100644 index 000000000..d605986d4 --- /dev/null +++ b/apps/codecs/libtta/README.rockbox @@ -0,0 +1,34 @@ +Library: True Audio +Imported by : Yoshihisa Uchida +Import date : 2010-03-05 +Baseed by : TTA library version 1.2 for hardware players (ttalib-hwplayer-1.2.tgz) + from http://true-audio.com/Free_Downloads + +This directory contains a decoder version of True Auido. + +LICENSING INFORMATION + +True Audio license is described in the README file or each source file (excepts filter_arm.S) +in this directory. + +Limitation + +In some players, the decoding speed is not enough ( < 110%). +(For example, H180: decoding speed < 60%) + +IMPORT DETAILS + +The .[ch] files from ttalib-hwplayer-1.2.tgz were imported into Rockbox. +But the following files do not include. + Makefile + tta.vcproj + samples/* + + +When source files import in Rockbox, I changed below. + +all files + - TAB => 4 spaces. + - // style comments changes to /* */ style. + +Moreover, I modify to optimize the decoding speed. diff --git a/apps/codecs/libtta/SOURCES b/apps/codecs/libtta/SOURCES new file mode 100644 index 000000000..35f2660dd --- /dev/null +++ b/apps/codecs/libtta/SOURCES @@ -0,0 +1,4 @@ +ttadec.c +#ifdef CPU_ARM +filter_arm.S +#endif diff --git a/apps/codecs/libtta/filter.h b/apps/codecs/libtta/filter.h new file mode 100644 index 000000000..6eef6dcf4 --- /dev/null +++ b/apps/codecs/libtta/filter.h @@ -0,0 +1,136 @@ +/* + * filter.h + * + * Description: TTAv1 filter functions + * Developed by: Alexander Djourik + * Pavel Zhilin + * + * 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. + */ + +#ifndef FILTER_H +#define FILTER_H + +///////// Filter Settings ////////// +static int flt_set[3] = {10, 9, 10}; + +#ifdef CPU_ARM +int hybrid_filter(fltst *fs, int *in); /* implements in filter_arm.S */ + +#else + +static inline void +memshl (register int *pA) { + register int *pB = pA + 16; + + *pA++ = *pB++; + *pA++ = *pB++; + *pA++ = *pB++; + *pA++ = *pB++; + *pA++ = *pB++; + *pA++ = *pB++; + *pA++ = *pB++; + *pA = *pB; +} + +static inline void +hybrid_filter (fltst *fs, int *in) { + register int *pA = fs->dl + fs->index; + register int *pB = fs->qm; + register int *pM = fs->dx + fs->index; + register int sum = fs->round; + + if (!fs->error) { + sum += *pA++ * *pB++; + sum += *pA++ * *pB++; + sum += *pA++ * *pB++; + sum += *pA++ * *pB++; + sum += *pA++ * *pB++; + sum += *pA++ * *pB++; + sum += *pA++ * *pB++; + sum += *pA * *pB; + pM += 8; + } else if (fs->error < 0) { + sum += *pA++ * (*pB++ -= *pM++); + sum += *pA++ * (*pB++ -= *pM++); + sum += *pA++ * (*pB++ -= *pM++); + sum += *pA++ * (*pB++ -= *pM++); + sum += *pA++ * (*pB++ -= *pM++); + sum += *pA++ * (*pB++ -= *pM++); + sum += *pA++ * (*pB++ -= *pM++); + sum += *pA * (*pB -= *pM++); + } else { + sum += *pA++ * (*pB++ += *pM++); + sum += *pA++ * (*pB++ += *pM++); + sum += *pA++ * (*pB++ += *pM++); + sum += *pA++ * (*pB++ += *pM++); + sum += *pA++ * (*pB++ += *pM++); + sum += *pA++ * (*pB++ += *pM++); + sum += *pA++ * (*pB++ += *pM++); + sum += *pA * (*pB += *pM++); + } + + pB = pA++; + fs->error = *in; + *in += (sum >> fs->shift); + *pA = *in; + + *pM-- = ((*pB-- >> 30) | 1) << 2; + *pM-- = ((*pB-- >> 30) | 1) << 1; + *pM-- = ((*pB-- >> 30) | 1) << 1; + *pM = ((*pB >> 30) | 1); + + *(pA-1) = *(pA-0) - *(pA-1); + *(pA-2) = *(pA-1) - *(pA-2); + *(pA-3) = *(pA-2) - *(pA-3); + + /* + * Rockbox speciffic + * in order to make speed up, memshl() is executed at the rate once every 16 times. + */ + if (++fs->index == 16) + { + memshl (fs->dl); + memshl (fs->dx); + fs->index = 0; + } +} +#endif + +static inline void +filter_init (fltst *fs, int shift) { + ci->memset (fs, 0, sizeof(fltst)); + fs->shift = shift; + fs->round = 1 << (shift - 1); + fs->index = 0; +} + +#endif /* FILTER_H */ diff --git a/apps/codecs/libtta/filter_arm.S b/apps/codecs/libtta/filter_arm.S new file mode 100644 index 000000000..37c515d3a --- /dev/null +++ b/apps/codecs/libtta/filter_arm.S @@ -0,0 +1,203 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2010 Yoshihisa Uchida + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ + +#include "config.h" + +/* + * The following are assembler optimised version of + * void hybrid_filter(fltst *fs, int *in) + */ + +#ifdef USE_IRAM + .section .icode, "ax", %progbits +#else + .text +#endif + .align + .global hybrid_filter + .type hybrid_filter, %function + +hybrid_filter: + @ input: r0 = fs, r1 = in + stmdb sp!, {r4 - r12, lr} + + @ get fs members + @ r2 pA := fs->dl + fs->index + @ r3 pM := fs->dx + fs->index + @ r4 pB := fs->qm + @ r5 fs->index + @ r6 fs->error + @ lr sum := fs->round + + add r2, r0, #148 @ r2 = fs->dl + add r3, r0, #52 @ r3 = fs->dx + add r4, r0, #20 @ r4 = fs->qm + ldmia r0, {r5, r6, lr} @ r5 = fs->index + @ r6 = fs->error + @ lr = fs->round + mov r5, r5, asl #2 + add r2, r2, r5 @ r2 = fs->dl + fs->index + add r3, r3, r5 @ r3 = fs->dx + fs->index + + cmp r6, #0 + bne .hf_positive + + @ case fs->error == 0 + + add r3, r3, #32 + ldmia r4!, {r5, r6, r7, r8 } + ldmia r2!, {r9, r10, r11, r12} + mla lr, r5, r9, lr + mla lr, r6, r10, lr + mla lr, r7, r11, lr + mla lr, r8, r12, lr + ldmia r4!, {r5, r6, r7, r8 } + b .hf2 + +.hf_positive: + blt .hf_negative + + @ case fs->error > 0 + + ldmia r4, {r5, r6, r7, r8 } + ldmia r3!, {r9, r10, r11, r12} + add r5, r5, r9 + add r6, r6, r10 + add r7, r7, r11 + add r8, r8, r12 + stmia r4!, {r5, r6, r7, r8 } @ update fs->qm[0], ..., fs->qm[3] + ldmia r2!, {r9, r10, r11, r12} + mla lr, r5, r9, lr + mla lr, r6, r10, lr + mla lr, r7, r11, lr + mla lr, r8, r12, lr + ldmia r4, {r5, r6, r7, r8 } + ldmia r3!, {r9, r10, r11, r12} + add r5, r5, r9 + add r6, r6, r10 + add r7, r7, r11 + add r8, r8, r12 + stmia r4!, {r5, r6, r7, r8 } @ update fs->qm[4], ..., fs->qm[7] + b .hf2 + +.hf_negative: + @ case fs->error < 0 + + ldmia r4, {r5, r6, r7, r8 } + ldmia r3!, {r9, r10, r11, r12} + sub r5, r5, r9 + sub r6, r6, r10 + sub r7, r7, r11 + sub r8, r8, r12 + stmia r4!, {r5, r6, r7, r8 } @ update fs->qm[0], ..., fs->qm[3] + ldmia r2!, {r9, r10, r11, r12} + mla lr, r5, r9, lr + mla lr, r6, r10, lr + mla lr, r7, r11, lr + mla lr, r8, r12, lr + ldmia r4, {r5, r6, r7, r8 } + ldmia r3!, {r9, r10, r11, r12} + sub r5, r5, r9 + sub r6, r6, r10 + sub r7, r7, r11 + sub r8, r8, r12 + stmia r4!, {r5, r6, r7, r8 } @ update fs->qm[4], ..., fs->qm[7] + +.hf2: + ldmia r2!, {r9, r10, r11, r12} + mla lr, r5, r9, lr + mla lr, r6, r10, lr + mla lr, r7, r11, lr + mla lr, r8, r12, lr + + @ fs->error = *in; + @ *in += (sum >> fs->shift) + @ *pA = *in + + ldr r5, [r1] @ r5 = *in + ldr r6, [r0, #12] @ r6 = fs->shift + add lr, r5, lr, asr r6 + str lr, [r1] @ *in += (sum >> fs->shift) + + @ update fs->index + + ldr r1, [r0] @ r1 = fs->index + add r1, r1, #1 + ands r1, r1, #15 @ set Z flag (after this, CPSR must keep !!) + stmia r0, {r1, r5} @ fs->index = (++fs->index & 15) + @ fs->error = (original) *in + + @ change *pM, *(pM-1), *(pM-2), *(pM-3) + @ r9 = *(pA-4), r5 = *(pM-3) + @ r10 = *(pA-3), r6 = *(pM-2) + @ r11 = *(pA-2), r7 = *(pM-1) + @ r12 = *(pA-1), r8 = *(pM-0) + @ lr = *(pA-0) + + mov r4, #1 + orr r5, r4, r9, asr #30 + orr r6, r4, r10, asr #30 + orr r7, r4, r11, asr #30 + orr r8, r4, r12, asr #30 + mov r6, r6, lsl #1 + mov r7, r7, lsl #1 + mov r8, r8, lsl #2 + + @ change *(pA-1), *(pA-2), *(pA-3) + sub r12, lr, r12 + sub r11, r12, r11 + sub r10, r11, r10 + + @ check fs->index is zero + beq .hf_memshl + + @ set to the memory: *pA, *(pA-1), *(pA-2), *(pA-3), *pM, *(pM-1), *(pM-2), *(pM-3) + stmda r2, {r10, r11, r12, lr} + stmda r3, {r5, r6, r7, r8} + ldmfd sp!, {r4-r12, pc} @ hybrid_filter end (when fs->index != 0) + +.hf_memshl: + @ memshl (fs->dl) + @ r9 = fs->dl[16 + 3] + @ r10 = fs->dl[16 + 4] + @ r11 = fs->dl[16 + 5] + @ r12 = fs->dl[16 + 6] + @ lr = fs->dl[16 + 7] + + add r2, r0, #212 @ r2 = fs->dl + 16 + ldmia r2, {r1, r3, r4} + sub r2, r2, #64 @ r2 = fs->dl + stmia r2, {r1, r3, r4, r9 - r12, lr} + + @ memshl (fs->dx) + @ r5 = fs->dx[16 + 4] + @ r6 = fs->dx[16 + 5] + @ r7 = fs->dx[16 + 6] + @ r8 = fs->dx[16 + 7] + + add r9, r0, #116 @ r9 = fs->dx + 16 + ldmia r9, {r1, r2, r3, r4} + sub r9, r9, #64 @ r9 = fs->dx + stmia r9, {r1 - r8} + ldmfd sp!, {r4 - r12, pc} @ hybrid_filter end (when fs->index == 0) + +hybrid_filter_end: + .size hybrid_filter, hybrid_filter_end - hybrid_filter diff --git a/apps/codecs/libtta/libtta.make b/apps/codecs/libtta/libtta.make new file mode 100644 index 000000000..c0a799ac6 --- /dev/null +++ b/apps/codecs/libtta/libtta.make @@ -0,0 +1,29 @@ +# __________ __ ___. +# Open \______ \ ____ ____ | | _\_ |__ _______ ___ +# Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / +# Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < +# Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ +# \/ \/ \/ \/ \/ +# $Id$ +# + +# libtta +TTALIB := $(CODECDIR)/libtta.a +TTALIB_SRC := $(call preprocess, $(APPSDIR)/codecs/libtta/SOURCES) +TTALIB_OBJ := $(call c2obj, $(TTALIB_SRC)) +OTHER_SRC += $(TTALIB_SRC) + +$(TTALIB): $(TTALIB_OBJ) + $(SILENT)$(shell rm -f $@) + $(call PRINTS,AR $(@F))$(AR) rcs $@ $^ >/dev/null + +TTAFLAGS = $(filter-out -O%,$(CODECFLAGS)) +TTAFLAGS += -O3 -funroll-loops -fomit-frame-pointer + +$(CODECDIR)/libtta/%.o: $(ROOTDIR)/apps/codecs/libtta/%.c + $(SILENT)mkdir -p $(dir $@) + $(call PRINTS,CC $(subst $(ROOTDIR)/,,$<))$(CC) $(TTAFLAGS) -c $< -o $@ + +$(CODECDIR)/libtta/%.o: $(ROOTDIR)/apps/codecs/libtta/%.S + $(SILENT)mkdir -p $(dir $@) + $(call PRINTS,CC $(subst $(ROOTDIR)/,,$<))$(CC) $(TTAFLAGS) -c $< -o $@ diff --git a/apps/codecs/libtta/ttadec.c b/apps/codecs/libtta/ttadec.c new file mode 100644 index 000000000..027b78a17 --- /dev/null +++ b/apps/codecs/libtta/ttadec.c @@ -0,0 +1,576 @@ +/* + * ttadec.c + * + * Description: TTAv1 decoder library for HW players + * Developed by: Alexander Djourik + * Pavel Zhilin + * + * 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 *iso_buffers_end = isobuffers + ISO_BUFFERS_SIZE; +static unsigned int pcm_buffer_size; + +static decoder tta[MAX_NCH]; /* decoder state */ +/* Rockbox speciffic: cache is defined in get_samples() (non static value) */ +/* static int cache[MAX_NCH]; // decoder cache */ + +tta_info *ttainfo; /* currently playing file info */ + +static unsigned int fframes; /* number of frames in file */ +static unsigned int framelen; /* the frame length in samples */ +static unsigned int lastlen; /* the length of the last frame in samples */ +static unsigned int data_pos; /* currently playing frame index */ +static unsigned int data_cur; /* the playing position in frame */ + +static int maxvalue; /* 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; +static unsigned int bit_count; +static unsigned int bit_cache; +static unsigned char *bitpos; + +/* 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; + + 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 */ diff --git a/apps/codecs/libtta/ttadec.h b/apps/codecs/libtta/ttadec.h new file mode 100644 index 000000000..43affd952 --- /dev/null +++ b/apps/codecs/libtta/ttadec.h @@ -0,0 +1,203 @@ +/* + * ttadec.h + * + * Description: TTAv1 decoder definitions and prototypes + * Developed by: Alexander Djourik + * Pavel Zhilin + * + * 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. + */ + +#ifndef TTADEC_H_ +#define TTADEC_H_ + +#define __ATTRIBUTE_PACKED__ __attribute__((packed)) + +#define TTA1_SIGN 0x31415454 +#define MAX_ORDER 8 + +#ifndef WAVE_FORMAT_PCM +#define WAVE_FORMAT_PCM 1 +#endif + +typedef unsigned long long uint64; + +static const unsigned int crc32_table[256] ICONST_ATTR = { + 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, + 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3, + 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, + 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, + 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, + 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, + 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, + 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5, + 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, + 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, + 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, + 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, + 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, + 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f, + 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, + 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, + 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, + 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, + 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, + 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01, + 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, + 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, + 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c, + 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, + 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, + 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, + 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, + 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, + 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, + 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, + 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, + 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, + 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, + 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, + 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, + 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, + 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, + 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7, + 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, + 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, + 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, + 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, + 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, + 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79, + 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, + 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, + 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, + 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, + 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, + 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713, + 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, + 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, + 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, + 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, + 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, + 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, + 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, + 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, + 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, + 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, + 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, + 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf, + 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, + 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d +}; + +static const unsigned int bit_mask[] ICONST_ATTR = { + 0x00000000, 0x00000001, 0x00000003, 0x00000007, + 0x0000000f, 0x0000001f, 0x0000003f, 0x0000007f, + 0x000000ff, 0x000001ff, 0x000003ff, 0x000007ff, + 0x00000fff, 0x00001fff, 0x00003fff, 0x00007fff, + 0x0000ffff, 0x0001ffff, 0x0003ffff, 0x0007ffff, + 0x000fffff, 0x001fffff, 0x003fffff, 0x007fffff, + 0x00ffffff, 0x01ffffff, 0x03ffffff, 0x07ffffff, + 0x0fffffff, 0x1fffffff, 0x3fffffff, 0x7fffffff, + 0xffffffff +}; + +static const unsigned int bit_shift[] ICONST_ATTR = { + 0x00000001, 0x00000002, 0x00000004, 0x00000008, + 0x00000010, 0x00000020, 0x00000040, 0x00000080, + 0x00000100, 0x00000200, 0x00000400, 0x00000800, + 0x00001000, 0x00002000, 0x00004000, 0x00008000, + 0x00010000, 0x00020000, 0x00040000, 0x00080000, + 0x00100000, 0x00200000, 0x00400000, 0x00800000, + 0x01000000, 0x02000000, 0x04000000, 0x08000000, + 0x10000000, 0x20000000, 0x40000000, 0x80000000, + 0x80000000, 0x80000000, 0x80000000, 0x80000000, + 0x80000000, 0x80000000, 0x80000000, 0x80000000 +}; + +static const unsigned int *shift_16 = bit_shift + 4; + +typedef unsigned char byte; + +#ifndef ROCKBOX_LITTLE_ENDIAN +#define ENDSWAP_INT16(x) (((((x)>>8)&0xFF)|(((x)&0xFF)<<8))) +#define ENDSWAP_INT32(x) (((((x)>>24)&0xFF)|(((x)>>8)&0xFF00)|(((x)&0xFF00)<<8)|(((x)&0xFF)<<24))) +#define WRITE_BUFFER(x, bsize, out) { \ + if (bsize > 2) *out++ = (byte)(*x >> 16)); \ + if (bsize > 1) *out++ = (byte)(*x >> 8); \ + *out++ = (byte) *x; } +#else +#define ENDSWAP_INT16(x) (x) +#define ENDSWAP_INT32(x) (x) +#define WRITE_BUFFER(x, bsize, out) { \ + *out++ = (byte) *x; \ + if (bsize > 1) *out++ = (byte)(*x >> 8); \ + if (bsize > 2) *out++ = (byte)(*x >> 16); } +#endif + +#define PREDICTOR1(x, k) ((int)((((uint64)x << k) - x) >> k)) +#define DEC(x) (((x)&1)?(((x)+1)>>1):(-(x)>>1)) + +typedef struct { + unsigned int TTAid; + unsigned short AudioFormat; + unsigned short NumChannels; + unsigned short BitsPerSample; + unsigned int SampleRate; + unsigned int DataLength; + unsigned int CRC32; +} __ATTRIBUTE_PACKED__ tta_hdr; + +typedef struct { + unsigned int k0; + unsigned int k1; + unsigned int sum0; + unsigned int sum1; +} adapt; + +typedef struct { + int index; /* Rockbox speciffic */ + int error; + int round; + int shift; + int mutex; + int qm[MAX_ORDER]; + int dx[MAX_ORDER * 3]; /* original: dx[MAX_ORDER + 1] */ + int dl[MAX_ORDER * 3]; /* original: dx[MAX_ORDER + 1] */ +} fltst; + +typedef struct { + fltst fst; + adapt rice; + int last; +} decoder; + +/* Rockbox speciffic: about id3 tags definitions delete. */ +#endif /* TTADEC_H_ */ diff --git a/apps/codecs/libtta/ttalib.h b/apps/codecs/libtta/ttalib.h new file mode 100644 index 000000000..861c119cd --- /dev/null +++ b/apps/codecs/libtta/ttalib.h @@ -0,0 +1,157 @@ +/* + * ttalib.h + * + * Description: TTAv1 player library prototypes + * Developed by: Alexander Djourik + * Pavel Zhilin + * + * 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. + */ + +#ifndef TTALIB_H_ +#define TTALIB_H_ + +#define MAX_BPS 24 /* Max supported Bit resolution */ +#define MAX_NCH 2 /* Max supported number of channels (Rockbox changes: 8 -> 2) */ + +#ifndef MAXLINE +#define MAX_LINE 1024 +#endif + +/* decoded pcm sample depth (sample 28bit + sign 1bit) */ +#define TTA_OUTPUT_DEPTH 29 + +/* return codes */ +#define NO_ERROR 0 /* No errors found */ +#define OPEN_ERROR 1 /* Can't open file */ +#define FORMAT_ERROR 2 /* Unknown TTA format version */ +#define PLAYER_ERROR 3 /* Not supported file format */ +#define FILE_ERROR 4 /* File is corrupted */ +#define READ_ERROR 5 /* Can't read from file */ +#define MEMORY_ERROR 6 /* Insufficient memory available */ + +/* Rockbox speciffic: does not use FRAME_TIME */ +/* #define FRAME_TIME 1.04489795918367346939 */ +#define MULTIPLY_FRAME_TIME(x) (256 * (x) / 245) /* = FRAME_TIME * x */ +#define SEEK_STEP (int)MULTIPLY_FRAME_TIME(1000) /* (FRAME_TIME * 1000) */ + +#define ISO_BUFFER_LENGTH (1024*32) +#define ISO_NBUFFERS (8) +#define ISO_BUFFERS_SIZE (4096) /* (ISO_BUFFER_LENGTH*ISO_NBUFFERS) */ +#define PCM_BUFFER_LENGTH (4608) +#define MAX_SEEK_TABLE_SIZE (4096) + +typedef struct { + /* FILE *HANDLE; // file handle (Rockbox does not use) */ + unsigned int FILESIZE; /* compressed size */ + unsigned short NCH; /* number of channels */ + unsigned short BPS; /* bits per sample */ + unsigned short BSIZE; /* byte size */ + unsigned short FORMAT; /* audio format */ + unsigned int SAMPLERATE; /* samplerate (sps) */ + unsigned int DATALENGTH; /* data length in samples */ + unsigned int FRAMELEN; /* frame length */ + unsigned int LENGTH; /* playback time (sec) */ + unsigned int STATE; /* return code */ + unsigned int DATAPOS; /* size of ID3v2 header */ + unsigned int BITRATE; /* average bitrate (kbps) */ + /* double COMPRESS; // compression ratio (Rockbox does not use) */ + /* id3_info ID3; // ID3 information (Rockbox does not use) */ +} tta_info; + +enum tta_seek_type +{ + TTA_SEEK_TIME, + TTA_SEEK_POS, +}; + +/*********************** Library functions *************************/ +/* Rockbox speciffic: open_tta_file() does not use */ + +/* Rockbox speciffic: It is used in place of open_tta_file(). */ +int set_tta_info( // FUNCTION: set tta file info structure + tta_info *info); // file info structure +/* + * RETURN VALUE + * This function returns 0 if success. Otherwise, -1 is returned + * and the variable STATE of the currently using info structure + * is set to indicate the error. + * + */ + +/* Rockbox speciffic: close_tta_file() does not use */ + +/* Rockbox speciffic: set_position() change arguments and return value. */ +/* + * FUNCTION: sets playback position + * pos: seek position + * seek_time_ms / SEEK_STEP (when type is TTA_SEEK_TIME) + * file position (when type is TTA_SEEK_POS) + */ +int set_position ( + unsigned int pos, + enum tta_seek_type type); + +/* + * RETURN VALUE + * This function returns the seeked data position (>= 0) if success. Otherwise, -1 is returned + * and the variable STATE of the currently using info structure + * is set to indicate the error. + * + */ + +int player_init ( // FUNCTION: initializes TTA player + tta_info *info); // file info structure +/* + * RETURN VALUE + * This function returns 0 if success. Otherwise, -1 is returned + * and the variable STATE of the currently using info structure + * is set to indicate the error. + * + */ + +void player_stop (void); // FUNCTION: destroys memory pools + +/* Rockbox speciffic: unsigned char -> int32_t */ +int get_samples ( // FUNCTION: decode PCM_BUFFER_LENGTH samples + int32_t *buffer); // into the current PCM buffer position + +/* + * RETURN VALUE + * This function returns the number of samples successfully decoded. + * Otherwise, -1 is returned and the variable STATE of the currently + * using info structure is set to indicate the error. + * + */ + +const char *get_error_str (int error); // FUNCTION: get error description + +#endif /* TTALIB_H_ */ diff --git a/apps/codecs/tta.c b/apps/codecs/tta.c new file mode 100644 index 000000000..541dc2b7e --- /dev/null +++ b/apps/codecs/tta.c @@ -0,0 +1,132 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2010 Yoshihisa Uchida + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ + +#include "codeclib.h" +#include "codecs/libtta/ttalib.h" + +CODEC_HEADER + +/* + * TTA (True Audio) codec: + * + * References + * [1] TRUE AUDIO CODEC SOFTWARE http://true-audio.com/ + */ + +static int32_t samples[PCM_BUFFER_LENGTH * 2] IBSS_ATTR; + +/* this is the codec entry point */ +enum codec_status codec_main(void) +{ + tta_info info; + int status = CODEC_OK; + unsigned int decodedsamples = 0; + int endofstream; + int new_pos = 0; + int sample_count; + + /* Generic codec initialisation */ + ci->configure(DSP_SET_SAMPLE_DEPTH, TTA_OUTPUT_DEPTH - 1); + + if (codec_init()) + { + DEBUGF("codec_init() error\n"); + status = CODEC_ERROR; + goto exit; + } + +next_track: + while (!*ci->taginfo_ready && !ci->stop_codec) + ci->sleep(1); + + if (set_tta_info(&info) < 0) + { + status = CODEC_ERROR; + goto exit; + } + if (player_init(&info) < 0) + { + status = CODEC_ERROR; + goto exit; + } + + codec_set_replaygain(ci->id3); + + ci->configure(DSP_SWITCH_FREQUENCY, ci->id3->frequency); + if (info.NCH == 2) { + ci->configure(DSP_SET_STEREO_MODE, STEREO_INTERLEAVED); + } else if (info.NCH == 1) { + ci->configure(DSP_SET_STEREO_MODE, STEREO_MONO); + } else { + DEBUGF("CODEC_ERROR: more than 2 channels\n"); + status = CODEC_ERROR; + goto done; + } + + /* The main decoder loop */ + endofstream = 0; + + if (ci->id3->offset > 0) + { + /* Need to save offset for later use (cleared indirectly by advance_buffer) */ + new_pos = set_position(ci->id3->offset, TTA_SEEK_POS); + if (new_pos >= 0) + decodedsamples = new_pos; + ci->seek_complete(); + } + + while (!endofstream) + { + ci->yield(); + if (ci->stop_codec || ci->new_track) + break; + + if (ci->seek_time) + { + new_pos = set_position(ci->seek_time / SEEK_STEP, TTA_SEEK_TIME); + if (new_pos >= 0) + { + decodedsamples = new_pos; + ci->seek_complete(); + } + } + + sample_count = get_samples(samples); + if (sample_count < 0) + { + status = CODEC_ERROR; + break; + } + ci->pcmbuf_insert(samples, NULL, sample_count); + decodedsamples += sample_count; + if (decodedsamples >= info.DATALENGTH) + endofstream = 1; + ci->set_elapsed((uint64_t)info.LENGTH * 1000 * decodedsamples / info.DATALENGTH); + } + status = CODEC_OK; +done: + player_stop(); + if (ci->request_next_track()) + goto next_track; + +exit: + return status; +} diff --git a/apps/filetypes.c b/apps/filetypes.c index 6548d46fd..11b9fa0c0 100644 --- a/apps/filetypes.c +++ b/apps/filetypes.c @@ -105,6 +105,7 @@ static const struct filetype inbuilt_filetypes[] = { { "snd", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA }, { "vox", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA }, { "w64", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA }, + { "tta", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA }, #endif { "m3u", FILE_ATTR_M3U, Icon_Playlist, LANG_PLAYLIST }, { "m3u8",FILE_ATTR_M3U, Icon_Playlist, LANG_PLAYLIST }, diff --git a/apps/metadata.c b/apps/metadata.c index 97f376035..076b75395 100644 --- a/apps/metadata.c +++ b/apps/metadata.c @@ -179,6 +179,9 @@ const struct afmt_entry audio_formats[AFMT_NUM_CODECS] = /* Wave64 */ [AFMT_WAVE64] = AFMT_ENTRY("WAVE64", "wav64", NULL, "w64\0" ), + /* True Audio */ + [AFMT_TTA] = + AFMT_ENTRY("TTA", "tta", NULL, "tta\0" ), #endif }; @@ -494,6 +497,14 @@ bool get_metadata(struct mp3entry* id3, int fd, const char* trackname) } break; + case AFMT_TTA: + if (!get_tta_metadata(fd, id3)) + { + DEBUGF("get_tta_metadata error\n"); + return false; + } + break; + #endif /* CONFIG_CODEC == SWCODEC */ default: diff --git a/apps/metadata.h b/apps/metadata.h index aa59eac2d..6b15f6dea 100644 --- a/apps/metadata.h +++ b/apps/metadata.h @@ -83,6 +83,7 @@ enum AFMT_AU, /* Sun Audio file */ AFMT_VOX, /* VOX */ AFMT_WAVE64, /* Wave64 */ + AFMT_TTA, /* True Audio */ #endif /* add new formats at any index above this line to have a sensible order - diff --git a/apps/metadata/metadata_parsers.h b/apps/metadata/metadata_parsers.h index 0e813ccb4..7238b71a3 100644 --- a/apps/metadata/metadata_parsers.h +++ b/apps/metadata/metadata_parsers.h @@ -22,6 +22,7 @@ char* id3_get_num_genre(unsigned int genre_num); int getid3v2len(int fd); bool setid3v1title(int fd, struct mp3entry *entry); +void setid3v2title(int fd, struct mp3entry *entry); bool get_mp3_metadata(int fd, struct mp3entry* id3, const char *filename); bool get_adx_metadata(int fd, struct mp3entry* id3); @@ -46,3 +47,4 @@ bool get_smaf_metadata(int fd, struct mp3entry* id3); bool get_au_metadata(int fd, struct mp3entry* id3); bool get_vox_metadata(int fd, struct mp3entry* id3); bool get_wave64_metadata(int fd, struct mp3entry* id3); +bool get_tta_metadata(int fd, struct mp3entry* id3); diff --git a/apps/metadata/mp3.c b/apps/metadata/mp3.c index 49d5c7362..0f786bd52 100644 --- a/apps/metadata/mp3.c +++ b/apps/metadata/mp3.c @@ -640,7 +640,7 @@ bool setid3v1title(int fd, struct mp3entry *entry) * * Returns: true if a title was found and created, else false */ -static void setid3v2title(int fd, struct mp3entry *entry) +void setid3v2title(int fd, struct mp3entry *entry) { int minframesize; int size; diff --git a/apps/metadata/tta.c b/apps/metadata/tta.c new file mode 100644 index 000000000..1d3d95f11 --- /dev/null +++ b/apps/metadata/tta.c @@ -0,0 +1,123 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2010 Yoshihisa Uchida + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ +#include +#include +#include +#include +#include + +#include "system.h" +#include "metadata.h" +#include "metadata_common.h" +#include "metadata_parsers.h" +#include "logf.h" + +#define TTA1_SIGN 0x31415454 + +#define TTA_HEADER_ID 0 +#define TTA_HEADER_AUDIO_FORMAT (TTA_HEADER_ID + sizeof(unsigned int)) +#define TTA_HEADER_NUM_CHANNELS (TTA_HEADER_AUDIO_FORMAT + sizeof(unsigned short)) +#define TTA_HEADER_BITS_PER_SAMPLE (TTA_HEADER_NUM_CHANNELS + sizeof(unsigned short)) +#define TTA_HEADER_SAMPLE_RATE (TTA_HEADER_BITS_PER_SAMPLE + sizeof(unsigned short)) +#define TTA_HEADER_DATA_LENGTH (TTA_HEADER_SAMPLE_RATE + sizeof(unsigned int)) +#define TTA_HEADER_CRC32 (TTA_HEADER_DATA_LENGTH + sizeof(unsigned int)) +#define TTA_HEADER_SIZE (TTA_HEADER_CRC32 + sizeof(unsigned int)) + +#define TTA_HEADER_GETTER_ID(x) get_long_le(x) +#define TTA_HEADER_GETTER_AUDIO_FORMAT(x) get_short_le(x) +#define TTA_HEADER_GETTER_NUM_CHANNELS(x) get_short_le(x) +#define TTA_HEADER_GETTER_BITS_PER_SAMPLE(x) get_short_le(x) +#define TTA_HEADER_GETTER_SAMPLE_RATE(x) get_long_le(x) +#define TTA_HEADER_GETTER_DATA_LENGTH(x) get_long_le(x) +#define TTA_HEADER_GETTER_CRC32(x) get_long_le(x) + +#define GET_HEADER(x, tag) TTA_HEADER_GETTER_ ## tag((x) + TTA_HEADER_ ## tag) + +static void read_id3_tags(int fd, struct mp3entry* id3) +{ + id3->title = NULL; + id3->filesize = filesize(fd); + id3->id3v2len = getid3v2len(fd); + id3->tracknum = 0; + id3->discnum = 0; + id3->vbr = false; /* All TTA files are CBR */ + + /* first get id3v2 tags. if no id3v2 tags ware found, get id3v1 tags */ + if (id3->id3v2len) + { + setid3v2title(fd, id3); + id3->first_frame_offset = id3->id3v2len; + return; + } + setid3v1title(fd, id3); +} + +bool get_tta_metadata(int fd, struct mp3entry* id3) +{ + unsigned char ttahdr[TTA_HEADER_SIZE]; + unsigned int datasize; + unsigned int origsize; + int bps; + + lseek(fd, 0, SEEK_SET); + + /* read id3 tags */ + read_id3_tags(fd, id3); + lseek(fd, id3->id3v2len, SEEK_SET); + + /* read TTA header */ + if (read(fd, ttahdr, TTA_HEADER_SIZE) < 0) + return false; + + /* check for TTA3 signature */ + if ((GET_HEADER(ttahdr, ID)) != TTA1_SIGN) + return false; + + /* skip check CRC */ + + id3->channels = (GET_HEADER(ttahdr, NUM_CHANNELS)); + id3->frequency = (GET_HEADER(ttahdr, SAMPLE_RATE)); + id3->length = ((GET_HEADER(ttahdr, DATA_LENGTH)) / id3->frequency) * 1000LL; + bps = (GET_HEADER(ttahdr, BITS_PER_SAMPLE)); + + datasize = id3->filesize - id3->first_frame_offset; + origsize = (GET_HEADER(ttahdr, DATA_LENGTH)) * ((bps + 7) / 8) * id3->channels; + + id3->bitrate = (int) ((uint64_t) datasize * id3->frequency * id3->channels * bps + / (origsize * 1000LL)); + + /* output header info (for debug) */ + DEBUGF("TTA header info ----\n"); + DEBUGF("id: %x\n", (unsigned int)(GET_HEADER(ttahdr, ID))); + DEBUGF("channels: %d\n", id3->channels); + DEBUGF("frequency: %ld\n", id3->frequency); + DEBUGF("length: %ld\n", id3->length); + DEBUGF("bitrate: %d\n", id3->bitrate); + DEBUGF("bits per sample: %d\n", bps); + DEBUGF("compressed size: %d\n", datasize); + DEBUGF("original size: %d\n", origsize); + DEBUGF("id3----\n"); + DEBUGF("artist: %s\n", id3->artist); + DEBUGF("title: %s\n", id3->title); + DEBUGF("genre: %s\n", id3->genre_string); + + return true; +} -- 2.11.4.GIT