xref: /haiku/src/add-ons/media/plugins/ape_reader/MAClib/APEInfo.cpp (revision 95c9effd68127df2dce202d5e254a7c86560010a)
1 /*****************************************************************************************
2 CAPEInfo:
3     -a class to make working with APE files and getting information about them simple
4 *****************************************************************************************/
5 #include "All.h"
6 #include "APEInfo.h"
7 #include IO_HEADER_FILE
8 #include "APECompress.h"
9 #include "APEHeader.h"
10 
11 /*****************************************************************************************
12 Construction
13 *****************************************************************************************/
14 CAPEInfo::CAPEInfo(int * pErrorCode, const wchar_t * pFilename, CAPETag * pTag)
15 {
16     *pErrorCode = ERROR_SUCCESS;
17     CloseFile();
18 
19     // open the file
20     m_spIO.Assign(new IO_CLASS_NAME);
21 
22     if (m_spIO->Open(pFilename) != 0)
23     {
24         CloseFile();
25         *pErrorCode = ERROR_INVALID_INPUT_FILE;
26         return;
27     }
28 
29     // get the file information
30     if (GetFileInformation(TRUE) != 0)
31     {
32         CloseFile();
33         *pErrorCode = ERROR_INVALID_INPUT_FILE;
34         return;
35     }
36 
37     // get the tag (do this second so that we don't do it on failure)
38     if (pTag == NULL)
39     {
40         // we don't want to analyze right away for non-local files
41         // since a single I/O object is shared, we can't tag and read at the same time (i.e. in multiple threads)
42         BOOL bAnalyzeNow = TRUE;
43         if ((strncmp(pFilename, "http://", 7) == 0) || (strncmp(pFilename, "m01p://", 7) == 0))		// SHINTA: wchar_t -> char
44             bAnalyzeNow = FALSE;
45 
46         m_spAPETag.Assign(new CAPETag(m_spIO, bAnalyzeNow));
47     }
48     else
49     {
50         m_spAPETag.Assign(pTag);
51     }
52 
53 }
54 
55 CAPEInfo::CAPEInfo(int * pErrorCode, CIO * pIO, CAPETag * pTag)
56 {
57     *pErrorCode = ERROR_SUCCESS;
58     CloseFile();
59 
60     m_spIO.Assign(pIO, FALSE, FALSE);
61 
62     // get the file information
63     if (GetFileInformation(TRUE) != 0)
64     {
65         CloseFile();
66         *pErrorCode = ERROR_INVALID_INPUT_FILE;
67         return;
68     }
69 
70     // get the tag (do this second so that we don't do it on failure)
71     if (pTag == NULL)
72         m_spAPETag.Assign(new CAPETag(m_spIO, TRUE));
73     else
74         m_spAPETag.Assign(pTag);
75 }
76 
77 
78 /*****************************************************************************************
79 Destruction
80 *****************************************************************************************/
81 CAPEInfo::~CAPEInfo()
82 {
83     CloseFile();
84 }
85 
86 /*****************************************************************************************
87 Close the file
88 *****************************************************************************************/
89 int CAPEInfo::CloseFile()
90 {
91     m_spIO.Delete();
92     m_APEFileInfo.spWaveHeaderData.Delete();
93     m_APEFileInfo.spSeekBitTable.Delete();
94     m_APEFileInfo.spSeekByteTable.Delete();
95     m_APEFileInfo.spAPEDescriptor.Delete();
96 
97     m_spAPETag.Delete();
98 
99     // re-initialize variables
100     m_APEFileInfo.nSeekTableElements = 0;
101     m_bHasFileInformationLoaded = FALSE;
102 
103     return ERROR_SUCCESS;
104 }
105 
106 /*****************************************************************************************
107 Get the file information about the file
108 *****************************************************************************************/
109 int CAPEInfo::GetFileInformation(BOOL bGetTagInformation)
110 {
111     // quit if there is no simple file
112     if (m_spIO == NULL) { return -1; }
113 
114     // quit if the file information has already been loaded
115     if (m_bHasFileInformationLoaded) { return ERROR_SUCCESS; }
116 
117     // use a CAPEHeader class to help us analyze the file
118     CAPEHeader APEHeader(m_spIO);
119     int nRetVal = APEHeader.Analyze(&m_APEFileInfo);
120 
121     // update our internal state
122     if (nRetVal == ERROR_SUCCESS)
123         m_bHasFileInformationLoaded = TRUE;
124 
125     // return
126     return nRetVal;
127 }
128 
129 /*****************************************************************************************
130 Primary query function
131 *****************************************************************************************/
132 int CAPEInfo::GetInfo(APE_DECOMPRESS_FIELDS Field, int nParam1, int nParam2)
133 {
134     int nRetVal = -1;
135 
136     switch (Field)
137     {
138     case APE_INFO_FILE_VERSION:
139         nRetVal = m_APEFileInfo.nVersion;
140         break;
141     case APE_INFO_COMPRESSION_LEVEL:
142         nRetVal = m_APEFileInfo.nCompressionLevel;
143         break;
144     case APE_INFO_FORMAT_FLAGS:
145         nRetVal = m_APEFileInfo.nFormatFlags;
146         break;
147     case APE_INFO_SAMPLE_RATE:
148         nRetVal = m_APEFileInfo.nSampleRate;
149         break;
150     case APE_INFO_BITS_PER_SAMPLE:
151         nRetVal = m_APEFileInfo.nBitsPerSample;
152         break;
153     case APE_INFO_BYTES_PER_SAMPLE:
154         nRetVal = m_APEFileInfo.nBytesPerSample;
155         break;
156     case APE_INFO_CHANNELS:
157         nRetVal = m_APEFileInfo.nChannels;
158         break;
159     case APE_INFO_BLOCK_ALIGN:
160         nRetVal = m_APEFileInfo.nBlockAlign;
161         break;
162     case APE_INFO_BLOCKS_PER_FRAME:
163         nRetVal = m_APEFileInfo.nBlocksPerFrame;
164         break;
165     case APE_INFO_FINAL_FRAME_BLOCKS:
166         nRetVal = m_APEFileInfo.nFinalFrameBlocks;
167         break;
168     case APE_INFO_TOTAL_FRAMES:
169         nRetVal = m_APEFileInfo.nTotalFrames;
170         break;
171     case APE_INFO_WAV_HEADER_BYTES:
172         nRetVal = m_APEFileInfo.nWAVHeaderBytes;
173         break;
174     case APE_INFO_WAV_TERMINATING_BYTES:
175         nRetVal = m_APEFileInfo.nWAVTerminatingBytes;
176         break;
177     case APE_INFO_WAV_DATA_BYTES:
178         nRetVal = m_APEFileInfo.nWAVDataBytes;
179         break;
180     case APE_INFO_WAV_TOTAL_BYTES:
181         nRetVal = m_APEFileInfo.nWAVTotalBytes;
182         break;
183     case APE_INFO_APE_TOTAL_BYTES:
184         nRetVal = m_APEFileInfo.nAPETotalBytes;
185         break;
186     case APE_INFO_TOTAL_BLOCKS:
187         nRetVal = m_APEFileInfo.nTotalBlocks;
188         break;
189     case APE_INFO_LENGTH_MS:
190         nRetVal = m_APEFileInfo.nLengthMS;
191         break;
192     case APE_INFO_AVERAGE_BITRATE:
193         nRetVal = m_APEFileInfo.nAverageBitrate;
194         break;
195     case APE_INFO_FRAME_BITRATE:
196     {
197         int nFrame = nParam1;
198 
199         nRetVal = 0;
200 
201         int nFrameBytes = GetInfo(APE_INFO_FRAME_BYTES, nFrame);
202         int nFrameBlocks = GetInfo(APE_INFO_FRAME_BLOCKS, nFrame);
203         if ((nFrameBytes > 0) && (nFrameBlocks > 0) && m_APEFileInfo.nSampleRate > 0)
204         {
205             int nFrameMS = (nFrameBlocks * 1000) / m_APEFileInfo.nSampleRate;
206             if (nFrameMS != 0)
207             {
208                 nRetVal = (nFrameBytes * 8) / nFrameMS;
209             }
210         }
211         break;
212     }
213     case APE_INFO_DECOMPRESSED_BITRATE:
214         nRetVal = m_APEFileInfo.nDecompressedBitrate;
215         break;
216     case APE_INFO_PEAK_LEVEL:
217         nRetVal = -1; // no longer supported
218         break;
219     case APE_INFO_SEEK_BIT:
220     {
221         int nFrame = nParam1;
222         if (GET_FRAMES_START_ON_BYTES_BOUNDARIES(this))
223         {
224             nRetVal = 0;
225         }
226         else
227         {
228             if (nFrame < 0 || nFrame >= m_APEFileInfo.nTotalFrames)
229                 nRetVal = 0;
230             else
231                 nRetVal = m_APEFileInfo.spSeekBitTable[nFrame];
232         }
233         break;
234     }
235     case APE_INFO_SEEK_BYTE:
236     {
237         int nFrame = nParam1;
238         if (nFrame < 0 || nFrame >= m_APEFileInfo.nTotalFrames)
239             nRetVal = 0;
240         else
241             nRetVal = m_APEFileInfo.spSeekByteTable[nFrame] + m_APEFileInfo.nJunkHeaderBytes;
242         break;
243     }
244     case APE_INFO_WAV_HEADER_DATA:
245     {
246         char * pBuffer = (char *) nParam1;
247         int nMaxBytes = nParam2;
248 
249         if (m_APEFileInfo.nFormatFlags & MAC_FORMAT_FLAG_CREATE_WAV_HEADER)
250         {
251             if (sizeof(WAVE_HEADER) > static_cast<uint32>(nMaxBytes))
252             {
253                 nRetVal = -1;
254             }
255             else
256             {
257                 WAVEFORMATEX wfeFormat; GetInfo(APE_INFO_WAVEFORMATEX, (int) &wfeFormat, 0);
258                 WAVE_HEADER WAVHeader; FillWaveHeader(&WAVHeader, m_APEFileInfo.nWAVDataBytes, &wfeFormat,
259                     m_APEFileInfo.nWAVTerminatingBytes);
260                 memcpy(pBuffer, &WAVHeader, sizeof(WAVE_HEADER));
261                 nRetVal = 0;
262             }
263         }
264         else
265         {
266             if (m_APEFileInfo.nWAVHeaderBytes > nMaxBytes)
267             {
268                 nRetVal = -1;
269             }
270             else
271             {
272                 memcpy(pBuffer, m_APEFileInfo.spWaveHeaderData, m_APEFileInfo.nWAVHeaderBytes);
273                 nRetVal = 0;
274             }
275         }
276         break;
277     }
278     case APE_INFO_WAV_TERMINATING_DATA:
279     {
280         char * pBuffer = (char *) nParam1;
281         int nMaxBytes = nParam2;
282 
283         if (m_APEFileInfo.nWAVTerminatingBytes > nMaxBytes)
284         {
285             nRetVal = -1;
286         }
287         else
288         {
289             if (m_APEFileInfo.nWAVTerminatingBytes > 0)
290             {
291                 // variables
292                 int nOriginalFileLocation = m_spIO->GetPosition();
293                 unsigned int nBytesRead = 0;
294 
295                 // check for a tag
296                 m_spIO->Seek(-(m_spAPETag->GetTagBytes() + m_APEFileInfo.nWAVTerminatingBytes), FILE_END);
297                 m_spIO->Read(pBuffer, m_APEFileInfo.nWAVTerminatingBytes, &nBytesRead);
298 
299                 // restore the file pointer
300                 m_spIO->Seek(nOriginalFileLocation, FILE_BEGIN);
301             }
302             nRetVal = 0;
303         }
304         break;
305     }
306     case APE_INFO_WAVEFORMATEX:
307     {
308         WAVEFORMATEX * pWaveFormatEx = (WAVEFORMATEX *) nParam1;
309         FillWaveFormatEx(pWaveFormatEx, m_APEFileInfo.nSampleRate, m_APEFileInfo.nBitsPerSample, m_APEFileInfo.nChannels);
310         nRetVal = 0;
311         break;
312     }
313     case APE_INFO_IO_SOURCE:
314         nRetVal = (int) m_spIO.GetPtr();
315         break;
316     case APE_INFO_FRAME_BYTES:
317     {
318         int nFrame = nParam1;
319 
320         // bound-check the frame index
321         if ((nFrame < 0) || (nFrame >= m_APEFileInfo.nTotalFrames))
322         {
323             nRetVal = -1;
324         }
325         else
326         {
327             if (nFrame != (m_APEFileInfo.nTotalFrames - 1))
328                 nRetVal = GetInfo(APE_INFO_SEEK_BYTE, nFrame + 1) - GetInfo(APE_INFO_SEEK_BYTE, nFrame);
329             else
330                 nRetVal = m_spIO->GetSize() - m_spAPETag->GetTagBytes() - m_APEFileInfo.nWAVTerminatingBytes - GetInfo(APE_INFO_SEEK_BYTE, nFrame);
331         }
332         break;
333     }
334     case APE_INFO_FRAME_BLOCKS:
335     {
336         int nFrame = nParam1;
337 
338         // bound-check the frame index
339         if ((nFrame < 0) || (nFrame >= m_APEFileInfo.nTotalFrames))
340         {
341             nRetVal = -1;
342         }
343         else
344         {
345             if (nFrame != (m_APEFileInfo.nTotalFrames - 1))
346                 nRetVal = m_APEFileInfo.nBlocksPerFrame;
347             else
348                 nRetVal = m_APEFileInfo.nFinalFrameBlocks;
349         }
350         break;
351     }
352     case APE_INFO_TAG:
353         nRetVal = (int) m_spAPETag.GetPtr();
354         break;
355     case APE_INTERNAL_INFO:
356         nRetVal = (int) &m_APEFileInfo;
357         break;
358     default:
359     	nRetVal=0;
360     }
361 
362     return nRetVal;
363 }
364