xref: /haiku/src/add-ons/media/plugins/ape_reader/MAClib/APECompressCreate.cpp (revision be878f60874196f746f336f235797c8efa25004e)
1 #include "All.h"
2 #include "IO.h"
3 #include "APECompressCreate.h"
4 
5 #include "APECompressCore.h"
6 
CAPECompressCreate()7 CAPECompressCreate::CAPECompressCreate()
8 {
9     m_nMaxFrames = 0;
10 }
11 
~CAPECompressCreate()12 CAPECompressCreate::~CAPECompressCreate()
13 {
14 }
15 
Start(CIO * pioOutput,const WAVEFORMATEX * pwfeInput,int nMaxAudioBytes,int nCompressionLevel,const void * pHeaderData,int nHeaderBytes)16 int 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 
GetFullFrameBytes()64 int CAPECompressCreate::GetFullFrameBytes()
65 {
66     return m_nSamplesPerFrame * m_wfeInput.nBlockAlign;
67 }
68 
EncodeFrame(const void * pInputData,int nInputBytes)69 int 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 
Finish(const void * pTerminatingData,int nTerminatingBytes,int nWAVTerminatingBytes)94 int 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 
SetSeekByte(int nFrame,int nByteOffset)106 int 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 
InitializeFile(CIO * pIO,const WAVEFORMATEX * pwfeInput,int nMaxFrames,int nCompressionLevel,const void * pHeaderData,int nHeaderBytes)113 int 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 
FinalizeFile(CIO * pIO,int nNumberOfFrames,int nFinalFrameBlocks,const void * pTerminatingData,int nTerminatingBytes,int nWAVTerminatingBytes,int nPeakLevel)167 int 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