S.O.N.G/libtta
Archived
3
0
Fork 0
This repository has been archived on 2022-02-26. You can view files and clone it, but cannot push or open issues or pull requests.
libtta/console/tta.c
2011-05-17 04:04:18 +00:00

602 lines
15 KiB
C

/*
* tta.c
*
* Description: TTA simple console frontend
* Copyright (c) 2011 Aleksander Djuric. All rights reserved.
* Distributed under the GNU Lesser General Public License (LGPL).
* The complete text of the license can be found in the COPYING
* file included in the distribution.
*
*/
#include "../libtta.h"
#include "../config.h"
#include "tta.h"
//////////////////////// Constants and definitions //////////////////////////
/////////////////////////////////////////////////////////////////////////////
#define TTA_VERSION L(VERSION)
#define RIFF_SIGN (0x46464952)
#define WAVE_SIGN (0x45564157)
#define fmt_SIGN (0x20746D66)
#define data_SIGN (0x61746164)
#define WAVE_FORMAT_PCM 1
#define WAVE_FORMAT_EXTENSIBLE 0xFFFE
#define PCM_BUFFER_LENGTH 5120
typedef struct {
TTAuint32 chunk_id;
TTAuint32 chunk_size;
TTAuint32 format;
TTAuint32 subchunk_id;
TTAuint32 subchunk_size;
TTAuint16 audio_format;
TTAuint16 num_channels;
TTAuint32 sample_rate;
TTAuint32 byte_rate;
TTAuint16 block_align;
TTAuint16 bits_per_sample;
} WAVE_hdr;
typedef struct {
TTAuint32 subchunk_id;
TTAuint32 subchunk_size;
} WAVE_subchunk_hdr;
typedef struct {
TTAuint32 f1;
TTAuint16 f2;
TTAuint16 f3;
TTAuint8 f4[8];
} WAVE_subformat;
typedef struct {
TTAuint16 cb_size;
TTAuint16 valid_bits;
TTAuint32 ch_mask;
WAVE_subformat est;
} WAVE_ext_hdr;
typedef struct {
TTA_io_callback iocb;
HANDLE handle;
} TTA_io_callback_wrapper;
static TTAwchar *myname = NULL;
/////////////////////////// TTA common functions ////////////////////////////
/////////////////////////////////////////////////////////////////////////////
void tta_strerror(TTA_CODEC_STATUS e) {
switch(e) {
case TTA_OPEN_ERROR: tta_print("\r%s: can't open file\n", myname); break;
case TTA_FORMAT_ERROR: tta_print("\r%s: not compatible file format\n", myname); break;
case TTA_FILE_ERROR: tta_print("\r%s: file is corrupted\n", myname); break;
case TTA_READ_ERROR: tta_print("\r%s: can't read from input file\n", myname); break;
case TTA_WRITE_ERROR: tta_print("\r%s: can't write to output file\n", myname); break;
case TTA_MEMORY_ERROR: tta_print("\r%s: insufficient memory available\n", myname); break;
case TTA_SEEK_ERROR: tta_print("\r%s: file seek error\n", myname); break;
case TTA_PASSWORD_ERROR: tta_print("\r%s: password protected file\n", myname); break;
case TTA_NOT_SUPPORTED: tta_print("\r%s: unsupported architecture type\n", myname); break;
default: tta_print("\rUnknown error\n"); break;
}
} // tta_strerror
void usage() {
tta_print("\rUsage:\ttta [-hebd][p password] input_file output_file\n\n");
tta_print("\t-h\tprint this help\n");
tta_print("\t-e\tencode file\n");
tta_print("\t-eb\tblindly mode (ignore data size info)\n");
tta_print("\t-ep|dp\tpassword protection\n");
tta_print("\t-d\tdecode file\n\n");
tta_print("when file is '-', use standard input/output.\n\n");
tta_print("Project site: http://www.true-audio.com/\n");
} // usage
int read_wav_hdr(HANDLE infile, WAVE_hdr *wave_hdr, TTAuint32 *subchunk_size) {
WAVE_subchunk_hdr subchunk_hdr;
TTAuint32 result;
TTAuint32 def_subchunk_size = 16;
// Read WAVE header
if (!tta_read(infile, wave_hdr, sizeof(WAVE_hdr), result) || !result)
return -1;
if (wave_hdr->audio_format == WAVE_FORMAT_EXTENSIBLE) {
WAVE_ext_hdr wave_hdr_ex;
if (!tta_read(infile, &wave_hdr_ex, sizeof(WAVE_ext_hdr), result) || !result)
return -1;
def_subchunk_size += sizeof(WAVE_ext_hdr);
wave_hdr->audio_format = wave_hdr_ex.est.f1;
}
// Skip extra format bytes
if (wave_hdr->subchunk_size > def_subchunk_size) {
TTAuint32 extra_len = wave_hdr->subchunk_size - def_subchunk_size;
if (tta_seek(infile, extra_len) < 0)
return -1;
}
// Skip unsupported chunks
while (1) {
TTAuint8 chunk_id[5];
if (!tta_read(infile, &subchunk_hdr, sizeof(WAVE_subchunk_hdr), result) || !result)
return -1;
if (subchunk_hdr.subchunk_id == data_SIGN) break;
if (tta_seek(infile, subchunk_hdr.subchunk_size) < 0)
return -1;
tta_memcpy(chunk_id, &subchunk_hdr.subchunk_id, 4);
chunk_id[4] = 0;
}
*subchunk_size = subchunk_hdr.subchunk_size;
return 0;
} // read_wav_hdr
int write_wav_hdr(HANDLE outfile, WAVE_hdr *wave_hdr, TTAuint32 data_size) {
TTAuint32 result;
WAVE_subchunk_hdr subchunk_hdr;
subchunk_hdr.subchunk_id = data_SIGN;
subchunk_hdr.subchunk_size = data_size;
// Write WAVE header
if (!tta_write(outfile, wave_hdr, sizeof(WAVE_hdr), result) ||
result != sizeof(WAVE_hdr)) return -1;
// Write Subchunk header
if (!tta_write(outfile, &subchunk_hdr, sizeof(WAVE_subchunk_hdr), result) ||
result != sizeof(WAVE_subchunk_hdr)) return -1;
return 0;
} // write_wav_hdr
#ifdef __GNUC__
TTAuint8 *convert_password(const TTAwchar *src, int *len) {
TTAuint8 *dst;
int n;
dst = tta_malloc(*len + 1);
if (dst == NULL) return NULL;
for (n = 0; n != *len; ++n) {
if ((src[n] & 0xf0) == 0xf0)
dst[n] = src[n] & 0x0f;
else if ((src[n] & 0xe0) == 0xe0)
dst[n] = src[n] & 0x1f;
else if ((src[n] & 0xc0) == 0xc0)
dst[n] = src[n] & 0x3f;
else if ((src[n] & 0x80) == 0x80)
dst[n] = src[n] & 0x7f;
else
dst[n] = src[n];
}
dst[n] = '\0';
*len = n;
return dst;
} // convert_passwd
#else // MSVC
TTAuint8 *convert_password(const TTAwchar *src, int *len) {
TTAuint8 *dst;
int i, n;
dst = tta_malloc(*len * 6 + 1);
if (dst == NULL) return NULL;
for (i = 0, n = 0; i != *len; ++i) {
TTAwchar c = src[i];
if (c < 0x80) {
dst[n++] = (c & 0xff);
} else if (c < 0x800) {
dst[n++] = ((c >> 6) & 0x1f);
dst[n++] = (c & 0x3f);
} else if (c < 0x10000) {
dst[n++] = ((c >> 12) & 0x0f);
dst[n++] = ((c >> 6) & 0x3f);
dst[n++] = (c & 0x3f);
} else if (c < 0x200000) {
dst[n++] = (((int)c >> 18) & 0x07);
dst[n++] = ((c >> 12) & 0x3f);
dst[n++] = ((c >> 6) & 0x3f);
dst[n++] = (c & 0x3f);
} else if (c < 0x4000000) {
dst[n++] = (((int)c >> 24) & 0x03);
dst[n++] = (((int)c >> 18) & 0x3f);
dst[n++] = ((c >> 12) & 0x3f);
dst[n++] = ((c >> 6) & 0x3f);
dst[n++] = (c & 0x3f);
} else if (c < 0x80000000) {
dst[n++] = (((int)c >> 30) & 0x01);
dst[n++] = (((int)c >> 24) & 0x3f);
dst[n++] = (((int)c >> 18) & 0x3f);
dst[n++] = ((c >> 12) & 0x3f);
dst[n++] = ((c >> 6) & 0x3f);
dst[n++] = (c & 0x3f);
}
}
dst[n] = '\0';
*len = n;
return dst;
} // convert_passwd
#endif // MSVC
/////////////////////////////// Callbacks ///////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
void CALLBACK tta_callback(TTAuint32 rate, TTAuint32 fnum, TTAuint32 frames) {
TTAuint32 pcnt = (TTAuint32)(fnum * 100. / frames);
if (!(pcnt % 10))
tta_print("\rProgress: %02d%%", pcnt);
} // tta_callback
TTAint32 CALLBACK read_callback(TTA_io_callback *io, TTAuint8 *buffer, TTAuint32 size) {
TTA_io_callback_wrapper *iocb = (TTA_io_callback_wrapper *)io;
TTAint32 result;
if (tta_read(iocb->handle, buffer, size, result))
return result;
return 0;
} // read_callback
TTAint32 CALLBACK write_callback(TTA_io_callback *io, TTAuint8 *buffer, TTAuint32 size) {
TTA_io_callback_wrapper *iocb = (TTA_io_callback_wrapper *)io;
TTAint32 result;
if (tta_write(iocb->handle, buffer, size, result))
return result;
return 0;
} // write_callback
TTAint64 CALLBACK seek_callback(TTA_io_callback *io, TTAint64 offset) {
TTA_io_callback_wrapper *iocb = (TTA_io_callback_wrapper *)io;
return tta_seek(iocb->handle, offset);
} // seek_callback
int test_libtta_compatibility() {
int ret = tta_binary_version();
if (ret == CPU_ARCH_UNDEFINED) return 0;
#if defined(CPU_X86)
{
int ax, bx, cx, dx;
tta_cpuid(1, ax, bx, cx, dx);
if (((dx & 0x4000000) && ret == CPU_ARCH_IX86_SSE2) ||
((cx & 0x0000001) && ret == CPU_ARCH_IX86_SSE3) ||
((cx & 0x0080000) && ret == CPU_ARCH_IX86_SSE4_1))
return 0;
}
#endif
return ret;
} // test_libtta_compatibility
//////////////////////////////// Compress ///////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
int compress(HANDLE infile, HANDLE outfile, HANDLE tmpfile, void const *passwd, int pwlen, int format) {
TTAuint32 data_size;
WAVE_hdr wave_hdr;
TTA_io_callback_wrapper io;
TTAuint8 *buffer = NULL;
TTAuint32 buf_size, smp_size, len, res;
TTA_info info;
int ret = -1;
io.iocb.write = &write_callback;
io.iocb.seek = &seek_callback;
io.iocb.read = NULL;
io.handle = outfile;
if (read_wav_hdr(infile, &wave_hdr, &data_size)) {
tta_strerror(TTA_READ_ERROR);
return -1;
}
tta_encoder_new((TTA_io_callback *) &io);
smp_size = (wave_hdr.num_channels * ((wave_hdr.bits_per_sample + 7) / 8));
info.nch = wave_hdr.num_channels;
info.bps = wave_hdr.bits_per_sample;
info.sps = wave_hdr.sample_rate;
info.format = format;
if (info.format == TTA_FORMAT_ENCRYPTED)
tta_encoder_set_password(passwd, pwlen);
buf_size = PCM_BUFFER_LENGTH * smp_size;
// allocate memory for PCM buffer
buffer = (TTAuint8 *) tta_malloc(buf_size + 4); // +4 for READ_BUFFER macro
if (buffer == NULL) {
tta_strerror(TTA_MEMORY_ERROR);
goto done;
}
if (tmpfile != INVALID_HANDLE_VALUE) {
data_size = 0;
while (tta_read(infile, buffer, buf_size, len) && len) {
if (!tta_write(tmpfile, buffer, len, res) || !res) {
tta_strerror(TTA_WRITE_ERROR);
return -1;
}
data_size += len;
}
tta_print("\rBuffered: %d bytes\n", data_size);
infile = tmpfile;
tta_reset(infile);
} else if (data_size >= 0x7fffffff) {
tta_print("\r%s: incorrect data size info in wav file\n", myname);
goto done;
}
info.samples = data_size / smp_size;
if (tta_encoder_init_set_info(&info, 0)) {
tta_strerror(tta_encoder_get_error());
goto done;
}
while (data_size > 0) {
if (!tta_read(infile, buffer, buf_size, len) || !len){
tta_strerror(TTA_READ_ERROR);
goto done;
}
if (len) {
if (tta_encoder_process_stream(buffer, len, tta_callback)){
tta_strerror(tta_encoder_get_error());
goto done;
}
} else break;
data_size -= len;
}
ret = tta_encoder_finalize();
if (ret) tta_strerror(tta_encoder_get_error());
done:
tta_encoder_done();
if (buffer) free(buffer);
return ret;
} // compress
/////////////////////////////// Decompress //////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
int decompress(HANDLE infile, HANDLE outfile, void const *passwd, int pwlen) {
WAVE_hdr wave_hdr;
TTA_io_callback_wrapper io;
TTAuint8 *buffer = NULL;
TTAuint32 buf_size, smp_size, data_size, res;
TTAint32 len;
TTA_info info;
int ret = -1;
io.iocb.read = &read_callback;
io.iocb.seek = &seek_callback;
io.iocb.write = NULL;
io.handle = infile;
tta_decoder_new((TTA_io_callback *) &io);
if (passwd && pwlen)
tta_decoder_set_password(passwd, pwlen);
if (tta_decoder_init_get_info(&info)) {
tta_strerror(tta_decoder_get_error());
goto done;
}
smp_size = info.nch * ((info.bps + 7) / 8);
buf_size = PCM_BUFFER_LENGTH * smp_size;
// allocate memory for PCM buffer
buffer = (TTAuint8 *) tta_malloc(buf_size + 4); // +4 for WRITE_BUFFER macro
if (buffer == NULL) {
tta_strerror(TTA_MEMORY_ERROR);
goto done;
}
// Fill in WAV header
data_size = info.samples * smp_size;
tta_memclear(&wave_hdr, sizeof (wave_hdr));
wave_hdr.chunk_id = RIFF_SIGN;
wave_hdr.chunk_size = data_size + 36;
wave_hdr.format = WAVE_SIGN;
wave_hdr.subchunk_id = fmt_SIGN;
wave_hdr.subchunk_size = 16;
wave_hdr.audio_format = 1;
wave_hdr.num_channels = (TTAuint16) info.nch;
wave_hdr.sample_rate = info.sps;
wave_hdr.bits_per_sample = info.bps;
wave_hdr.byte_rate = info.sps * smp_size;
wave_hdr.block_align = (TTAuint16) smp_size;
// Write WAVE header
if (write_wav_hdr(outfile, &wave_hdr, data_size)){
tta_strerror(TTA_WRITE_ERROR);
goto done;
}
while (1) {
len = tta_decoder_process_stream(buffer, buf_size, tta_callback);
if (len < 0) {
tta_strerror(tta_decoder_get_error());
goto done;
} else if (len) {
if (!tta_write(outfile, buffer, len * smp_size, res) || !res) {
tta_strerror(TTA_WRITE_ERROR);
goto done;
}
} else break;
}
ret = 0;
done:
tta_decoder_done();
if (buffer) free(buffer);
return ret;
} // decompress
//////////////////////////// The main function //////////////////////////////
/////////////////////////////////////////////////////////////////////////////
int tta_main(int argc, TTAwchar **argv) {
TTAwchar *fname_in, *fname_out;
TTAwchar fname_tmp[10] = {'T','T','A','X','X','X','X','X','X','\0'};
HANDLE infile = INVALID_HANDLE_VALUE;
HANDLE outfile = INVALID_HANDLE_VALUE;
HANDLE tmpfile = INVALID_HANDLE_VALUE;
TTAuint8 *pwstr = NULL;
TTAuint32 start, end;
int act = 0;
int pwlen = 0;
int blind = 0;
int format = TTA_FORMAT_SIMPLE;
int ret = -1;
char c;
setlocale(LC_ALL, LOCALE);
myname = argv[0];
tta_print("\r\nTTA1 lossless audio encoder/decoder, version %s\n\n", TTA_VERSION);
if (test_libtta_compatibility()) {
tta_print("\r%s: unsupported architecture type\n", myname);
return ret;
}
if (argc > 3) {
fname_in = argv[argc - 2];
fname_out = argv[argc - 1];
argc -= 2;
} else {
usage();
goto done;
}
while ((c = getopt(argc, argv, "hedbp:")) != -1)
switch (c) {
case 'h': // print help
usage();
goto done;
case 'e': // encode file
if (act == 2) {
tta_print("\r%s: can't combine two options '-e & -d'\n", myname);
goto done;
}
act = 1;
break;
case 'd': // decode file
if (act == 1) {
tta_print("\r%s: can't combine two options '-d & -e'\n", myname);
goto done;
}
act = 2;
break;
case 'p': // password protection
format = TTA_FORMAT_ENCRYPTED;
pwlen = tta_strlen(optarg);
pwstr = convert_password(optarg, &pwlen);
if (pwstr == NULL) {
tta_strerror(TTA_MEMORY_ERROR);
goto done;
}
break;
case 'b': // blindly mode
if (act == 2) {
tta_print("\r%s: option '-b' is not supported by decoder\n", myname);
goto done;
}
blind = 1;
break;
case '?':
default:
goto done;
}
if (!act) {
tta_print("\r%s: commandline options incomplete\n", myname);
goto done;
}
if (*fname_in == '-' && *(fname_in + 1) == '\0')
infile = STDIN_FILENO;
else infile = tta_open_read(fname_in);
if (infile == INVALID_HANDLE_VALUE) {
tta_strerror(TTA_OPEN_ERROR);
goto done;
}
if (*fname_out == '-' && *(fname_out + 1) == '\0')
outfile = STDOUT_FILENO;
else outfile = tta_open_write(fname_out);
if (outfile == INVALID_HANDLE_VALUE) {
tta_strerror(TTA_OPEN_ERROR);
goto done;
}
start = GetTickCount();
// process
switch (act) {
case 1:
tta_print("\rEncoding: \"%s\" to \"%s\"\n", fname_in, fname_out);
if (blind) {
tmpfile = mkstemp(fname_tmp);
if (tmpfile == INVALID_HANDLE_VALUE) {
tta_print("\r%s: can't create tempfile: \"%s\"\n", fname_tmp, myname);
goto done;
} else tta_print("\rTempfile: \"%s\"\n", fname_tmp);
}
ret = compress(infile, outfile, tmpfile, pwstr, pwlen, format);
if (blind && tmpfile != INVALID_HANDLE_VALUE) {
tta_close(tmpfile);
tta_unlink(fname_tmp);
}
break;
case 2:
tta_print("\rDecoding: \"%s\" to \"%s\"\n", fname_in, fname_out);
ret = decompress(infile, outfile, pwstr, pwlen);
break;
}
if (infile != STDIN_FILENO) tta_close(infile);
if (outfile != STDOUT_FILENO) {
tta_close(outfile);
if (ret) tta_unlink(fname_out);
}
if (!ret) {
end = GetTickCount();
tta_print("\rTime: %.3f sec.\n",
(end - start) / 1000.);
}
done:
if (pwstr) free(pwstr);
return ret;
}
/* eof */