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
26 #include "wavewriter.h"
29 // little-endian helper class
31 class LittleEndianArray
: public QByteArray
{
33 void appendUInt16(int i
) {
35 append((char)(i
>> 8));
38 void appendUInt32(long i
) {
40 append((char)(i
>> 8));
41 append((char)(i
>> 16));
42 append((char)(i
>> 24));
48 WaveWriter::WaveWriter() :
53 WaveWriter::~WaveWriter() {
55 debug("WARNING: WaveWriter::~WaveWriter(): File has not been closed, closing it now");
60 bool WaveWriter::open(const QString
&fn
, long sr
, bool s
) {
61 bool b
= AudioFileWriter::open(fn
+ ".wav", sr
, s
);
66 updateHeaderInterval
= sampleRate
; // update header every second
67 nextUpdateHeader
= sampleRate
;
69 int channels
= stereo
? 2 : 1;
70 LittleEndianArray array
;
74 array
.append("RIFF"); // RIFF signature
75 fileSizeOffset
= array
.size();
76 array
.appendUInt32(0); // file size excluding signature and this size
77 array
.append("WAVE"); // RIFF type
79 array
.append("fmt "); // chunk name
80 array
.appendUInt32(16); // chunk size excluding name and this size
81 array
.appendUInt16(1); // compression code, 1 == PCM uncompressed
82 array
.appendUInt16(channels
); // number of channels
83 array
.appendUInt32(sampleRate
); // sample rate
84 array
.appendUInt32(channels
* 2 * sampleRate
); // average bytes per second, block align * sample rate
85 array
.appendUInt16(channels
* 2); // block align for each sample group, (usually) significant bits / 8 * number of channels
86 array
.appendUInt16(16); // significant bits per sample
88 array
.append("data"); // chunk name
89 dataSizeOffset
= array
.size();
90 array
.appendUInt32(0); // chunk size excluding name and this size
93 qint64 w
= file
.write(array
);
95 fileSize
= array
.size() - 8;
101 // Note: the file size field and the "data" chunk size field can't be
102 // filled in yet, which is why we put zero in there for now. some
103 // players can play those files anyway, but we'll seek back and update
104 // these fields every now and then, so that even if we crash, we'll
105 // have a valid wav file (with potentially trailing data)
110 void WaveWriter::close() {
111 if (!file
.isOpen()) {
112 debug("WARNING: WaveWriter::close() called, but file not open");
117 debug("WARNING: WaveWriter::close() called but no flush happened, flushing now");
118 QByteArray dummy1
, dummy2
;
119 write(dummy1
, dummy2
, 0, true);
122 AudioFileWriter::close();
125 bool WaveWriter::write(QByteArray
&left
, QByteArray
&right
, int samples
, bool flush
) {
129 // interleave data... TODO: is this something that advanced
130 // processors instructions can handle faster?
132 output
.resize(samples
* 4);
133 qint16
*outputData
= reinterpret_cast<qint16
*>(output
.data());
134 qint16
*leftData
= reinterpret_cast<qint16
*>(left
.data());
135 qint16
*rightData
= reinterpret_cast<qint16
*>(right
.data());
137 for (int i
= 0; i
< samples
; i
++) {
138 outputData
[i
* 2] = leftData
[i
];
139 outputData
[i
* 2 + 1] = rightData
[i
];
143 output
.truncate(samples
* 2);
146 bool ret
= file
.write(output
);
148 fileSize
+= output
.size();
149 dataSize
+= output
.size();
150 samplesWritten
+= samples
;
152 left
.remove(0, samples
* 2);
154 right
.remove(0, samples
* 2);
159 nextUpdateHeader
-= samples
;
160 if (flush
|| nextUpdateHeader
<= 0) {
161 nextUpdateHeader
= updateHeaderInterval
;
171 void WaveWriter::updateHeader() {
172 qint64 pos
= file
.pos();
173 LittleEndianArray tmp
;
175 tmp
.appendUInt32(fileSize
);
176 file
.seek(fileSizeOffset
);
180 tmp
.appendUInt32(dataSize
);
181 file
.seek(dataSizeOffset
);