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.
This commit is contained in:
Aaron Giles 2012-02-17 15:34:14 +00:00
parent 37cbf2808e
commit 9e8ea7466f
5 changed files with 256 additions and 7 deletions

View File

@ -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
//**************************************************************************
//-------------------------------------------------
@ -1188,6 +1236,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
//**************************************************************************

View File

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

View File

@ -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;
}
@ -552,6 +562,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

View File

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

View File

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