288 lines
12 KiB
C++
Executable File
288 lines
12 KiB
C++
Executable File
#include "All.h"
|
|
#include "IO.h"
|
|
#include "APECompressCreate.h"
|
|
|
|
#include "APECompressCore.h"
|
|
|
|
CAPECompressCreate::CAPECompressCreate()
|
|
{
|
|
m_nMaxFrames = 0;
|
|
}
|
|
|
|
CAPECompressCreate::~CAPECompressCreate()
|
|
{
|
|
}
|
|
|
|
int CAPECompressCreate::Start(CIO * pioOutput, const WAVEFORMATEX * pwfeInput, int nMaxAudioBytes, int nCompressionLevel, const void * pHeaderData, int nHeaderBytes)
|
|
{
|
|
// verify the parameters
|
|
if (pioOutput == NULL || pwfeInput == NULL)
|
|
return ERROR_BAD_PARAMETER;
|
|
|
|
// verify the wave format
|
|
if ((pwfeInput->nChannels != 1) && (pwfeInput->nChannels != 2))
|
|
{
|
|
return ERROR_INPUT_FILE_UNSUPPORTED_CHANNEL_COUNT;
|
|
}
|
|
if ((pwfeInput->wBitsPerSample != 8) && (pwfeInput->wBitsPerSample != 16) && (pwfeInput->wBitsPerSample != 24))
|
|
{
|
|
return ERROR_INPUT_FILE_UNSUPPORTED_BIT_DEPTH;
|
|
}
|
|
|
|
// initialize (creates the base classes)
|
|
m_nSamplesPerFrame = 73728;
|
|
if (nCompressionLevel == COMPRESSION_LEVEL_EXTRA_HIGH)
|
|
m_nSamplesPerFrame *= 4;
|
|
else if (nCompressionLevel == COMPRESSION_LEVEL_INSANE)
|
|
m_nSamplesPerFrame *= 16;
|
|
|
|
m_spIO.Assign(pioOutput, FALSE, FALSE);
|
|
m_spAPECompressCore.Assign(new CAPECompressCore(m_spIO, pwfeInput, m_nSamplesPerFrame, nCompressionLevel));
|
|
|
|
// copy the format
|
|
memcpy(&m_wfeInput, pwfeInput, sizeof(WAVEFORMATEX));
|
|
|
|
// the compression level
|
|
m_nCompressionLevel = nCompressionLevel;
|
|
m_nFrameIndex = 0;
|
|
m_nLastFrameBlocks = m_nSamplesPerFrame;
|
|
|
|
// initialize the file
|
|
if (nMaxAudioBytes < 0)
|
|
nMaxAudioBytes = 2147483647;
|
|
|
|
uint32 nMaxAudioBlocks = nMaxAudioBytes / pwfeInput->nBlockAlign;
|
|
int nMaxFrames = nMaxAudioBlocks / m_nSamplesPerFrame;
|
|
if ((nMaxAudioBlocks % m_nSamplesPerFrame) != 0) nMaxFrames++;
|
|
|
|
InitializeFile(m_spIO, &m_wfeInput, nMaxFrames,
|
|
m_nCompressionLevel, pHeaderData, nHeaderBytes);
|
|
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
int CAPECompressCreate::GetFullFrameBytes()
|
|
{
|
|
return m_nSamplesPerFrame * m_wfeInput.nBlockAlign;
|
|
}
|
|
|
|
int CAPECompressCreate::EncodeFrame(const void * pInputData, int nInputBytes)
|
|
{
|
|
int nInputBlocks = nInputBytes / m_wfeInput.nBlockAlign;
|
|
|
|
if ((nInputBlocks < m_nSamplesPerFrame) && (m_nLastFrameBlocks < m_nSamplesPerFrame))
|
|
{
|
|
return -1; // can only pass a smaller frame for the very last time
|
|
}
|
|
|
|
// update the seek table
|
|
m_spAPECompressCore->GetBitArray()->AdvanceToByteBoundary();
|
|
int nRetVal = SetSeekByte(m_nFrameIndex, m_spIO->GetPosition() + (m_spAPECompressCore->GetBitArray()->GetCurrentBitIndex() / 8));
|
|
if (nRetVal != ERROR_SUCCESS)
|
|
return nRetVal;
|
|
|
|
// compress
|
|
nRetVal = m_spAPECompressCore->EncodeFrame(pInputData, nInputBytes);
|
|
|
|
// update stats
|
|
m_nLastFrameBlocks = nInputBlocks;
|
|
m_nFrameIndex++;
|
|
|
|
return nRetVal;
|
|
}
|
|
|
|
int CAPECompressCreate::Finish(const void * pTerminatingData, int nTerminatingBytes, int nWAVTerminatingBytes)
|
|
{
|
|
// clear the bit array
|
|
RETURN_ON_ERROR(m_spAPECompressCore->GetBitArray()->OutputBitArray(TRUE));
|
|
// finalize the file
|
|
RETURN_ON_ERROR(FinalizeFile(m_spIO, m_nFrameIndex, m_nLastFrameBlocks,
|
|
pTerminatingData, nTerminatingBytes, nWAVTerminatingBytes, m_spAPECompressCore->GetPeakLevel()));
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
int CAPECompressCreate::SetSeekByte(int nFrame, int nByteOffset)
|
|
{
|
|
if (nFrame >= m_nMaxFrames) return ERROR_APE_COMPRESS_TOO_MUCH_DATA;
|
|
m_spSeekTable[nFrame] = nByteOffset;
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
int CAPECompressCreate::InitializeFile(CIO * pIO, const WAVEFORMATEX * pwfeInput, int nMaxFrames, int nCompressionLevel, const void * pHeaderData, int nHeaderBytes)
|
|
{
|
|
// error check the parameters
|
|
if (pIO == NULL || pwfeInput == NULL || nMaxFrames <= 0)
|
|
return ERROR_BAD_PARAMETER;
|
|
|
|
APE_DESCRIPTOR APEDescriptor; memset(&APEDescriptor, 0, sizeof(APEDescriptor));
|
|
APE_HEADER APEHeader; memset(&APEHeader, 0, sizeof(APEHeader));
|
|
|
|
APE_DESCRIPTOR APEDescriptor_tmp; memset(&APEDescriptor, 0, sizeof(APEDescriptor));
|
|
APE_HEADER APEHeader_tmp; memset(&APEHeader, 0, sizeof(APEHeader));
|
|
|
|
// create the descriptor (only fill what we know)
|
|
APEDescriptor.cID[0] = 'M';
|
|
APEDescriptor.cID[1] = 'A';
|
|
APEDescriptor.cID[2] = 'C';
|
|
APEDescriptor.cID[3] = ' ';
|
|
APEDescriptor.nVersion = MAC_VERSION_NUMBER;
|
|
|
|
APEDescriptor.nDescriptorBytes = sizeof(APEDescriptor);
|
|
APEDescriptor.nHeaderBytes = sizeof(APEHeader);
|
|
APEDescriptor.nSeekTableBytes = nMaxFrames * sizeof(unsigned int);
|
|
APEDescriptor.nHeaderDataBytes = (nHeaderBytes == CREATE_WAV_HEADER_ON_DECOMPRESSION) ? 0 : nHeaderBytes;
|
|
|
|
// create the header (only fill what we know now)
|
|
APEHeader.nBitsPerSample = pwfeInput->wBitsPerSample;
|
|
APEHeader.nChannels = pwfeInput->nChannels;
|
|
APEHeader.nSampleRate = pwfeInput->nSamplesPerSec;
|
|
|
|
APEHeader.nCompressionLevel = (uint16) nCompressionLevel;
|
|
APEHeader.nFormatFlags = (nHeaderBytes == CREATE_WAV_HEADER_ON_DECOMPRESSION) ? MAC_FORMAT_FLAG_CREATE_WAV_HEADER : 0;
|
|
|
|
APEHeader.nBlocksPerFrame = m_nSamplesPerFrame;
|
|
|
|
// write the data to the file
|
|
|
|
APEDescriptor_tmp.cID[0] = 'M';
|
|
APEDescriptor_tmp.cID[1] = 'A';
|
|
APEDescriptor_tmp.cID[2] = 'C';
|
|
APEDescriptor_tmp.cID[3] = ' ';
|
|
APEDescriptor_tmp.nVersion = swap_endian32(APEDescriptor.nVersion);
|
|
APEDescriptor_tmp.nDescriptorBytes = swap_endian32(APEDescriptor.nDescriptorBytes);
|
|
APEDescriptor_tmp.nHeaderBytes = swap_endian32(APEDescriptor.nHeaderBytes);
|
|
APEDescriptor_tmp.nSeekTableBytes = swap_endian32(APEDescriptor.nSeekTableBytes);
|
|
APEDescriptor_tmp.nHeaderDataBytes = swap_endian32(APEDescriptor.nHeaderDataBytes);
|
|
|
|
APEHeader_tmp.nBitsPerSample = swap_endian16(APEHeader.nBitsPerSample);
|
|
APEHeader_tmp.nChannels = swap_endian16(APEHeader.nChannels);
|
|
APEHeader_tmp.nSampleRate = swap_endian32(APEHeader.nSampleRate);
|
|
APEHeader_tmp.nCompressionLevel = swap_endian16(APEHeader.nCompressionLevel);
|
|
APEHeader_tmp.nFormatFlags = swap_endian16(APEHeader.nFormatFlags);
|
|
APEHeader_tmp.nBlocksPerFrame = swap_endian32(APEHeader.nBlocksPerFrame);
|
|
|
|
unsigned int nBytesWritten = 0;
|
|
/*
|
|
RETURN_ON_ERROR(pIO->Write(&APEDescriptor, sizeof(APEDescriptor), &nBytesWritten))
|
|
RETURN_ON_ERROR(pIO->Write(&APEHeader, sizeof(APEHeader), &nBytesWritten))
|
|
*/
|
|
RETURN_ON_ERROR(pIO->Write(&APEDescriptor_tmp, sizeof(APEDescriptor), &nBytesWritten))
|
|
RETURN_ON_ERROR(pIO->Write(&APEHeader_tmp, sizeof(APEHeader), &nBytesWritten))
|
|
|
|
// write an empty seek table
|
|
m_spSeekTable.Assign(new uint32 [nMaxFrames], TRUE);
|
|
if (m_spSeekTable == NULL) { return ERROR_INSUFFICIENT_MEMORY; }
|
|
ZeroMemory(m_spSeekTable, nMaxFrames * 4);
|
|
RETURN_ON_ERROR(pIO->Write(m_spSeekTable, (nMaxFrames * 4), &nBytesWritten))
|
|
m_nMaxFrames = nMaxFrames;
|
|
|
|
// write the WAV data
|
|
if ((pHeaderData != NULL) && (nHeaderBytes > 0) && (nHeaderBytes != CREATE_WAV_HEADER_ON_DECOMPRESSION))
|
|
{
|
|
m_spAPECompressCore->GetBitArray()->GetMD5Helper().AddData(pHeaderData, nHeaderBytes);
|
|
RETURN_ON_ERROR(pIO->Write((void *) pHeaderData, nHeaderBytes, &nBytesWritten))
|
|
}
|
|
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
|
|
int CAPECompressCreate::FinalizeFile(CIO * pIO, int nNumberOfFrames, int nFinalFrameBlocks, const void * pTerminatingData, int nTerminatingBytes, int nWAVTerminatingBytes, int nPeakLevel)
|
|
{
|
|
int i;
|
|
// store the tail position
|
|
int nTailPosition = pIO->GetPosition();
|
|
|
|
// append the terminating data
|
|
unsigned int nBytesWritten = 0;
|
|
unsigned int nBytesRead = 0;
|
|
int nRetVal = 0;
|
|
if (nTerminatingBytes > 0)
|
|
{
|
|
m_spAPECompressCore->GetBitArray()->GetMD5Helper().AddData(pTerminatingData, nTerminatingBytes);
|
|
if (pIO->Write((void *) pTerminatingData, nTerminatingBytes, &nBytesWritten) != 0) { return ERROR_IO_WRITE; }
|
|
}
|
|
|
|
// go to the beginning and update the information
|
|
nRetVal = pIO->Seek(0, FILE_BEGIN);
|
|
|
|
// get the descriptor
|
|
APE_DESCRIPTOR APEDescriptor, APEDescriptor_tmp;
|
|
|
|
nRetVal = pIO->Read(&APEDescriptor, sizeof(APEDescriptor), &nBytesRead);
|
|
if ((nRetVal != 0) || (nBytesRead != sizeof(APEDescriptor))) { return ERROR_IO_READ; }
|
|
APEDescriptor.nVersion = swap_endian32(APEDescriptor.nVersion);
|
|
APEDescriptor.nDescriptorBytes = swap_endian32(APEDescriptor.nDescriptorBytes);
|
|
APEDescriptor.nHeaderBytes = swap_endian32(APEDescriptor.nHeaderBytes);
|
|
APEDescriptor.nSeekTableBytes = swap_endian32(APEDescriptor.nSeekTableBytes);
|
|
APEDescriptor.nHeaderDataBytes = swap_endian32(APEDescriptor.nHeaderDataBytes);
|
|
|
|
// get the header
|
|
APE_HEADER APEHeader, APEHeader_tmp;
|
|
nRetVal = pIO->Read(&APEHeader, sizeof(APEHeader), &nBytesRead);
|
|
if (nRetVal != 0 || nBytesRead != sizeof(APEHeader)) { return ERROR_IO_READ; }
|
|
APEHeader.nBitsPerSample = swap_endian16(APEHeader.nBitsPerSample);
|
|
APEHeader.nChannels = swap_endian16(APEHeader.nChannels);
|
|
APEHeader.nSampleRate = swap_endian32(APEHeader.nSampleRate);
|
|
APEHeader.nCompressionLevel = swap_endian16(APEHeader.nCompressionLevel);
|
|
APEHeader.nFormatFlags = swap_endian16(APEHeader.nFormatFlags);
|
|
APEHeader.nBlocksPerFrame = swap_endian32(APEHeader.nBlocksPerFrame);
|
|
|
|
// update the header
|
|
APEHeader.nFinalFrameBlocks = nFinalFrameBlocks;
|
|
APEHeader.nTotalFrames = nNumberOfFrames;
|
|
|
|
// update the descriptor
|
|
APEDescriptor.nAPEFrameDataBytes = nTailPosition - (APEDescriptor.nDescriptorBytes + APEDescriptor.nHeaderBytes + APEDescriptor.nSeekTableBytes + APEDescriptor.nHeaderDataBytes);
|
|
APEDescriptor.nAPEFrameDataBytesHigh = 0;
|
|
APEDescriptor.nTerminatingDataBytes = nTerminatingBytes;
|
|
|
|
// update the MD5
|
|
m_spAPECompressCore->GetBitArray()->GetMD5Helper().AddData(&APEHeader, sizeof(APEHeader));
|
|
m_spAPECompressCore->GetBitArray()->GetMD5Helper().AddData(m_spSeekTable, m_nMaxFrames * 4);
|
|
m_spAPECompressCore->GetBitArray()->GetMD5Helper().GetResult(APEDescriptor.cFileMD5);
|
|
|
|
// set the pointer and re-write the updated header and peak level
|
|
nRetVal = pIO->Seek(0, FILE_BEGIN);
|
|
|
|
APEDescriptor_tmp.cID[0] = 'M';
|
|
APEDescriptor_tmp.cID[1] = 'A';
|
|
APEDescriptor_tmp.cID[2] = 'C';
|
|
APEDescriptor_tmp.cID[3] = ' ';
|
|
APEDescriptor_tmp.nVersion = swap_endian32(APEDescriptor.nVersion);
|
|
APEDescriptor_tmp.nDescriptorBytes = swap_endian32(APEDescriptor.nDescriptorBytes);
|
|
APEDescriptor_tmp.nHeaderBytes = swap_endian32(APEDescriptor.nHeaderBytes);
|
|
APEDescriptor_tmp.nSeekTableBytes = swap_endian32(APEDescriptor.nSeekTableBytes);
|
|
APEDescriptor_tmp.nHeaderDataBytes = swap_endian32(APEDescriptor.nHeaderDataBytes);
|
|
APEDescriptor_tmp.nAPEFrameDataBytes = swap_endian32(APEDescriptor.nAPEFrameDataBytes);
|
|
APEDescriptor_tmp.nAPEFrameDataBytesHigh = swap_endian32(APEDescriptor.nAPEFrameDataBytesHigh);
|
|
APEDescriptor_tmp.nTerminatingDataBytes = swap_endian32(APEDescriptor.nTerminatingDataBytes);
|
|
|
|
APEHeader_tmp.nBitsPerSample = swap_endian16(APEHeader.nBitsPerSample);
|
|
APEHeader_tmp.nChannels = swap_endian16(APEHeader.nChannels);
|
|
APEHeader_tmp.nSampleRate = swap_endian32(APEHeader.nSampleRate);
|
|
APEHeader_tmp.nCompressionLevel = swap_endian16(APEHeader.nCompressionLevel);
|
|
APEHeader_tmp.nFormatFlags = swap_endian16(APEHeader.nFormatFlags);
|
|
APEHeader_tmp.nBlocksPerFrame = swap_endian32(APEHeader.nBlocksPerFrame);
|
|
APEHeader_tmp.nFinalFrameBlocks = swap_endian32(APEHeader.nFinalFrameBlocks);
|
|
APEHeader_tmp.nTotalFrames = swap_endian32(APEHeader.nTotalFrames);
|
|
|
|
/*
|
|
if (pIO->Write(&APEDescriptor, sizeof(APEDescriptor), &nBytesWritten) != 0) { return ERROR_IO_WRITE; }
|
|
if (pIO->Write(&APEHeader, sizeof(APEHeader), &nBytesWritten) != 0) { return ERROR_IO_WRITE; }
|
|
*/
|
|
if (pIO->Write(&APEDescriptor_tmp, sizeof(APEDescriptor), &nBytesWritten) != 0) { return ERROR_IO_WRITE; }
|
|
if (pIO->Write(&APEHeader_tmp, sizeof(APEHeader), &nBytesWritten) != 0) { return ERROR_IO_WRITE; }
|
|
|
|
// write the updated seek table
|
|
for(i=0;i<m_nMaxFrames;i++) {
|
|
m_spSeekTable[i] = swap_endian32(m_spSeekTable[i]);
|
|
}
|
|
if (pIO->Write(m_spSeekTable, m_nMaxFrames * 4, &nBytesWritten) != 0) { return ERROR_IO_WRITE; }
|
|
for(i=0;i<m_nMaxFrames;i++) {
|
|
m_spSeekTable[i] = swap_endian32(m_spSeekTable[i]);
|
|
}
|
|
|
|
return ERROR_SUCCESS;
|
|
}
|