/* * ttaenc.c * * Description: TTAv1 lossless audio encoder/decoder. * Developed by: Alexander Djourik * Pavel Zhilin * * Copyright (c) 1999-2005 Alexander Djourik. All rights reserved. * */ /* * 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 * aint with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * Please see the file COPYING in this directory for full copyright * information. */ #ifdef _WIN32 #include #endif #include "ttaenc.h" #include "crc32.h" #include "filters.h" /******************* static variables and structures *******************/ static unsigned char bit_buffer[BIT_BUFFER_SIZE + 8]; static unsigned char *BIT_BUFFER_END = bit_buffer + BIT_BUFFER_SIZE; static struct { unsigned long TTAid; unsigned short AudioFormat; unsigned short NumChannels; unsigned short BitsPerSample; unsigned long SampleRate; unsigned long DataLength; unsigned long CRC32; } __ATTRIBUTE_PACKED__ tta_hdr; static unsigned long *seek_table; static struct { unsigned char id[3]; unsigned short version; unsigned char flags; unsigned char size[4]; } __ATTRIBUTE_PACKED__ id3v2; static struct { unsigned long ChunkID; unsigned long ChunkSize; unsigned long Format; unsigned long Subchunk1ID; unsigned long Subchunk1Size; unsigned short AudioFormat; unsigned short NumChannels; unsigned long SampleRate; unsigned long ByteRate; unsigned short BlockAlign; unsigned short BitsPerSample; } __ATTRIBUTE_PACKED__ wave_hdr; static struct { unsigned long SubchunkID; unsigned long SubchunkSize; } subchunk_hdr; typedef struct { unsigned int f1; unsigned short f2; unsigned short f3; char f4[8]; } EXT_SUBFORMAT; typedef struct { unsigned short cbSize; unsigned short validBits; unsigned int chMask; EXT_SUBFORMAT est; } EXTENSIBLE_WAV_HDR; static unsigned long frame_crc32; static unsigned long bit_count; static unsigned long bit_cache; static unsigned char *bitpos; static unsigned long lastpos; static struct flist *files_list = NULL; static struct flist *files_list_tail = NULL; static FILE *fdin, *fdout; static wchar_t file_in[_MAX_FNAME]; static wchar_t file_out[_MAX_FNAME]; static wchar_t out_path[_MAX_FNAME]; static unsigned long show_stat = 1; static unsigned long fixed_out = 0; static unsigned long clean_src = 0; static unsigned long wave_ext = 0; static uint64 total_input_bytes; static uint64 total_output_bytes; static unsigned long input_byte_count; static unsigned long output_byte_count; const unsigned long 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 long 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 long *shift_16 = bit_shift + 4; struct flist; struct flist { wchar_t fname[_MAX_FNAME]; unsigned long size; struct flist *next; }; /************************* common functions ****************************/ wchar_t *print_path(wchar_t *filename, int mode) { static wchar_t showname[_MAX_FNAME]; wchar_t *p; if (!mode && (p = wcsrchr(filename, _SEP))) p++; else p = filename; wcsncpy(showname, p, _MAX_FNAME - 1); return showname; } void tta_error(long error, wchar_t *name) { ERASE_STDERR; switch (error) { case COMMAND_ERROR: fwprintf(stderr, L"Error: unknown command '%ls'\n%hs\n", name, LINE); break; case FORMAT_ERROR: fwprintf(stderr, L"Error: not compatible file format\n%hs\n", LINE); break; case FILE_ERROR: fwprintf(stderr, L"Error: file is corrupted\n%hs\n", LINE); break; case FIND_ERROR: fwprintf(stderr, L"Error: file(s) not found '%ls'\n%hs\n\n", name, LINE); exit(1); case CREATE_ERROR: fwprintf(stderr, L"Error: problem creating directory '%ls'\n%hs\n\n", name, LINE); exit(1); case OPEN_ERROR: fwprintf(stderr, L"Error: can't open file '%ls'\n%hs\n\n", name, LINE); exit(1); case MEMORY_ERROR: fwprintf(stdout, L"Error: insufficient memory available\n%hs\n\n", LINE); exit(1); case WRITE_ERROR: fwprintf(stdout, L"Error: can't write to output file\n%hs\n\n", LINE); exit(1); case READ_ERROR: fwprintf(stdout, L"Error: can't read from input file\n%hs\n\n", LINE); exit(1); } } void *tta_malloc(size_t num, size_t size) { void *array; if ((array = calloc(num, size)) == NULL) tta_error(MEMORY_ERROR, NULL); return (array); } /************************* bit operations ******************************/ void init_buffer_read(unsigned long pos) { frame_crc32 = 0xFFFFFFFFUL; bit_count = bit_cache = lastpos = 0; bitpos = BIT_BUFFER_END; lastpos = pos; } void init_buffer_write(unsigned long pos) { frame_crc32 = 0xFFFFFFFFUL; bit_count = bit_cache = 0; bitpos = bit_buffer; lastpos = pos; } __inline void get_binary(unsigned long *value, unsigned long bits) { while (bit_count < bits) { if (bitpos == BIT_BUFFER_END) { long res = fread(bit_buffer, 1, BIT_BUFFER_SIZE, fdin); if (!res) { tta_error(READ_ERROR, NULL); return; } input_byte_count += res; bitpos = bit_buffer; } 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]; } __inline void get_unary(unsigned long *value) { *value = 0; while (!(bit_cache ^ bit_mask[bit_count])) { if (bitpos == BIT_BUFFER_END) { long res = fread(bit_buffer, 1, BIT_BUFFER_SIZE, fdin); if (!res) { tta_error(READ_ERROR, NULL); return; } input_byte_count += res; bitpos = bit_buffer; } *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--; } __inline void put_binary(unsigned long value, unsigned long bits) { while (bit_count >= 8) { if (bitpos == BIT_BUFFER_END) { long res = fwrite(bit_buffer, 1, BIT_BUFFER_SIZE, fdout); if (!res) { tta_error(WRITE_ERROR, NULL); return; } output_byte_count += res; bitpos = bit_buffer; } *bitpos = (unsigned char) (bit_cache & 0xFF); UPDATE_CRC32(*bitpos, frame_crc32); bit_cache >>= 8; bit_count -= 8; bitpos++; } bit_cache |= (value & bit_mask[bits]) << bit_count; bit_count += bits; } __inline void put_unary(unsigned long value) { do { while (bit_count >= 8) { if (bitpos == BIT_BUFFER_END) { long res = fwrite(bit_buffer, 1, BIT_BUFFER_SIZE, fdout); if (!res) { tta_error(WRITE_ERROR, NULL); return; } output_byte_count += res; bitpos = bit_buffer; } *bitpos = (unsigned char) (bit_cache & 0xFF); UPDATE_CRC32(*bitpos, frame_crc32); bit_cache >>= 8; bit_count -= 8; bitpos++; } if (value > 23) { bit_cache |= bit_mask[23] << bit_count; bit_count += 23; value -= 23; } else { bit_cache |= bit_mask[value] << bit_count; bit_count += value + 1; value = 0; } } while (value); } int done_buffer_write() { unsigned long res, bytes_to_write; while (bit_count) { *bitpos = (unsigned char) (bit_cache & 0xFF); UPDATE_CRC32(*bitpos, frame_crc32); bit_cache >>= 8; bit_count = (bit_count > 8) ? (bit_count - 8) : 0; bitpos++; } frame_crc32 ^= 0xFFFFFFFFUL; frame_crc32 = ENDSWAP_INT32(frame_crc32); memcpy(bitpos, &frame_crc32, 4); bytes_to_write = bitpos + sizeof(long) - bit_buffer; res = fwrite(bit_buffer, 1, bytes_to_write, fdout); if (!res) { tta_error(WRITE_ERROR, NULL); return 0; } output_byte_count += res; bitpos = bit_buffer; frame_crc32 = 0xFFFFFFFFUL; res = output_byte_count - lastpos; lastpos = output_byte_count; return res; } int done_buffer_read() { unsigned long crc32, rbytes, res; frame_crc32 ^= 0xFFFFFFFFUL; rbytes = BIT_BUFFER_END - bitpos; if (rbytes < sizeof(long)) { memcpy(bit_buffer, bitpos, 4); res = fread(bit_buffer + rbytes, 1, BIT_BUFFER_SIZE - rbytes, fdin); if (!res) { tta_error(READ_ERROR, NULL); return 1; } input_byte_count += res; bitpos = bit_buffer; } memcpy(&crc32, bitpos, 4); crc32 = ENDSWAP_INT32(crc32); bitpos += sizeof(long); res = (crc32 != frame_crc32); bit_cache = bit_count = 0; frame_crc32 = 0xFFFFFFFFUL; return res; } /************************** WAV functions ******************************/ long read_wave(long *data, long byte_size, unsigned long len, FILE *fdin) { unsigned long res; unsigned char *buffer, *src; long *dst = data; src = buffer = tta_malloc(len, byte_size); if (!(res = fread(buffer, byte_size, len, fdin))) tta_error(READ_ERROR, NULL); switch (byte_size) { case 1: for (; src < buffer + res; dst++) *dst = (signed long) *src++ - 0x80; break; case 2: for (; src < buffer + (res * 2); dst++) { *dst = (unsigned char) *src++; *dst |= (signed char) *src++ << 8; } break; case 3: for (; src < buffer + (res * 3); dst++) { *dst = (unsigned char) *src++; *dst |= (unsigned char) *src++ << 8; *dst |= (signed char) *src++ << 16; } break; case 4: for (; src < buffer + (res * 4); dst += 2) { *dst = (unsigned char) *src++; *dst |= (unsigned char) *src++ << 8; *dst |= (unsigned char) *src++ << 16; *dst |= (signed char) *src++ << 24; } break; } free(buffer); return res; } long write_wave(long *data, long byte_size, long num_chan, unsigned long len, FILE *fdout) { unsigned long res; unsigned char *buffer, *dst; long *src = data; dst = buffer = tta_malloc(len * num_chan, byte_size); switch (byte_size) { case 1: for (; src < data + (len * num_chan); src++) *dst++ = (unsigned char) (*src + 0x80); break; case 2: for (; src < data + (len * num_chan); src++) { *dst++ = (unsigned char) *src; *dst++ = (unsigned char) (*src >> 8); } break; case 3: for (; src < data + (len * num_chan); src++) { *dst++ = (unsigned char) *src; *dst++ = (unsigned char) (*src >> 8); *dst++ = (unsigned char) (*src >> 16); } break; case 4: for (; src < data + (len * num_chan * 2); src += 2) { *dst++ = (unsigned char) *src; *dst++ = (unsigned char) (*src >> 8); *dst++ = (unsigned char) (*src >> 16); *dst++ = (unsigned char) (*src >> 24); } break; } if (!(res = fwrite(buffer, byte_size, len * num_chan, fdout))) tta_error(WRITE_ERROR, NULL); free(buffer); return res; } /************************* basic functions *****************************/ void rice_init(adapt *rice, unsigned long k0, unsigned long k1) { rice->k0 = k0; rice->k1 = k1; rice->sum0 = shift_16[k0]; rice->sum1 = shift_16[k1]; } void encoder_init(encoder *tta, long nch, long byte_size) { long *fset = flt_set[byte_size - 1]; long i; for (i = 0; i < nch; i++) { filter_init(&tta[i].fst, fset[0], fset[1]); rice_init(&tta[i].rice, 10, 10); tta[i].last = 0; } } int compress(FILE *fdin, FILE *fdout) { long *p, *data, tmp, prev; unsigned long num_chan, data_size, byte_size, data_len; unsigned long is_float, framelen, lastlen, fframes; unsigned long value, k, unary, binary, st_size, *st; unsigned long offset = 0, def_subchunk_size = 16; encoder *tta, *enc; time_t stime = time(NULL); // print process banner fwprintf(stderr, L"Encode: processing ..\r"); // copy ID3V2 header if present if (fread(&id3v2, sizeof(id3v2), 1, fdin) == 0) tta_error(READ_ERROR, NULL); if (!memcmp(id3v2.id, "ID3", 3)) { char buffer[512]; if (id3v2.size[0] & 0x80) { tta_error(FILE_ERROR, NULL); return 1; } offset = (id3v2.size[0] & 0x7f); offset = (offset << 7) | (id3v2.size[1] & 0x7f); offset = (offset << 7) | (id3v2.size[2] & 0x7f); offset = (offset << 7) | (id3v2.size[3] & 0x7f); if (id3v2.flags & (1 << 4)) offset += 10; data_len = offset, offset += 10; // write ID3V2 header if (fwrite(&id3v2, sizeof(id3v2), 1, fdout) == 0) tta_error(WRITE_ERROR, NULL); while (data_len > 0) { int len = (data_len > sizeof(buffer))? sizeof(buffer):data_len; if (!fread(buffer, len, 1, fdin)) tta_error(READ_ERROR, NULL); if (!fwrite(buffer, len, 1, fdout)) tta_error(WRITE_ERROR, NULL); data_len -= len; } } else fseek(fdin, 0, SEEK_SET); // read WAVE header fread(&wave_hdr, sizeof(wave_hdr), 1, fdin); if (ferror(fdin)) tta_error(READ_ERROR, NULL); wave_hdr.ChunkID = ENDSWAP_INT32(wave_hdr.ChunkID); wave_hdr.ChunkSize = ENDSWAP_INT32(wave_hdr.ChunkSize); wave_hdr.Format = ENDSWAP_INT32(wave_hdr.Format); wave_hdr.Subchunk1ID = ENDSWAP_INT32(wave_hdr.Subchunk1ID); wave_hdr.Subchunk1Size = ENDSWAP_INT32(wave_hdr.Subchunk1Size); wave_hdr.AudioFormat = ENDSWAP_INT16(wave_hdr.AudioFormat); wave_hdr.NumChannels = ENDSWAP_INT16(wave_hdr.NumChannels); wave_hdr.SampleRate = ENDSWAP_INT32(wave_hdr.SampleRate); wave_hdr.ByteRate = ENDSWAP_INT32(wave_hdr.ByteRate); wave_hdr.BlockAlign = ENDSWAP_INT16(wave_hdr.BlockAlign); wave_hdr.BitsPerSample = ENDSWAP_INT16(wave_hdr.BitsPerSample); // check for supported formats if ((wave_hdr.ChunkID != RIFF_SIGN) || (wave_hdr.Format != WAVE_SIGN) || (wave_hdr.Subchunk1ID != fmt_SIGN) || (wave_hdr.Subchunk1Size > wave_hdr.ChunkSize) || (wave_hdr.NumChannels == 0) || (wave_hdr.BitsPerSample > MAX_BPS)) { tta_error(FORMAT_ERROR, NULL); return 1; } if (wave_hdr.AudioFormat == WAVE_FORMAT_EXTENSIBLE) { EXTENSIBLE_WAV_HDR wave_hdr_ex; fread(&wave_hdr_ex, sizeof(wave_hdr_ex), 1, fdin); if (ferror(fdin)) tta_error(READ_ERROR, NULL); def_subchunk_size += sizeof(wave_hdr_ex); switch (ENDSWAP_INT32(wave_hdr_ex.est.f1)) { case WAVE_FORMAT_IEEE_FLOAT: wave_hdr.AudioFormat = WAVE_FORMAT_IEEE_FLOAT; break; case WAVE_FORMAT_PCM: wave_hdr.AudioFormat = WAVE_FORMAT_PCM; break; default: tta_error(FORMAT_ERROR, NULL); return 1; } } switch (wave_hdr.AudioFormat) { case WAVE_FORMAT_IEEE_FLOAT: is_float = 1; break; case WAVE_FORMAT_PCM: is_float = 0; break; default: tta_error(FORMAT_ERROR, NULL); return 1; } // skip extra format bytes if (wave_hdr.Subchunk1Size > def_subchunk_size) { fseek(fdin, wave_hdr.Subchunk1Size - def_subchunk_size, SEEK_CUR); fwprintf(stderr, L"Encode: skiped %d extra format bytes\r\n", (int) wave_hdr.Subchunk1Size - def_subchunk_size); } if ((is_float && wave_hdr.BitsPerSample != MAX_BPS) || (!is_float && wave_hdr.BitsPerSample == MAX_BPS)) { tta_error(FORMAT_ERROR, NULL); return 1; } // skip unsupported chunks while (fread(&subchunk_hdr, sizeof(subchunk_hdr), 1, fdin) && subchunk_hdr.SubchunkID != ENDSWAP_INT32(data_SIGN)) { char chunk_id[5]; subchunk_hdr.SubchunkSize = ENDSWAP_INT32(subchunk_hdr.SubchunkSize); subchunk_hdr.SubchunkID = ENDSWAP_INT32(subchunk_hdr.SubchunkID); if (ferror(fdin)) tta_error(READ_ERROR, NULL); if (subchunk_hdr.SubchunkSize & 0x80000000UL) { tta_error(FILE_ERROR, NULL); return 1; } memcpy(chunk_id, &subchunk_hdr.SubchunkID, 4); chunk_id[4] = 0; fseek(fdin, subchunk_hdr.SubchunkSize, SEEK_CUR); fwprintf(stderr, L"Encode: skiped unsupported '%hs' chunk\r\n", chunk_id); } subchunk_hdr.SubchunkSize = ENDSWAP_INT32(subchunk_hdr.SubchunkSize); framelen = (long) (FRAME_TIME * wave_hdr.SampleRate); num_chan = wave_hdr.NumChannels; data_size = subchunk_hdr.SubchunkSize; byte_size = (wave_hdr.BitsPerSample + 7) / 8; data_len = data_size / (byte_size * num_chan); input_byte_count = ftell(fdin); output_byte_count = 0; lastlen = data_len % framelen; fframes = data_len / framelen + (lastlen ? 1 : 0); st_size = (fframes + 1); num_chan <<= is_float; tta_hdr.TTAid = ENDSWAP_INT32(TTA1_SIGN); tta_hdr.AudioFormat = ENDSWAP_INT16(wave_hdr.AudioFormat); tta_hdr.NumChannels = ENDSWAP_INT16(wave_hdr.NumChannels); tta_hdr.BitsPerSample = ENDSWAP_INT16(wave_hdr.BitsPerSample); tta_hdr.SampleRate = ENDSWAP_INT32(wave_hdr.SampleRate); tta_hdr.DataLength = ENDSWAP_INT32(data_len); tta_hdr.CRC32 = crc32((unsigned char *) &tta_hdr, sizeof(tta_hdr) - sizeof(long)); tta_hdr.CRC32 = ENDSWAP_INT32(tta_hdr.CRC32); // grab some space for an encoder buffers data = (long *) tta_malloc(num_chan * framelen, sizeof(long)); st = seek_table = (long *) tta_malloc(st_size, sizeof(long)); enc = tta = tta_malloc(num_chan, sizeof(encoder)); // write TTA header if (fwrite(&tta_hdr, sizeof(tta_hdr), 1, fdout) == 0) tta_error(WRITE_ERROR, NULL); else output_byte_count += sizeof(tta_hdr); // allocate space for a seek table if (fwrite(seek_table, st_size, sizeof(long), fdout) == 0) tta_error(WRITE_ERROR, NULL); else output_byte_count += st_size * sizeof(long); // init bit writer init_buffer_write(output_byte_count); while (fframes--) { if (!fframes && lastlen) framelen = lastlen; read_wave(data, byte_size, framelen * (num_chan >> is_float), fdin); encoder_init(tta, num_chan, byte_size); for (p = data, prev = 0; p < data + framelen * num_chan; p++) { fltst *fst = &enc->fst; adapt *rice = &enc->rice; long *last = &enc->last; // transform data if (!is_float) { if (enc < tta + num_chan - 1) *p = prev = *(p + 1) - *p; else *p -= prev / 2; } else if (!((p - data) & 1)) { unsigned long t = *p; unsigned long negative = (t & 0x80000000) ? -1 : 1; unsigned long data_hi = (t & 0x7FFF0000) >> 16; unsigned long data_lo = (t & 0x0000FFFF); *p = (data_hi || data_lo) ? (data_hi - 0x3F80) : 0; *(p + 1) = (SWAP16(data_lo) + 1) * negative; } // compress stage 1: fixed order 1 prediction tmp = *p; switch (byte_size) { case 1: *p -= PREDICTOR1(*last, 4); break; // bps 8 case 2: *p -= PREDICTOR1(*last, 5); break; // bps 16 case 3: *p -= PREDICTOR1(*last, 5); break; // bps 24 case 4: *p -= *last; break; // bps 32 } *last = tmp; // compress stage 2: adaptive hybrid filter hybrid_filter(fst, p, 1); value = ENC(*p); // encode Rice unsigned k = 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++; if (value >= bit_shift[k]) { value -= bit_shift[k]; k = rice->k1; 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++; unary = 1 + (value >> k); } else unary = 0; put_unary(unary); if (k) { binary = value & bit_mask[k]; put_binary(binary, k); } if (enc < tta + num_chan - 1) enc++; else enc = tta; } *st++ = done_buffer_write(); input_byte_count += (num_chan * byte_size * framelen) >> is_float; if (show_stat) { ERASE_STDERR; fwprintf(stderr, L"Encode: wrote %d bytes, %d%% complete, ratio: %.2f, time: %d\r", (int) output_byte_count, (int) ((float) input_byte_count / (data_size + 1) * 100), (float) output_byte_count / (input_byte_count + 1), (int) (time(NULL) - stime)); } } // update the seek table fseek(fdout, sizeof(tta_hdr) + offset, SEEK_SET); for (st = seek_table; st < (seek_table + st_size - 1); st++) *st = ENDSWAP_INT32(*st); seek_table[st_size - 1] = crc32((unsigned char *) seek_table, (st_size - 1) * sizeof(long)); seek_table[st_size - 1] = ENDSWAP_INT32(seek_table[st_size - 1]); if (fwrite(seek_table, st_size, sizeof(long), fdout) == 0) tta_error(WRITE_ERROR, NULL); free(seek_table); free(data); free(tta); ERASE_STDERR; fwprintf(stdout, L"Encode: wrote %d bytes, done, ratio: %.2f, time: %d\n", (int) output_byte_count, (float) output_byte_count / (input_byte_count + 1), (int) (time(NULL) - stime)); fwprintf(stdout, L"%hs\n", LINE); return 0; } int test_file(FILE *fdin, long size) { unsigned long byte_size, data_size, checksum, errors; unsigned long framelen, lastlen, fframes; unsigned long framesize, st_size, *st; char *data; // print process banner fwprintf(stderr, L"Test: processing ..\r"); // skip ID3V2 header if (fread(&id3v2, sizeof(id3v2), 1, fdin) == 0) tta_error(READ_ERROR, NULL); if (!memcmp(id3v2.id, "ID3", 3)) { long len; if (id3v2.size[0] & 0x80) { fwprintf(stderr, L"Error: ID3 header is corrupted\n"); return 1; } len = (id3v2.size[0] & 0x7f); len = (len << 7) | (id3v2.size[1] & 0x7f); len = (len << 7) | (id3v2.size[2] & 0x7f); len = (len << 7) | (id3v2.size[3] & 0x7f); len += 10; if (id3v2.flags & (1 << 4)) len += 10; fseek(fdin, len, SEEK_SET); } else fseek(fdin, 0, SEEK_SET); // clear statistics input_byte_count = 0; // read TTA header if (fread(&tta_hdr, sizeof(tta_hdr), 1, fdin) == 0) tta_error(READ_ERROR, NULL); else input_byte_count += sizeof(tta_hdr); // check for supported formats if (ENDSWAP_INT32(tta_hdr.TTAid) != TTA1_SIGN) { fwprintf(stderr, L"Error: TTA ID is not found\n"); return 1; } tta_hdr.AudioFormat = ENDSWAP_INT16(tta_hdr.AudioFormat); tta_hdr.NumChannels = ENDSWAP_INT16(tta_hdr.NumChannels); tta_hdr.BitsPerSample = ENDSWAP_INT16(tta_hdr.BitsPerSample); tta_hdr.SampleRate = ENDSWAP_INT32(tta_hdr.SampleRate); tta_hdr.DataLength = ENDSWAP_INT32(tta_hdr.DataLength); tta_hdr.CRC32 = ENDSWAP_INT32(tta_hdr.CRC32); checksum = crc32((unsigned char *) &tta_hdr, sizeof(tta_hdr) - sizeof(long)); if (checksum != tta_hdr.CRC32) { fwprintf(stderr, L"Error: Header checksum failed\n"); return 1; } byte_size = (tta_hdr.BitsPerSample + 7) / 8; framelen = (long) (FRAME_TIME * tta_hdr.SampleRate); data_size = tta_hdr.DataLength * byte_size * tta_hdr.NumChannels; framesize = framelen * tta_hdr.NumChannels * byte_size + 4; lastlen = tta_hdr.DataLength % framelen; fframes = tta_hdr.DataLength / framelen + (lastlen ? 1 : 0); st_size = (fframes + 1); // grab some space for a buffer data = (char *) tta_malloc(framesize, 1); seek_table = (long *) tta_malloc(st_size, sizeof(long)); // read seek table if (fread(seek_table, st_size, sizeof(long), fdin) == 0) tta_error(READ_ERROR, NULL); else input_byte_count += st_size * sizeof(long); checksum = crc32((unsigned char *) seek_table, (st_size - 1) * sizeof(long)); if (checksum != ENDSWAP_INT32(seek_table[st_size - 1])) { fwprintf(stderr, L"Error: seek table corrupted\n"); free(seek_table); free(data); return 1; } // check frames for (st = seek_table, errors = 0; st < (seek_table + st_size - 1); st++) { long ret = 0; *st = ENDSWAP_INT32(*st); ret = fread(data, 1, *st, fdin); if (!ret) tta_error(READ_ERROR, NULL); input_byte_count += ret; memcpy(&frame_crc32, data + (ret - 4), 4); checksum = crc32(data, *st - 4); if (checksum != ENDSWAP_INT32(frame_crc32)) errors++; if (show_stat) { ERASE_STDERR; fwprintf(stderr, L"Test: read %d bytes, %d%% complete\r", (int) input_byte_count, (int) ((float) input_byte_count / (size + 1) * 100)); } } free(seek_table); free(data); ERASE_STDERR; if (errors) { fwprintf(stderr, L"Error: %d frame(s) corrupted\n", (int)errors); return 1; } return 0; } int decompress(FILE *fdin, FILE *fdout) { long *p, *data, value; unsigned long num_chan, byte_size, data_size, checksum; unsigned long k, depth, framelen, lastlen, fframes; unsigned long unary, binary, st_size, st_state, *st; unsigned long is_float, def_subchunk_size = 16; encoder *tta, *enc; time_t stime = time(NULL); // skip ID3V2 header if (fread(&id3v2, sizeof(id3v2), 1, fdin) == 0) tta_error(READ_ERROR, NULL); if (!memcmp(id3v2.id, "ID3", 3)) { long len; if (id3v2.size[0] & 0x80) { tta_error(FILE_ERROR, NULL); return 1; } len = (id3v2.size[0] & 0x7f); len = (len << 7) | (id3v2.size[1] & 0x7f); len = (len << 7) | (id3v2.size[2] & 0x7f); len = (len << 7) | (id3v2.size[3] & 0x7f); len += 10; if (id3v2.flags & (1 << 4)) len += 10; fseek(fdin, len, SEEK_SET); } else fseek(fdin, 0, SEEK_SET); // print process banner fwprintf(stderr, L"Decode: processing ..\r"); // clear statistics input_byte_count = output_byte_count = 0; // read TTA header if (fread(&tta_hdr, sizeof(tta_hdr), 1, fdin) == 0) tta_error(READ_ERROR, NULL); else input_byte_count += sizeof(tta_hdr); // check for supported formats if (ENDSWAP_INT32(tta_hdr.TTAid) != TTA1_SIGN) { tta_error(FORMAT_ERROR, NULL); return 1; } tta_hdr.CRC32 = ENDSWAP_INT32(tta_hdr.CRC32); checksum = crc32((unsigned char *) &tta_hdr, sizeof(tta_hdr) - sizeof(long)); if (checksum != tta_hdr.CRC32) { tta_error(FILE_ERROR, NULL); return 1; } tta_hdr.AudioFormat = ENDSWAP_INT16(tta_hdr.AudioFormat); tta_hdr.NumChannels = ENDSWAP_INT16(tta_hdr.NumChannels); tta_hdr.BitsPerSample = ENDSWAP_INT16(tta_hdr.BitsPerSample); tta_hdr.SampleRate = ENDSWAP_INT32(tta_hdr.SampleRate); tta_hdr.DataLength = ENDSWAP_INT32(tta_hdr.DataLength); byte_size = (tta_hdr.BitsPerSample + 7) / 8; framelen = (long) (FRAME_TIME * tta_hdr.SampleRate); num_chan = tta_hdr.NumChannels; data_size = tta_hdr.DataLength * byte_size * num_chan; is_float = (tta_hdr.AudioFormat == WAVE_FORMAT_IEEE_FLOAT); if (wave_ext) { def_subchunk_size += sizeof(EXTENSIBLE_WAV_HDR); wave_hdr.AudioFormat = ENDSWAP_INT16(WAVE_FORMAT_EXTENSIBLE); } else wave_hdr.AudioFormat = ENDSWAP_INT16(tta_hdr.AudioFormat); wave_hdr.ChunkID = ENDSWAP_INT32(RIFF_SIGN); wave_hdr.ChunkSize = ENDSWAP_INT32(data_size + 36); wave_hdr.Format = ENDSWAP_INT32(WAVE_SIGN); wave_hdr.Subchunk1ID = ENDSWAP_INT32(fmt_SIGN); wave_hdr.Subchunk1Size = ENDSWAP_INT32(def_subchunk_size); wave_hdr.NumChannels = ENDSWAP_INT16((unsigned short) num_chan); wave_hdr.SampleRate = ENDSWAP_INT32(tta_hdr.SampleRate); wave_hdr.BitsPerSample = ENDSWAP_INT16(tta_hdr.BitsPerSample); wave_hdr.ByteRate = tta_hdr.SampleRate * byte_size * num_chan; wave_hdr.ByteRate = ENDSWAP_INT32(wave_hdr.ByteRate); wave_hdr.BlockAlign = (unsigned short) (num_chan * byte_size); wave_hdr.BlockAlign = ENDSWAP_INT16(wave_hdr.BlockAlign); subchunk_hdr.SubchunkID = ENDSWAP_INT32(data_SIGN); subchunk_hdr.SubchunkSize = ENDSWAP_INT32(data_size); lastlen = tta_hdr.DataLength % framelen; fframes = tta_hdr.DataLength / framelen + (lastlen ? 1 : 0); st_size = (fframes + 1); st_state = 0; num_chan <<= is_float; // grab some space for a buffer data = (long *) tta_malloc(num_chan * framelen, sizeof(long)); enc = tta = tta_malloc(num_chan, sizeof(encoder)); seek_table = (long *) tta_malloc(st_size, sizeof(long)); // read seek table if (fread(seek_table, st_size, sizeof(long), fdin) == 0) tta_error(READ_ERROR, NULL); else input_byte_count += st_size * sizeof(long); checksum = crc32((unsigned char *) seek_table, (st_size - 1) * sizeof(long)); if (checksum != ENDSWAP_INT32(seek_table[st_size - 1])) fwprintf(stdout, L"Decode: warning, seek table corrupted\r\n"); else st_state = 1; for (st = seek_table; st < (seek_table + st_size); st++) *st = ENDSWAP_INT32(*st); // write WAVE header if (fwrite(&wave_hdr, sizeof(wave_hdr), 1, fdout) == 0) tta_error(WRITE_ERROR, NULL); else output_byte_count += sizeof(wave_hdr); if (wave_ext) { EXTENSIBLE_WAV_HDR wave_hdr_ex; unsigned int chMask = 0; switch (tta_hdr.NumChannels) { case 2: chMask = 0x00000003; break; case 3: chMask = 0x0000000B; break; case 4: chMask = 0x00000033; break; case 6: chMask = 0x0000003F; break; case 7: chMask = 0x0000013F; break; case 8: chMask = 0x000000FF; break; }; wave_hdr_ex.cbSize = ENDSWAP_INT16(22); wave_hdr_ex.validBits = ENDSWAP_INT16(wave_hdr.BitsPerSample); wave_hdr_ex.chMask = ENDSWAP_INT32(chMask); wave_hdr_ex.est.f1 = ENDSWAP_INT32(tta_hdr.AudioFormat); wave_hdr_ex.est.f2 = 0; wave_hdr_ex.est.f3 = ENDSWAP_INT16(0x10); wave_hdr_ex.est.f4[0] = 0x80; wave_hdr_ex.est.f4[1] = 0x00; wave_hdr_ex.est.f4[2] = 0x00; wave_hdr_ex.est.f4[3] = 0xaa; wave_hdr_ex.est.f4[4] = 0x00; wave_hdr_ex.est.f4[5] = 0x38; wave_hdr_ex.est.f4[6] = 0x9b; wave_hdr_ex.est.f4[7] = 0x71; if (fwrite(&wave_hdr_ex, sizeof(wave_hdr_ex), 1, fdout) == 0) tta_error(WRITE_ERROR, NULL); else output_byte_count += sizeof(wave_hdr_ex); } // write Subchunk header if (fwrite(&subchunk_hdr, sizeof(subchunk_hdr), 1, fdout) == 0) tta_error(WRITE_ERROR, NULL); else output_byte_count += sizeof(subchunk_hdr); // init bit reader init_buffer_read(input_byte_count); st = seek_table; while (fframes--) { if (!fframes && lastlen) framelen = lastlen; encoder_init(tta, num_chan, byte_size); for (p = data; p < data + framelen * num_chan; p++) { fltst *fst = &enc->fst; adapt *rice = &enc->rice; long *last = &enc->last; // 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++; } *p = DEC(value); // decompress stage 1: adaptive hybrid filter hybrid_filter(fst, p, 0); // decompress stage 2: fixed order 1 prediction switch (byte_size) { case 1: *p += PREDICTOR1(*last, 4); break; // bps 8 case 2: *p += PREDICTOR1(*last, 5); break; // bps 16 case 3: *p += PREDICTOR1(*last, 5); break; // bps 24 case 4: *p += *last; break; // bps 32 } *last = *p; // combine data if (is_float && ((p - data) & 1)) { unsigned long negative = *p & 0x80000000; unsigned long data_hi = *(p - 1); unsigned long data_lo = abs(*p) - 1; data_hi += (data_hi || data_lo) ? 0x3F80 : 0; *(p - 1) = (data_hi << 16) | SWAP16(data_lo) | negative; } if (enc < tta + num_chan - 1) enc++; else { if (!is_float && num_chan > 1) { long *r = p - 1; for (*p += *r/2; r > p - num_chan; r--) *r = *(r + 1) - *r; } enc = tta; } } lastpos += *st++; if (done_buffer_read()) { ERASE_STDERR; if (st_state) { fwprintf(stdout, L"Decode: checksum error, %ld samples wiped\r\n", framelen); memset(data, 0, num_chan * framelen * sizeof(long)); fseek(fdin, lastpos, SEEK_SET); init_buffer_read(lastpos); } else { tta_error(FILE_ERROR, NULL); goto done; } fflush(stderr); } output_byte_count += write_wave(data, byte_size, num_chan >> is_float, framelen, fdout) * byte_size; if (show_stat) { ERASE_STDERR; fwprintf(stderr, L"Decode: wrote %d bytes, %d%% complete, ratio: %.2f, time: %d\r", (int) (output_byte_count), (int) ((float) output_byte_count / (data_size + 1) * 100), (float) output_byte_count / (input_byte_count + 1), (int) (time(NULL) - stime)); } } done: free(seek_table); free(data); free(tta); ERASE_STDERR; fwprintf(stdout, L"Decode: wrote %d bytes, done, ratio: %.2f, time: %d\n", (int) (output_byte_count), (float) output_byte_count / (input_byte_count + 1), (int) (time(NULL) - stime)); fwprintf(stdout, L"%hs\n", LINE); return 0; } int fill_out_name_with_extention(wchar_t *ext) { long len; wchar_t *p, *filename; len = wcslen(file_out); if (len && (file_out[len] != _SEP)) file_out[len++] = _SEP; filename = (wcsrchr(file_in, _SEP) == 0) ? file_in : (wcsrchr(file_in, _SEP) + 1); wcscat(file_out, filename); p = (wcsrchr(file_out, '.') == 0) ? file_out : (wcsrchr(file_out, '.') + 1); if (!wcsicmp(p, ext)) return 1; else wcscpy(p, ext); return 0; } void path_strcat(wchar_t *pstr1, wchar_t *pstr2) { long len; len = wcslen(pstr1); if (len && (pstr1[len] != _SEP)) pstr1[len++] = _SEP; wcsncat(pstr1, pstr2, _MAX_FNAME - len - 1); } void add_to_files_list(wchar_t *path, unsigned long size, struct flist **head, struct flist **tail) { struct flist *new_item; new_item = (struct flist *) tta_malloc(1, sizeof(struct flist)); wcscpy(new_item->fname, path); new_item->next = NULL; new_item->size = size; if (*head == NULL) *head = *tail = new_item; else *tail = (*tail)->next = new_item; } void clear_files_list(void) { struct flist *item; while (files_list != NULL) { item = files_list; files_list = files_list->next; free(item); } } void usage(void) { fwprintf(stdout, L"usage:\t%hs [command] [options] file(s).. \n\n", MYNAME); fwprintf(stdout, L"%hs\n", LINE); fwprintf(stdout, L"commands:\n"); fwprintf(stdout, L"%hs\n", LINE); fwprintf(stdout, L"\t-e\tencode file(s)\n"); fwprintf(stdout, L"\t-d\tdecode file(s)\n"); fwprintf(stdout, L"\t-dx\tdecode file(s), wave-extensible\n"); fwprintf(stdout, L"\t-t\ttest file(s)\n"); fwprintf(stdout, L"\t-o name\tspecify output file name\n"); fwprintf(stdout, L"\t-v\tshow codec version\n"); fwprintf(stdout, L"\t-h\tthis help\n"); fwprintf(stdout, L"%hs\n", LINE); fwprintf(stdout, L"options:\n"); fwprintf(stdout, L"%hs\n", LINE); fwprintf(stdout, L"\t-u\tdelete source file if successful\n"); fwprintf(stdout, L"\t-s\tsilent mode\n"); fwprintf(stdout, L"%hs\n", LINE); fwprintf(stdout, L"examples:\tttaenc -e *.wav; ttaenc -d *.tta \\audio\n\n"); #ifdef _WIN32 fwprintf(stdout, L"Press any key to continue.."); while (!_getch()); fwprintf(stdout, L"\n"); #endif exit(0); } void process_files(long act, long files) { struct flist *item; time_t ftime = time(NULL); int count = 0; int processed = 0; int ret = 0; #ifdef _WIN32 char status[256]; #endif for (item = files_list; item != NULL; item = item->next) { #ifdef _WIN32 sprintf(status, "TTA: %d/%d - %d file(s) processed", count + 1, files, processed); SetConsoleTitle(status); #endif memset(file_in, 0, sizeof(file_in)); memset(file_out, 0, sizeof(file_out)); wcscpy(file_in, item->fname); wcscpy(file_out, out_path); if (!fixed_out) switch (act) { case 1: ret = fill_out_name_with_extention(L"tta"); break; case 2: ret = fill_out_name_with_extention(L"wav"); break; } // print file banner fwprintf(stdout, L"File: [%ls]\n", print_path(file_in, 0)); if (ret) { tta_error(FORMAT_ERROR, NULL); goto done; } fdin = wfopen(file_in, "rb"); if (!fdin) tta_error(OPEN_ERROR, file_in); fdout = wfopen(file_out, "wb"); if (!fdout) tta_error(OPEN_ERROR, file_out); switch (act) { case 1: ret = compress(fdin, fdout); break; case 2: ret = decompress(fdin, fdout); break; } fclose(fdin); fflush(fdout); fclose(fdout); done: if (!ret) { total_input_bytes += item->size; total_output_bytes += output_byte_count; if (clean_src) wunlink(file_in); processed++; } count++; } #ifdef _WIN32 sprintf(status, "TTA: %d/%d - %d file(s) processed", count, files, processed); SetConsoleTitle(status); #endif ftime = (int) (time(NULL) - ftime); fwprintf(stdout, L"Total: [%d/%ld, %.1f/%.1f Mb], ratio: %.3f, time: %ld'%02ld\n", processed, files, (float) total_output_bytes / 1048576, (float) total_input_bytes / 1048576, (float) total_output_bytes / (total_input_bytes + 1), ftime / 60, ftime % 60); fwprintf(stdout, L"%hs\n\n", LINE); } void test_files(long act, long files) { struct flist *item; time_t ftime = time(NULL); int count = 0; int processed = 0; int ret = 0; #ifdef _WIN32 char status[256]; #endif for (item = files_list; item != NULL; item = item->next) { #ifdef _WIN32 sprintf(status, "TTA: %d/%d - %d file(s) processed", count + 1, files, processed); SetConsoleTitle(status); #endif wcscpy(file_in, item->fname); fdin = wfopen(file_in, "rb"); if (!fdin) tta_error(OPEN_ERROR, file_in); // print file banner fwprintf(stdout, L"File: [%ls]\n", print_path(file_in, 0)); ret = test_file(fdin, item->size); fclose(fdin); if (!ret) { total_input_bytes += item->size; processed++; } count++; } #ifdef _WIN32 sprintf(status, "TTA: %d/%d - %d file(s) processed", count, files, processed); SetConsoleTitle(status); #endif ftime = (int) (time(NULL) - ftime); fwprintf(stdout, L"%hs\nTotal: [%d/%ld] succeeded, time: %ld'%02ld\n", LINE, processed, files, ftime / 60, ftime % 60); fwprintf(stdout, L"%hs\n\n", LINE); } /******************************* main **********************************/ int #ifdef _WIN32 __cdecl wmain(int argc, wchar_t **argv) #else main(int argc, char **argv) #endif { long i; long farg = 0, parg = 0, act = 0; #ifdef _WIN32 struct _wfinddata_t fdata; long hFile; wchar_t *p; #else struct stat st; #endif #ifdef _WIN32 setlocale(LC_ALL, ".OCP"); #else setlocale(LC_ALL, ""); #endif fwprintf(stdout, L"TTA1 lossless audio encoder/decoder, release %hs\n%hs\n", VERSION, COPYRIGHT); fwprintf(stdout, L"For more information see %hs\n%hs\n", PROJECT_URL, LINE); total_input_bytes = total_output_bytes = 0; *out_path = *file_in = *file_out = L'\0'; if (argc) { for (i = 1; i < argc; i++) { if (argv[i][0] == '-') switch (argv[i][1]) { case 'e': act = 1; break; case 'd': act = 2; if (argv[i][2] == 'x') wave_ext = 1; break; case 't': act = 3; break; case 'u': clean_src = 1; break; case 'o': fixed_out = 1; i++; wstrncpy(file_out, argv[i], _MAX_FNAME-1); break; case 's': show_stat = 0; break; case 'v': fwprintf(stdout, L"%hs: version %hs build %hs\n\n", MYNAME, VERSION, BUILD); exit(0); case 'h': usage(); default: wstrncpy(file_in, argv[i], _MAX_FNAME-1); tta_error(COMMAND_ERROR, file_in); usage(); } else { #ifdef _WIN32 wstrncpy(file_in, argv[i], wcslen(argv[i])+1); if ((hFile = _wfindfirst(file_in, &fdata)) == -1) { if (i == argc - 1 && farg) { wcscpy(out_path, file_in); parg = 1; if (_wmkdir(out_path) && errno != EEXIST) tta_error(CREATE_ERROR, out_path); continue; } else tta_error(FIND_ERROR, file_in); } switch (fdata.attrib) { case _A_SUBDIR: if (i == argc - 1 && farg) { wcscpy(out_path, file_in); parg = 1; } else tta_error(FIND_ERROR, file_in); break; default: p = (wcsrchr(file_in, L'\\') == 0) ? file_in : (wcsrchr(file_in, L'\\') + 1); *p = L'\0'; wcscat(file_in, fdata.name); add_to_files_list(file_in, fdata.size, &files_list, &files_list_tail); farg++; while (_wfindnext(hFile, &fdata) == 0) { *p = L'\0'; wcscat(file_in, fdata.name); add_to_files_list(file_in, fdata.size, &files_list, &files_list_tail); farg++; } } _findclose(hFile); #else wstrncpy(file_in, argv[i], strlen(argv[i])+1); if (stat(argv[i], &st) && errno == ENOENT) { if (i == argc - 1 && farg) { wcscpy(out_path, file_in); parg = 1; if (mkdir(argv[i], S_IRUSR | S_IWUSR | S_IXUSR)) tta_error(CREATE_ERROR, out_path); continue; } else tta_error(FIND_ERROR, file_in); } add_to_files_list(file_in, st.st_size, &files_list, &files_list_tail); farg++; #endif } } } else usage(); if (!act || !farg) usage(); if (act == 3) { test_files(act, farg); goto done; } if (fixed_out) { path_strcat(out_path, file_out); fwprintf(stdout, L"output file '%ls'\n\n", print_path(out_path, parg)); } else if (parg) fwprintf(stdout, L"output path '%ls'\n\n", print_path(out_path, 1)); process_files(act, farg); done: clear_files_list(); return 0; } /* eof */