3 #include "APECompressCreate.h"
5 #include "APECompressCore.h"
7 CAPECompressCreate::CAPECompressCreate()
12 CAPECompressCreate::~CAPECompressCreate()
16 int CAPECompressCreate::Start(CIO
* pioOutput
, const WAVEFORMATEX
* pwfeInput
, int nMaxAudioBytes
, int nCompressionLevel
, const void * pHeaderData
, int nHeaderBytes
)
18 // verify the parameters
19 if (pioOutput
== NULL
|| pwfeInput
== NULL
)
20 return ERROR_BAD_PARAMETER
;
22 // verify the wave format
23 if ((pwfeInput
->nChannels
!= 1) && (pwfeInput
->nChannels
!= 2))
25 return ERROR_INPUT_FILE_UNSUPPORTED_CHANNEL_COUNT
;
27 if ((pwfeInput
->wBitsPerSample
!= 8) && (pwfeInput
->wBitsPerSample
!= 16) && (pwfeInput
->wBitsPerSample
!= 24))
29 return ERROR_INPUT_FILE_UNSUPPORTED_BIT_DEPTH
;
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;
39 m_spIO
.Assign(pioOutput
, FALSE
, FALSE
);
40 m_spAPECompressCore
.Assign(new CAPECompressCore(m_spIO
, pwfeInput
, m_nSamplesPerFrame
, nCompressionLevel
));
43 memcpy(&m_wfeInput
, pwfeInput
, sizeof(WAVEFORMATEX
));
45 // the compression level
46 m_nCompressionLevel
= nCompressionLevel
;
48 m_nLastFrameBlocks
= m_nSamplesPerFrame
;
50 // initialize the file
51 if (nMaxAudioBytes
< 0)
52 nMaxAudioBytes
= 2147483647;
54 uint32 nMaxAudioBlocks
= nMaxAudioBytes
/ pwfeInput
->nBlockAlign
;
55 int nMaxFrames
= nMaxAudioBlocks
/ m_nSamplesPerFrame
;
56 if ((nMaxAudioBlocks
% m_nSamplesPerFrame
) != 0) nMaxFrames
++;
58 InitializeFile(m_spIO
, &m_wfeInput
, nMaxFrames
,
59 m_nCompressionLevel
, pHeaderData
, nHeaderBytes
);
64 int CAPECompressCreate::GetFullFrameBytes()
66 return m_nSamplesPerFrame
* m_wfeInput
.nBlockAlign
;
69 int CAPECompressCreate::EncodeFrame(const void * pInputData
, int nInputBytes
)
71 int nInputBlocks
= nInputBytes
/ m_wfeInput
.nBlockAlign
;
73 if ((nInputBlocks
< m_nSamplesPerFrame
) && (m_nLastFrameBlocks
< m_nSamplesPerFrame
))
75 return -1; // can only pass a smaller frame for the very last time
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
)
85 nRetVal
= m_spAPECompressCore
->EncodeFrame(pInputData
, nInputBytes
);
88 m_nLastFrameBlocks
= nInputBlocks
;
94 int CAPECompressCreate::Finish(const void * pTerminatingData
, int nTerminatingBytes
, int nWAVTerminatingBytes
)
96 // clear the bit array
97 RETURN_ON_ERROR(m_spAPECompressCore
->GetBitArray()->OutputBitArray(TRUE
));
100 RETURN_ON_ERROR(FinalizeFile(m_spIO
, m_nFrameIndex
, m_nLastFrameBlocks
,
101 pTerminatingData
, nTerminatingBytes
, nWAVTerminatingBytes
, m_spAPECompressCore
->GetPeakLevel()));
103 return ERROR_SUCCESS
;
106 int CAPECompressCreate::SetSeekByte(int nFrame
, int nByteOffset
)
108 if (nFrame
>= m_nMaxFrames
) return ERROR_APE_COMPRESS_TOO_MUCH_DATA
;
109 m_spSeekTable
[nFrame
] = nByteOffset
;
110 return ERROR_SUCCESS
;
113 int CAPECompressCreate::InitializeFile(CIO
* pIO
, const WAVEFORMATEX
* pwfeInput
, int nMaxFrames
, int nCompressionLevel
, const void * pHeaderData
, int nHeaderBytes
)
115 // error check the parameters
116 if (pIO
== NULL
|| pwfeInput
== NULL
|| nMaxFrames
<= 0)
117 return ERROR_BAD_PARAMETER
;
119 APE_DESCRIPTOR APEDescriptor
; memset(&APEDescriptor
, 0, sizeof(APEDescriptor
));
120 APE_HEADER APEHeader
; memset(&APEHeader
, 0, sizeof(APEHeader
));
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
;
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
;
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
;
139 APEHeader
.nCompressionLevel
= (uint16
) nCompressionLevel
;
140 APEHeader
.nFormatFlags
= (nHeaderBytes
== CREATE_WAV_HEADER_ON_DECOMPRESSION
) ? MAC_FORMAT_FLAG_CREATE_WAV_HEADER
: 0;
142 APEHeader
.nBlocksPerFrame
= m_nSamplesPerFrame
;
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
))
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
;
156 // write the WAV data
157 if ((pHeaderData
!= NULL
) && (nHeaderBytes
> 0) && (nHeaderBytes
!= CREATE_WAV_HEADER_ON_DECOMPRESSION
))
159 m_spAPECompressCore
->GetBitArray()->GetMD5Helper().AddData(pHeaderData
, nHeaderBytes
);
160 RETURN_ON_ERROR(pIO
->Write((void *) pHeaderData
, nHeaderBytes
, &nBytesWritten
))
163 return ERROR_SUCCESS
;
167 int CAPECompressCreate::FinalizeFile(CIO
* pIO
, int nNumberOfFrames
, int nFinalFrameBlocks
, const void * pTerminatingData
, int nTerminatingBytes
, int nWAVTerminatingBytes
, int nPeakLevel
)
169 // store the tail position
170 int nTailPosition
= pIO
->GetPosition();
172 // append the terminating data
173 unsigned int nBytesWritten
= 0;
174 unsigned int nBytesRead
= 0;
176 if (nTerminatingBytes
> 0)
178 m_spAPECompressCore
->GetBitArray()->GetMD5Helper().AddData(pTerminatingData
, nTerminatingBytes
);
179 if (pIO
->Write((void *) pTerminatingData
, nTerminatingBytes
, &nBytesWritten
) != 0) { return ERROR_IO_WRITE
; }
182 // go to the beginning and update the information
183 nRetVal
= pIO
->Seek(0, FILE_BEGIN
);
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
; }
191 APE_HEADER APEHeader
;
192 nRetVal
= pIO
->Read(&APEHeader
, sizeof(APEHeader
), &nBytesRead
);
193 if (nRetVal
!= 0 || nBytesRead
!= sizeof(APEHeader
)) { return ERROR_IO_READ
; }
196 APEHeader
.nFinalFrameBlocks
= nFinalFrameBlocks
;
197 APEHeader
.nTotalFrames
= nNumberOfFrames
;
199 // update the descriptor
200 APEDescriptor
.nAPEFrameDataBytes
= nTailPosition
- (APEDescriptor
.nDescriptorBytes
+ APEDescriptor
.nHeaderBytes
+ APEDescriptor
.nSeekTableBytes
+ APEDescriptor
.nHeaderDataBytes
);
201 APEDescriptor
.nAPEFrameDataBytesHigh
= 0;
202 APEDescriptor
.nTerminatingDataBytes
= nTerminatingBytes
;
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
);
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
; }
214 // write the updated seek table
215 if (pIO
->Write(m_spSeekTable
, m_nMaxFrames
* 4, &nBytesWritten
) != 0) { return ERROR_IO_WRITE
; }
217 return ERROR_SUCCESS
;