3 Copyright (C) 2008 jlh (jlh at gmx dot ch)
5 This program is free software; you can redistribute it and/or modify it
6 under the terms of the GNU General Public License as published by the
7 Free Software Foundation; either version 2 of the License, version 3 of
8 the License, or (at your option) any later version.
10 This program is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
15 You should have received a copy of the GNU General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19 The GNU General Public License version 2 is included with the source of
20 this program under the file name COPYING. You can also get a copy on
27 #include "wavewriter.h"
30 // little-endian helper class
32 class LittleEndianArray
: public QByteArray
{
34 void appendUInt16(int i
) {
36 append((char)(i
>> 8));
39 void appendUInt32(long i
) {
41 append((char)(i
>> 8));
42 append((char)(i
>> 16));
43 append((char)(i
>> 24));
49 WaveWriter::WaveWriter() :
54 WaveWriter::~WaveWriter() {
56 debug("WARNING: WaveWriter::~WaveWriter(): File has not been closed, closing it now");
61 bool WaveWriter::open(const QString
&fn
, long sr
, bool s
) {
62 bool b
= AudioFileWriter::open(fn
+ ".wav", sr
, s
);
67 updateHeaderInterval
= sampleRate
; // update header every second
68 nextUpdateHeader
= sampleRate
;
70 int channels
= stereo
? 2 : 1;
71 LittleEndianArray array
;
75 array
.append("RIFF"); // RIFF signature
76 fileSizeOffset
= array
.size();
77 array
.appendUInt32(0); // file size excluding signature and this size
78 array
.append("WAVE"); // RIFF type
80 array
.append("fmt "); // chunk name
81 array
.appendUInt32(16); // chunk size excluding name and this size
82 array
.appendUInt16(1); // compression code, 1 == PCM uncompressed
83 array
.appendUInt16(channels
); // number of channels
84 array
.appendUInt32(sampleRate
); // sample rate
85 array
.appendUInt32(channels
* 2 * sampleRate
); // average bytes per second, block align * sample rate
86 array
.appendUInt16(channels
* 2); // block align for each sample group, (usually) significant bits / 8 * number of channels
87 array
.appendUInt16(16); // significant bits per sample
89 array
.append("data"); // chunk name
90 dataSizeOffset
= array
.size();
91 array
.appendUInt32(0); // chunk size excluding name and this size
94 qint64 w
= file
.write(array
);
96 fileSize
= array
.size() - 8;
102 // Note: the file size field and the "data" chunk size field can't be
103 // filled in yet, which is why we put zero in there for now. some
104 // players can play those files anyway, but we'll seek back and update
105 // these fields every now and then, so that even if we crash, we'll
106 // have a valid wav file (with potentially trailing data)
111 void WaveWriter::close() {
112 if (!file
.isOpen()) {
113 debug("WARNING: WaveWriter::close() called, but file not open");
118 debug("WARNING: WaveWriter::close() called but no flush happened, flushing now");
119 QByteArray dummy1
, dummy2
;
120 write(dummy1
, dummy2
, 0, true);
123 AudioFileWriter::close();
126 bool WaveWriter::write(QByteArray
&left
, QByteArray
&right
, long samples
, bool flush
) {
130 // interleave data... TODO: is this something that advanced
131 // processors instructions can handle faster?
133 output
.resize(samples
* 4);
134 qint16
*outputData
= reinterpret_cast<qint16
*>(output
.data());
135 qint16
*leftData
= reinterpret_cast<qint16
*>(left
.data());
136 qint16
*rightData
= reinterpret_cast<qint16
*>(right
.data());
138 for (long i
= 0; i
< samples
; i
++) {
139 outputData
[i
* 2] = leftData
[i
];
140 outputData
[i
* 2 + 1] = rightData
[i
];
144 output
.truncate(samples
* 2);
147 bool ret
= file
.write(output
);
149 fileSize
+= output
.size();
150 dataSize
+= output
.size();
151 samplesWritten
+= samples
;
153 left
.remove(0, samples
* 2);
155 right
.remove(0, samples
* 2);
160 nextUpdateHeader
-= samples
;
161 if (flush
|| nextUpdateHeader
<= 0) {
162 nextUpdateHeader
= updateHeaderInterval
;
172 void WaveWriter::updateHeader() {
173 qint64 pos
= file
.pos();
174 LittleEndianArray tmp
;
176 tmp
.appendUInt32(fileSize
);
177 file
.seek(fileSizeOffset
);
181 tmp
.appendUInt32(dataSize
);
182 file
.seek(dataSizeOffset
);