diff options
author | Alexey Yakovenko <wakeroid@gmail.com> | 2010-05-31 22:27:19 +0200 |
---|---|---|
committer | Alexey Yakovenko <wakeroid@gmail.com> | 2010-05-31 22:27:19 +0200 |
commit | d1cf5097c00388345932d244e8170d35be46df3d (patch) | |
tree | d58fb82dbc3bf42d18ba86ecb4e72cd95e8f46bc | |
parent | 5f86a54bab8190c4253a2c3254667cb77df5a800 (diff) |
WIP tta plugin
-rw-r--r-- | configure.ac | 12 | ||||
-rw-r--r-- | plugins/ffmpeg/ffmpeg.c | 2 | ||||
-rw-r--r-- | plugins/tta/Makefile.am | 14 | ||||
-rw-r--r-- | plugins/tta/README | 72 | ||||
-rw-r--r-- | plugins/tta/filter.h | 117 | ||||
-rw-r--r-- | plugins/tta/ttadec.c | 711 | ||||
-rw-r--r-- | plugins/tta/ttadec.h | 307 | ||||
-rw-r--r-- | plugins/tta/ttalib.h | 152 | ||||
-rw-r--r-- | plugins/tta/ttaplug.c | 315 |
9 files changed, 1699 insertions, 3 deletions
diff --git a/configure.ac b/configure.ac index 3958c1fa..a9f1960a 100644 --- a/configure.ac +++ b/configure.ac @@ -86,7 +86,8 @@ AC_ARG_ENABLE(dumb, [ --disable-dumb disable D.U.M.B. plugin for M AC_ARG_ENABLE(notify, [ --disable-notify disable notification-daemon support plugin (default: enabled)], [enable_notify=$enableval], [enable_notify=yes]) AC_ARG_ENABLE(emidi, [ --disable-emidi disable emidi plugin (default: enabled)], [enable_emidi=$enableval], [enable_emidi=yes]) AC_ARG_ENABLE(musepack, [ --disable-musepack disable musepack plugin (default: enabled)], [enable_musepack=$enableval], [enable_musepack=yes]) -AC_ARG_ENABLE(wildmidi, [ --disable-wildmidi disable wildmidi plugin (default: enabled)], [enable_wildmidi=$enableval], [enable_wildmidi=yes]) +AC_ARG_ENABLE(wildmidi, [ --disable-wildmidi disable wildmidi plugin (default: enabled)], [enable_wildmidi=$enableval], [enable_wildmidi=yes]) +AC_ARG_ENABLE(tta, [ --disable-tta disable tta plugin (default: enabled)], [enable_tta=$enableval], [enable_tta=yes]) PKG_CHECK_MODULES(DEPS, samplerate) @@ -366,7 +367,11 @@ if test "x$enable_wildmidi" != "xno" ; then HAVE_WILDMIDI=yes fi -PLUGINS_DIRS="plugins/lastfm plugins/mpgmad plugins/vorbis plugins/flac plugins/wavpack plugins/sndfile plugins/vfs_curl plugins/cdda plugins/gtkui plugins/alsa plugins/ffmpeg plugins/hotkeys plugins/oss plugins/artwork plugins/adplug plugins/ffap plugins/sid plugins/nullout plugins/supereq plugins/vtx plugins/gme plugins/dumb plugins/pulse plugins/notify plugins/emidi plugins/musepack plugins/wildmidi" +if test "x$enable_tta" != "xno" ; then + HAVE_TTA=yes +fi + +PLUGINS_DIRS="plugins/lastfm plugins/mpgmad plugins/vorbis plugins/flac plugins/wavpack plugins/sndfile plugins/vfs_curl plugins/cdda plugins/gtkui plugins/alsa plugins/ffmpeg plugins/hotkeys plugins/oss plugins/artwork plugins/adplug plugins/ffap plugins/sid plugins/nullout plugins/supereq plugins/vtx plugins/gme plugins/dumb plugins/pulse plugins/notify plugins/emidi plugins/musepack plugins/wildmidi plugins/tta" AM_CONDITIONAL(HAVE_VORBIS, test "x$HAVE_VORBISPLUGIN" = "xyes") AM_CONDITIONAL(HAVE_FLAC, test "x$HAVE_FLACPLUGIN" = "xyes") @@ -395,6 +400,7 @@ AM_CONDITIONAL(HAVE_NOTIFY, test "x$HAVE_NOTIFY" = "xyes") AM_CONDITIONAL(HAVE_EMIDI, test "x$HAVE_EMIDI" = "xyes") AM_CONDITIONAL(HAVE_MUSEPACK, test "x$HAVE_MUSEPACK" = "xyes") AM_CONDITIONAL(HAVE_WILDMIDI, test "x$HAVE_WILDMIDI" = "xyes") +AM_CONDITIONAL(HAVE_TTA, test "x$HAVE_TTA" = "xyes") AC_SUBST(PLUGINS_DIRS) @@ -450,6 +456,7 @@ PRINT_PLUGIN_INFO([notify],[notification-daemon support plugin],[test "x$HAVE_NO PRINT_PLUGIN_INFO([emidi],[Emu de MIDI player plugin],[test "x$HAVE_EMIDI" = "xyes"]) PRINT_PLUGIN_INFO([musepack],[musepack player plugin],[test "x$HAVE_MUSEPACK" = "xyes"]) PRINT_PLUGIN_INFO([wildmidi],[WildMidi player plugin],[test "x$HAVE_WILDMIDI" = "xyes"]) +PRINT_PLUGIN_INFO([tta],[TTA player plugin],[test "x$HAVE_TTA" = "xyes"]) echo @@ -484,6 +491,7 @@ plugins/notify/Makefile plugins/emidi/Makefile plugins/musepack/Makefile plugins/wildmidi/Makefile +plugins/tta/Makefile intl/Makefile po/Makefile.in deadbeef.desktop diff --git a/plugins/ffmpeg/ffmpeg.c b/plugins/ffmpeg/ffmpeg.c index 47117e96..6fa3623f 100644 --- a/plugins/ffmpeg/ffmpeg.c +++ b/plugins/ffmpeg/ffmpeg.c @@ -55,7 +55,7 @@ static DB_decoder_t plugin; static DB_functions_t *deadbeef; -static const char * exts[] = { "m4a", "mp4", "mp+", "mpp", "wma", "shn", "aa3", "oma", "ac3", "vqf", "tta", NULL }; +static const char * exts[] = { "m4a", "mp4", "mp+", "mpp", "wma", "shn", "aa3", "oma", "ac3", "vqf", NULL }; enum { FT_AAC = 0, diff --git a/plugins/tta/Makefile.am b/plugins/tta/Makefile.am new file mode 100644 index 00000000..7d377642 --- /dev/null +++ b/plugins/tta/Makefile.am @@ -0,0 +1,14 @@ +if HAVE_TTA +ttapath=@top_srcdir@/plugins/tta + +EXTRA_DIST = $(ttapath)/README + +pkglib_LTLIBRARIES = tta.la + +tta_la_SOURCES = ttaplug.c filter.h ttadec.c ttadec.h ttalib.h + +tta_la_LDFLAGS = -module + +AM_CFLAGS = $(CFLAGS) -std=c99 -fPIC +endif + diff --git a/plugins/tta/README b/plugins/tta/README new file mode 100644 index 00000000..8b1a1b23 --- /dev/null +++ b/plugins/tta/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 <ald@true-audio.com> + Pavel Zhilin <pzh@true-audio.com> + +* 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/plugins/tta/filter.h b/plugins/tta/filter.h new file mode 100644 index 00000000..6e8b13da --- /dev/null +++ b/plugins/tta/filter.h @@ -0,0 +1,117 @@ +/* + * filter.h + * + * Description: TTAv1 filter functions + * 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. + */ + +#ifndef FILTER_H +#define FILTER_H + +///////// Filter Settings ////////// +static int flt_set[3] = {10, 9, 10}; + +__inline void +memshl (register int *pA, register int *pB) { + *pA++ = *pB++; + *pA++ = *pB++; + *pA++ = *pB++; + *pA++ = *pB++; + *pA++ = *pB++; + *pA++ = *pB++; + *pA++ = *pB++; + *pA = *pB; +} + +__inline void +hybrid_filter (fltst *fs, int *in) { + register int *pA = fs->dl; + register int *pB = fs->qm; + register int *pM = fs->dx; + register int sum = fs->round; + + if (!fs->error) { + sum += *pA++ * *pB, pB++; + sum += *pA++ * *pB, pB++; + sum += *pA++ * *pB, pB++; + sum += *pA++ * *pB, pB++; + sum += *pA++ * *pB, pB++; + sum += *pA++ * *pB, pB++; + sum += *pA++ * *pB, pB++; + sum += *pA++ * *pB, pB++; pM += 8; + } else if (fs->error < 0) { + sum += *pA++ * (*pB -= *pM++), pB++; + sum += *pA++ * (*pB -= *pM++), pB++; + sum += *pA++ * (*pB -= *pM++), pB++; + sum += *pA++ * (*pB -= *pM++), pB++; + sum += *pA++ * (*pB -= *pM++), pB++; + sum += *pA++ * (*pB -= *pM++), pB++; + sum += *pA++ * (*pB -= *pM++), pB++; + sum += *pA++ * (*pB -= *pM++), pB++; + } else { + sum += *pA++ * (*pB += *pM++), pB++; + sum += *pA++ * (*pB += *pM++), pB++; + sum += *pA++ * (*pB += *pM++), pB++; + sum += *pA++ * (*pB += *pM++), pB++; + sum += *pA++ * (*pB += *pM++), pB++; + sum += *pA++ * (*pB += *pM++), pB++; + sum += *pA++ * (*pB += *pM++), pB++; + sum += *pA++ * (*pB += *pM++), pB++; + } + + *(pM-0) = ((*(pA-1) >> 30) | 1) << 2; + *(pM-1) = ((*(pA-2) >> 30) | 1) << 1; + *(pM-2) = ((*(pA-3) >> 30) | 1) << 1; + *(pM-3) = ((*(pA-4) >> 30) | 1); + + fs->error = *in; + *in += (sum >> fs->shift); + *pA = *in; + + *(pA-1) = *(pA-0) - *(pA-1); + *(pA-2) = *(pA-1) - *(pA-2); + *(pA-3) = *(pA-2) - *(pA-3); + + memshl (fs->dl, fs->dl + 1); + memshl (fs->dx, fs->dx + 1); +} + +void +filter_init (fltst *fs, int shift) { + memset (fs, 0, sizeof(fltst)); + fs->shift = shift; + fs->round = 1 << (shift - 1); +} + +#endif /* FILTER_H */ diff --git a/plugins/tta/ttadec.c b/plugins/tta/ttadec.c new file mode 100644 index 00000000..c843e08f --- /dev/null +++ b/plugins/tta/ttadec.c @@ -0,0 +1,711 @@ +/* + * 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 <stdlib.h> +#include <stdio.h> +#include <string.h> + +#include "ttalib.h" +#include "ttadec.h" +#include "filter.h" + +/******************* static variables and structures *******************/ + +static unsigned char isobuffers[ISO_BUFFERS_SIZE + 4]; +static unsigned char *iso_buffers_end = isobuffers + ISO_BUFFERS_SIZE; +static unsigned int pcm_buffer_size; + +static decoder tta[MAX_NCH]; // decoder state +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 +static unsigned int *seek_table; // 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; + +static int read_id3_tags (tta_info *info); + +/************************* 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->HANDLE)) { \ + 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->HANDLE)) { \ + 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--; + +static void init_buffer_read() { + frame_crc32 = 0xFFFFFFFFUL; + bit_count = bit_cache = 0; + bitpos = iso_buffers_end; +} + +static int done_buffer_read() { + unsigned int crc32, rbytes; + + frame_crc32 ^= 0xFFFFFFFFUL; + rbytes = iso_buffers_end - bitpos; + + if (rbytes < sizeof(int)) { + memcpy(isobuffers, bitpos, 4); + if (!fread(isobuffers + rbytes, 1, + ISO_BUFFERS_SIZE - rbytes, ttainfo->HANDLE)) + return -1; + bitpos = isobuffers; + } + + 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 open_tta_file (const char *filename, tta_info *info, unsigned int data_offset) { + unsigned int checksum; + unsigned int datasize; + unsigned int origsize; + FILE *infile; + tta_hdr ttahdr; + + // clear the memory + memset (info, 0, sizeof(tta_info)); + + // open file + infile = fopen(filename, "rb"); + if (!infile) { + info->STATE = OPEN_ERROR; + return -1; + } + info->HANDLE = infile; + + // get file size + fseek (infile, 0, SEEK_END); + info->FILESIZE = ftell (infile); + fseek (infile, 0, SEEK_SET); + + // read id3 tags + if (!data_offset) { +// if ((data_offset = skip_id3_tag (info)) < 0) { + if ((data_offset = read_id3_tags (info)) < 0) { + fclose(infile); + return -1; + } + } else fseek (infile, data_offset, SEEK_SET); + + // read TTA header + if (fread (&ttahdr, 1, sizeof (ttahdr), infile) == 0) { + fclose (infile); + info->STATE = READ_ERROR; + return -1; + } + + // check for TTA3 signature + if (ENDSWAP_INT32(ttahdr.TTAid) != TTA1_SIGN) { + fclose (infile); + 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) { + fclose (infile); + 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)) { + fclose (infile); + info->STATE = FORMAT_ERROR; + 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) (FRAME_TIME * ttahdr.SampleRate); + info->LENGTH = ttahdr.DataLength / ttahdr.SampleRate; + info->DATAPOS = data_offset; + + datasize = info->FILESIZE - info->DATAPOS; + origsize = info->DATALENGTH * info->BSIZE * info->NCH; + + info->COMPRESS = (double) datasize / origsize; + info->BITRATE = (int) (info->COMPRESS * info->SAMPLERATE * + info->NCH * info->BPS / 1000); + + 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) { + unsigned int seek_pos; + + if (pos >= fframes) return 0; + if (!st_state) { + ttainfo->STATE = FILE_ERROR; + return -1; + } + + seek_pos = ttainfo->DATAPOS + seek_table[data_pos = pos]; + if (fseek(ttainfo->HANDLE, seek_pos, SEEK_SET) < 0) { + ttainfo->STATE = READ_ERROR; + return -1; + } + + data_cur = 0; + framelen = 0; + + // init bit reader + init_buffer_read(); + + return 0; +} + +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); + + seek_table = (unsigned int *) malloc(st_size); + if (!seek_table) { + ttainfo->STATE = MEMORY_ERROR; + return -1; + } + + // read seek table + if (!fread(seek_table, st_size, 1, ttainfo->HANDLE)) { + 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(); + + pcm_buffer_size = PCM_BUFFER_LENGTH * ttainfo->BSIZE * ttainfo->NCH; + maxvalue = (1UL << ttainfo->BPS) - 1; + + return 0; +} + +void close_tta_file (tta_info *info) { + if (info->HANDLE) { + fclose (info->HANDLE); + info->HANDLE = NULL; + } +} + +void player_stop () { + if (seek_table) { + free(seek_table); + seek_table = NULL; + } +} + +int get_samples (byte *buffer) { + unsigned int k, depth, unary, binary; + byte *p = buffer; + decoder *dec = tta; + int *prev = cache; + int value, res; + + for (res = 0; p < buffer + pcm_buffer_size;) { + fltst *fst = &dec->fst; + adapt *rice = &dec->rice; + int *last = &dec->last; + + if (data_cur == framelen) { + if (data_pos == fframes) break; + if (framelen && done_buffer_read()) { + if (set_position(data_pos)) 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; + + switch (depth) { + case 1: + 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]; + default: + 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); + + // decompress stage 1: adaptive hybrid filter + hybrid_filter(fst, &value); + + // decompress stage 2: fixed order 1 prediction + switch (ttainfo->BSIZE) { + case 1: value += PREDICTOR1(*last, 4); break; // bps 8 + case 2: value += PREDICTOR1(*last, 5); break; // bps 16 + case 3: value += PREDICTOR1(*last, 5); break; // bps 24 + } *last = value; + + // check for errors + if (abs(value) > maxvalue) { + unsigned int tail = + pcm_buffer_size / (ttainfo->BSIZE * ttainfo->NCH) - res; + memset(buffer, 0, pcm_buffer_size); + data_cur += tail; res += tail; + break; + } + + if (dec < tta + (ttainfo->NCH - 1)) { + *prev++ = value; dec++; + } else { + *prev = value; + if (ttainfo->NCH > 1) { + int *r = prev - 1; + for (*prev += *r/2; r >= cache; r--) + *r = *(r + 1) - *r; + for (r = cache; r < prev; r++) + WRITE_BUFFER(r, ttainfo->BSIZE, p) + } + WRITE_BUFFER(prev, ttainfo->BSIZE, p) + prev = cache; + data_cur++; res++; + dec = tta; + } + } + + return res; +} + +/* + * Description: ID3 tags manipulation routines + * Provides read access to ID3 tags v1.1, v2.3.x, v2.4.x + * Supported ID3v2 frames: Title, Artist, Album, Track, + * Year, Genre, Comment. + * + * Copyright (c) 2004 Alexander Djourik. All rights reserved. + * + */ + +static unsigned int unpack_sint28 (const char *ptr) { + unsigned int value = 0; + + if (ptr[0] & 0x80) return 0; + + value = value | (ptr[0] & 0x7f); + value = (value << 7) | (ptr[1] & 0x7f); + value = (value << 7) | (ptr[2] & 0x7f); + value = (value << 7) | (ptr[3] & 0x7f); + + return value; +} + +static unsigned int unpack_sint32 (const char *ptr) { + unsigned int value = 0; + + if (ptr[0] & 0x80) return 0; + + value = (value << 8) | ptr[0]; + value = (value << 8) | ptr[1]; + value = (value << 8) | ptr[2]; + value = (value << 8) | ptr[3]; + + return value; +} + +static int get_frame_id (const char *id) { + if (!memcmp(id, "TIT2", 4)) return TIT2; // Title + if (!memcmp(id, "TPE1", 4)) return TPE1; // Artist + if (!memcmp(id, "TALB", 4)) return TALB; // Album + if (!memcmp(id, "TRCK", 4)) return TRCK; // Track + if (!memcmp(id, "TYER", 4)) return TYER; // Year + if (!memcmp(id, "TCON", 4)) return TCON; // Genre + if (!memcmp(id, "COMM", 4)) return COMM; // Comment + return 0; +} + +#if 0 + +static int skip_id3_tag (tta_info *info) { + id3v2_tag id3v2; + int id3v2_size; + + //////////////////////////////////////// + // skip ID3v2 tag + if (!fread(&id3v2, 1, sizeof(id3v2_tag), info->HANDLE)) + goto read_error; + + if (memcmp(id3v2.id, "ID3", 3)) { + if (fseek (info->HANDLE, 0, SEEK_SET) < 0) + goto read_error; + return 0; + } + + if (id3v2.size[0] & 0x80) goto file_error; + id3v2_size = unpack_sint28(id3v2.size); + + id3v2_size += (id3v2.flags & + ID3_FOOTERPRESENT_FLAG) ? 20 : 10; + fseek (info->HANDLE, id3v2_size, SEEK_SET); + info->ID3.size = id3v2_size; + + return id3v2_size; + +file_error: + ttainfo->STATE = FILE_ERROR; + return -1; + +read_error: + ttainfo->STATE = READ_ERROR; + return -1; +} + +#endif + +static int read_id3_tags (tta_info *info) { + id3v1_tag id3v1; + id3v2_tag id3v2; + id3v2_frame frame_header; + int id3v2_size; + char *buffer = NULL; + char *ptr; + + //////////////////////////////////////// + // ID3v1 support + if (fseek (info->HANDLE, -(int) sizeof(id3v1_tag), + SEEK_END) < 0) goto read_error; + + if (!fread (&id3v1, sizeof(id3v1_tag), 1, + info->HANDLE)) goto read_error; + + if (!memcmp (id3v1.id, "TAG", 3)) { + memcpy(info->ID3.title, id3v1.title, 30); + memcpy(info->ID3.artist, id3v1.artist, 30); + memcpy(info->ID3.album, id3v1.album, 30); + memcpy(info->ID3.year, id3v1.year, 4); + memcpy(info->ID3.comment, id3v1.comment, 28); + + if (id3v1.genre > GENRES-1) id3v1.genre = 12; + sprintf(info->ID3.track, "%02d", id3v1.track); + if (id3v1.genre && id3v1.genre != 0xFF) + sprintf(info->ID3.genre, "%s", genre[id3v1.genre]); + info->ID3.id3has |= 1; + } + + if (fseek (info->HANDLE, 0, SEEK_SET) < 0) + goto read_error; + + //////////////////////////////////////// + // ID3v2 minimal support + if (!fread(&id3v2, 1, sizeof(id3v2_tag), info->HANDLE)) + goto read_error; + + if (memcmp(id3v2.id, "ID3", 3)) { + if (fseek (info->HANDLE, 0, SEEK_SET) < 0) + goto read_error; + return 0; + } + + if (id3v2.size[0] & 0x80) goto file_error; + id3v2_size = unpack_sint28(id3v2.size); + + if (!(buffer = (unsigned char *) malloc (id3v2_size))) { + ttainfo->STATE = MEMORY_ERROR; + goto read_done; + } + + if ((id3v2.flags & ID3_UNSYNCHRONISATION_FLAG) || + (id3v2.flags & ID3_EXPERIMENTALTAG_FLAG) || + (id3v2.version < 3)) goto read_done; + + if (!fread(buffer, 1, id3v2_size, info->HANDLE)) { + free (buffer); + goto read_error; + } + + ptr = buffer; + + // skip extended header if present + if (id3v2.flags & ID3_EXTENDEDHEADER_FLAG) { + int offset = unpack_sint32(ptr); + ptr += offset; + } + + // read id3v2 frames + while (ptr - buffer < id3v2_size) { + char *data = NULL; + int data_size, frame_id; + int size = 0; + + // get frame header + memcpy(&frame_header, ptr, sizeof(id3v2_frame)); + ptr += sizeof(id3v2_frame); + data_size = unpack_sint32(frame_header.size); + + // skip unsupported frames + if (!(frame_id = get_frame_id(frame_header.id)) || + frame_header.flags & FRAME_COMPRESSION_FLAG || + frame_header.flags & FRAME_ENCRYPTION_FLAG || + frame_header.flags & FRAME_UNSYNCHRONISATION_FLAG || ( + *ptr != FIELD_TEXT_ISO_8859_1 && + *ptr != FIELD_TEXT_UTF_8)) { + ptr += data_size; + continue; + } + + data_size--; ptr++; + + switch (frame_id) { + case TIT2: data = info->ID3.title; + size = sizeof(info->ID3.title) - 1; break; + case TPE1: data = info->ID3.artist; + size = sizeof(info->ID3.artist) - 1; break; + case TALB: data = info->ID3.album; + size = sizeof(info->ID3.album) - 1; break; + case TRCK: data = info->ID3.track; + size = sizeof(info->ID3.track) - 1; break; + case TYER: data = info->ID3.year; + size = sizeof(info->ID3.year) - 1; break; + case TCON: data = info->ID3.genre; + size = sizeof(info->ID3.genre) - 1; break; + case COMM: data = info->ID3.comment; + size = sizeof(info->ID3.comment) - 1; + data_size -= 3; ptr += 3; + + // skip zero short description + if (*ptr == 0) { data_size--; ptr++; } + break; + } + + if (data_size < size) size = data_size; + memcpy(data, ptr, size); data[size] = '\0'; + ptr += data_size; + } + + info->ID3.id3has |= 2; + +read_done: + if (buffer) free(buffer); + + id3v2_size += (id3v2.flags & + ID3_FOOTERPRESENT_FLAG) ? 20 : 10; + fseek (info->HANDLE, id3v2_size, SEEK_SET); + info->ID3.size = id3v2_size; + + return id3v2_size; + +file_error: + ttainfo->STATE = FILE_ERROR; + return -1; + +read_error: + ttainfo->STATE = READ_ERROR; + return -1; +} + +/* eof */ diff --git a/plugins/tta/ttadec.h b/plugins/tta/ttadec.h new file mode 100644 index 00000000..90ae1562 --- /dev/null +++ b/plugins/tta/ttadec.h @@ -0,0 +1,307 @@ +/* + * ttadec.h + * + * Description: TTAv1 decoder definitions and prototypes + * 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. + */ + +#ifndef TTADEC_H_ +#define TTADEC_H_ + +#ifdef _WIN32 +#pragma pack(1) +#define __ATTRIBUTE_PACKED__ +#else +#define __ATTRIBUTE_PACKED__ __attribute__((packed)) +#endif + +#define TTA1_SIGN 0x31415454 +#define FRAME_TIME 1.04489795918367346939 +#define MAX_ORDER 8 + +#ifndef WAVE_FORMAT_PCM +#define WAVE_FORMAT_PCM 1 +#endif + +#ifdef _WIN32 + typedef unsigned __int64 uint64; +#else + typedef unsigned long long uint64; +#endif + +const unsigned int crc32_table[256] = { + 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 +}; + +const unsigned int bit_mask[] = { + 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 +}; + +const unsigned int bit_shift[] = { + 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 +}; + +const unsigned int *shift_16 = bit_shift + 4; + +typedef unsigned char byte; + +#ifdef _BIG_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):(-(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 shift; + int round; + int error; + int mutex; + int qm[MAX_ORDER+1]; + int dx[MAX_ORDER+1]; + int dl[MAX_ORDER+1]; +} fltst; + +typedef struct { + fltst fst; + adapt rice; + int last; +} decoder; + +/***************************************************************** + * ID3 reader definitions and prototypes * + *****************************************************************/ + +#define ID3_VERSION 3 + +/* ID3 common headers set */ + +#define TIT2 1 +#define TPE1 2 +#define TALB 3 +#define TRCK 4 +#define TYER 5 +#define TCON 6 +#define COMM 7 + +/* ID3 tag checked flags */ + +#define ID3_UNSYNCHRONISATION_FLAG 0x80 +#define ID3_EXTENDEDHEADER_FLAG 0x40 +#define ID3_EXPERIMENTALTAG_FLAG 0x20 +#define ID3_FOOTERPRESENT_FLAG 0x10 + +/* ID3 frame checked flags */ + +#define FRAME_COMPRESSION_FLAG 0x0008 +#define FRAME_ENCRYPTION_FLAG 0x0004 +#define FRAME_UNSYNCHRONISATION_FLAG 0x0002 + +/* ID3 field text encoding */ + +#define FIELD_TEXT_ISO_8859_1 0x00 +#define FIELD_TEXT_UTF_16 0x01 +#define FIELD_TEXT_UTF_16BE 0x02 +#define FIELD_TEXT_UTF_8 0x03 + +typedef struct { + unsigned char id[3]; + unsigned char title[30]; + unsigned char artist[30]; + unsigned char album[30]; + unsigned char year[4]; + unsigned char comment[28]; + unsigned char zero; + unsigned char track; + unsigned char genre; +} __ATTRIBUTE_PACKED__ id3v1_tag; + +typedef struct { + unsigned char id[3]; + unsigned short version; + unsigned char flags; + unsigned char size[4]; +} __ATTRIBUTE_PACKED__ id3v2_tag; + +typedef struct { + unsigned char id[4]; + unsigned char size[4]; + unsigned short flags; +} __ATTRIBUTE_PACKED__ id3v2_frame; + +static char *genre[] = { + "Blues", "Classic Rock", "Country", "Dance", "Disco", "Funk", + "Grunge", "Hip-Hop", "Jazz", "Metal", "New Age", "Oldies", + "Other", "Pop", "R&B", "Rap", "Reggae", "Rock", "Techno", + "Industrial", "Alternative", "Ska", "Death Metal", "Pranks", + "Soundtrack", "Euro-Techno", "Ambient", "Trip-Hop", "Vocal", + "Jazz+Funk", "Fusion", "Trance", "Classical", "Instrumental", + "Acid", "House", "Game", "Sound Clip", "Gospel", "Noise", + "AlternRock", "Bass", "Soul", "Punk", "Space", "Meditative", + "Instrumental Pop", "Instrumental Rock", "Ethnic", "Gothic", + "Darkwave", "Techno-Industrial", "Electronic", "Pop-Folk", + "Eurodance", "Dream", "Southern Rock", "Comedy", "Cult", + "Gangsta", "Top 40", "Christian Rap", "Pop/Funk", "Jungle", + "Native American", "Cabaret", "New Wave", "Psychedelic", + "Rave", "Showtunes", "Trailer", "Lo-Fi", "Tribal","Acid Punk", + "Acid Jazz", "Polka", "Retro", "Musical", "Rock & Roll", + "Hard Rock", "Folk", "Folk/Rock", "National Folk", "Swing", + "Fast-Fusion", "Bebob", "Latin", "Revival", "Celtic", + "Bluegrass", "Avantgarde", "Gothic Rock", "Progressive Rock", + "Psychedelic Rock", "Symphonic Rock", "Slow Rock", "Big Band", + "Chorus", "Easy Listening", "Acoustic", "Humour", "Speech", + "Chanson", "Opera", "Chamber Music", "Sonata", "Symphony", + "Booty Bass", "Primus", "Porn Groove", "Satire", "Slow Jam", + "Club", "Tango", "Samba", "Folklore", "Ballad", "Power Ballad", + "Rhythmic Soul", "Freestyle", "Duet", "Punk Rock", "Drum Solo", + "A Cappella", "Euro-House", "Dance Hall", "Goa", "Drum & Bass", + "Club-House", "Hardcore", "Terror", "Indie", "BritPop", + "Negerpunk", "Polsk Punk", "Beat", "Christian Gangsta Rap", + "Heavy Metal", "Black Metal", "Crossover", + "Contemporary Christian", "Christian Rock", "Merengue", + "Salsa", "Thrash Metal", "Anime", "JPop", "Synthpop" +}; + +#define GENRES (sizeof genre / sizeof genre[0]) + +#endif /* TTADEC_H_ */ diff --git a/plugins/tta/ttalib.h b/plugins/tta/ttalib.h new file mode 100644 index 00000000..9c2a34b4 --- /dev/null +++ b/plugins/tta/ttalib.h @@ -0,0 +1,152 @@ +/* + * ttalib.h + * + * Description: TTAv1 player library prototypes + * 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. + */ + +#ifndef TTALIB_H_ +#define TTALIB_H_ + +#include <stdio.h> + +//#define _BIG_ENDIAN + +#define MAX_BPS 24 // Max supported Bit resolution +#define MAX_NCH 8 // Max supported number of channels + +#ifndef MAXLINE +#define MAX_LINE 1024 +#endif + +// 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 + +#define FRAME_TIME 1.04489795918367346939 +#define SEEK_STEP (int)(FRAME_TIME * 1000) + +#define ISO_BUFFER_LENGTH (1024*32) +#define ISO_NBUFFERS (8) +#define ISO_BUFFERS_SIZE (ISO_BUFFER_LENGTH*ISO_NBUFFERS) +#define PCM_BUFFER_LENGTH (4608) + +typedef struct { + unsigned char name[MAX_LINE]; + unsigned char title[MAX_LINE]; + unsigned char artist[MAX_LINE]; + unsigned char album[MAX_LINE]; + unsigned char comment[MAX_LINE]; + unsigned char year[5]; + unsigned char track[3]; + unsigned char genre[256]; + unsigned int size; + unsigned char id3has; +} id3_info; + +typedef struct { + FILE *HANDLE; // file handle + 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 + id3_info ID3; // ID3 information +} tta_info; + +/*********************** Library functions *************************/ + +int open_tta_file ( // FUNCTION: opens TTA file + const char *filename, // file to open + tta_info *info, // file info structure + unsigned int offset); // ID3v2 header size +/* + * 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 close_tta_file ( // FUNCTION: closes currently playing file + tta_info *info); // file info structure + +int set_position ( // FUNCTION: sets playback position + unsigned int pos); // seek position = seek_time_ms / SEEK_STEP +/* + * 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. + * + */ + +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 + +int get_samples ( // FUNCTION: decode PCM_BUFFER_LENGTH samples + unsigned char *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/plugins/tta/ttaplug.c b/plugins/tta/ttaplug.c new file mode 100644 index 00000000..32fa3574 --- /dev/null +++ b/plugins/tta/ttaplug.c @@ -0,0 +1,315 @@ +/* + DeaDBeeF - ultimate music player for GNU/Linux systems with X11 + Copyright (C) 2009-2010 Alexey Yakovenko <waker@users.sourceforge.net> + + 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 program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ +#include <string.h> +#include <stdlib.h> +#include <assert.h> +#include <limits.h> +#include <unistd.h> +#include "ttalib.h" +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif +#include "../../deadbeef.h" + +#pragma GCC optimize("O0") + +#define min(x,y) ((x)<(y)?(x):(y)) +#define max(x,y) ((x)>(y)?(x):(y)) + +#define trace(...) { fprintf (stderr, __VA_ARGS__); } +//#define trace(fmt,...) + +static DB_decoder_t plugin; +static DB_functions_t *deadbeef; + +#define MAX_BSIZE (MAX_BPS>>3) + +typedef struct { + DB_fileinfo_t info; + tta_info tta; + int currentsample; + int startsample; + int endsample; + char buffer[PCM_BUFFER_LENGTH * MAX_BSIZE * MAX_NCH]; + int remaining; +} tta_info_t; + +static DB_fileinfo_t * +tta_open (void) { + DB_fileinfo_t *_info = malloc (sizeof (tta_info_t)); + tta_info_t *info = (tta_info_t *)_info; + memset (info, 0, sizeof (tta_info_t)); + return _info; +} + +static int +tta_init (DB_fileinfo_t *_info, DB_playItem_t *it) { + tta_info_t *info = (tta_info_t *)_info; + + if (open_tta_file (it->fname, &info->tta, 0) != 0) { + fprintf (stderr, "tta: failed to open %s\n", it->fname); + return -1; + } + + if (player_init (&info->tta) != 0) { + fprintf (stderr, "tta: failed to init player for %s\n", it->fname); + return -1; + } + + _info->bps = info->tta.BPS; + _info->channels = info->tta.NCH; + _info->samplerate = info->tta.SAMPLERATE; + _info->readpos = 0; + _info->plugin = &plugin; + + if (it->endsample > 0) { + info->startsample = it->startsample; + info->endsample = it->endsample; + plugin.seek_sample (_info, 0); + } + else { + info->startsample = 0; + info->endsample = (info->tta.DATALENGTH)-1; + } + return 0; +} + +static void +tta_free (DB_fileinfo_t *_info) { + tta_info_t *info = (tta_info_t *)_info; + if (info) { + player_stop (); + close_tta_file (&info->tta); + free (info); + } +} + +static int +tta_read_int16 (DB_fileinfo_t *_info, char *bytes, int size) { + tta_info_t *info = (tta_info_t *)_info; + if (info->currentsample + size / (2 * _info->channels) > info->endsample) { + size = (info->endsample - info->currentsample + 1) * 2 * _info->channels; + if (size <= 0) { + return 0; + } + } + + int initsize = size; + int out_channels = _info->channels; + if (out_channels > 2) { + out_channels = 2; + } + int sample_size = ((_info->bps >> 3) * out_channels); + + while (size > 0) { + if (info->remaining > 0) { + int n = size / sample_size; + n = min (n, info->remaining); + int nn = n; + char *p = info->buffer; + while (n > 0) { + memcpy (bytes, p, 2); + bytes += 2; + if (_info->channels == 2) { + memcpy (bytes, p + 2, 2); + bytes += 2; + } + n--; + size -= sample_size; + p += info->tta.NCH * info->tta.BSIZE; + } + if (info->remaining > nn) { + memmove (info->buffer, p, (info->remaining - nn) * sizeof (float) * _info->channels); + } + info->remaining -= nn; + } + + if (size > 0 && !info->remaining) { + info->remaining = get_samples (info->buffer); + if (!info->remaining) { + break; + } + } + } + return initsize-size; +} + +static int +tta_seek_sample (DB_fileinfo_t *_info, int sample) { + tta_info_t *info = (tta_info_t *)_info; + if (set_position (sample * 1000 / info->tta.SAMPLERATE / SEEK_STEP) != 0) { + fprintf (stderr, "tta: seek failed\n"); + return -1; + } + info->currentsample = sample; + _info->readpos = (sample - info->startsample) / _info->samplerate; + return 0; +} + +static int +tta_seek (DB_fileinfo_t *_info, float time) { + tta_info_t *info = (tta_info_t *)_info; + return tta_seek_sample (_info, time * _info->samplerate); +} + +static DB_playItem_t * +tta_insert (DB_playItem_t *after, const char *fname) { + tta_info tta; + if (open_tta_file (fname, &tta, 0) != 0) { + fprintf (stderr, "tta: failed to open %s\n", fname); + return NULL; + } + + if (tta.BPS != 16) { + fprintf (stderr, "tta: only 16 bit is supported yet, skipped %s\n", fname); + return NULL; + } + + int totalsamples = tta.DATALENGTH; + double dur = tta.LENGTH; + + DB_playItem_t *it = deadbeef->pl_item_alloc (); + it->decoder_id = deadbeef->plug_get_decoder_id (plugin.plugin.id); + it->fname = strdup (fname); + it->filetype = "TTA"; + deadbeef->pl_set_item_duration (it, dur); + + close_tta_file (&tta); + DB_FILE *fp = deadbeef->fopen (fname); + if (fp) { + /*int v2err = */deadbeef->junk_id3v2_read (it, fp); + /*int v1err = */deadbeef->junk_id3v1_read (it, fp); + deadbeef->fclose (fp); + } + + // embedded cue + deadbeef->pl_lock (); + const char *cuesheet = deadbeef->pl_find_meta (it, "cuesheet"); + DB_playItem_t *cue = NULL; + if (cuesheet) { + cue = deadbeef->pl_insert_cue_from_buffer (after, it, cuesheet, strlen (cuesheet), totalsamples, tta.SAMPLERATE); + if (cue) { + deadbeef->pl_item_unref (it); + deadbeef->pl_item_unref (cue); + deadbeef->pl_unlock (); + return cue; + } + } + deadbeef->pl_unlock (); + + cue = deadbeef->pl_insert_cue (after, it, totalsamples, tta.SAMPLERATE); + if (cue) { + deadbeef->pl_item_unref (it); + deadbeef->pl_item_unref (cue); + return cue; + } + + deadbeef->pl_add_meta (it, "title", NULL); + after = deadbeef->pl_insert_item (after, it); + deadbeef->pl_item_unref (it); + + return after; +} + +static int tta_read_metadata (DB_playItem_t *it) { + DB_FILE *fp = deadbeef->fopen (it->fname); + if (!fp) { + return -1; + } + deadbeef->pl_delete_all_meta (it); + /*int v2err = */deadbeef->junk_id3v2_read (it, fp); + /*int v1err = */deadbeef->junk_id3v1_read (it, fp); + deadbeef->pl_add_meta (it, "title", NULL); + deadbeef->fclose (fp); + return 0; +} + +static int tta_write_metadata (DB_playItem_t *it) { + // get options + + int strip_id3v2 = 0; + int strip_id3v1 = 0; + int write_id3v2 = 1; + int write_id3v1 = 1; + + uint32_t junk_flags = 0; + if (strip_id3v2) { + junk_flags |= JUNK_STRIP_ID3V2; + } + if (strip_id3v1) { + junk_flags |= JUNK_STRIP_ID3V1; + } + if (write_id3v2) { + junk_flags |= JUNK_WRITE_ID3V2; + } + if (write_id3v1) { + junk_flags |= JUNK_WRITE_ID3V1; + } + + int id3v2_version = 4; + const char *id3v1_encoding = deadbeef->conf_get_str ("mp3.id3v1_encoding", "iso8859-1"); + return deadbeef->junk_rewrite_tags (it, junk_flags, id3v2_version, id3v1_encoding); +} + + +static int +tta_start (void) { + return 0; +} + +static int +tta_stop (void) { + return 0; +} + +static const char * exts[] = { "tta", NULL }; +static const char *filetypes[] = { "TTA", NULL }; + +// define plugin interface +static DB_decoder_t plugin = { + DB_PLUGIN_SET_API_VERSION + .plugin.version_major = 0, + .plugin.version_minor = 1, + .plugin.type = DB_PLUGIN_DECODER, + .plugin.id = "tta", + .plugin.name = "tta decoder", + .plugin.descr = "tta decoder using libmppdec", + .plugin.author = "Alexey Yakovenko", + .plugin.email = "waker@users.sourceforge.net", + .plugin.website = "http://deadbeef.sf.net", + .plugin.start = tta_start, + .plugin.stop = tta_stop, + .open = tta_open, + .init = tta_init, + .free = tta_free, + .read_int16 = tta_read_int16, +// .read_float32 = tta_read_float32, + .seek = tta_seek, + .seek_sample = tta_seek_sample, + .insert = tta_insert, + .read_metadata = tta_read_metadata, + .write_metadata = tta_write_metadata, + .exts = exts, + .filetypes = filetypes +}; + +DB_plugin_t * +tta_load (DB_functions_t *api) { + deadbeef = api; + return DB_PLUGIN (&plugin); +} |