summaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
author Aaron Giles <aaron@aarongiles.com>2012-02-26 07:18:28 +0000
committer Aaron Giles <aaron@aarongiles.com>2012-02-26 07:18:28 +0000
commit5409b54206fcb09e72ee79a73721dd7530ab9bee (patch)
tree6ef498c2244e78657e21480d33fafd57ea9d702c
parent570a3b5010e43ac532aab067b80dd6f27f902180 (diff)
Better management of FLAC block sizes, aiming to keep them around 2k
samples. Increased CD sectors per hunk to 8 for better compression. [David Haywood] Re-did the non-CD FLAC codec to be "smart" in that it can choose either big-endian or little-endian on the fly and uses the best one. Added FLAC as a default codec for hard disks, which helps a lot for certain disks. [Aaron Giles]
-rw-r--r--src/lib/util/cdrom.h2
-rw-r--r--src/lib/util/chdcodec.c153
-rw-r--r--src/lib/util/chdcodec.h3
-rw-r--r--src/tools/chdman.c4
4 files changed, 90 insertions, 72 deletions
diff --git a/src/lib/util/cdrom.h b/src/lib/util/cdrom.h
index 02f032a699c..7ac5bead1b7 100644
--- a/src/lib/util/cdrom.h
+++ b/src/lib/util/cdrom.h
@@ -59,7 +59,7 @@ const UINT32 CD_TRACK_PADDING = 4;
#define CD_MAX_SUBCODE_DATA (96)
#define CD_FRAME_SIZE (CD_MAX_SECTOR_DATA + CD_MAX_SUBCODE_DATA)
-#define CD_FRAMES_PER_HUNK (4) // should be 8 for v5 CDs, with a 4-frame pad at the end to maintain SHA1 compatibility
+#define CD_FRAMES_PER_HUNK (8)
#define CD_METADATA_WORDS (1+(CD_MAX_TRACKS * 6))
diff --git a/src/lib/util/chdcodec.c b/src/lib/util/chdcodec.c
index 45c1727d629..3bd0dbecab3 100644
--- a/src/lib/util/chdcodec.c
+++ b/src/lib/util/chdcodec.c
@@ -225,35 +225,20 @@ class chd_flac_compressor : public chd_compressor
{
public:
// construction/destruction
- chd_flac_compressor(chd_file &chd, bool lossy, bool bigendian);
+ chd_flac_compressor(chd_file &chd, bool lossy);
// core functionality
virtual UINT32 compress(const UINT8 *src, UINT32 srclen, UINT8 *dest);
+ // static helpers
+ static UINT32 blocksize(UINT32 bytes);
+
private:
// internal state
- bool m_swap_endian;
+ bool m_big_endian;
flac_encoder m_encoder;
};
-// big-endian variant
-class chd_flac_compressor_be : public chd_flac_compressor
-{
-public:
- // construction/destruction
- chd_flac_compressor_be(chd_file &chd, bool lossy)
- : chd_flac_compressor(chd, lossy, true) { }
-};
-
-// little-endian variant
-class chd_flac_compressor_le : public chd_flac_compressor
-{
-public:
- // construction/destruction
- chd_flac_compressor_le(chd_file &chd, bool lossy)
- : chd_flac_compressor(chd, lossy, false) { }
-};
-
// ======================> chd_flac_decompressor
@@ -262,35 +247,17 @@ class chd_flac_decompressor : public chd_decompressor
{
public:
// construction/destruction
- chd_flac_decompressor(chd_file &chd, bool lossy, bool bigendian);
+ chd_flac_decompressor(chd_file &chd, bool lossy);
// core functionality
virtual void decompress(const UINT8 *src, UINT32 complen, UINT8 *dest, UINT32 destlen);
private:
// internal state
- bool m_swap_endian;
+ bool m_big_endian;
flac_decoder m_decoder;
};
-// big-endian variant
-class chd_flac_decompressor_be : public chd_flac_decompressor
-{
-public:
- // construction/destruction
- chd_flac_decompressor_be(chd_file &chd, bool lossy)
- : chd_flac_decompressor(chd, lossy, true) { }
-};
-
-// little-endian variant
-class chd_flac_decompressor_le : public chd_flac_decompressor
-{
-public:
- // construction/destruction
- chd_flac_decompressor_le(chd_file &chd, bool lossy)
- : chd_flac_decompressor(chd, lossy, false) { }
-};
-
// ======================> chd_cd_flac_compressor
@@ -305,6 +272,9 @@ public:
// core functionality
virtual UINT32 compress(const UINT8 *src, UINT32 srclen, UINT8 *dest);
+ // static helpers
+ static UINT32 blocksize(UINT32 bytes);
+
private:
// internal state
bool m_swap_endian;
@@ -390,8 +360,7 @@ const chd_codec_list::codec_entry chd_codec_list::s_codec_list[] =
{ CHD_CODEC_ZLIB, false, "Deflate", &chd_codec_list::construct_compressor<chd_zlib_compressor>, &chd_codec_list::construct_decompressor<chd_zlib_decompressor> },
{ CHD_CODEC_LZMA, false, "LZMA", &chd_codec_list::construct_compressor<chd_lzma_compressor>, &chd_codec_list::construct_decompressor<chd_lzma_decompressor> },
{ 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_FLAC, false, "FLAC", &chd_codec_list::construct_compressor<chd_flac_compressor>, &chd_codec_list::construct_decompressor<chd_flac_decompressor> },
{ 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> },
};
@@ -1157,21 +1126,18 @@ void chd_huffman_decompressor::decompress(const UINT8 *src, UINT32 complen, UINT
// chd_flac_compressor - constructor
//-------------------------------------------------
-chd_flac_compressor::chd_flac_compressor(chd_file &chd, bool lossy, bool bigendian)
+chd_flac_compressor::chd_flac_compressor(chd_file &chd, bool lossy)
: chd_compressor(chd, lossy)
{
// determine whether we want native or swapped samples
UINT16 native_endian = 0;
*reinterpret_cast<UINT8 *>(&native_endian) = 1;
- if (native_endian == 1)
- m_swap_endian = bigendian;
- else
- m_swap_endian = !bigendian;
-
+ m_big_endian = (native_endian == 0x100);
+
// configure the encoder
m_encoder.set_sample_rate(44100);
m_encoder.set_num_channels(2);
- m_encoder.set_block_size(chd.hunk_bytes() / 4);
+ m_encoder.set_block_size(blocksize(chd.hunk_bytes()));
m_encoder.set_strip_metadata(true);
}
@@ -1182,16 +1148,49 @@ chd_flac_compressor::chd_flac_compressor(chd_file &chd, bool lossy, bool bigendi
UINT32 chd_flac_compressor::compress(const UINT8 *src, UINT32 srclen, UINT8 *dest)
{
- // reset and encode
- m_encoder.reset(dest, chd().hunk_bytes());
- if (!m_encoder.encode_interleaved(reinterpret_cast<const INT16 *>(src), srclen / 4, m_swap_endian))
+ // reset and encode big-endian
+ m_encoder.reset(dest + 1, chd().hunk_bytes() - 1);
+ if (!m_encoder.encode_interleaved(reinterpret_cast<const INT16 *>(src), srclen / 4, !m_big_endian))
throw CHDERR_COMPRESSION_ERROR;
-
- // finish up
- UINT32 complen = m_encoder.finish();
- if (complen >= chd().hunk_bytes())
+ UINT32 complen_be = m_encoder.finish();
+
+ // reset and encode little-endian
+ m_encoder.reset(dest + 1, chd().hunk_bytes() - 1);
+ if (!m_encoder.encode_interleaved(reinterpret_cast<const INT16 *>(src), srclen / 4, m_big_endian))
throw CHDERR_COMPRESSION_ERROR;
- return complen;
+ UINT32 complen_le = m_encoder.finish();
+
+ // pick the best one and add a byte
+ UINT32 complen = MIN(complen_le, complen_be);
+ if (complen + 1 >= chd().hunk_bytes())
+ throw CHDERR_COMPRESSION_ERROR;
+
+ // if big-endian was better, re-do it
+ dest[0] = 'L';
+ if (complen != complen_le)
+ {
+ dest[0] = 'B';
+ m_encoder.reset(dest + 1, chd().hunk_bytes() - 1);
+ if (!m_encoder.encode_interleaved(reinterpret_cast<const INT16 *>(src), srclen / 4, !m_big_endian))
+ throw CHDERR_COMPRESSION_ERROR;
+ m_encoder.finish();
+ }
+ return complen + 1;
+}
+
+
+//-------------------------------------------------
+// blocksize - return the optimal block size
+//-------------------------------------------------
+
+UINT32 chd_flac_compressor::blocksize(UINT32 bytes)
+{
+ // determine FLAC block size, which must be 16-65535
+ // clamp to 2k since that's supposed to be the sweet spot
+ UINT32 blocksize = bytes / 4;
+ while (blocksize > 2048)
+ blocksize /= 2;
+ return blocksize;
}
@@ -1204,16 +1203,13 @@ UINT32 chd_flac_compressor::compress(const UINT8 *src, UINT32 srclen, UINT8 *des
// chd_flac_decompressor - constructor
//-------------------------------------------------
-chd_flac_decompressor::chd_flac_decompressor(chd_file &chd, bool lossy, bool bigendian)
+chd_flac_decompressor::chd_flac_decompressor(chd_file &chd, bool lossy)
: chd_decompressor(chd, lossy)
{
// determine whether we want native or swapped samples
UINT16 native_endian = 0;
*reinterpret_cast<UINT8 *>(&native_endian) = 1;
- if (native_endian == 1)
- m_swap_endian = bigendian;
- else
- m_swap_endian = !bigendian;
+ m_big_endian = (native_endian == 0x100);
}
@@ -1224,10 +1220,19 @@ chd_flac_decompressor::chd_flac_decompressor(chd_file &chd, bool lossy, bool big
void chd_flac_decompressor::decompress(const UINT8 *src, UINT32 complen, UINT8 *dest, UINT32 destlen)
{
+ // determine the endianness
+ bool swap_endian;
+ if (src[0] == 'L')
+ swap_endian = m_big_endian;
+ else if (src[0] == 'B')
+ swap_endian = !m_big_endian;
+ else
+ throw CHDERR_DECOMPRESSION_ERROR;
+
// reset and decode
- if (!m_decoder.reset(44100, 2, chd().hunk_bytes() / 4, src, complen))
+ if (!m_decoder.reset(44100, 2, chd_flac_compressor::blocksize(destlen), src + 1, complen - 1))
throw CHDERR_DECOMPRESSION_ERROR;
- if (!m_decoder.decode_interleaved(reinterpret_cast<INT16 *>(dest), destlen / 4, m_swap_endian))
+ if (!m_decoder.decode_interleaved(reinterpret_cast<INT16 *>(dest), destlen / 4, swap_endian))
throw CHDERR_DECOMPRESSION_ERROR;
// finish up
@@ -1260,7 +1265,7 @@ chd_cd_flac_compressor::chd_cd_flac_compressor(chd_file &chd, bool lossy)
// 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_block_size(blocksize((chd.hunk_bytes() / CD_FRAME_SIZE) * CD_MAX_SECTOR_DATA));
m_encoder.set_strip_metadata(true);
// initialize the deflater
@@ -1333,6 +1338,20 @@ UINT32 chd_cd_flac_compressor::compress(const UINT8 *src, UINT32 srclen, UINT8 *
}
+//-------------------------------------------------
+// blocksize - return the optimal block size
+//-------------------------------------------------
+
+UINT32 chd_cd_flac_compressor::blocksize(UINT32 bytes)
+{
+ // for CDs it seems that CD_MAX_SECTOR_DATA is the right target
+ UINT32 blocksize = bytes / 4;
+ while (blocksize > CD_MAX_SECTOR_DATA)
+ blocksize /= 2;
+ return blocksize;
+}
+
+
//**************************************************************************
// CD FLAC DECOMPRESSOR
@@ -1387,8 +1406,8 @@ chd_cd_flac_decompressor::~chd_cd_flac_decompressor()
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))
+ UINT32 frames = destlen / CD_FRAME_SIZE;
+ if (!m_decoder.reset(44100, 2, chd_cd_flac_compressor::blocksize(frames * CD_MAX_SECTOR_DATA), 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))
diff --git a/src/lib/util/chdcodec.h b/src/lib/util/chdcodec.h
index 4f7c7918df5..d8b4a609fd0 100644
--- a/src/lib/util/chdcodec.h
+++ b/src/lib/util/chdcodec.h
@@ -198,8 +198,7 @@ const chd_codec_type CHD_CODEC_NONE = 0;
const chd_codec_type CHD_CODEC_ZLIB = CHD_MAKE_TAG('z','l','i','b');
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_FLAC = CHD_MAKE_TAG('f','l','a','c');
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');
diff --git a/src/tools/chdman.c b/src/tools/chdman.c
index 2a024a70a7a..3433faa16d7 100644
--- a/src/tools/chdman.c
+++ b/src/tools/chdman.c
@@ -481,8 +481,8 @@ 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_raw_compression[4] = { CHD_CODEC_LZMA, CHD_CODEC_ZLIB, CHD_CODEC_HUFFMAN, CHD_CODEC_FLAC };
+static const chd_codec_type s_default_hd_compression[4] = { CHD_CODEC_LZMA, CHD_CODEC_ZLIB, CHD_CODEC_HUFFMAN, CHD_CODEC_FLAC };
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 };