summaryrefslogtreecommitdiffstatshomepage
path: root/src
diff options
context:
space:
mode:
author Aaron Giles <aaron@aarongiles.com>2012-02-17 15:34:14 +0000
committer Aaron Giles <aaron@aarongiles.com>2012-02-17 15:34:14 +0000
commit9e8ea7466f2733f496ff32ac340618b511fac4d0 (patch)
tree14a3cd4d4d0e059a041ecf5f797c9627eb578ddd /src
parent37cbf2808eff9b0a2c9aa3aabf173991ed0d2705 (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.c229
-rw-r--r--src/lib/util/chdcodec.h1
-rw-r--r--src/lib/util/flac.c26
-rw-r--r--src/lib/util/flac.h3
-rw-r--r--src/tools/chdman.c4
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,
}
},