xref: /haiku/src/add-ons/media/plugins/ape_reader/MAClib/WAVInputSource.cpp (revision adb0d19d561947362090081e81d90dde59142026)
1 #include "All.h"
2 #include "WAVInputSource.h"
3 #include IO_HEADER_FILE
4 #include "MACLib.h"
5 #include "GlobalFunctions.h"
6 
7 struct RIFF_HEADER
8 {
9     char cRIFF[4];            // the characters 'RIFF' indicating that it's a RIFF file
10     unsigned long nBytes;    // the number of bytes following this header
11 };
12 
13 struct DATA_TYPE_ID_HEADER
14 {
15     char cDataTypeID[4];    // should equal 'WAVE' for a WAV file
16 };
17 
18 struct WAV_FORMAT_HEADER
19 {
20     unsigned short nFormatTag;            // the format of the WAV...should equal 1 for a PCM file
21     unsigned short nChannels;            // the number of channels
22     unsigned long nSamplesPerSecond;    // the number of samples per second
23     unsigned long nBytesPerSecond;        // the bytes per second
24     unsigned short nBlockAlign;            // block alignment
25     unsigned short nBitsPerSample;        // the number of bits per sample
26 };
27 
28 struct RIFF_CHUNK_HEADER
29 {
30     char cChunkLabel[4];        // should equal "data" indicating the data chunk
31     unsigned long nChunkBytes;  // the bytes of the chunk
32 };
33 
34 
35 CInputSource * __stdcall	CreateInputSource(const wchar_t * pSourceName, WAVEFORMATEX * pwfeSource, int * pTotalBlocks, int * pHeaderBytes, int * pTerminatingBytes, int * pErrorCode)
36 {
37     // error check the parameters
38     if ((pSourceName == NULL) || (wcslen(pSourceName) == 0))
39     {
40         if (pErrorCode) *pErrorCode = ERROR_BAD_PARAMETER;
41         return NULL;
42     }
43 
44     // get the extension
45     const wchar_t * pExtension = &pSourceName[wcslen(pSourceName)];
46     while ((pExtension > pSourceName) && (*pExtension != '.'))
47         pExtension--;
48 
49     // create the proper input source
50     // SHINTA -->
51 //    if (wcsicmp(pExtension, L".wav") == 0)
52 //    {
53         if (pErrorCode) *pErrorCode = ERROR_SUCCESS;
54         return new CWAVInputSource(pSourceName, pwfeSource, pTotalBlocks, pHeaderBytes, pTerminatingBytes, pErrorCode);
55 //    }
56 //    else
57 //    {
58 //        if (pErrorCode) *pErrorCode = ERROR_INVALID_INPUT_FILE;
59 //        return NULL;
60 //    }
61 	// <-- SHINTA
62 }
63 
64 CWAVInputSource::CWAVInputSource(CIO * pIO, WAVEFORMATEX * pwfeSource, int * pTotalBlocks, int * pHeaderBytes, int * pTerminatingBytes, int * pErrorCode)
65     : CInputSource(pIO, pwfeSource, pTotalBlocks, pHeaderBytes, pTerminatingBytes, pErrorCode)
66 {
67     m_bIsValid = FALSE;
68 
69     if (pIO == NULL || pwfeSource == NULL)
70     {
71         if (pErrorCode) *pErrorCode = ERROR_BAD_PARAMETER;
72         return;
73     }
74 
75     m_spIO.Assign(pIO, FALSE, FALSE);
76 
77     int nRetVal = AnalyzeSource();
78     if (nRetVal == ERROR_SUCCESS)
79     {
80         // fill in the parameters
81         if (pwfeSource) memcpy(pwfeSource, &m_wfeSource, sizeof(WAVEFORMATEX));
82         if (pTotalBlocks) *pTotalBlocks = m_nDataBytes / m_wfeSource.nBlockAlign;
83         if (pHeaderBytes) *pHeaderBytes = m_nHeaderBytes;
84         if (pTerminatingBytes) *pTerminatingBytes = m_nTerminatingBytes;
85 
86         m_bIsValid = TRUE;
87     }
88 
89     if (pErrorCode) *pErrorCode = nRetVal;
90 }
91 
92 CWAVInputSource::CWAVInputSource(const wchar_t * pSourceName, WAVEFORMATEX * pwfeSource, int * pTotalBlocks, int * pHeaderBytes, int * pTerminatingBytes, int * pErrorCode)
93     : CInputSource(pSourceName, pwfeSource, pTotalBlocks, pHeaderBytes, pTerminatingBytes, pErrorCode)
94 {
95     m_bIsValid = FALSE;
96 
97     if (pSourceName == NULL || pwfeSource == NULL)
98     {
99         if (pErrorCode) *pErrorCode = ERROR_BAD_PARAMETER;
100         return;
101     }
102 
103     m_spIO.Assign(new IO_CLASS_NAME);
104     if (m_spIO->Open(pSourceName) != ERROR_SUCCESS)
105     {
106         m_spIO.Delete();
107         if (pErrorCode) *pErrorCode = ERROR_INVALID_INPUT_FILE;
108         return;
109     }
110 
111     int nRetVal = AnalyzeSource();
112     if (nRetVal == ERROR_SUCCESS)
113     {
114         // fill in the parameters
115         if (pwfeSource) memcpy(pwfeSource, &m_wfeSource, sizeof(WAVEFORMATEX));
116         if (pTotalBlocks) *pTotalBlocks = m_nDataBytes / m_wfeSource.nBlockAlign;
117         if (pHeaderBytes) *pHeaderBytes = m_nHeaderBytes;
118         if (pTerminatingBytes) *pTerminatingBytes = m_nTerminatingBytes;
119 
120         m_bIsValid = TRUE;
121     }
122 
123     if (pErrorCode) *pErrorCode = nRetVal;
124 }
125 
126 CWAVInputSource::~CWAVInputSource()
127 {
128 
129 
130 }
131 
132 int CWAVInputSource::AnalyzeSource()
133 {
134     // seek to the beginning (just in case)
135     m_spIO->Seek(0, FILE_BEGIN);
136 
137     // get the file size
138     m_nFileBytes = m_spIO->GetSize();
139 
140     // get the RIFF header
141     RIFF_HEADER RIFFHeader;
142     RETURN_ON_ERROR(ReadSafe(m_spIO, &RIFFHeader, sizeof(RIFFHeader)))
143 
144     // make sure the RIFF header is valid
145     if (!(RIFFHeader.cRIFF[0] == 'R' && RIFFHeader.cRIFF[1] == 'I' && RIFFHeader.cRIFF[2] == 'F' && RIFFHeader.cRIFF[3] == 'F'))
146         return ERROR_INVALID_INPUT_FILE;
147 
148     // read the data type header
149     DATA_TYPE_ID_HEADER DataTypeIDHeader;
150     RETURN_ON_ERROR(ReadSafe(m_spIO, &DataTypeIDHeader, sizeof(DataTypeIDHeader)))
151 
152     // make sure it's the right data type
153     if (!(DataTypeIDHeader.cDataTypeID[0] == 'W' && DataTypeIDHeader.cDataTypeID[1] == 'A' && DataTypeIDHeader.cDataTypeID[2] == 'V' && DataTypeIDHeader.cDataTypeID[3] == 'E'))
154         return ERROR_INVALID_INPUT_FILE;
155 
156     // find the 'fmt ' chunk
157     RIFF_CHUNK_HEADER RIFFChunkHeader;
158     RETURN_ON_ERROR(ReadSafe(m_spIO, &RIFFChunkHeader, sizeof(RIFFChunkHeader)))
159 
160     while (!(RIFFChunkHeader.cChunkLabel[0] == 'f' && RIFFChunkHeader.cChunkLabel[1] == 'm' && RIFFChunkHeader.cChunkLabel[2] == 't' && RIFFChunkHeader.cChunkLabel[3] == ' '))
161     {
162         // move the file pointer to the end of this chunk
163         m_spIO->Seek(RIFFChunkHeader.nChunkBytes, FILE_CURRENT);
164 
165         // check again for the data chunk
166         RETURN_ON_ERROR(ReadSafe(m_spIO, &RIFFChunkHeader, sizeof(RIFFChunkHeader)))
167     }
168 
169     // read the format info
170     WAV_FORMAT_HEADER WAVFormatHeader;
171     RETURN_ON_ERROR(ReadSafe(m_spIO, &WAVFormatHeader, sizeof(WAVFormatHeader)))
172 
173     // error check the header to see if we support it
174     if (WAVFormatHeader.nFormatTag != 1)
175         return ERROR_INVALID_INPUT_FILE;
176 
177     // copy the format information to the WAVEFORMATEX passed in
178     FillWaveFormatEx(&m_wfeSource, WAVFormatHeader.nSamplesPerSecond, WAVFormatHeader.nBitsPerSample, WAVFormatHeader.nChannels);
179 
180     // skip over any extra data in the header
181     int nWAVFormatHeaderExtra = RIFFChunkHeader.nChunkBytes - sizeof(WAVFormatHeader);
182     if (nWAVFormatHeaderExtra < 0)
183         return ERROR_INVALID_INPUT_FILE;
184     else
185         m_spIO->Seek(nWAVFormatHeaderExtra, FILE_CURRENT);
186 
187     // find the data chunk
188     RETURN_ON_ERROR(ReadSafe(m_spIO, &RIFFChunkHeader, sizeof(RIFFChunkHeader)))
189 
190     while (!(RIFFChunkHeader.cChunkLabel[0] == 'd' && RIFFChunkHeader.cChunkLabel[1] == 'a' && RIFFChunkHeader.cChunkLabel[2] == 't' && RIFFChunkHeader.cChunkLabel[3] == 'a'))
191     {
192         // move the file pointer to the end of this chunk
193         m_spIO->Seek(RIFFChunkHeader.nChunkBytes, FILE_CURRENT);
194 
195         // check again for the data chunk
196         RETURN_ON_ERROR(ReadSafe(m_spIO, &RIFFChunkHeader, sizeof(RIFFChunkHeader)))
197     }
198 
199     // we're at the data block
200     m_nHeaderBytes = m_spIO->GetPosition();
201     m_nDataBytes = RIFFChunkHeader.nChunkBytes;
202     if (m_nDataBytes < 0)
203         m_nDataBytes = m_nFileBytes - m_nHeaderBytes;
204 
205     // make sure the data bytes is a whole number of blocks
206     if ((m_nDataBytes % m_wfeSource.nBlockAlign) != 0)
207         return ERROR_INVALID_INPUT_FILE;
208 
209     // calculate the terminating byts
210     m_nTerminatingBytes = m_nFileBytes - m_nDataBytes - m_nHeaderBytes;
211 
212     // we made it this far, everything must be cool
213     return ERROR_SUCCESS;
214 }
215 
216 int CWAVInputSource::GetData(unsigned char * pBuffer, int nBlocks, int * pBlocksRetrieved)
217 {
218     if (!m_bIsValid) return ERROR_UNDEFINED;
219 
220     int nBytes = (m_wfeSource.nBlockAlign * nBlocks);
221     unsigned int nBytesRead = 0;
222 
223     if (m_spIO->Read(pBuffer, nBytes, &nBytesRead) != ERROR_SUCCESS)
224         return ERROR_IO_READ;
225 
226     if (pBlocksRetrieved) *pBlocksRetrieved = (nBytesRead / m_wfeSource.nBlockAlign);
227 
228     return ERROR_SUCCESS;
229 }
230 
231 int CWAVInputSource::GetHeaderData(unsigned char * pBuffer)
232 {
233     if (!m_bIsValid) return ERROR_UNDEFINED;
234 
235     int nRetVal = ERROR_SUCCESS;
236 
237     if (m_nHeaderBytes > 0)
238     {
239         int nOriginalFileLocation = m_spIO->GetPosition();
240 
241         m_spIO->Seek(0, FILE_BEGIN);
242 
243         unsigned int nBytesRead = 0;
244         int nReadRetVal = m_spIO->Read(pBuffer, m_nHeaderBytes, &nBytesRead);
245 
246         if ((nReadRetVal != ERROR_SUCCESS) || (m_nHeaderBytes != int(nBytesRead)))
247         {
248             nRetVal = ERROR_UNDEFINED;
249         }
250 
251         m_spIO->Seek(nOriginalFileLocation, FILE_BEGIN);
252     }
253 
254     return nRetVal;
255 }
256 
257 int CWAVInputSource::GetTerminatingData(unsigned char * pBuffer)
258 {
259     if (!m_bIsValid) return ERROR_UNDEFINED;
260 
261     int nRetVal = ERROR_SUCCESS;
262 
263     if (m_nTerminatingBytes > 0)
264     {
265         int nOriginalFileLocation = m_spIO->GetPosition();
266 
267         m_spIO->Seek(-m_nTerminatingBytes, FILE_END);
268 
269         unsigned int nBytesRead = 0;
270         int nReadRetVal = m_spIO->Read(pBuffer, m_nTerminatingBytes, &nBytesRead);
271 
272         if ((nReadRetVal != ERROR_SUCCESS) || (m_nTerminatingBytes != int(nBytesRead)))
273         {
274             nRetVal = ERROR_UNDEFINED;
275         }
276 
277         m_spIO->Seek(nOriginalFileLocation, FILE_BEGIN);
278     }
279 
280     return nRetVal;
281 }
282