diff options
author | 2012-02-17 15:34:14 +0000 | |
---|---|---|
committer | 2012-02-17 15:34:14 +0000 | |
commit | 9e8ea7466f2733f496ff32ac340618b511fac4d0 (patch) | |
tree | 14a3cd4d4d0e059a041ecf5f797c9627eb578ddd /src | |
parent | 37cbf2808eff9b0a2c9aa3aabf173991ed0d2705 (diff) |
Added new CHD codec: CD-FLAC which knows how to shuffle CD data
to more optimally use FLAC. Updated flac wrapper to implement a
tell callback so FLAC can tell us how much we've decoded. Updated
chdman to use CD-FLAC codec in preference over the existing
codecs for CDs by default. Credit Haze for the idea.
Diffstat (limited to 'src')
-rw-r--r-- | src/lib/util/chdcodec.c | 229 | ||||
-rw-r--r-- | src/lib/util/chdcodec.h | 1 | ||||
-rw-r--r-- | src/lib/util/flac.c | 26 | ||||
-rw-r--r-- | src/lib/util/flac.h | 3 | ||||
-rw-r--r-- | src/tools/chdman.c | 4 |
5 files changed, 256 insertions, 7 deletions
diff --git a/src/lib/util/chdcodec.c b/src/lib/util/chdcodec.c index c09f46079b8..acc9be0c418 100644 --- a/src/lib/util/chdcodec.c +++ b/src/lib/util/chdcodec.c @@ -41,6 +41,7 @@ #include "hashing.h" #include "avhuff.h" #include "flac.h" +#include "cdrom.h" #include <zlib.h> #include "lib7z/LzmaEnc.h" #include "lib7z/LzmaDec.h" @@ -291,6 +292,52 @@ public: }; +// ======================> chd_cd_flac_compressor + +// CD/FLAC compressor +class chd_cd_flac_compressor : public chd_compressor +{ +public: + // construction/destruction + chd_cd_flac_compressor(chd_file &chd, bool lossy); + ~chd_cd_flac_compressor(); + + // core functionality + virtual UINT32 compress(const UINT8 *src, UINT32 srclen, UINT8 *dest); + +private: + // internal state + bool m_swap_endian; + flac_encoder m_encoder; + z_stream m_deflater; + chd_zlib_allocator m_allocator; + dynamic_buffer m_buffer; +}; + + +// ======================> chd_cd_flac_decompressor + +// FLAC decompressor +class chd_cd_flac_decompressor : public chd_decompressor +{ +public: + // construction/destruction + chd_cd_flac_decompressor(chd_file &chd, bool lossy); + ~chd_cd_flac_decompressor(); + + // core functionality + virtual void decompress(const UINT8 *src, UINT32 complen, UINT8 *dest, UINT32 destlen); + +private: + // internal state + bool m_swap_endian; + flac_decoder m_decoder; + z_stream m_inflater; + chd_zlib_allocator m_allocator; + dynamic_buffer m_buffer; +}; + + // ======================> chd_avhuff_compressor // A/V compressor @@ -345,6 +392,7 @@ const chd_codec_list::codec_entry chd_codec_list::s_codec_list[] = { CHD_CODEC_HUFFMAN, false, "Huffman", &chd_codec_list::construct_compressor<chd_huffman_compressor>, &chd_codec_list::construct_decompressor<chd_huffman_decompressor> }, { CHD_CODEC_FLAC_BE, false, "FLAC, big-endian", &chd_codec_list::construct_compressor<chd_flac_compressor_be>, &chd_codec_list::construct_decompressor<chd_flac_decompressor_be> }, { CHD_CODEC_FLAC_LE, false, "FLAC, little-endian", &chd_codec_list::construct_compressor<chd_flac_compressor_le>, &chd_codec_list::construct_decompressor<chd_flac_decompressor_le> }, + { CHD_CODEC_CD_FLAC, false, "CD FLAC", &chd_codec_list::construct_compressor<chd_cd_flac_compressor>, &chd_codec_list::construct_decompressor<chd_cd_flac_decompressor> }, { CHD_CODEC_AVHUFF, false, "A/V Huffman", &chd_codec_list::construct_compressor<chd_avhuff_compressor>, &chd_codec_list::construct_decompressor<chd_avhuff_decompressor> }, }; @@ -759,7 +807,7 @@ UINT32 chd_zlib_compressor::compress(const UINT8 *src, UINT32 srclen, UINT8 *des chd_zlib_decompressor::chd_zlib_decompressor(chd_file &chd, bool lossy) : chd_decompressor(chd, lossy) { - // init the inflater first + // init the inflater m_inflater.next_in = (Bytef *)this; // bogus, but that's ok m_inflater.avail_in = 0; m_allocator.install(m_inflater); @@ -1149,7 +1197,7 @@ UINT32 chd_flac_compressor::compress(const UINT8 *src, UINT32 srclen, UINT8 *des //************************************************************************** -// FLAC COMPRESSOR +// FLAC DECOMPRESSOR //************************************************************************** //------------------------------------------------- @@ -1189,6 +1237,183 @@ void chd_flac_decompressor::decompress(const UINT8 *src, UINT32 complen, UINT8 * //************************************************************************** +// CD FLAC COMPRESSOR +//************************************************************************** + +//------------------------------------------------- +// chd_cd_flac_compressor - constructor +//------------------------------------------------- + +chd_cd_flac_compressor::chd_cd_flac_compressor(chd_file &chd, bool lossy) + : chd_compressor(chd, lossy), + m_buffer(chd.hunk_bytes()) +{ + // determine whether we want native or swapped samples + UINT16 native_endian = 0; + *reinterpret_cast<UINT8 *>(&native_endian) = 1; + m_swap_endian = (native_endian == 1); + + // configure the encoder + m_encoder.set_sample_rate(44100); + m_encoder.set_num_channels(2); + m_encoder.set_block_size((chd.hunk_bytes() / CD_FRAME_SIZE) * (CD_MAX_SECTOR_DATA/4)); + m_encoder.set_strip_metadata(true); + + // initialize the deflater + m_deflater.next_in = (Bytef *)this; // bogus, but that's ok + m_deflater.avail_in = 0; + m_allocator.install(m_deflater); + int zerr = deflateInit2(&m_deflater, Z_BEST_COMPRESSION, Z_DEFLATED, -MAX_WBITS, 8, Z_DEFAULT_STRATEGY); + + // convert errors + if (zerr == Z_MEM_ERROR) + throw std::bad_alloc(); + else if (zerr != Z_OK) + throw CHDERR_CODEC_ERROR; +} + + +//------------------------------------------------- +// ~chd_cd_flac_compressor - destructor +//------------------------------------------------- + +chd_cd_flac_compressor::~chd_cd_flac_compressor() +{ + deflateEnd(&m_deflater); +} + + +//------------------------------------------------- +// compress - compress data using the FLAC codec, +// and use zlib on the subcode data +//------------------------------------------------- + +UINT32 chd_cd_flac_compressor::compress(const UINT8 *src, UINT32 srclen, UINT8 *dest) +{ + // copy audio data followed by subcode data + UINT32 frames = chd().hunk_bytes() / CD_FRAME_SIZE; + for (UINT32 framenum = 0; framenum < frames; framenum++) + { + memcpy(&m_buffer[framenum * CD_MAX_SECTOR_DATA], &src[framenum * CD_FRAME_SIZE], CD_MAX_SECTOR_DATA); + memcpy(&m_buffer[frames * CD_MAX_SECTOR_DATA + framenum * CD_MAX_SUBCODE_DATA], &src[framenum * CD_FRAME_SIZE + CD_MAX_SECTOR_DATA], CD_MAX_SUBCODE_DATA); + } + + // reset and encode the audio portion + m_encoder.reset(dest, chd().hunk_bytes()); + UINT8 *buffer = m_buffer; + if (!m_encoder.encode_interleaved(reinterpret_cast<INT16 *>(buffer), frames * CD_MAX_SECTOR_DATA/4, m_swap_endian)) + throw CHDERR_COMPRESSION_ERROR; + + // finish up + UINT32 complen = m_encoder.finish(); + + // deflate the subcode data + m_deflater.next_in = const_cast<Bytef *>(&m_buffer[frames * CD_MAX_SECTOR_DATA]); + m_deflater.avail_in = frames * CD_MAX_SUBCODE_DATA; + m_deflater.total_in = 0; + m_deflater.next_out = &dest[complen]; + m_deflater.avail_out = chd().hunk_bytes() - complen; + m_deflater.total_out = 0; + int zerr = deflateReset(&m_deflater); + if (zerr != Z_OK) + throw CHDERR_COMPRESSION_ERROR; + + // do it + zerr = deflate(&m_deflater, Z_FINISH); + + // if we ended up with more data than we started with, return an error + complen += m_deflater.total_out; + if (zerr != Z_STREAM_END || complen >= srclen) + throw CHDERR_COMPRESSION_ERROR; + return complen; +} + + + +//************************************************************************** +// CD FLAC DECOMPRESSOR +//************************************************************************** + +//------------------------------------------------- +// chd_cd_flac_decompressor - constructor +//------------------------------------------------- + +chd_cd_flac_decompressor::chd_cd_flac_decompressor(chd_file &chd, bool lossy) + : chd_decompressor(chd, lossy), + m_buffer(chd.hunk_bytes()) +{ + // determine whether we want native or swapped samples + UINT16 native_endian = 0; + *reinterpret_cast<UINT8 *>(&native_endian) = 1; + m_swap_endian = (native_endian == 1); + + // init the inflater + m_inflater.next_in = (Bytef *)this; // bogus, but that's ok + m_inflater.avail_in = 0; + m_allocator.install(m_inflater); + int zerr = inflateInit2(&m_inflater, -MAX_WBITS); + + // convert errors + if (zerr == Z_MEM_ERROR) + throw std::bad_alloc(); + else if (zerr != Z_OK) + throw CHDERR_CODEC_ERROR; +} + + +//------------------------------------------------- +// ~chd_cd_flac_decompressor - destructor +//------------------------------------------------- + +chd_cd_flac_decompressor::~chd_cd_flac_decompressor() +{ + inflateEnd(&m_inflater); +} + + +//------------------------------------------------- +// decompress - decompress data using the FLAC +// codec +//------------------------------------------------- + +void chd_cd_flac_decompressor::decompress(const UINT8 *src, UINT32 complen, UINT8 *dest, UINT32 destlen) +{ + // reset and decode + UINT32 frames = chd().hunk_bytes() / CD_FRAME_SIZE; + if (!m_decoder.reset(44100, 2, frames * CD_MAX_SECTOR_DATA/4, src, complen)) + throw CHDERR_DECOMPRESSION_ERROR; + UINT8 *buffer = m_buffer; + if (!m_decoder.decode_interleaved(reinterpret_cast<INT16 *>(buffer), frames * CD_MAX_SECTOR_DATA/4, m_swap_endian)) + throw CHDERR_DECOMPRESSION_ERROR; + + // inflate the subcode data + UINT32 offset = m_decoder.finish(); + m_inflater.next_in = const_cast<Bytef *>(src + offset); + m_inflater.avail_in = complen - offset; + m_inflater.total_in = 0; + m_inflater.next_out = &m_buffer[frames * CD_MAX_SECTOR_DATA]; + m_inflater.avail_out = frames * CD_MAX_SUBCODE_DATA; + m_inflater.total_out = 0; + int zerr = inflateReset(&m_inflater); + if (zerr != Z_OK) + throw CHDERR_DECOMPRESSION_ERROR; + + // do it + zerr = inflate(&m_inflater, Z_FINISH); + if (m_inflater.total_out != frames * CD_MAX_SUBCODE_DATA) + throw CHDERR_DECOMPRESSION_ERROR; + + // reassemble the data + for (UINT32 framenum = 0; framenum < frames; framenum++) + { + memcpy(&dest[framenum * CD_FRAME_SIZE], &m_buffer[framenum * CD_MAX_SECTOR_DATA], CD_MAX_SECTOR_DATA); + memcpy(&dest[framenum * CD_FRAME_SIZE + CD_MAX_SECTOR_DATA], &m_buffer[frames * CD_MAX_SECTOR_DATA + framenum * CD_MAX_SUBCODE_DATA], CD_MAX_SUBCODE_DATA); + } +} + + + +//************************************************************************** // AVHUFF COMPRESSOR //************************************************************************** diff --git a/src/lib/util/chdcodec.h b/src/lib/util/chdcodec.h index fd47e811ccb..1d63c6966be 100644 --- a/src/lib/util/chdcodec.h +++ b/src/lib/util/chdcodec.h @@ -200,6 +200,7 @@ const chd_codec_type CHD_CODEC_LZMA = CHD_MAKE_TAG('l','z','m','a'); const chd_codec_type CHD_CODEC_HUFFMAN = CHD_MAKE_TAG('h','u','f','f'); const chd_codec_type CHD_CODEC_FLAC_BE = CHD_MAKE_TAG('f','l','c','b'); const chd_codec_type CHD_CODEC_FLAC_LE = CHD_MAKE_TAG('f','l','c','l'); +const chd_codec_type CHD_CODEC_CD_FLAC = CHD_MAKE_TAG('c','d','f','l'); const chd_codec_type CHD_CODEC_AVHUFF = CHD_MAKE_TAG('a','v','h','u'); // A/V codec configuration parameters diff --git a/src/lib/util/flac.c b/src/lib/util/flac.c index aaefe301779..360d8eb2304 100644 --- a/src/lib/util/flac.c +++ b/src/lib/util/flac.c @@ -367,7 +367,7 @@ flac_decoder::~flac_decoder() bool flac_decoder::reset() { m_compressed_offset = 0; - if (FLAC__stream_decoder_init_stream(m_decoder, &flac_decoder::read_callback_static, NULL, NULL, NULL, NULL, &flac_decoder::write_callback_static, NULL, &flac_decoder::error_callback_static, this) != FLAC__STREAM_DECODER_INIT_STATUS_OK) + if (FLAC__stream_decoder_init_stream(m_decoder, &flac_decoder::read_callback_static, NULL, &flac_decoder::tell_callback_static, NULL, NULL, &flac_decoder::write_callback_static, NULL, &flac_decoder::error_callback_static, this) != FLAC__STREAM_DECODER_INIT_STATUS_OK) return false; return FLAC__stream_decoder_process_until_end_of_metadata(m_decoder); } @@ -499,9 +499,19 @@ bool flac_decoder::decode(INT16 **samples, UINT32 num_samples, bool swap_endian) // finish - finish up the decode //------------------------------------------------- -void flac_decoder::finish() +UINT32 flac_decoder::finish() { + // get the final decoding position and move forward + FLAC__uint64 position = 0; + FLAC__stream_decoder_get_decode_position(m_decoder, &position); FLAC__stream_decoder_finish(m_decoder); + + // adjust position if we provided the header + if (position == 0) + return 0; + if (m_compressed_start == reinterpret_cast<const FLAC__byte *>(m_custom_header)) + position -= m_compressed_length; + return position; } @@ -553,6 +563,18 @@ FLAC__StreamDecoderReadStatus flac_decoder::read_callback(FLAC__byte buffer[], s //------------------------------------------------- +// tell_callback - handle requests to find out +// where in the input stream we are +//------------------------------------------------- + +FLAC__StreamDecoderTellStatus flac_decoder::tell_callback_static(const FLAC__StreamDecoder *decoder, FLAC__uint64 *absolute_byte_offset, void *client_data) +{ + *absolute_byte_offset = reinterpret_cast<flac_decoder *>(client_data)->m_compressed_offset; + return FLAC__STREAM_DECODER_TELL_STATUS_OK; +} + + +//------------------------------------------------- // write_callback - handle writes to the output // stream //------------------------------------------------- diff --git a/src/lib/util/flac.h b/src/lib/util/flac.h index da37b49b75b..5f2a4cbf71a 100644 --- a/src/lib/util/flac.h +++ b/src/lib/util/flac.h @@ -138,12 +138,13 @@ public: bool decode(INT16 **samples, UINT32 num_samples, bool swap_endian = false); // finish up - void finish(); + UINT32 finish(); private: // internal helpers static FLAC__StreamDecoderReadStatus read_callback_static(const FLAC__StreamDecoder *decoder, FLAC__byte buffer[], size_t *bytes, void *client_data); FLAC__StreamDecoderReadStatus read_callback(FLAC__byte buffer[], size_t *bytes); + static FLAC__StreamDecoderTellStatus tell_callback_static(const FLAC__StreamDecoder *decoder, FLAC__uint64 *absolute_byte_offset, void *client_data); static FLAC__StreamDecoderWriteStatus write_callback_static(const FLAC__StreamDecoder *decoder, const ::FLAC__Frame *frame, const FLAC__int32 * const buffer[], void *client_data); FLAC__StreamDecoderWriteStatus write_callback(const ::FLAC__Frame *frame, const FLAC__int32 * const buffer[]); static void error_callback_static(const FLAC__StreamDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data); diff --git a/src/tools/chdman.c b/src/tools/chdman.c index 32cddcd65d2..f3fb89b5605 100644 --- a/src/tools/chdman.c +++ b/src/tools/chdman.c @@ -478,7 +478,7 @@ static clock_t lastprogress = 0; // default compressors static const chd_codec_type s_default_raw_compression[4] = { CHD_CODEC_LZMA, CHD_CODEC_ZLIB, CHD_CODEC_HUFFMAN }; static const chd_codec_type s_default_hd_compression[4] = { CHD_CODEC_LZMA, CHD_CODEC_ZLIB, CHD_CODEC_HUFFMAN }; -static const chd_codec_type s_default_cd_compression[4] = { CHD_CODEC_LZMA, CHD_CODEC_ZLIB, CHD_CODEC_HUFFMAN, CHD_CODEC_FLAC_BE }; +static const chd_codec_type s_default_cd_compression[4] = { CHD_CODEC_LZMA, CHD_CODEC_ZLIB, CHD_CODEC_HUFFMAN, CHD_CODEC_CD_FLAC }; static const chd_codec_type s_default_ld_compression[4] = { CHD_CODEC_AVHUFF }; @@ -634,7 +634,7 @@ static const command_description s_commands[] = REQUIRED OPTION_OUTPUT, OPTION_OUTPUT_BIN, OPTION_OUTPUT_FORCE, - REQUIRED OPTION_INPUT + REQUIRED OPTION_INPUT, OPTION_INPUT_PARENT, } }, |