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
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): ______________________________________.
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
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
73 * Revision 1.41 2005/10/30 23:25:52 csoutheren
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
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
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
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>
234 #pragma implementation "pwavfile.h"
238 #include <ptlib/pfactory.h>
239 #include <ptclib/pwavfile.h>
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)
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
;
281 # define SWAB(a,b,c) {}
284 ///////////////////////////////////////////////////////////////////////////////
287 PWAVFile::PWAVFile(unsigned fmt
)
288 : PFile(), origFmt(fmt
)
294 PWAVFile
* PWAVFile::format(const PString
& format
)
296 PWAVFile
* file
= new PWAVFile
;
297 file
->origFmt
= 0xffffffff;
299 file
->SelectFormat(format
);
303 PWAVFile::PWAVFile(OpenMode mode
, int opts
, unsigned fmt
)
304 : PFile(mode
, opts
), origFmt(fmt
)
310 PWAVFile
* PWAVFile::format(
311 const PString
& format
,
312 PFile::OpenMode mode
,
316 PWAVFile
* file
= new PWAVFile(mode
, opts
);
317 file
->origFmt
= 0xffffffff;
319 file
->SelectFormat(format
);
323 PWAVFile::PWAVFile(const PFilePath
& name
, OpenMode mode
, int opts
, unsigned fmt
)
328 Open(name
, mode
, opts
);
332 const PString
& format
,
333 const PFilePath
& name
,
338 origFmt
= 0xffffffff;
340 SelectFormat(format
);
341 Open(name
, mode
, opts
);
344 PWAVFile::~PWAVFile()
347 if (formatHandler
!= NULL
)
348 delete formatHandler
;
352 void PWAVFile::Construct()
357 header_needs_updating
= 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
)))
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
) {
416 if (mode
== ReadWrite
|| mode
== WriteOnly
) {
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
) {
435 BOOL
PWAVFile::Open(const PFilePath
& name
, OpenMode mode
, int opts
)
440 return Open(mode
, opts
);
444 BOOL
PWAVFile::Close()
446 if (autoConverter
!= NULL
) {
447 autoConverter
= NULL
;
450 if (!PFile::IsOpen())
453 if (header_needs_updating
)
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()
473 // Performs necessary byte-order swapping on for big-endian platforms.
474 BOOL
PWAVFile::Read(void * buf
, PINDEX len
)
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
))
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
)
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
)
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();
572 if (pos
>= lenHeader
) {
584 unsigned PWAVFile::GetFormat() const
587 return wavFmtChunk
.format
;
592 PString
PWAVFile::GetFormatAsString() const
594 if (isValidWAV
&& formatHandler
!= NULL
)
595 return formatHandler
->GetFormat();
597 return PString::Empty();
600 unsigned PWAVFile::GetChannels() const
603 return wavFmtChunk
.numChannels
;
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
622 return wavFmtChunk
.sampleRate
;
627 void PWAVFile::SetSampleRate(unsigned v
)
629 wavFmtChunk
.sampleRate
= (WORD
)v
;
630 header_needs_updating
= TRUE
;
633 unsigned PWAVFile::GetSampleSize() const
636 return wavFmtChunk
.bitsPerSample
;
641 void PWAVFile::SetSampleSize(unsigned v
)
643 wavFmtChunk
.bitsPerSample
= (WORD
)v
;
644 header_needs_updating
= TRUE
;
647 unsigned PWAVFile::GetBytesPerSecond() const
650 return wavFmtChunk
.bytesPerSec
;
655 void PWAVFile::SetBytesPerSecond(unsigned v
)
657 wavFmtChunk
.bytesPerSec
= (WORD
)v
;
658 header_needs_updating
= TRUE
;
661 off_t
PWAVFile::GetHeaderLength() const
670 off_t
PWAVFile::GetDataLength()
672 if (autoConverter
!= NULL
)
673 return autoConverter
->GetDataLength(*this);
675 return RawGetDataLength();
678 off_t
PWAVFile::RawGetDataLength()
681 // Updates data length before returns.
682 lenData
= PFile::GetLength() - lenHeader
;
690 BOOL
PWAVFile::SetFormat(unsigned fmt
)
692 if (IsOpen() || isValidWAV
)
700 BOOL
PWAVFile::SetFormat(const PString
& format
)
702 if (IsOpen() || isValidWAV
)
705 SelectFormat(format
);
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.
727 PTRACE(1,"WAV\tProcessHeader: Not Open");
731 // go to the beginning of the file
732 if (!PFile::SetPosition(0)) {
733 PTRACE(1,"WAV\tProcessHeader: Cannot Set Pos");
737 // Read the RIFF chunk.
738 struct PWAV::RIFFChunkHeader riffChunk
;
739 if (!ReadAndCheck(*this, &riffChunk
, sizeof(riffChunk
)))
742 // check if tags are correct
743 if (strncmp(riffChunk
.hdr
.tag
, WAVLabelRIFF
, sizeof(WAVLabelRIFF
)) != 0) {
744 PTRACE(1,"WAV\tProcessHeader: Not RIFF");
748 if (strncmp(riffChunk
.tag
, WAVLabelWAVE
, sizeof(WAVLabelWAVE
)) != 0) {
749 PTRACE(1,"WAV\tProcessHeader: Not WAVE");
753 // Read the known part of the FORMAT chunk.
754 if (!ReadAndCheck(*this, &wavFmtChunk
, sizeof(wavFmtChunk
)))
757 // check if labels are correct
758 if (strncmp(wavFmtChunk
.hdr
.tag
, WAVLabelFMT_
, sizeof(WAVLabelFMT_
)) != 0) {
759 PTRACE(1,"WAV\tProcessHeader: Not FMT");
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
) {
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()))
780 // give format handler a chance to read extra chunks
781 if (!formatHandler
->ReadExtraChunks(*this))
784 PWAV::ChunkHeader chunkHeader
;
786 // ignore chunks until we see a DATA chunk
788 if (!ReadAndCheck(*this, &chunkHeader
, sizeof(chunkHeader
)))
790 if (strncmp(chunkHeader
.tag
, WAVLabelDATA
, sizeof(WAVLabelDATA
)) == 0)
792 if (!PFile::SetPosition(PFile::GetPosition() + + chunkHeader
.len
)) {
793 PTRACE(1,"WAV\tProcessHeader: Cannot set new position");
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();
816 // Generates the wave file header.
817 // Two types of header are supported.
818 // a) PCM data, set to 8000Hz, mono, 16-bit samples
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
;
831 PTRACE(1, "WAV\tGenerateHeader: Not Open");
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.
840 audioDataLen
= LONG_MAX
- wavFmtChunk
.hdr
.len
;
841 header_needs_updating
= TRUE
;
843 audioDataLen
= lenData
;
846 // go to the beginning of the file
847 if (!PFile::SetPosition(0)) {
848 PTRACE(1,"WAV\tGenerateHeader: Cannot Set Pos");
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
)))
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!");
870 formatHandler
->CreateHeader(wavFmtChunk
, extendedHeader
);
872 // write the basic WAV header
874 !WriteAndCheck(*this, &wavFmtChunk
, sizeof(wavFmtChunk
)) ||
875 ((extendedHeader
.GetSize() > 0) && !WriteAndCheck(*this, extendedHeader
.GetPointer(), extendedHeader
.GetSize()))
879 // allow the format handler to write additional chunks
880 if (!formatHandler
->WriteExtraChunks(*this))
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
)))
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
);
907 // Update the WAV header according to the file length
908 BOOL
PWAVFile::UpdateHeader()
910 // Check file is still open
912 PTRACE(1,"WAV\tUpdateHeader: Not Open");
916 // Check there is already a valid header
918 PTRACE(1,"WAV\tUpdateHeader: File not valid");
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
)))
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
)))
937 if(formatHandler
== NULL
){
938 PTRACE(1,"WAV\tGenerateHeader: format handler is null!");
941 formatHandler
->UpdateHeader(wavFmtChunk
, extendedHeader
);
943 PFile::SetPosition(12);
944 if (!WriteAndCheck(*this, &wavFmtChunk
, sizeof(wavFmtChunk
)))
947 if (!WriteAndCheck(*this, extendedHeader
.GetPointer(), extendedHeader
.GetSize()))
950 header_needs_updating
= FALSE
;
956 //////////////////////////////////////////////////////////////////
958 BOOL
PWAVFileFormat::Read(PWAVFile
& file
, void * buf
, PINDEX
& len
)
960 if (!file
.FileRead(buf
, len
))
963 len
= file
.GetLastReadCount();
967 BOOL
PWAVFileFormat::Write(PWAVFile
& file
, const void * buf
, PINDEX
& len
)
969 if (!file
.FileWrite(buf
, len
))
972 len
= file
.GetLastWriteCount();
976 //////////////////////////////////////////////////////////////////
978 class PWAVFileFormatPCM
: public PWAVFileFormat
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
990 PString
GetFormatString() const
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
))
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
);
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
))
1048 len
= file
.GetLastWriteCount();
1052 //////////////////////////////////////////////////////////////////
1055 #define P_PACKED __attribute__ ((packed));
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.
1078 class PWAVFileFormatG7231
: public PWAVFileFormat
1081 PWAVFileFormatG7231(unsigned short _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
1093 BOOL
Read(PWAVFile
& file
, void * buf
, PINDEX
& len
);
1094 BOOL
Write(PWAVFile
& file
, const void * buf
, PINDEX
& len
);
1097 unsigned short g7231
;
1098 BYTE cacheBuffer
[24];
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))
1151 // calculate actual length of frame
1152 PINDEX frameLen
= G7231FrameSizes
[cacheBuffer
[0] & 3];
1153 if (frameLen
== 20 || frameLen
== 24) {
1154 cacheLen
= frameLen
;
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
;
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
1179 BYTE frameBuffer
[24];
1182 // calculate actual length of frame
1183 PINDEX frameLen
= G7231FrameSizes
[(*(char *)origData
) & 3];
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
;
1197 memcpy(frameBuffer
, origData
, 20);
1204 if (buf
!= NULL
&& !file
.FileWrite(buf
, 24))
1209 origData
= (char *)origData
+ frameLen
;
1218 class PWAVFileFormatG7231_vivo
: public PWAVFileFormatG7231
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
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
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();
1273 BOOL
PWAVFileConverterPCM::SetPosition(PWAVFile
& file
, off_t pos
, PFile::FilePositionOrigin origin
)
1276 return file
.SetPosition(pos
, origin
);
1279 unsigned PWAVFileConverterPCM::GetSampleSize(const PWAVFile
&) const
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
);
1299 // read the PCM data with 8 bits per sample
1300 PINDEX samples
= (len
/ 2);
1302 if (!file
.PWAVFile::RawRead(pcm8
.GetPointer(samples
), samples
))
1305 // convert to PCM-16
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
);
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
);
1327 PWAVFileConverterFactory::Worker
<PWAVFileConverterPCM
> pcmConverter(PWAVFile::fmt_PCM
, true);
1329 //////////////////////////////////////////////////////////////////