1 |
//////////////////////////////////////////////////////////////////////////////// |
2 |
/// |
3 |
/// Classes for easy reading & writing of WAV sound files. |
4 |
/// |
5 |
/// For big-endian CPU, define _BIG_ENDIAN_ during compile-time to correctly |
6 |
/// parse the WAV files with such processors. |
7 |
/// |
8 |
/// Admittingly, more complete WAV reader routines may exist in public domain, |
9 |
/// but the reason for 'yet another' one is that those generic WAV reader |
10 |
/// libraries are exhaustingly large and cumbersome! Wanted to have something |
11 |
/// simpler here, i.e. something that's not already larger than rest of the |
12 |
/// SoundTouch/SoundStretch program... |
13 |
/// |
14 |
/// Author : Copyright (c) Olli Parviainen |
15 |
/// Author e-mail : oparviai 'at' iki.fi |
16 |
/// SoundTouch WWW: http://www.surina.net/soundtouch |
17 |
/// |
18 |
//////////////////////////////////////////////////////////////////////////////// |
19 |
// |
20 |
// Last changed : $Date: 2009-02-21 18:00:14 +0200 (Sat, 21 Feb 2009) $ |
21 |
// File revision : $Revision: 4 $ |
22 |
// |
23 |
// $Id: WavFile.cpp 63 2009-02-21 16:00:14Z oparviai $ |
24 |
// |
25 |
//////////////////////////////////////////////////////////////////////////////// |
26 |
// |
27 |
// License : |
28 |
// |
29 |
// SoundTouch audio processing library |
30 |
// Copyright (c) Olli Parviainen |
31 |
// |
32 |
// This library is free software; you can redistribute it and/or |
33 |
// modify it under the terms of the GNU Lesser General Public |
34 |
// License as published by the Free Software Foundation; either |
35 |
// version 2.1 of the License, or (at your option) any later version. |
36 |
// |
37 |
// This library is distributed in the hope that it will be useful, |
38 |
// but WITHOUT ANY WARRANTY; without even the implied warranty of |
39 |
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
40 |
// Lesser General Public License for more details. |
41 |
// |
42 |
// You should have received a copy of the GNU Lesser General Public |
43 |
// License along with this library; if not, write to the Free Software |
44 |
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
45 |
// |
46 |
//////////////////////////////////////////////////////////////////////////////// |
47 |
|
48 |
#include <stdio.h> |
49 |
#include <stdexcept> |
50 |
#include <string> |
51 |
#include <cstring> |
52 |
#include <assert.h> |
53 |
#include <limits.h> |
54 |
|
55 |
#include "WavFile.h" |
56 |
|
57 |
using namespace std; |
58 |
|
59 |
static const char riffStr[] = "RIFF"; |
60 |
static const char waveStr[] = "WAVE"; |
61 |
static const char fmtStr[] = "fmt "; |
62 |
static const char dataStr[] = "data"; |
63 |
|
64 |
|
65 |
////////////////////////////////////////////////////////////////////////////// |
66 |
// |
67 |
// Helper functions for swapping byte order to correctly read/write WAV files |
68 |
// with big-endian CPU's: Define compile-time definition _BIG_ENDIAN_ to |
69 |
// turn-on the conversion if it appears necessary. |
70 |
// |
71 |
// For example, Intel x86 is little-endian and doesn't require conversion, |
72 |
// while PowerPC of Mac's and many other RISC cpu's are big-endian. |
73 |
|
74 |
#ifdef BYTE_ORDER |
75 |
// In gcc compiler detect the byte order automatically |
76 |
#if BYTE_ORDER == BIG_ENDIAN |
77 |
// big-endian platform. |
78 |
#define _BIG_ENDIAN_ |
79 |
#endif |
80 |
#endif |
81 |
|
82 |
#ifdef _BIG_ENDIAN_ |
83 |
// big-endian CPU, swap bytes in 16 & 32 bit words |
84 |
|
85 |
// helper-function to swap byte-order of 32bit integer |
86 |
static inline void _swap32(unsigned int &dwData) |
87 |
{ |
88 |
dwData = ((dwData >> 24) & 0x000000FF) | |
89 |
((dwData >> 8) & 0x0000FF00) | |
90 |
((dwData << 8) & 0x00FF0000) | |
91 |
((dwData << 24) & 0xFF000000); |
92 |
} |
93 |
|
94 |
// helper-function to swap byte-order of 16bit integer |
95 |
static inline void _swap16(unsigned short &wData) |
96 |
{ |
97 |
wData = ((wData >> 8) & 0x00FF) | |
98 |
((wData << 8) & 0xFF00); |
99 |
} |
100 |
|
101 |
// helper-function to swap byte-order of buffer of 16bit integers |
102 |
static inline void _swap16Buffer(unsigned short *pData, unsigned int dwNumWords) |
103 |
{ |
104 |
unsigned long i; |
105 |
|
106 |
for (i = 0; i < dwNumWords; i ++) |
107 |
{ |
108 |
_swap16(pData[i]); |
109 |
} |
110 |
} |
111 |
|
112 |
#else // BIG_ENDIAN |
113 |
// little-endian CPU, WAV file is ok as such |
114 |
|
115 |
// dummy helper-function |
116 |
static inline void _swap32(unsigned int &dwData) |
117 |
{ |
118 |
// do nothing |
119 |
} |
120 |
|
121 |
// dummy helper-function |
122 |
static inline void _swap16(unsigned short &wData) |
123 |
{ |
124 |
// do nothing |
125 |
} |
126 |
|
127 |
// dummy helper-function |
128 |
static inline void _swap16Buffer(unsigned short *pData, unsigned int dwNumBytes) |
129 |
{ |
130 |
// do nothing |
131 |
} |
132 |
|
133 |
#endif // BIG_ENDIAN |
134 |
|
135 |
|
136 |
////////////////////////////////////////////////////////////////////////////// |
137 |
// |
138 |
// Class WavInFile |
139 |
// |
140 |
|
141 |
WavInFile::WavInFile(const char *fileName) |
142 |
{ |
143 |
// Try to open the file for reading |
144 |
fptr = fopen(fileName, "rb"); |
145 |
if (fptr == NULL) |
146 |
{ |
147 |
// didn't succeed |
148 |
string msg = "Error : Unable to open file \""; |
149 |
msg += fileName; |
150 |
msg += "\" for reading."; |
151 |
throw runtime_error(msg); |
152 |
} |
153 |
|
154 |
init(); |
155 |
} |
156 |
|
157 |
|
158 |
WavInFile::WavInFile(FILE *file) |
159 |
{ |
160 |
// Try to open the file for reading |
161 |
fptr = file; |
162 |
if (!file) |
163 |
{ |
164 |
// didn't succeed |
165 |
string msg = "Error : Unable to access input stream for reading"; |
166 |
throw runtime_error(msg); |
167 |
} |
168 |
|
169 |
init(); |
170 |
} |
171 |
|
172 |
|
173 |
/// Init the WAV file stream |
174 |
void WavInFile::init() |
175 |
{ |
176 |
int hdrsOk; |
177 |
|
178 |
// assume file stream is already open |
179 |
assert(fptr); |
180 |
|
181 |
// Read the file headers |
182 |
hdrsOk = readWavHeaders(); |
183 |
if (hdrsOk != 0) |
184 |
{ |
185 |
// Something didn't match in the wav file headers |
186 |
string msg = "Input file is corrupt or not a WAV file"; |
187 |
throw runtime_error(msg); |
188 |
} |
189 |
|
190 |
if (header.format.fixed != 1) |
191 |
{ |
192 |
string msg = "Input file uses unsupported encoding."; |
193 |
throw runtime_error(msg); |
194 |
} |
195 |
|
196 |
dataRead = 0; |
197 |
} |
198 |
|
199 |
|
200 |
|
201 |
WavInFile::~WavInFile() |
202 |
{ |
203 |
if (fptr) fclose(fptr); |
204 |
fptr = NULL; |
205 |
} |
206 |
|
207 |
|
208 |
|
209 |
void WavInFile::rewind() |
210 |
{ |
211 |
int hdrsOk; |
212 |
|
213 |
fseek(fptr, 0, SEEK_SET); |
214 |
hdrsOk = readWavHeaders(); |
215 |
assert(hdrsOk == 0); |
216 |
dataRead = 0; |
217 |
} |
218 |
|
219 |
|
220 |
int WavInFile::checkCharTags() const |
221 |
{ |
222 |
// header.format.fmt should equal to 'fmt ' |
223 |
if (memcmp(fmtStr, header.format.fmt, 4) != 0) return -1; |
224 |
// header.data.data_field should equal to 'data' |
225 |
if (memcmp(dataStr, header.data.data_field, 4) != 0) return -1; |
226 |
|
227 |
return 0; |
228 |
} |
229 |
|
230 |
|
231 |
int WavInFile::read(char *buffer, int maxElems) |
232 |
{ |
233 |
int numBytes; |
234 |
uint afterDataRead; |
235 |
|
236 |
// ensure it's 8 bit format |
237 |
if (header.format.bits_per_sample != 8) |
238 |
{ |
239 |
throw runtime_error("Error: WavInFile::read(char*, int) works only with 8bit samples."); |
240 |
} |
241 |
assert(sizeof(char) == 1); |
242 |
|
243 |
numBytes = maxElems; |
244 |
afterDataRead = dataRead + numBytes; |
245 |
if (afterDataRead > header.data.data_len) |
246 |
{ |
247 |
// Don't read more samples than are marked available in header |
248 |
numBytes = (int)header.data.data_len - (int)dataRead; |
249 |
assert(numBytes >= 0); |
250 |
} |
251 |
|
252 |
assert(buffer); |
253 |
numBytes = fread(buffer, 1, numBytes, fptr); |
254 |
dataRead += numBytes; |
255 |
|
256 |
return numBytes; |
257 |
} |
258 |
|
259 |
|
260 |
int WavInFile::read(short *buffer, int maxElems) |
261 |
{ |
262 |
unsigned int afterDataRead; |
263 |
int numBytes; |
264 |
int numElems; |
265 |
|
266 |
assert(buffer); |
267 |
if (header.format.bits_per_sample == 8) |
268 |
{ |
269 |
// 8 bit format |
270 |
char *temp = new char[maxElems]; |
271 |
int i; |
272 |
|
273 |
numElems = read(temp, maxElems); |
274 |
// convert from 8 to 16 bit |
275 |
for (i = 0; i < numElems; i ++) |
276 |
{ |
277 |
buffer[i] = temp[i] << 8; |
278 |
} |
279 |
delete[] temp; |
280 |
} |
281 |
else |
282 |
{ |
283 |
// 16 bit format |
284 |
assert(header.format.bits_per_sample == 16); |
285 |
assert(sizeof(short) == 2); |
286 |
|
287 |
numBytes = maxElems * 2; |
288 |
afterDataRead = dataRead + numBytes; |
289 |
if (afterDataRead > header.data.data_len) |
290 |
{ |
291 |
// Don't read more samples than are marked available in header |
292 |
numBytes = (int)header.data.data_len - (int)dataRead; |
293 |
assert(numBytes >= 0); |
294 |
} |
295 |
|
296 |
numBytes = fread(buffer, 1, numBytes, fptr); |
297 |
dataRead += numBytes; |
298 |
numElems = numBytes / 2; |
299 |
|
300 |
// 16bit samples, swap byte order if necessary |
301 |
_swap16Buffer((unsigned short *)buffer, numElems); |
302 |
} |
303 |
|
304 |
return numElems; |
305 |
} |
306 |
|
307 |
|
308 |
|
309 |
int WavInFile::read(float *buffer, int maxElems) |
310 |
{ |
311 |
short *temp = new short[maxElems]; |
312 |
int num; |
313 |
int i; |
314 |
double fscale; |
315 |
|
316 |
num = read(temp, maxElems); |
317 |
|
318 |
fscale = 1.0 / 32768.0; |
319 |
// convert to floats, scale to range [-1..+1[ |
320 |
for (i = 0; i < num; i ++) |
321 |
{ |
322 |
buffer[i] = (float)(fscale * (double)temp[i]); |
323 |
} |
324 |
|
325 |
delete[] temp; |
326 |
|
327 |
return num; |
328 |
} |
329 |
|
330 |
|
331 |
int WavInFile::eof() const |
332 |
{ |
333 |
// return true if all data has been read or file eof has reached |
334 |
return (dataRead == header.data.data_len || feof(fptr)); |
335 |
} |
336 |
|
337 |
|
338 |
|
339 |
// test if character code is between a white space ' ' and little 'z' |
340 |
static int isAlpha(char c) |
341 |
{ |
342 |
return (c >= ' ' && c <= 'z') ? 1 : 0; |
343 |
} |
344 |
|
345 |
|
346 |
// test if all characters are between a white space ' ' and little 'z' |
347 |
static int isAlphaStr(const char *str) |
348 |
{ |
349 |
char c; |
350 |
|
351 |
c = str[0]; |
352 |
while (c) |
353 |
{ |
354 |
if (isAlpha(c) == 0) return 0; |
355 |
str ++; |
356 |
c = str[0]; |
357 |
} |
358 |
|
359 |
return 1; |
360 |
} |
361 |
|
362 |
|
363 |
int WavInFile::readRIFFBlock() |
364 |
{ |
365 |
if (fread(&(header.riff), sizeof(WavRiff), 1, fptr) != 1) return -1; |
366 |
|
367 |
// swap 32bit data byte order if necessary |
368 |
_swap32((unsigned int &)header.riff.package_len); |
369 |
|
370 |
// header.riff.riff_char should equal to 'RIFF'); |
371 |
if (memcmp(riffStr, header.riff.riff_char, 4) != 0) return -1; |
372 |
// header.riff.wave should equal to 'WAVE' |
373 |
if (memcmp(waveStr, header.riff.wave, 4) != 0) return -1; |
374 |
|
375 |
return 0; |
376 |
} |
377 |
|
378 |
|
379 |
|
380 |
|
381 |
int WavInFile::readHeaderBlock() |
382 |
{ |
383 |
char label[5]; |
384 |
string sLabel; |
385 |
|
386 |
// lead label string |
387 |
if (fread(label, 1, 4, fptr) !=4) return -1; |
388 |
label[4] = 0; |
389 |
|
390 |
if (isAlphaStr(label) == 0) return -1; // not a valid label |
391 |
|
392 |
// Decode blocks according to their label |
393 |
if (strcmp(label, fmtStr) == 0) |
394 |
{ |
395 |
int nLen, nDump; |
396 |
|
397 |
// 'fmt ' block |
398 |
memcpy(header.format.fmt, fmtStr, 4); |
399 |
|
400 |
// read length of the format field |
401 |
if (fread(&nLen, sizeof(int), 1, fptr) != 1) return -1; |
402 |
// swap byte order if necessary |
403 |
_swap32((unsigned int &)nLen); // int format_len; |
404 |
header.format.format_len = nLen; |
405 |
|
406 |
// calculate how much length differs from expected |
407 |
nDump = nLen - ((int)sizeof(header.format) - 8); |
408 |
|
409 |
// if format_len is larger than expected, read only as much data as we've space for |
410 |
if (nDump > 0) |
411 |
{ |
412 |
nLen = sizeof(header.format) - 8; |
413 |
} |
414 |
|
415 |
// read data |
416 |
if (fread(&(header.format.fixed), nLen, 1, fptr) != 1) return -1; |
417 |
|
418 |
// swap byte order if necessary |
419 |
_swap16((unsigned short &)header.format.fixed); // short int fixed; |
420 |
_swap16((unsigned short &)header.format.channel_number); // short int channel_number; |
421 |
_swap32((unsigned int &)header.format.sample_rate); // int sample_rate; |
422 |
_swap32((unsigned int &)header.format.byte_rate); // int byte_rate; |
423 |
_swap16((unsigned short &)header.format.byte_per_sample); // short int byte_per_sample; |
424 |
_swap16((unsigned short &)header.format.bits_per_sample); // short int bits_per_sample; |
425 |
|
426 |
// if format_len is larger than expected, skip the extra data |
427 |
if (nDump > 0) |
428 |
{ |
429 |
fseek(fptr, nDump, SEEK_CUR); |
430 |
} |
431 |
|
432 |
return 0; |
433 |
} |
434 |
else if (strcmp(label, dataStr) == 0) |
435 |
{ |
436 |
// 'data' block |
437 |
memcpy(header.data.data_field, dataStr, 4); |
438 |
if (fread(&(header.data.data_len), sizeof(uint), 1, fptr) != 1) return -1; |
439 |
|
440 |
// swap byte order if necessary |
441 |
_swap32((unsigned int &)header.data.data_len); |
442 |
|
443 |
return 1; |
444 |
} |
445 |
else |
446 |
{ |
447 |
uint len, i; |
448 |
uint temp; |
449 |
// unknown block |
450 |
|
451 |
// read length |
452 |
if (fread(&len, sizeof(len), 1, fptr) != 1) return -1; |
453 |
// scan through the block |
454 |
for (i = 0; i < len; i ++) |
455 |
{ |
456 |
if (fread(&temp, 1, 1, fptr) != 1) return -1; |
457 |
if (feof(fptr)) return -1; // unexpected eof |
458 |
} |
459 |
} |
460 |
return 0; |
461 |
} |
462 |
|
463 |
|
464 |
int WavInFile::readWavHeaders() |
465 |
{ |
466 |
int res; |
467 |
|
468 |
memset(&header, 0, sizeof(header)); |
469 |
|
470 |
res = readRIFFBlock(); |
471 |
if (res) return 1; |
472 |
// read header blocks until data block is found |
473 |
do |
474 |
{ |
475 |
// read header blocks |
476 |
res = readHeaderBlock(); |
477 |
if (res < 0) return 1; // error in file structure |
478 |
} while (res == 0); |
479 |
// check that all required tags are legal |
480 |
return checkCharTags(); |
481 |
} |
482 |
|
483 |
|
484 |
uint WavInFile::getNumChannels() const |
485 |
{ |
486 |
return header.format.channel_number; |
487 |
} |
488 |
|
489 |
|
490 |
uint WavInFile::getNumBits() const |
491 |
{ |
492 |
return header.format.bits_per_sample; |
493 |
} |
494 |
|
495 |
|
496 |
uint WavInFile::getBytesPerSample() const |
497 |
{ |
498 |
return getNumChannels() * getNumBits() / 8; |
499 |
} |
500 |
|
501 |
|
502 |
uint WavInFile::getSampleRate() const |
503 |
{ |
504 |
return header.format.sample_rate; |
505 |
} |
506 |
|
507 |
|
508 |
|
509 |
uint WavInFile::getDataSizeInBytes() const |
510 |
{ |
511 |
return header.data.data_len; |
512 |
} |
513 |
|
514 |
|
515 |
uint WavInFile::getNumSamples() const |
516 |
{ |
517 |
if (header.format.byte_per_sample == 0) return 0; |
518 |
return header.data.data_len / (unsigned short)header.format.byte_per_sample; |
519 |
} |
520 |
|
521 |
|
522 |
uint WavInFile::getLengthMS() const |
523 |
{ |
524 |
uint numSamples; |
525 |
uint sampleRate; |
526 |
|
527 |
numSamples = getNumSamples(); |
528 |
sampleRate = getSampleRate(); |
529 |
|
530 |
assert(numSamples < UINT_MAX / 1000); |
531 |
return (1000 * numSamples / sampleRate); |
532 |
} |
533 |
|
534 |
|
535 |
////////////////////////////////////////////////////////////////////////////// |
536 |
// |
537 |
// Class WavOutFile |
538 |
// |
539 |
|
540 |
WavOutFile::WavOutFile(const char *fileName, int sampleRate, int bits, int channels) |
541 |
{ |
542 |
bytesWritten = 0; |
543 |
fptr = fopen(fileName, "wb"); |
544 |
if (fptr == NULL) |
545 |
{ |
546 |
string msg = "Error : Unable to open file \""; |
547 |
msg += fileName; |
548 |
msg += "\" for writing."; |
549 |
//pmsg = msg.c_str; |
550 |
throw runtime_error(msg); |
551 |
} |
552 |
|
553 |
fillInHeader(sampleRate, bits, channels); |
554 |
writeHeader(); |
555 |
} |
556 |
|
557 |
|
558 |
WavOutFile::WavOutFile(FILE *file, int sampleRate, int bits, int channels) |
559 |
{ |
560 |
bytesWritten = 0; |
561 |
fptr = file; |
562 |
if (fptr == NULL) |
563 |
{ |
564 |
string msg = "Error : Unable to access output file stream."; |
565 |
throw runtime_error(msg); |
566 |
} |
567 |
|
568 |
fillInHeader(sampleRate, bits, channels); |
569 |
writeHeader(); |
570 |
} |
571 |
|
572 |
|
573 |
|
574 |
WavOutFile::~WavOutFile() |
575 |
{ |
576 |
finishHeader(); |
577 |
if (fptr) fclose(fptr); |
578 |
fptr = NULL; |
579 |
} |
580 |
|
581 |
|
582 |
|
583 |
void WavOutFile::fillInHeader(uint sampleRate, uint bits, uint channels) |
584 |
{ |
585 |
// fill in the 'riff' part.. |
586 |
|
587 |
// copy string 'RIFF' to riff_char |
588 |
memcpy(&(header.riff.riff_char), riffStr, 4); |
589 |
// package_len unknown so far |
590 |
header.riff.package_len = 0; |
591 |
// copy string 'WAVE' to wave |
592 |
memcpy(&(header.riff.wave), waveStr, 4); |
593 |
|
594 |
|
595 |
// fill in the 'format' part.. |
596 |
|
597 |
// copy string 'fmt ' to fmt |
598 |
memcpy(&(header.format.fmt), fmtStr, 4); |
599 |
|
600 |
header.format.format_len = 0x10; |
601 |
header.format.fixed = 1; |
602 |
header.format.channel_number = (short)channels; |
603 |
header.format.sample_rate = (int)sampleRate; |
604 |
header.format.bits_per_sample = (short)bits; |
605 |
header.format.byte_per_sample = (short)(bits * channels / 8); |
606 |
header.format.byte_rate = header.format.byte_per_sample * (int)sampleRate; |
607 |
header.format.sample_rate = (int)sampleRate; |
608 |
|
609 |
// fill in the 'data' part.. |
610 |
|
611 |
// copy string 'data' to data_field |
612 |
memcpy(&(header.data.data_field), dataStr, 4); |
613 |
// data_len unknown so far |
614 |
header.data.data_len = 0; |
615 |
} |
616 |
|
617 |
|
618 |
void WavOutFile::finishHeader() |
619 |
{ |
620 |
// supplement the file length into the header structure |
621 |
header.riff.package_len = bytesWritten + 36; |
622 |
header.data.data_len = bytesWritten; |
623 |
|
624 |
writeHeader(); |
625 |
} |
626 |
|
627 |
|
628 |
|
629 |
void WavOutFile::writeHeader() |
630 |
{ |
631 |
WavHeader hdrTemp; |
632 |
int res; |
633 |
|
634 |
// swap byte order if necessary |
635 |
hdrTemp = header; |
636 |
_swap32((unsigned int &)hdrTemp.riff.package_len); |
637 |
_swap32((unsigned int &)hdrTemp.format.format_len); |
638 |
_swap16((unsigned short &)hdrTemp.format.fixed); |
639 |
_swap16((unsigned short &)hdrTemp.format.channel_number); |
640 |
_swap32((unsigned int &)hdrTemp.format.sample_rate); |
641 |
_swap32((unsigned int &)hdrTemp.format.byte_rate); |
642 |
_swap16((unsigned short &)hdrTemp.format.byte_per_sample); |
643 |
_swap16((unsigned short &)hdrTemp.format.bits_per_sample); |
644 |
_swap32((unsigned int &)hdrTemp.data.data_len); |
645 |
|
646 |
// write the supplemented header in the beginning of the file |
647 |
fseek(fptr, 0, SEEK_SET); |
648 |
res = fwrite(&hdrTemp, sizeof(hdrTemp), 1, fptr); |
649 |
if (res != 1) |
650 |
{ |
651 |
throw runtime_error("Error while writing to a wav file."); |
652 |
} |
653 |
|
654 |
// jump back to the end of the file |
655 |
fseek(fptr, 0, SEEK_END); |
656 |
} |
657 |
|
658 |
|
659 |
|
660 |
void WavOutFile::write(const char *buffer, int numElems) |
661 |
{ |
662 |
int res; |
663 |
|
664 |
if (header.format.bits_per_sample != 8) |
665 |
{ |
666 |
throw runtime_error("Error: WavOutFile::write(const char*, int) accepts only 8bit samples."); |
667 |
} |
668 |
assert(sizeof(char) == 1); |
669 |
|
670 |
res = fwrite(buffer, 1, numElems, fptr); |
671 |
if (res != numElems) |
672 |
{ |
673 |
throw runtime_error("Error while writing to a wav file."); |
674 |
} |
675 |
|
676 |
bytesWritten += numElems; |
677 |
} |
678 |
|
679 |
|
680 |
void WavOutFile::write(const short *buffer, int numElems) |
681 |
{ |
682 |
int res; |
683 |
|
684 |
// 16 bit samples |
685 |
if (numElems < 1) return; // nothing to do |
686 |
|
687 |
if (header.format.bits_per_sample == 8) |
688 |
{ |
689 |
int i; |
690 |
char *temp = new char[numElems]; |
691 |
// convert from 16bit format to 8bit format |
692 |
for (i = 0; i < numElems; i ++) |
693 |
{ |
694 |
temp[i] = buffer[i] >> 8; |
695 |
} |
696 |
// write in 8bit format |
697 |
write(temp, numElems); |
698 |
delete[] temp; |
699 |
} |
700 |
else |
701 |
{ |
702 |
// 16bit format |
703 |
unsigned short *pTemp = new unsigned short[numElems]; |
704 |
|
705 |
assert(header.format.bits_per_sample == 16); |
706 |
|
707 |
// allocate temp buffer to swap byte order if necessary |
708 |
memcpy(pTemp, buffer, numElems * 2); |
709 |
_swap16Buffer(pTemp, numElems); |
710 |
|
711 |
res = fwrite(pTemp, 2, numElems, fptr); |
712 |
|
713 |
delete[] pTemp; |
714 |
|
715 |
if (res != numElems) |
716 |
{ |
717 |
throw runtime_error("Error while writing to a wav file."); |
718 |
} |
719 |
bytesWritten += 2 * numElems; |
720 |
} |
721 |
} |
722 |
|
723 |
|
724 |
void WavOutFile::write(const float *buffer, int numElems) |
725 |
{ |
726 |
int i; |
727 |
short *temp = new short[numElems]; |
728 |
int iTemp; |
729 |
|
730 |
// convert to 16 bit integer |
731 |
for (i = 0; i < numElems; i ++) |
732 |
{ |
733 |
// convert to integer |
734 |
iTemp = (int)(32768.0f * buffer[i]); |
735 |
|
736 |
// saturate |
737 |
if (iTemp < -32768) iTemp = -32768; |
738 |
if (iTemp > 32767) iTemp = 32767; |
739 |
temp[i] = (short)iTemp; |
740 |
} |
741 |
|
742 |
write(temp, numElems); |
743 |
|
744 |
delete[] temp; |
745 |
} |