1#include "All.h"
2#include "IO.h"
3#include "APECompressCreate.h"
4
5#include "APECompressCore.h"
6
7CAPECompressCreate::CAPECompressCreate()
8{
9    m_nMaxFrames = 0;
10}
11
12CAPECompressCreate::~CAPECompressCreate()
13{
14}
15
16int CAPECompressCreate::Start(CIO * pioOutput, const WAVEFORMATEX * pwfeInput, int nMaxAudioBytes, int nCompressionLevel, const void * pHeaderData, int nHeaderBytes)
17{
18    // verify the parameters
19    if (pioOutput == NULL || pwfeInput == NULL)
20        return ERROR_BAD_PARAMETER;
21
22    // verify the wave format
23    if ((pwfeInput->nChannels != 1) && (pwfeInput->nChannels != 2))
24    {
25        return ERROR_INPUT_FILE_UNSUPPORTED_CHANNEL_COUNT;
26    }
27    if ((pwfeInput->wBitsPerSample != 8) && (pwfeInput->wBitsPerSample != 16) && (pwfeInput->wBitsPerSample != 24))
28    {
29        return ERROR_INPUT_FILE_UNSUPPORTED_BIT_DEPTH;
30    }
31
32    // initialize (creates the base classes)
33    m_nSamplesPerFrame = 73728;
34    if (nCompressionLevel == COMPRESSION_LEVEL_EXTRA_HIGH)
35        m_nSamplesPerFrame *= 4;
36    else if (nCompressionLevel == COMPRESSION_LEVEL_INSANE)
37        m_nSamplesPerFrame *= 16;
38
39    m_spIO.Assign(pioOutput, FALSE, FALSE);
40    m_spAPECompressCore.Assign(new CAPECompressCore(m_spIO, pwfeInput, m_nSamplesPerFrame, nCompressionLevel));
41
42    // copy the format
43    memcpy(&m_wfeInput, pwfeInput, sizeof(WAVEFORMATEX));
44
45    // the compression level
46    m_nCompressionLevel = nCompressionLevel;
47    m_nFrameIndex = 0;
48    m_nLastFrameBlocks = m_nSamplesPerFrame;
49
50    // initialize the file
51    if (nMaxAudioBytes < 0)
52        nMaxAudioBytes = 2147483647;
53
54    uint32 nMaxAudioBlocks = nMaxAudioBytes / pwfeInput->nBlockAlign;
55    int nMaxFrames = nMaxAudioBlocks / m_nSamplesPerFrame;
56    if ((nMaxAudioBlocks % m_nSamplesPerFrame) != 0) nMaxFrames++;
57
58    InitializeFile(m_spIO, &m_wfeInput, nMaxFrames,
59        m_nCompressionLevel, pHeaderData, nHeaderBytes);
60
61    return ERROR_SUCCESS;
62}
63
64int CAPECompressCreate::GetFullFrameBytes()
65{
66    return m_nSamplesPerFrame * m_wfeInput.nBlockAlign;
67}
68
69int CAPECompressCreate::EncodeFrame(const void * pInputData, int nInputBytes)
70{
71    int nInputBlocks = nInputBytes / m_wfeInput.nBlockAlign;
72
73    if ((nInputBlocks < m_nSamplesPerFrame) && (m_nLastFrameBlocks < m_nSamplesPerFrame))
74    {
75        return -1; // can only pass a smaller frame for the very last time
76    }
77
78    // update the seek table
79    m_spAPECompressCore->GetBitArray()->AdvanceToByteBoundary();
80    int nRetVal = SetSeekByte(m_nFrameIndex, m_spIO->GetPosition() + (m_spAPECompressCore->GetBitArray()->GetCurrentBitIndex() / 8));
81    if (nRetVal != ERROR_SUCCESS)
82        return nRetVal;
83
84    // compress
85    nRetVal = m_spAPECompressCore->EncodeFrame(pInputData, nInputBytes);
86
87    // update stats
88    m_nLastFrameBlocks = nInputBlocks;
89    m_nFrameIndex++;
90
91    return nRetVal;
92}
93
94int CAPECompressCreate::Finish(const void * pTerminatingData, int nTerminatingBytes, int nWAVTerminatingBytes)
95{
96    // clear the bit array
97    RETURN_ON_ERROR(m_spAPECompressCore->GetBitArray()->OutputBitArray(TRUE));
98
99    // finalize the file
100    RETURN_ON_ERROR(FinalizeFile(m_spIO, m_nFrameIndex, m_nLastFrameBlocks,
101        pTerminatingData, nTerminatingBytes, nWAVTerminatingBytes, m_spAPECompressCore->GetPeakLevel()));
102
103    return ERROR_SUCCESS;
104}
105
106int CAPECompressCreate::SetSeekByte(int nFrame, int nByteOffset)
107{
108    if (nFrame >= m_nMaxFrames) return ERROR_APE_COMPRESS_TOO_MUCH_DATA;
109    m_spSeekTable[nFrame] = nByteOffset;
110    return ERROR_SUCCESS;
111}
112
113int CAPECompressCreate::InitializeFile(CIO * pIO, const WAVEFORMATEX * pwfeInput, int nMaxFrames, int nCompressionLevel, const void * pHeaderData, int nHeaderBytes)
114{
115    // error check the parameters
116    if (pIO == NULL || pwfeInput == NULL || nMaxFrames <= 0)
117        return ERROR_BAD_PARAMETER;
118
119    APE_DESCRIPTOR APEDescriptor; memset(&APEDescriptor, 0, sizeof(APEDescriptor));
120    APE_HEADER APEHeader; memset(&APEHeader, 0, sizeof(APEHeader));
121
122    // create the descriptor (only fill what we know)
123    APEDescriptor.cID[0] = 'M';
124    APEDescriptor.cID[1] = 'A';
125    APEDescriptor.cID[2] = 'C';
126    APEDescriptor.cID[3] = ' ';
127    APEDescriptor.nVersion = MAC_VERSION_NUMBER;
128
129    APEDescriptor.nDescriptorBytes = sizeof(APEDescriptor);
130    APEDescriptor.nHeaderBytes = sizeof(APEHeader);
131    APEDescriptor.nSeekTableBytes = nMaxFrames * sizeof(unsigned int);
132    APEDescriptor.nHeaderDataBytes = (nHeaderBytes == CREATE_WAV_HEADER_ON_DECOMPRESSION) ? 0 : nHeaderBytes;
133
134    // create the header (only fill what we know now)
135    APEHeader.nBitsPerSample = pwfeInput->wBitsPerSample;
136    APEHeader.nChannels = pwfeInput->nChannels;
137    APEHeader.nSampleRate = pwfeInput->nSamplesPerSec;
138
139    APEHeader.nCompressionLevel = (uint16) nCompressionLevel;
140    APEHeader.nFormatFlags = (nHeaderBytes == CREATE_WAV_HEADER_ON_DECOMPRESSION) ? MAC_FORMAT_FLAG_CREATE_WAV_HEADER : 0;
141
142    APEHeader.nBlocksPerFrame = m_nSamplesPerFrame;
143
144    // write the data to the file
145    unsigned int nBytesWritten = 0;
146    RETURN_ON_ERROR(pIO->Write(&APEDescriptor, sizeof(APEDescriptor), &nBytesWritten))
147    RETURN_ON_ERROR(pIO->Write(&APEHeader, sizeof(APEHeader), &nBytesWritten))
148
149    // write an empty seek table
150    m_spSeekTable.Assign(new uint32 [nMaxFrames], TRUE);
151    if (m_spSeekTable == NULL) { return ERROR_INSUFFICIENT_MEMORY; }
152    ZeroMemory(m_spSeekTable, nMaxFrames * 4);
153    RETURN_ON_ERROR(pIO->Write(m_spSeekTable, (nMaxFrames * 4), &nBytesWritten))
154    m_nMaxFrames = nMaxFrames;
155
156    // write the WAV data
157    if ((pHeaderData != NULL) && (nHeaderBytes > 0) && (nHeaderBytes != CREATE_WAV_HEADER_ON_DECOMPRESSION))
158    {
159        m_spAPECompressCore->GetBitArray()->GetMD5Helper().AddData(pHeaderData, nHeaderBytes);
160        RETURN_ON_ERROR(pIO->Write((void *) pHeaderData, nHeaderBytes, &nBytesWritten))
161    }
162
163    return ERROR_SUCCESS;
164}
165
166
167int CAPECompressCreate::FinalizeFile(CIO * pIO, int nNumberOfFrames, int nFinalFrameBlocks, const void * pTerminatingData, int nTerminatingBytes, int nWAVTerminatingBytes, int nPeakLevel)
168{
169    // store the tail position
170    int nTailPosition = pIO->GetPosition();
171
172    // append the terminating data
173    unsigned int nBytesWritten = 0;
174    unsigned int nBytesRead = 0;
175    int nRetVal = 0;
176    if (nTerminatingBytes > 0)
177    {
178        m_spAPECompressCore->GetBitArray()->GetMD5Helper().AddData(pTerminatingData, nTerminatingBytes);
179        if (pIO->Write((void *) pTerminatingData, nTerminatingBytes, &nBytesWritten) != 0) { return ERROR_IO_WRITE; }
180    }
181
182    // go to the beginning and update the information
183    nRetVal = pIO->Seek(0, FILE_BEGIN);
184
185    // get the descriptor
186    APE_DESCRIPTOR APEDescriptor;
187    nRetVal = pIO->Read(&APEDescriptor, sizeof(APEDescriptor), &nBytesRead);
188    if ((nRetVal != 0) || (nBytesRead != sizeof(APEDescriptor))) { return ERROR_IO_READ; }
189
190    // get the header
191    APE_HEADER APEHeader;
192    nRetVal = pIO->Read(&APEHeader, sizeof(APEHeader), &nBytesRead);
193    if (nRetVal != 0 || nBytesRead != sizeof(APEHeader)) { return ERROR_IO_READ; }
194
195    // update the header
196    APEHeader.nFinalFrameBlocks = nFinalFrameBlocks;
197    APEHeader.nTotalFrames = nNumberOfFrames;
198
199    // update the descriptor
200    APEDescriptor.nAPEFrameDataBytes = nTailPosition - (APEDescriptor.nDescriptorBytes + APEDescriptor.nHeaderBytes + APEDescriptor.nSeekTableBytes + APEDescriptor.nHeaderDataBytes);
201    APEDescriptor.nAPEFrameDataBytesHigh = 0;
202    APEDescriptor.nTerminatingDataBytes = nTerminatingBytes;
203
204    // update the MD5
205    m_spAPECompressCore->GetBitArray()->GetMD5Helper().AddData(&APEHeader, sizeof(APEHeader));
206    m_spAPECompressCore->GetBitArray()->GetMD5Helper().AddData(m_spSeekTable, m_nMaxFrames * 4);
207    m_spAPECompressCore->GetBitArray()->GetMD5Helper().GetResult(APEDescriptor.cFileMD5);
208
209    // set the pointer and re-write the updated header and peak level
210    nRetVal = pIO->Seek(0, FILE_BEGIN);
211    if (pIO->Write(&APEDescriptor, sizeof(APEDescriptor), &nBytesWritten) != 0) { return ERROR_IO_WRITE; }
212    if (pIO->Write(&APEHeader, sizeof(APEHeader), &nBytesWritten) != 0) { return ERROR_IO_WRITE; }
213
214    // write the updated seek table
215    if (pIO->Write(m_spSeekTable, m_nMaxFrames * 4, &nBytesWritten) != 0) { return ERROR_IO_WRITE; }
216
217    return ERROR_SUCCESS;
218}
219