Fixed DevStudio 2003 build with memory check code.
[pwlib.git] / src / ptclib / pwavfile.cxx
blobeeb7ab6f8ad41c58629607cd12af41087db4d5bb
1 /*
2 * pwavfile.cxx
4 * WAV file I/O channel class.
6 * Portable Windows Library
8 * Copyright (c) 2001 Equivalence Pty. Ltd.
10 * The contents of this file are subject to the Mozilla Public License
11 * Version 1.0 (the "License"); you may not use this file except in
12 * compliance with the License. You may obtain a copy of the License at
13 * http://www.mozilla.org/MPL/
15 * Software distributed under the License is distributed on an "AS IS"
16 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
17 * the License for the specific language governing rights and limitations
18 * under the License.
20 * The Original Code is Portable Windows Library.
22 * The Initial Developer of the Original Code is
23 * Roger Hardiman <roger@freebsd.org>
24 * and Shawn Pai-Hsiang Hsiao <shawn@eecs.harvard.edu>
26 * All Rights Reserved.
28 * Contributor(s): ______________________________________.
30 * $Log$
31 * Revision 1.53 2007/09/08 11:34:28 rjongbloed
32 * Improved memory checking (leaks etc), especially when using MSVC debug library.
34 * Revision 1.52 2007/05/09 12:04:23 csoutheren
35 * Applied 1705775 - PWAVFile::UpdateHeader() redundancy
36 * Thanks to Fabrizio Ammollo
38 * Revision 1.51 2007/04/20 07:59:29 csoutheren
39 * Applied 1675658 - various pwavfile.[h|cxx] improvments
40 * Thanks to Drazen Dimoti
42 * Revision 1.50 2007/04/20 07:26:51 csoutheren
43 * Applied 1703655 - PWAVFile fixes to stereo recording
44 * Thanks to Fabrizio Ammollo
46 * Revision 1.49 2006/07/05 04:00:25 csoutheren
47 * Ensure Read and Write fail gracefully when file not open
49 * Revision 1.48 2006/06/21 03:28:44 csoutheren
50 * Various cleanups thanks for Frederic Heem
52 * Revision 1.47 2006/04/10 23:57:27 csoutheren
53 * Checked in changes to remove some warnings with gcc effc++ flag
55 * Revision 1.46 2006/04/09 22:22:35 dereksmithies
56 * Fix a warning about comparison of signed and unsigned numbers.
58 * Revision 1.45 2006/04/06 00:39:37 csoutheren
59 * Ensure autoconvert format is preserved across file close
61 * Revision 1.44 2006/01/16 07:31:57 csoutheren
62 * Removed deletion of PWAVFIle format converters.
63 * These look like memory leaks, but are not - the converters are static objects that
64 * cannot be deleted
66 * Revision 1.43 2005/11/30 12:47:41 csoutheren
67 * Removed tabs, reformatted some code, and changed tags for Doxygen
69 * Revision 1.42 2005/11/25 01:01:15 csoutheren
70 * Applied patch #1351168
71 * PWlib various fixes
73 * Revision 1.41 2005/10/30 23:25:52 csoutheren
74 * Fixed formatting
75 * Removed throw() declarations (PWLib does not do exceptions)
76 * Removed duplicate destructor declarations and definitions
78 * Revision 1.40 2005/10/30 19:41:53 dominance
79 * fixed most of the warnings occuring during compilation
81 * Revision 1.39 2005/06/09 00:33:20 csoutheren
82 * Fixed crash problem caused by recent leak fix
83 * Removed bogus error when reading all of file contents in a single read
85 * Revision 1.38 2005/06/07 09:28:46 csoutheren
86 * Fixed bug #1204964 - ensure full cleanup when WAV file is closed
87 * Thanks to Zdenek Broz
89 * Revision 1.37 2005/03/22 07:32:55 csoutheren
90 * Fixed problem with incorrect message being displayed when reading past end of file
92 * Revision 1.36 2005/01/04 08:09:42 csoutheren
93 * Fixed Linux configure problems
95 * Revision 1.35 2004/11/08 04:07:40 csoutheren
96 * Fixed crash opportunity under some conditions
97 * Fixed incorrect WAV file type display
99 * Revision 1.34 2004/07/19 12:32:25 csoutheren
100 * Removed vestigial debug comment
102 * Revision 1.33 2004/07/19 12:23:38 csoutheren
103 * Removed compiler crash under gcc 3.4.0
105 * Revision 1.32 2004/07/15 03:12:42 csoutheren
106 * Migrated changes from crs_vxnml_devel branch into main trunk
108 * Revision 1.31.4.4 2004/07/13 08:13:05 csoutheren
109 * Lots of implementation of factory-based PWAVFile
111 * Revision 1.31.4.3 2004/07/12 09:17:20 csoutheren
112 * Fixed warnings and errors under Linux
114 * Revision 1.31.4.2 2004/07/12 08:30:16 csoutheren
115 * More fixes for abstract factory implementation of PWAVFile
117 * Revision 1.31.4.1 2004/07/07 07:07:42 csoutheren
118 * Changed PWAVFile to use abstract factories (extensively)
119 * Removed redundant blocking/unblocking when using G.723.1
120 * More support for call transfer
122 * Revision 1.31 2003/07/29 11:27:16 csoutheren
123 * Changed to use autoconf detected swab function
125 * Revision 1.30 2003/07/28 18:39:09 dsandras
126 * Linux has a swab function. Patch from Alexander Larsson <alexl@redhat.com>.
128 * Revision 1.29 2003/02/20 23:32:00 robertj
129 * More RTEMS support patches, thanks Sebastian Meyer.
131 * Revision 1.28 2002/12/20 08:43:42 robertj
132 * Fixed incorrect header length for MS-GSM, thanks Martijn Roest & Kanchana
134 * Revision 1.27 2002/07/12 01:25:25 craigs
135 * Repaired reintroduced problem with SID frames in WAV files
137 * Revision 1.26 2002/07/02 06:25:25 craigs
138 * Added ability to create files in MS G.723.1 format
140 * Revision 1.25 2002/06/20 00:54:41 craigs
141 * Added explicit class names to some functions to alloew overriding
143 * Revision 1.24 2002/06/12 07:28:16 craigs
144 * Fixed problem with opening WAV files in read mode
146 * Revision 1.23 2002/05/23 05:04:11 robertj
147 * Set error code if get invalid sized write for G.723.1 wav file.
149 * Revision 1.22 2002/05/23 03:59:55 robertj
150 * Changed G.723.1 WAV file so every frame is 24 bytes long.
152 * Revision 1.21 2002/05/21 01:59:54 robertj
153 * Removed the enum which made yet another set of magic numbers for audio
154 * formats, now uses the WAV file format numbers.
155 * Fixed missing Open() function which does not have file name parameter.
156 * Added ability to set the audio format after construction.
157 * Added automatic expansion of G.723.1 SID frames into 24 zero bytes as
158 * those formats do not currently support 4 byte frames.
159 * Fixed trace output to include "module" section.
161 * Revision 1.20 2002/02/06 00:52:23 robertj
162 * Fixed GNU warning.
164 * Revision 1.19 2002/01/31 15:29:26 rogerh
165 * Fix a problem with .wav files recorded in GoldWave. The GoldWave copyright
166 * string (embedded at the end of the wav file) was returned as audio data and
167 * heared as noise. Javi <fjmchm@hotmail.com> reported the problem.
169 * Revision 1.18 2002/01/22 03:55:59 craigs
170 * Added include of ptclib/pwavfile.cxx as this is now in PTCLib
172 * Revision 1.17 2002/01/13 21:01:55 rogerh
173 * The class contructor is now used to specify the type of new WAV files
174 * (eg PCM or G7231)
176 * Revision 1.16 2002/01/11 16:33:46 rogerh
177 * Create a PWAVFile Open() function, which processes the WAV header
179 * Revision 1.15 2001/10/16 13:27:37 rogerh
180 * Add support for writing G.723.1 WAV files.
181 * MS Windows can play G.723.1 WAV Files in Media Player and Sound Recorder.
182 * Sound Recorder can also convert them to normal PCM format WAV files.
183 * Thanks go to M.Stoychev <M.Stoychev@cnsys.bg> for sample WAV files.
185 * Revision 1.14 2001/10/15 11:48:15 rogerh
186 * Add GetFormat to return the format of a WAV file
188 * Revision 1.13 2001/10/15 07:27:38 rogerh
189 * Add support for reading WAV fils containing G.723.1 audio data.
191 * Revision 1.12 2001/09/29 07:41:42 rogerh
192 * Add fix from Patrick Koorevaar <pkoorevaar@hotmail.com>
194 * Revision 1.11 2001/08/15 12:52:20 rogerh
195 * Fix typo
197 * Revision 1.10 2001/08/15 12:21:45 rogerh
198 * Make Solaris use our swab() function instead of the C library version.
199 * Submitted by Andre Schulze <as8@rncmm2.urz.tu-dresden.de>
201 * Revision 1.9 2001/07/23 02:57:42 robertj
202 * Fixed swab definition for Linux alpha.
204 * Revision 1.8 2001/07/23 01:20:20 rogerh
205 * Add updates from Shawn - ensure isvalidWAV is false for zero length files.
206 * GetDataLength uses actual file size to support file updates as well as appends.
207 * Add updates from Roger - Update Header() just writes to specific fields which
208 * preserves any 'extra' data in an existing header between FORMAT and DATA chunks.
210 * Revision 1.7 2001/07/20 07:32:36 rogerh
211 * Back out previous change. BSD systems already have swab in the C library.
212 * Also use swab in Write()
214 * Revision 1.6 2001/07/20 07:09:12 rogerh
215 * We need to byte swap on more then just Linux and BeOS.
217 * Revision 1.5 2001/07/20 04:14:47 robertj
218 * Fixed swab implementation on Linux alpha
220 * Revision 1.4 2001/07/20 03:30:59 robertj
221 * Minor cosmetic changes to new PWAVFile class.
223 * Revision 1.3 2001/07/19 09:57:24 rogerh
224 * Use correct filename
226 * Revision 1.2 2001/07/19 09:53:29 rogerh
227 * Add the PWAVFile class to read and write .wav files
228 * The PWAVFile class was written by Roger Hardiman <roger@freebsd.org>
229 * and Shawn Pai-Hsiang Hsiao <shawn@eecs.harvard.edu>
233 #ifdef __GNUC__
234 #pragma implementation "pwavfile.h"
235 #endif
237 #include <ptlib.h>
238 #include <ptlib/pfactory.h>
239 #include <ptclib/pwavfile.h>
241 #define new PNEW
244 const char WAVLabelRIFF[4] = { 'R', 'I', 'F', 'F' };
245 const char WAVLabelWAVE[4] = { 'W', 'A', 'V', 'E' };
246 const char WAVLabelFMT_[4] = { 'f', 'm', 't', ' ' };
247 const char WAVLabelFACT[4] = { 'F', 'A', 'C', 'T' };
248 const char WAVLabelDATA[4] = { 'd', 'a', 't', 'a' };
250 PINSTANTIATE_FACTORY(PWAVFileFormat, unsigned)
251 PINSTANTIATE_FACTORY(PWAVFileConverter, unsigned)
253 inline BOOL ReadAndCheck(PWAVFile & file, void * buf, PINDEX len)
255 return file.FileRead(buf, len) && (file.PFile::GetLastReadCount() == len);
258 inline BOOL WriteAndCheck(PWAVFile & file, void * buf, PINDEX len)
260 return file.FileWrite(buf, len) && (file.GetLastWriteCount() == len);
263 #if PBYTE_ORDER==PBIG_ENDIAN
264 # if defined(USE_SYSTEM_SWAB)
265 # define SWAB(a,b,c) ::swab(a,b,c)
266 # else
267 static void SWAB(const void * void_from, void * void_to, register size_t len)
269 register const char * from = (const char *)void_from;
270 register char * to = (char *)void_to;
272 while (len > 1) {
273 char b = *from++;
274 *to++ = *from++;
275 *to++ = b;
276 len -= 2;
279 # endif
280 #else
281 # define SWAB(a,b,c) {}
282 #endif
284 ///////////////////////////////////////////////////////////////////////////////
285 // PWAVFile
287 PWAVFile::PWAVFile(unsigned fmt)
288 : PFile(), origFmt(fmt)
290 Construct();
291 SelectFormat(fmt);
294 PWAVFile * PWAVFile::format(const PString & format)
296 PWAVFile * file = new PWAVFile;
297 file->origFmt = 0xffffffff;
298 file->Construct();
299 file->SelectFormat(format);
300 return file;
303 PWAVFile::PWAVFile(OpenMode mode, int opts, unsigned fmt)
304 : PFile(mode, opts), origFmt(fmt)
306 Construct();
307 SelectFormat(fmt);
310 PWAVFile * PWAVFile::format(
311 const PString & format,
312 PFile::OpenMode mode,
313 int opts
316 PWAVFile * file = new PWAVFile(mode, opts);
317 file->origFmt = 0xffffffff;
318 file->Construct();
319 file->SelectFormat(format);
320 return file;
323 PWAVFile::PWAVFile(const PFilePath & name, OpenMode mode, int opts, unsigned fmt)
324 : origFmt(fmt)
326 Construct();
327 SelectFormat(fmt);
328 Open(name, mode, opts);
331 PWAVFile::PWAVFile(
332 const PString & format,
333 const PFilePath & name,
334 OpenMode mode,
335 int opts
338 origFmt = 0xffffffff;
339 Construct();
340 SelectFormat(format);
341 Open(name, mode, opts);
344 PWAVFile::~PWAVFile()
346 Close();
347 if (formatHandler != NULL)
348 delete formatHandler;
352 void PWAVFile::Construct()
354 lenData = 0;
355 lenHeader = 0;
356 isValidWAV = FALSE;
357 header_needs_updating = FALSE;
358 autoConvert = FALSE;
359 autoConverter = NULL;
361 formatHandler = NULL;
362 wavFmtChunk.hdr.len = sizeof(wavFmtChunk) - sizeof(wavFmtChunk.hdr);
365 void PWAVFile::SelectFormat(unsigned fmt)
367 if (formatHandler != NULL) {
368 delete formatHandler;
369 formatHandler = NULL;
371 if (fmt != fmt_NotKnown) {
372 formatHandler = PWAVFileFormatByIDFactory::CreateInstance(fmt);
373 wavFmtChunk.format = (WORD)fmt;
377 void PWAVFile::SelectFormat(const PString & format)
379 if (formatHandler != NULL) {
380 delete formatHandler;
381 formatHandler = NULL;
383 if (!format.IsEmpty())
384 formatHandler = PWAVFileFormatByFormatFactory::CreateInstance(format);
385 if (formatHandler != NULL) {
386 wavFmtChunk.format = (WORD)formatHandler->GetFormat();
387 if (origFmt == 0xffffffff)
388 origFmt = wavFmtChunk.format;
392 BOOL PWAVFile::Open(OpenMode mode, int opts)
394 if (!(PFile::Open(mode, opts)))
395 return FALSE;
397 isValidWAV = FALSE;
399 // Try and process the WAV file header information.
400 // Either ProcessHeader() or GenerateHeader() must be called.
402 if (PFile::GetLength() > 0) {
404 // try and process the WAV file header information
405 if (mode == ReadOnly || mode == ReadWrite) {
406 isValidWAV = ProcessHeader();
408 if (mode == WriteOnly) {
409 lenData = -1;
410 GenerateHeader();
413 else {
415 // generate header
416 if (mode == ReadWrite || mode == WriteOnly) {
417 lenData = -1;
418 GenerateHeader();
420 if (mode == ReadOnly) {
421 isValidWAV = FALSE; // ReadOnly on a zero length file
425 // if we did not know the format when we opened, then we had better know it now
426 if (formatHandler == NULL) {
427 Close();
428 return FALSE;
431 return TRUE;
435 BOOL PWAVFile::Open(const PFilePath & name, OpenMode mode, int opts)
437 if (IsOpen())
438 Close();
439 SetFilePath(name);
440 return Open(mode, opts);
444 BOOL PWAVFile::Close()
446 if (autoConverter != NULL) {
447 autoConverter = NULL;
450 if (!PFile::IsOpen())
451 return TRUE;
453 if (header_needs_updating)
454 UpdateHeader();
456 if (formatHandler != NULL)
457 formatHandler->OnStop();
459 delete formatHandler;
460 formatHandler = NULL;
461 if (origFmt != 0xffffffff)
462 SelectFormat(origFmt);
464 return PFile::Close();
467 void PWAVFile::SetAutoconvert()
469 autoConvert = TRUE;
473 // Performs necessary byte-order swapping on for big-endian platforms.
474 BOOL PWAVFile::Read(void * buf, PINDEX len)
476 if (!IsOpen())
477 return FALSE;
479 if (autoConverter != NULL)
480 return autoConverter->Read(*this, buf, len);
482 return RawRead(buf, len);
485 BOOL PWAVFile::RawRead(void * buf, PINDEX len)
487 // Some wav files have extra data after the sound samples in a LIST chunk.
488 // e.g. WAV files made in GoldWave have a copyright and a URL in this chunk.
489 // We do not want to return this data by mistake.
490 PINDEX readlen = len;
491 off_t pos = PFile::GetPosition();
492 if (pos >= (lenHeader+lenData))
493 return FALSE;
495 if ((pos + len) > (lenHeader+lenData))
496 readlen = (lenHeader+lenData) - pos;
498 if (formatHandler != NULL)
499 return formatHandler->Read(*this, buf, readlen);
501 return FileRead(buf, readlen);
504 BOOL PWAVFile::FileRead(void * buf, PINDEX len)
506 return PFile::Read(buf, len);
509 // Performs necessary byte-order swapping on for big-endian platforms.
510 BOOL PWAVFile::Write(const void * buf, PINDEX len)
512 if (!IsOpen())
513 return FALSE;
515 // Needs to update header on close.
516 header_needs_updating = TRUE;
518 if (autoConverter != NULL)
519 return autoConverter->Write(*this, buf, len);
521 return RawWrite(buf, len);
524 BOOL PWAVFile::RawWrite(const void * buf, PINDEX len)
526 // Needs to update header on close.
527 header_needs_updating = TRUE;
529 if (formatHandler != NULL)
530 return formatHandler->Write(*this, buf, len);
532 return FileWrite(buf, len);
535 BOOL PWAVFile::FileWrite(const void * buf, PINDEX len)
537 return PFile::Write(buf, len);
540 // Both SetPosition() and GetPosition() are offset by lenHeader.
541 BOOL PWAVFile::SetPosition(off_t pos, FilePositionOrigin origin)
543 if (autoConverter != NULL)
544 return autoConverter->SetPosition(*this, pos, origin);
546 return RawSetPosition(pos, origin);
549 BOOL PWAVFile::RawSetPosition(off_t pos, FilePositionOrigin origin)
551 if (isValidWAV) {
552 pos += lenHeader;
555 return PFile::SetPosition(pos, origin);
559 off_t PWAVFile::GetPosition() const
561 if (autoConverter != NULL)
562 return autoConverter->GetPosition(*this);
564 return RawGetPosition();
567 off_t PWAVFile::RawGetPosition() const
569 off_t pos = PFile::GetPosition();
571 if (isValidWAV) {
572 if (pos >= lenHeader) {
573 pos -= lenHeader;
575 else {
576 pos = 0;
580 return (pos);
584 unsigned PWAVFile::GetFormat() const
586 if (isValidWAV)
587 return wavFmtChunk.format;
588 else
589 return 0;
592 PString PWAVFile::GetFormatAsString() const
594 if (isValidWAV && formatHandler != NULL)
595 return formatHandler->GetFormat();
596 else
597 return PString::Empty();
600 unsigned PWAVFile::GetChannels() const
602 if (isValidWAV)
603 return wavFmtChunk.numChannels;
604 else
605 return 0;
608 void PWAVFile::SetChannels(unsigned v)
610 wavFmtChunk.numChannels = (WORD)v;
611 if (wavFmtChunk.numChannels == 1 || wavFmtChunk.numChannels == 2)
613 wavFmtChunk.bytesPerSample = (wavFmtChunk.bitsPerSample/8) * wavFmtChunk.numChannels;
614 wavFmtChunk.bytesPerSec = wavFmtChunk.sampleRate * wavFmtChunk.bytesPerSample;
616 header_needs_updating = TRUE;
619 unsigned PWAVFile::GetSampleRate() const
621 if (isValidWAV)
622 return wavFmtChunk.sampleRate;
623 else
624 return 0;
627 void PWAVFile::SetSampleRate(unsigned v)
629 wavFmtChunk.sampleRate = (WORD)v;
630 header_needs_updating = TRUE;
633 unsigned PWAVFile::GetSampleSize() const
635 if (isValidWAV)
636 return wavFmtChunk.bitsPerSample;
637 else
638 return 0;
641 void PWAVFile::SetSampleSize(unsigned v)
643 wavFmtChunk.bitsPerSample = (WORD)v;
644 header_needs_updating = TRUE;
647 unsigned PWAVFile::GetBytesPerSecond() const
649 if (isValidWAV)
650 return wavFmtChunk.bytesPerSec;
651 else
652 return 0;
655 void PWAVFile::SetBytesPerSecond(unsigned v)
657 wavFmtChunk.bytesPerSec = (WORD)v;
658 header_needs_updating = TRUE;
661 off_t PWAVFile::GetHeaderLength() const
663 if (isValidWAV)
664 return lenHeader;
665 else
666 return 0;
670 off_t PWAVFile::GetDataLength()
672 if (autoConverter != NULL)
673 return autoConverter->GetDataLength(*this);
675 return RawGetDataLength();
678 off_t PWAVFile::RawGetDataLength()
680 if (isValidWAV) {
681 // Updates data length before returns.
682 lenData = PFile::GetLength() - lenHeader;
683 return lenData;
685 else
686 return 0;
690 BOOL PWAVFile::SetFormat(unsigned fmt)
692 if (IsOpen() || isValidWAV)
693 return FALSE;
695 SelectFormat(fmt);
697 return TRUE;
700 BOOL PWAVFile::SetFormat(const PString & format)
702 if (IsOpen() || isValidWAV)
703 return FALSE;
705 SelectFormat(format);
707 return TRUE;
710 static inline BOOL NeedsConverter(const PWAV::FMTChunk & fmtChunk)
712 return (fmtChunk.format != PWAVFile::fmt_PCM) || (fmtChunk.bitsPerSample != 16);
715 BOOL PWAVFile::ProcessHeader()
717 if (autoConverter != NULL) {
718 delete autoConverter;
719 autoConverter = NULL;
722 // Process the header information
723 // This comes in 3 or 4 chunks, either RIFF, FORMAT and DATA
724 // or RIFF, FORMAT, FACT and DATA.
726 if (!IsOpen()) {
727 PTRACE(1,"WAV\tProcessHeader: Not Open");
728 return (FALSE);
731 // go to the beginning of the file
732 if (!PFile::SetPosition(0)) {
733 PTRACE(1,"WAV\tProcessHeader: Cannot Set Pos");
734 return (FALSE);
737 // Read the RIFF chunk.
738 struct PWAV::RIFFChunkHeader riffChunk;
739 if (!ReadAndCheck(*this, &riffChunk, sizeof(riffChunk)))
740 return FALSE;
742 // check if tags are correct
743 if (strncmp(riffChunk.hdr.tag, WAVLabelRIFF, sizeof(WAVLabelRIFF)) != 0) {
744 PTRACE(1,"WAV\tProcessHeader: Not RIFF");
745 return (FALSE);
748 if (strncmp(riffChunk.tag, WAVLabelWAVE, sizeof(WAVLabelWAVE)) != 0) {
749 PTRACE(1,"WAV\tProcessHeader: Not WAVE");
750 return (FALSE);
753 // Read the known part of the FORMAT chunk.
754 if (!ReadAndCheck(*this, &wavFmtChunk, sizeof(wavFmtChunk)))
755 return FALSE;
757 // check if labels are correct
758 if (strncmp(wavFmtChunk.hdr.tag, WAVLabelFMT_, sizeof(WAVLabelFMT_)) != 0) {
759 PTRACE(1,"WAV\tProcessHeader: Not FMT");
760 return (FALSE);
763 // if we opened the file without knowing the format, then try and set the format now
764 if (formatHandler == NULL) {
765 SelectFormat(wavFmtChunk.format);
766 if (formatHandler == NULL) {
767 Close();
768 return FALSE;
772 // read the extended format chunk (if any)
773 extendedHeader.SetSize(0);
774 if ((unsigned)wavFmtChunk.hdr.len > (sizeof(wavFmtChunk) - sizeof(wavFmtChunk.hdr))) {
775 extendedHeader.SetSize(wavFmtChunk.hdr.len - (sizeof(wavFmtChunk) - sizeof(wavFmtChunk.hdr)));
776 if (!ReadAndCheck(*this, extendedHeader.GetPointer(), extendedHeader.GetSize()))
777 return FALSE;
780 // give format handler a chance to read extra chunks
781 if (!formatHandler->ReadExtraChunks(*this))
782 return FALSE;
784 PWAV::ChunkHeader chunkHeader;
786 // ignore chunks until we see a DATA chunk
787 for (;;) {
788 if (!ReadAndCheck(*this, &chunkHeader, sizeof(chunkHeader)))
789 return FALSE;
790 if (strncmp(chunkHeader.tag, WAVLabelDATA, sizeof(WAVLabelDATA)) == 0)
791 break;
792 if (!PFile::SetPosition(PFile::GetPosition() + + chunkHeader.len)) {
793 PTRACE(1,"WAV\tProcessHeader: Cannot set new position");
794 return FALSE;
798 // calculate the size of header and data for accessing the WAV data.
799 lenHeader = PFile::GetPosition();
800 lenData = chunkHeader.len;
802 // get ptr to data handler if in autoconvert mode
803 if (autoConvert && NeedsConverter(wavFmtChunk)) {
804 autoConverter = PWAVFileConverterFactory::CreateInstance(wavFmtChunk.format);
805 if (autoConverter == NULL) {
806 PTRACE(1, "PWAVFile\tNo format converter for type " << (int)wavFmtChunk.format);
810 formatHandler->OnStart();
812 return TRUE;
816 // Generates the wave file header.
817 // Two types of header are supported.
818 // a) PCM data, set to 8000Hz, mono, 16-bit samples
819 // b) G.723.1 data
820 // When this function is called with lenData < 0, it will write the header
821 // as if the lenData is LONG_MAX minus header length.
822 // Note: If it returns FALSE, the file may be left in inconsistent state.
824 BOOL PWAVFile::GenerateHeader()
826 if (autoConverter != NULL) {
827 autoConverter = NULL;
830 if (!IsOpen()) {
831 PTRACE(1, "WAV\tGenerateHeader: Not Open");
832 return (FALSE);
835 // length of audio data is set to a large value if lenData does not
836 // contain a valid (non negative) number. We must then write out real values
837 // when we close the wav file.
838 int audioDataLen;
839 if (lenData < 0) {
840 audioDataLen = LONG_MAX - wavFmtChunk.hdr.len;
841 header_needs_updating = TRUE;
842 } else {
843 audioDataLen = lenData;
846 // go to the beginning of the file
847 if (!PFile::SetPosition(0)) {
848 PTRACE(1,"WAV\tGenerateHeader: Cannot Set Pos");
849 return (FALSE);
852 // write the WAV file header
853 PWAV::RIFFChunkHeader riffChunk;
854 memcpy(riffChunk.hdr.tag, WAVLabelRIFF, sizeof(WAVLabelRIFF));
855 memcpy(riffChunk.tag, WAVLabelWAVE, sizeof(WAVLabelWAVE));
856 riffChunk.hdr.len = lenHeader + audioDataLen - sizeof(riffChunk.hdr);
858 if (!WriteAndCheck(*this, &riffChunk, sizeof(riffChunk)))
859 return FALSE;
861 // populate and write the WAV header with the default data
862 memcpy(wavFmtChunk.hdr.tag, WAVLabelFMT_, sizeof(WAVLabelFMT_));
863 wavFmtChunk.hdr.len = sizeof(wavFmtChunk) - sizeof(wavFmtChunk.hdr); // set default length assuming no extra bytes
865 // allow the format handler to modify the header and extra bytes
866 if(formatHandler == NULL){
867 PTRACE(1,"WAV\tGenerateHeader: format handler is null!");
868 return FALSE;
870 formatHandler->CreateHeader(wavFmtChunk, extendedHeader);
872 // write the basic WAV header
873 if (
874 !WriteAndCheck(*this, &wavFmtChunk, sizeof(wavFmtChunk)) ||
875 ((extendedHeader.GetSize() > 0) && !WriteAndCheck(*this, extendedHeader.GetPointer(), extendedHeader.GetSize()))
877 return FALSE;
879 // allow the format handler to write additional chunks
880 if (!formatHandler->WriteExtraChunks(*this))
881 return FALSE;
883 // Write the DATA chunk.
884 PWAV::ChunkHeader dataChunk;
885 memcpy(dataChunk.tag, WAVLabelDATA, sizeof(WAVLabelDATA));
886 dataChunk.len = audioDataLen;
887 if (!WriteAndCheck(*this, &dataChunk, sizeof(dataChunk)))
888 return FALSE;
890 isValidWAV = TRUE;
892 // get the length of the header
893 lenHeader = PFile::GetPosition();
895 // get pointer to auto converter
896 if (autoConvert && NeedsConverter(wavFmtChunk)) {
897 autoConverter = PWAVFileConverterFactory::CreateInstance(wavFmtChunk.format);
898 if (autoConverter == NULL) {
899 PTRACE(1, "PWAVFile\tNo format converter for type " << (int)wavFmtChunk.format);
900 return FALSE;
904 return (TRUE);
907 // Update the WAV header according to the file length
908 BOOL PWAVFile::UpdateHeader()
910 // Check file is still open
911 if (!IsOpen()) {
912 PTRACE(1,"WAV\tUpdateHeader: Not Open");
913 return (FALSE);
916 // Check there is already a valid header
917 if (!isValidWAV) {
918 PTRACE(1,"WAV\tUpdateHeader: File not valid");
919 return (FALSE);
922 // Find out the length of the audio data
923 lenData = PFile::GetLength() - lenHeader;
925 // rewrite the length in the RIFF chunk
926 PInt32l riffChunkLen = (lenHeader - 8) + lenData; // size does not include first 8 bytes
927 PFile::SetPosition(4);
928 if (!WriteAndCheck(*this, &riffChunkLen, sizeof(riffChunkLen)))
929 return FALSE;
931 // rewrite the data length field in the data chunk
932 PInt32l dataChunkLen = lenData;
933 PFile::SetPosition(lenHeader - 4);
934 if (!WriteAndCheck(*this, &dataChunkLen, sizeof(dataChunkLen)))
935 return FALSE;
937 if(formatHandler == NULL){
938 PTRACE(1,"WAV\tGenerateHeader: format handler is null!");
939 return FALSE;
941 formatHandler->UpdateHeader(wavFmtChunk, extendedHeader);
943 PFile::SetPosition(12);
944 if (!WriteAndCheck(*this, &wavFmtChunk, sizeof(wavFmtChunk)))
945 return FALSE;
947 if (!WriteAndCheck(*this, extendedHeader.GetPointer(), extendedHeader.GetSize()))
948 return FALSE;
950 header_needs_updating = FALSE;
952 return TRUE;
956 //////////////////////////////////////////////////////////////////
958 BOOL PWAVFileFormat::Read(PWAVFile & file, void * buf, PINDEX & len)
960 if (!file.FileRead(buf, len))
961 return FALSE;
963 len = file.GetLastReadCount();
964 return TRUE;
967 BOOL PWAVFileFormat::Write(PWAVFile & file, const void * buf, PINDEX & len)
969 if (!file.FileWrite(buf, len))
970 return FALSE;
972 len = file.GetLastWriteCount();
973 return TRUE;
976 //////////////////////////////////////////////////////////////////
978 class PWAVFileFormatPCM : public PWAVFileFormat
980 public:
981 void CreateHeader(PWAV::FMTChunk & wavFmtChunk, PBYTEArray & extendedHeader);
982 void UpdateHeader(PWAV::FMTChunk & wavFmtChunk, PBYTEArray & extendedHeader);
984 unsigned GetFormat() const
985 { return PWAVFile::fmt_PCM; }
987 PString GetDescription() const
988 { return "PCM"; }
990 PString GetFormatString() const
991 { return "PCM-16"; }
993 BOOL Read(PWAVFile & file, void * buf, PINDEX & len);
994 BOOL Write(PWAVFile & file, const void * buf, PINDEX & len);
997 PWAVFileFormatByIDFactory::Worker<PWAVFileFormatPCM> pcmIDWAVFormat(PWAVFile::fmt_PCM);
998 PWAVFileFormatByFormatFactory::Worker<PWAVFileFormatPCM> pcmFormatWAVFormat("PCM-16");
1000 void PWAVFileFormatPCM::CreateHeader(PWAV::FMTChunk & wavFmtChunk,
1001 PBYTEArray & /*extendedHeader*/)
1003 wavFmtChunk.hdr.len = sizeof(wavFmtChunk) - sizeof(wavFmtChunk.hdr); // no extended information
1004 wavFmtChunk.format = PWAVFile::fmt_PCM;
1005 wavFmtChunk.numChannels = 1;
1006 wavFmtChunk.sampleRate = 8000;
1007 wavFmtChunk.bytesPerSample = 2;
1008 wavFmtChunk.bitsPerSample = 16;
1009 wavFmtChunk.bytesPerSec = wavFmtChunk.sampleRate * wavFmtChunk.bytesPerSample;
1012 void PWAVFileFormatPCM::UpdateHeader(PWAV::FMTChunk & wavFmtChunk,
1013 PBYTEArray & /*extendedHeader*/)
1015 wavFmtChunk.bytesPerSample = 2 * wavFmtChunk.numChannels;
1016 wavFmtChunk.bytesPerSec = wavFmtChunk.sampleRate * 2 * wavFmtChunk.numChannels;
1019 BOOL PWAVFileFormatPCM::Read(PWAVFile & file, void * buf, PINDEX & len)
1021 if (!file.FileRead(buf, len))
1022 return FALSE;
1024 len = file.GetLastReadCount();
1026 // WAV files are little-endian. So swap the bytes if this is
1027 // a big endian machine and we have 16 bit samples
1028 // Note: swab only works on even length buffers.
1029 if (file.wavFmtChunk.bitsPerSample == 16) {
1030 SWAB(buf, buf, len);
1033 return TRUE;
1036 BOOL PWAVFileFormatPCM::Write(PWAVFile & file, const void * buf, PINDEX & len)
1038 // WAV files are little-endian. So swap the bytes if this is
1039 // a big endian machine and we have 16 bit samples
1040 // Note: swab only works on even length buffers.
1041 if (file.wavFmtChunk.bitsPerSample == 16) {
1042 SWAB(buf, (void *)buf, len);
1045 if (!file.FileWrite(buf, len))
1046 return FALSE;
1048 len = file.GetLastWriteCount();
1049 return TRUE;
1052 //////////////////////////////////////////////////////////////////
1054 #ifdef __GNUC__
1055 #define P_PACKED __attribute__ ((packed));
1056 #else
1057 #define P_PACKED
1058 #pragma pack(1)
1059 #endif
1061 struct G7231ExtendedInfo {
1062 PInt16l data1 P_PACKED; // 1
1063 PInt16l data2 P_PACKED; // 480
1066 struct G7231FACTChunk {
1067 PWAV::ChunkHeader hdr;
1068 PInt32l data1 P_PACKED; // 0 Should be number of samples.
1071 #ifdef __GNUC__
1072 #undef P_PACKED
1073 #else
1074 #pragma pack()
1075 #endif
1078 class PWAVFileFormatG7231 : public PWAVFileFormat
1080 public:
1081 PWAVFileFormatG7231(unsigned short _g7231)
1082 : g7231(_g7231) { }
1084 virtual ~PWAVFileFormatG7231() {}
1086 void CreateHeader(PWAV::FMTChunk & wavFmtChunk, PBYTEArray & extendedHeader);
1087 BOOL WriteExtraChunks(PWAVFile & file);
1089 PString GetFormatString() const
1090 { return "G.723.1"; } // must match string in mediafmt.h
1092 void OnStart();
1093 BOOL Read(PWAVFile & file, void * buf, PINDEX & len);
1094 BOOL Write(PWAVFile & file, const void * buf, PINDEX & len);
1096 protected:
1097 unsigned short g7231;
1098 BYTE cacheBuffer[24];
1099 PINDEX cacheLen;
1100 PINDEX cachePos;
1103 void PWAVFileFormatG7231::CreateHeader(PWAV::FMTChunk & wavFmtChunk, PBYTEArray & extendedHeader)
1105 wavFmtChunk.hdr.len = sizeof(wavFmtChunk) - sizeof(wavFmtChunk.hdr) + sizeof(sizeof(G7231ExtendedInfo));
1106 wavFmtChunk.format = g7231;
1107 wavFmtChunk.numChannels = 1;
1108 wavFmtChunk.sampleRate = 8000;
1109 wavFmtChunk.bytesPerSample = 24;
1110 wavFmtChunk.bitsPerSample = 0;
1111 wavFmtChunk.bytesPerSec = 800;
1113 extendedHeader.SetSize(sizeof(G7231ExtendedInfo));
1114 G7231ExtendedInfo * g7231Info = (G7231ExtendedInfo *)extendedHeader.GetPointer(sizeof(G7231ExtendedInfo));
1116 g7231Info->data1 = 1;
1117 g7231Info->data2 = 480;
1120 BOOL PWAVFileFormatG7231::WriteExtraChunks(PWAVFile & file)
1122 // write the fact chunk
1123 G7231FACTChunk factChunk;
1124 memcpy(factChunk.hdr.tag, "FACT", 4);
1125 factChunk.hdr.len = sizeof(factChunk) - sizeof(factChunk.hdr);
1126 factChunk.data1 = 0;
1127 return file.FileWrite(&factChunk, sizeof(factChunk));
1130 static PINDEX G7231FrameSizes[4] = { 24, 20, 4, 1 };
1132 void PWAVFileFormatG7231::OnStart()
1134 cacheLen = cachePos = 0;
1137 BOOL PWAVFileFormatG7231::Read(PWAVFile & file, void * origData, PINDEX & origLen)
1139 // Note that Microsoft && VivoActive G.2723.1 codec cannot do SID frames, so
1140 // we must parse the data and remove SID frames
1141 // also note that frames are always written as 24 byte frames, so each frame must be unpadded
1143 PINDEX bytesRead = 0;
1144 while (bytesRead < origLen) {
1146 // keep reading until we find a 20 or 24 byte frame
1147 while (cachePos == cacheLen) {
1148 if (!file.FileRead(cacheBuffer, 24))
1149 return FALSE;
1151 // calculate actual length of frame
1152 PINDEX frameLen = G7231FrameSizes[cacheBuffer[0] & 3];
1153 if (frameLen == 20 || frameLen == 24) {
1154 cacheLen = frameLen;
1155 cachePos = 0;
1159 // copy data to requested buffer
1160 PINDEX copyLen = PMIN(origLen-bytesRead, cacheLen-cachePos);
1161 memcpy(origData, cacheBuffer+cachePos, copyLen);
1162 origData = copyLen + (char *)origData;
1163 cachePos += copyLen;
1164 bytesRead += copyLen;
1167 origLen = bytesRead;
1169 return TRUE;
1172 BOOL PWAVFileFormatG7231::Write(PWAVFile & file, const void * origData, PINDEX & len)
1174 // Note that Microsoft && VivoActive G.2723.1 codec cannot do SID frames, so
1175 // we must parse the data and remove SID frames
1176 // also note that frames are always written as 24 byte frames, so each frame must be padded
1177 PINDEX written = 0;
1179 BYTE frameBuffer[24];
1180 while (len > 0) {
1182 // calculate actual length of frame
1183 PINDEX frameLen = G7231FrameSizes[(*(char *)origData) & 3];
1184 if (len < frameLen)
1185 return FALSE;
1187 // we can write 24 byte frame straight out,
1188 // 20 byte frames need to be reblocked
1189 // we ignore any other frames
1191 const void * buf = NULL;
1192 switch (frameLen) {
1193 case 24:
1194 buf = origData;
1195 break;
1196 case 20:
1197 memcpy(frameBuffer, origData, 20);
1198 buf = frameBuffer;
1199 break;
1200 default:
1201 break;
1204 if (buf != NULL && !file.FileWrite(buf, 24))
1205 return FALSE;
1206 else
1207 written += 24;
1209 origData = (char *)origData + frameLen;
1210 len -= frameLen;
1213 len = written;
1215 return TRUE;
1218 class PWAVFileFormatG7231_vivo : public PWAVFileFormatG7231
1220 public:
1221 PWAVFileFormatG7231_vivo()
1222 : PWAVFileFormatG7231(PWAVFile::fmt_VivoG7231) { }
1223 virtual ~PWAVFileFormatG7231_vivo() {}
1224 unsigned GetFormat() const
1225 { return PWAVFile::fmt_VivoG7231; }
1226 PString GetDescription() const
1227 { return GetFormatString() & "Vivo"; }
1230 PWAVFileFormatByIDFactory::Worker<PWAVFileFormatG7231_vivo> g7231VivoWAVFormat(PWAVFile::fmt_VivoG7231);
1231 PWAVFileFormatByFormatFactory::Worker<PWAVFileFormatG7231_vivo> g7231FormatWAVFormat("G.723.1");
1233 class PWAVFileFormatG7231_ms : public PWAVFileFormatG7231
1235 public:
1236 PWAVFileFormatG7231_ms()
1237 : PWAVFileFormatG7231(PWAVFile::fmt_MSG7231) { }
1238 virtual ~PWAVFileFormatG7231_ms() {}
1239 unsigned GetFormat() const
1240 { return PWAVFile::fmt_MSG7231; }
1241 PString GetDescription() const
1242 { return GetFormatString() & "MS"; }
1245 PWAVFileFormatByIDFactory::Worker<PWAVFileFormatG7231_ms> g7231MSWAVFormat(PWAVFile::fmt_MSG7231);
1247 //////////////////////////////////////////////////////////////////
1249 class PWAVFileConverterPCM : public PWAVFileConverter
1251 public:
1252 virtual ~PWAVFileConverterPCM() {}
1253 unsigned GetFormat (const PWAVFile & file) const;
1254 off_t GetPosition (const PWAVFile & file) const;
1255 BOOL SetPosition (PWAVFile & file, off_t pos, PFile::FilePositionOrigin origin);
1256 unsigned GetSampleSize(const PWAVFile & file) const;
1257 off_t GetDataLength (PWAVFile & file);
1258 BOOL Read (PWAVFile & file, void * buf, PINDEX len);
1259 BOOL Write (PWAVFile & file, const void * buf, PINDEX len);
1262 unsigned PWAVFileConverterPCM::GetFormat(const PWAVFile &) const
1264 return PWAVFile::fmt_PCM;
1267 off_t PWAVFileConverterPCM::GetPosition(const PWAVFile & file) const
1269 off_t pos = file.RawGetPosition();
1270 return pos * 2;
1273 BOOL PWAVFileConverterPCM::SetPosition(PWAVFile & file, off_t pos, PFile::FilePositionOrigin origin)
1275 pos /= 2;
1276 return file.SetPosition(pos, origin);
1279 unsigned PWAVFileConverterPCM::GetSampleSize(const PWAVFile &) const
1281 return 16;
1284 off_t PWAVFileConverterPCM::GetDataLength(PWAVFile & file)
1286 return file.RawGetDataLength() * 2;
1289 BOOL PWAVFileConverterPCM::Read(PWAVFile & file, void * buf, PINDEX len)
1291 if (file.wavFmtChunk.bitsPerSample == 16)
1292 return file.PWAVFile::RawRead(buf, len);
1294 if (file.wavFmtChunk.bitsPerSample != 8) {
1295 PTRACE(1, "PWAVFile\tAttempt to read autoconvert PCM data with unsupported number of bits per sample " << (int)file.wavFmtChunk.bitsPerSample);
1296 return FALSE;
1299 // read the PCM data with 8 bits per sample
1300 PINDEX samples = (len / 2);
1301 PBYTEArray pcm8;
1302 if (!file.PWAVFile::RawRead(pcm8.GetPointer(samples), samples))
1303 return FALSE;
1305 // convert to PCM-16
1306 PINDEX i;
1307 short * pcmPtr = (short *)buf;
1308 for (i = 0; i < samples; i++)
1309 *pcmPtr++ = (unsigned short)((pcm8[i] << 8) - 0x8000);
1311 // fake the lastReadCount
1312 file.SetLastReadCount(len);
1314 return TRUE;
1318 BOOL PWAVFileConverterPCM::Write(PWAVFile & file, const void * buf, PINDEX len)
1320 if (file.wavFmtChunk.bitsPerSample == 16)
1321 return file.PWAVFile::RawWrite(buf, len);
1323 PTRACE(1, "PWAVFile\tAttempt to write autoconvert PCM data with unsupported number of bits per sample " << (int)file.wavFmtChunk.bitsPerSample);
1324 return FALSE;
1327 PWAVFileConverterFactory::Worker<PWAVFileConverterPCM> pcmConverter(PWAVFile::fmt_PCM, true);
1329 //////////////////////////////////////////////////////////////////