1 #include "All.h" 2 #include "IO.h" 3 #include "APECompressCreate.h" 4 5 #include "APECompressCore.h" 6 7 CAPECompressCreate::CAPECompressCreate() 8 { 9 m_nMaxFrames = 0; 10 } 11 12 CAPECompressCreate::~CAPECompressCreate() 13 { 14 } 15 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 64 int CAPECompressCreate::GetFullFrameBytes() 65 { 66 return m_nSamplesPerFrame * m_wfeInput.nBlockAlign; 67 } 68 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 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 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 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 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