1 /* This file is part of UKNCBTL.
2 UKNCBTL is free software: you can redistribute it and/or modify it under the terms
3 of the GNU Lesser General Public License as published by the Free Software Foundation,
4 either version 3 of the License, or (at your option) any later version.
5 UKNCBTL is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
6 without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
7 See the GNU Lesser General Public License for more details.
8 You should have received a copy of the GNU Lesser General Public License along with
9 UKNCBTL. If not, see <http://www.gnu.org/licenses/>. */
10
11 // WavPcmFile.cpp
12
13 #include "stdafx.h"
14 #include "WavPcmFile.h"
15 #include <stdio.h>
16
17
18 //////////////////////////////////////////////////////////////////////
19
20 // WAV PCM format description: https://ccrma.stanford.edu/courses/422/projects/WaveFormat/
21
22 static const char magic1[4] = { 'R', 'I', 'F', 'F' };
23 static const char magic2[4] = { 'W', 'A', 'V', 'E' };
24 static const char format_tag_id[4] = { 'f', 'm', 't', ' ' };
25 static const char data_tag_id[4] = { 'd', 'a', 't', 'a' };
26
27 const int WAV_FORMAT_PCM = 1;
28
29 struct WAVPCMFILE
30 {
31 FILE* fpFile;
32 int nChannels;
33 int nBitsPerSample;
34 int nSampleFrequency;
35 int nBlockAlign;
36 quint32 dwDataOffset;
37 quint32 dwDataSize;
38 quint32 dwCurrentPosition;
39 bool okWriting;
40 };
41
WavPcmFile_GetFrequency(HWAVPCMFILE wavpcmfile)42 int WavPcmFile_GetFrequency(HWAVPCMFILE wavpcmfile)
43 {
44 if (wavpcmfile == INVALID_HANDLE_VALUE)
45 return 0;
46
47 WAVPCMFILE* pWavPcm = (WAVPCMFILE*) wavpcmfile;
48
49 return pWavPcm->nSampleFrequency;
50 }
51
WavPcmFile_GetLength(HWAVPCMFILE wavpcmfile)52 quint32 WavPcmFile_GetLength(HWAVPCMFILE wavpcmfile)
53 {
54 if (wavpcmfile == INVALID_HANDLE_VALUE)
55 return 0;
56
57 WAVPCMFILE* pWavPcm = (WAVPCMFILE*) wavpcmfile;
58
59 return pWavPcm->dwDataSize / pWavPcm->nBlockAlign;
60 }
61
WavPcmFile_GetPosition(HWAVPCMFILE wavpcmfile)62 quint32 WavPcmFile_GetPosition(HWAVPCMFILE wavpcmfile)
63 {
64 if (wavpcmfile == INVALID_HANDLE_VALUE)
65 return 0;
66
67 WAVPCMFILE* pWavPcm = (WAVPCMFILE*) wavpcmfile;
68
69 return pWavPcm->dwCurrentPosition;
70 }
71
WavPcmFile_SetPosition(HWAVPCMFILE wavpcmfile,quint32 position)72 void WavPcmFile_SetPosition(HWAVPCMFILE wavpcmfile, quint32 position)
73 {
74 if (wavpcmfile == INVALID_HANDLE_VALUE)
75 return;
76
77 WAVPCMFILE* pWavPcm = (WAVPCMFILE*) wavpcmfile;
78
79 quint32 offsetInData = position * pWavPcm->nBlockAlign;
80 ::fseek(pWavPcm->fpFile, pWavPcm->dwDataOffset + offsetInData, SEEK_SET);
81
82 pWavPcm->dwCurrentPosition = position;
83 }
84
WavPcmFile_Create(LPCTSTR filename,int sampleRate)85 HWAVPCMFILE WavPcmFile_Create(LPCTSTR filename, int sampleRate)
86 {
87 const int bitsPerSample = 8;
88 const int channels = 1;
89 const int blockAlign = channels * bitsPerSample / 8;
90
91 FILE* fpFileNew = ::_tfopen(filename, _T("w+b"));
92 if (fpFileNew == nullptr)
93 return (HWAVPCMFILE) INVALID_HANDLE_VALUE; // Failed to create file
94
95 // Prepare and write file header
96 quint8 consolidated_header[12 + 8 + 16 + 8];
97 ::memset(consolidated_header, 0, sizeof(consolidated_header));
98 size_t bytesWritten;
99
100 memcpy(&consolidated_header[0], magic1, 4); // RIFF
101 memcpy(&consolidated_header[8], magic2, 4); // WAVE
102
103 memcpy(&consolidated_header[12], format_tag_id, 4); // fmt
104 *((quint32*)(consolidated_header + 16)) = 16; // Size of "fmt" chunk
105 *((quint16*)(consolidated_header + 20)) = WAV_FORMAT_PCM; // AudioFormat = PCM
106 *((quint16*)(consolidated_header + 22)) = channels; // NumChannels = mono
107 *((quint32*)(consolidated_header + 24)) = sampleRate; // SampleRate
108 *((quint32*)(consolidated_header + 28)) = sampleRate * channels * bitsPerSample / 8; // ByteRate
109 *((quint16*)(consolidated_header + 32)) = blockAlign;
110 *((quint16*)(consolidated_header + 34)) = bitsPerSample;
111
112 memcpy(&consolidated_header[36], data_tag_id, 4); // data
113
114 // Write consolidated header
115 bytesWritten = ::fwrite(consolidated_header, 1, sizeof(consolidated_header), fpFileNew);
116 if (bytesWritten != sizeof(consolidated_header))
117 {
118 ::fclose(fpFileNew);
119 return (HWAVPCMFILE) INVALID_HANDLE_VALUE; // Failed to write consolidated header
120 }
121
122 WAVPCMFILE* pWavPcm = (WAVPCMFILE*) ::malloc(sizeof(WAVPCMFILE));
123 if (pWavPcm == nullptr)
124 {
125 ::fclose(fpFileNew);
126 return (HWAVPCMFILE) INVALID_HANDLE_VALUE; // Failed to allocate memory
127 }
128 memset(pWavPcm, 0, sizeof(WAVPCMFILE));
129 pWavPcm->fpFile = fpFileNew;
130 pWavPcm->nChannels = channels;
131 pWavPcm->nSampleFrequency = sampleRate;
132 pWavPcm->nBitsPerSample = bitsPerSample;
133 pWavPcm->nBlockAlign = blockAlign;
134 pWavPcm->dwDataOffset = sizeof(consolidated_header);
135 pWavPcm->dwDataSize = 0;
136 pWavPcm->okWriting = true;
137
138 WavPcmFile_SetPosition((HWAVPCMFILE) pWavPcm, 0);
139
140 return (HWAVPCMFILE) pWavPcm;
141 }
142
WavPcmFile_Open(LPCTSTR filename)143 HWAVPCMFILE WavPcmFile_Open(LPCTSTR filename)
144 {
145 FILE* fpFileOpen = ::_tfopen(filename, _T("rb"));
146 if (fpFileOpen == nullptr)
147 return (HWAVPCMFILE) INVALID_HANDLE_VALUE; // Failed to open file
148
149 quint32 offset = 0;
150 size_t bytesRead;
151 ::fseek(fpFileOpen, 0, SEEK_END);
152 quint32 fileSize = ::ftell(fpFileOpen);
153 ::fseek(fpFileOpen, 0, SEEK_SET);
154
155 quint8 fileHeader[12];
156 bytesRead = ::fread(fileHeader, 1, sizeof(fileHeader), fpFileOpen);
157 if (bytesRead != sizeof(fileHeader) ||
158 memcmp(&fileHeader[0], magic1, 4) != 0 ||
159 memcmp(&fileHeader[8], magic2, 4) != 0)
160 {
161 ::fclose(fpFileOpen);
162 return (HWAVPCMFILE) INVALID_HANDLE_VALUE; // Failed to read file header OR invalid 'RIFF' tag OR invalid 'WAVE' tag
163 }
164 offset += (quint32)bytesRead;
165
166 quint32 statedSize = *((quint32*)(fileHeader + 4)) + 8;
167 if (statedSize > fileSize)
168 statedSize = fileSize;
169
170 quint8 tagHeader[8];
171 quint16 formatTag[8];
172 bool formatSpecified = false;
173 int formatType, channels = 1, bitsPerSample, blockAlign;
174 quint32 sampleFrequency, bytesPerSecond, dataOffset, dataSize = 0;
175 while (offset < statedSize)
176 {
177 bytesRead = ::fread(tagHeader, 1, sizeof(tagHeader), fpFileOpen);
178 if (bytesRead != sizeof(tagHeader))
179 {
180 ::fclose(fpFileOpen);
181 return (HWAVPCMFILE) INVALID_HANDLE_VALUE; // Failed to read tag header
182 }
183 offset += (quint32)bytesRead;
184
185 quint32 tagSize = *(quint32*)(tagHeader + 4);
186 if (!memcmp(tagHeader, format_tag_id, 4))
187 {
188 if (formatSpecified || tagSize < sizeof(formatTag))
189 {
190 ::fclose(fpFileOpen);
191 return (HWAVPCMFILE) INVALID_HANDLE_VALUE; // Wrong tag header
192 }
193 formatSpecified = true;
194
195 bytesRead = ::fread(formatTag, 1, sizeof(formatTag), fpFileOpen);
196 if (bytesRead != sizeof(formatTag))
197 {
198 ::fclose(fpFileOpen);
199 return (HWAVPCMFILE) INVALID_HANDLE_VALUE; // Failed to read format tag
200 }
201
202 formatType = formatTag[0];
203 channels = formatTag[1];
204 sampleFrequency = formatTag[2];
205 bytesPerSecond = formatTag[4];
206 blockAlign = formatTag[6];
207 bitsPerSample = formatTag[7];
208
209 if (formatType != WAV_FORMAT_PCM)
210 {
211 ::fclose(fpFileOpen);
212 return (HWAVPCMFILE) INVALID_HANDLE_VALUE; // Unsupported format
213 }
214 if (sampleFrequency * bitsPerSample * channels / 8 != bytesPerSecond ||
215 (bitsPerSample != 8 && bitsPerSample != 16 && bitsPerSample != 32))
216 {
217 ::fclose(fpFileOpen);
218 return (HWAVPCMFILE) INVALID_HANDLE_VALUE; // Wrong format tag
219 }
220 }
221 else if (!memcmp(tagHeader, data_tag_id, 4))
222 {
223 if (!formatSpecified)
224 {
225 ::fclose(fpFileOpen);
226 return (HWAVPCMFILE) INVALID_HANDLE_VALUE; // Wrong tag
227 }
228
229 dataOffset = offset;
230 dataSize = tagSize;
231 }
232 else // Ignore all other tags
233 {
234 }
235
236 offset += tagSize;
237 ::fseek(fpFileOpen, offset, SEEK_SET);
238 }
239
240 WAVPCMFILE* pWavPcm = (WAVPCMFILE*) ::malloc(sizeof(WAVPCMFILE));
241 if (pWavPcm == nullptr)
242 {
243 ::fclose(fpFileOpen);
244 return (HWAVPCMFILE) INVALID_HANDLE_VALUE; // Failed to allocate memory
245 }
246 ::memset(pWavPcm, 0, sizeof(WAVPCMFILE));
247 pWavPcm->fpFile = fpFileOpen;
248 pWavPcm->nChannels = channels;
249 pWavPcm->nSampleFrequency = sampleFrequency;
250 pWavPcm->nBitsPerSample = bitsPerSample;
251 pWavPcm->nBlockAlign = blockAlign;
252 pWavPcm->dwDataOffset = dataOffset;
253 pWavPcm->dwDataSize = dataSize;
254 pWavPcm->okWriting = false;
255
256 WavPcmFile_SetPosition((HWAVPCMFILE) pWavPcm, 0);
257
258 return (HWAVPCMFILE) pWavPcm;
259 }
260
WavPcmFile_Close(HWAVPCMFILE wavpcmfile)261 void WavPcmFile_Close(HWAVPCMFILE wavpcmfile)
262 {
263 if (wavpcmfile == INVALID_HANDLE_VALUE)
264 return;
265
266 WAVPCMFILE* pWavPcm = (WAVPCMFILE*) wavpcmfile;
267
268 if (pWavPcm->okWriting)
269 {
270 size_t bytesWritten;
271 // Write data chunk size
272 ::fseek(pWavPcm->fpFile, 4, SEEK_SET);
273 quint32 chunkSize = 36 + pWavPcm->dwDataSize;
274 bytesWritten = ::fwrite(&chunkSize, 1, 4, pWavPcm->fpFile);
275 // Write data subchunk size
276 ::fseek(pWavPcm->fpFile, 40, SEEK_SET);
277 bytesWritten = ::fwrite(&(pWavPcm->dwDataSize), 1, 4, pWavPcm->fpFile);
278 }
279
280 ::fclose(pWavPcm->fpFile);
281 pWavPcm->fpFile = nullptr;
282 ::free(pWavPcm);
283 }
284
WavPcmFile_WriteOne(HWAVPCMFILE wavpcmfile,unsigned int value)285 bool WavPcmFile_WriteOne(HWAVPCMFILE wavpcmfile, unsigned int value)
286 {
287 if (wavpcmfile == INVALID_HANDLE_VALUE)
288 return false;
289
290 WAVPCMFILE* pWavPcm = (WAVPCMFILE*) wavpcmfile;
291 if (!pWavPcm->okWriting)
292 return false;
293 ASSERT(pWavPcm->nBitsPerSample == 8);
294 ASSERT(pWavPcm->nChannels == 1);
295
296 quint8 data = (value >> 24) & 0xff;
297
298 size_t bytesWritten = ::fwrite(&data, 1, 1, pWavPcm->fpFile);
299 if (bytesWritten != 1)
300 return false;
301
302 pWavPcm->dwCurrentPosition++;
303 pWavPcm->dwDataSize += pWavPcm->nBlockAlign;
304
305 return true;
306 }
307
WavPcmFile_ReadOne(HWAVPCMFILE wavpcmfile)308 unsigned int WavPcmFile_ReadOne(HWAVPCMFILE wavpcmfile)
309 {
310 if (wavpcmfile == INVALID_HANDLE_VALUE)
311 return 0;
312
313 WAVPCMFILE* pWavPcm = (WAVPCMFILE*) wavpcmfile;
314 if (pWavPcm->okWriting)
315 return 0;
316
317 // Read one sample
318 quint32 bytesToRead = pWavPcm->nBlockAlign;
319 size_t bytesRead;
320 quint8 data[16];
321 bytesRead = ::fread(data, 1, bytesToRead, pWavPcm->fpFile);
322 if (bytesRead != bytesToRead)
323 return 0;
324
325 pWavPcm->dwCurrentPosition++;
326
327 // Decode first channel
328 unsigned int value = 0;
329 switch (pWavPcm->nBitsPerSample)
330 {
331 case 8:
332 value = *data;
333 value = value << 24;
334 break;
335 case 16:
336 value = *((quint16*)data);
337 value = value << 16;
338 break;
339 case 32:
340 value = *((quint32*)data);
341 break;
342 }
343
344 return value;
345 }
346
347
348 //////////////////////////////////////////////////////////////////////
349