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]
This commit is contained in:
Aaron Giles 2012-02-26 07:18:28 +00:00
parent 570a3b5010
commit 5409b54206
4 changed files with 91 additions and 73 deletions

View File

@ -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))

View File

@ -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;
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;
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;
}
// finish up
UINT32 complen = m_encoder.finish();
if (complen >= chd().hunk_bytes())
throw CHDERR_COMPRESSION_ERROR;
return complen;
//-------------------------------------------------
// 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)
{
// reset and decode
if (!m_decoder.reset(44100, 2, chd().hunk_bytes() / 4, src, complen))
// 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;
if (!m_decoder.decode_interleaved(reinterpret_cast<INT16 *>(dest), destlen / 4, m_swap_endian))
// reset and decode
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, 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))

View File

@ -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');

View File

@ -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 };