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() {
51 bool WaveWriter::open(const QString
&fn
, long sr
, bool s
) {
52 bool b
= AudioFileWriter::open(fn
+ ".wav", sr
, s
);
57 updateHeaderInterval
= sampleRate
; // update header every second
58 nextUpdateHeader
= sampleRate
;
60 int channels
= stereo
? 2 : 1;
61 LittleEndianArray array
;
65 array
.append("RIFF"); // RIFF signature
66 fileSizeOffset
= array
.size();
67 array
.appendUInt32(0); // file size excluding signature and this size
68 array
.append("WAVE"); // RIFF type
70 array
.append("fmt "); // chunk name
71 array
.appendUInt32(16); // chunk size excluding name and this size
72 array
.appendUInt16(1); // compression code, 1 == PCM uncompressed
73 array
.appendUInt16(channels
); // number of channels
74 array
.appendUInt32(sampleRate
); // sample rate
75 array
.appendUInt32(channels
* 2 * sampleRate
); // average bytes per second, block align * sample rate
76 array
.appendUInt16(channels
* 2); // block align for each sample group, (usually) significant bits / 8 * number of channels
77 array
.appendUInt16(16); // significant bits per sample
79 array
.append("data"); // chunk name
80 dataSizeOffset
= array
.size();
81 array
.appendUInt32(0); // chunk size excluding name and this size
84 qint64 w
= file
.write(array
);
86 fileSize
= array
.size() - 8;
92 // Note: the file size field and the "data" chunk size field can't be
93 // filled in yet, which is why we put zero in there for now. some
94 // players can play those files anyway, but we'll seek back and update
95 // these fields every now and then, so that even if we crash, we'll
96 // have a valid wav file (with potentially trailing data)
101 bool WaveWriter::write(QByteArray
&left
, QByteArray
&right
, int samples
, bool flush
) {
102 // interleave data... TODO: is this something that advanced processors
103 // instructions can handle faster?
105 QByteArray interleaved
;
106 interleaved
.resize(samples
* 4);
107 qint16
*interleavedData
= reinterpret_cast<qint16
*>(interleaved
.data());
108 qint16
*leftData
= reinterpret_cast<qint16
*>(left
.data());
109 qint16
*rightData
= reinterpret_cast<qint16
*>(right
.data());
111 for (int i
= 0; i
< samples
; i
++) {
112 interleavedData
[i
* 2] = leftData
[i
];
113 interleavedData
[i
* 2 + 1] = rightData
[i
];
116 bool ret
= file
.write(interleaved
);
118 fileSize
+= interleaved
.size();
119 dataSize
+= interleaved
.size();
121 left
.remove(0, samples
* 2);
122 right
.remove(0, samples
* 2);
127 nextUpdateHeader
-= samples
;
128 if (flush
|| nextUpdateHeader
<= 0) {
129 nextUpdateHeader
= updateHeaderInterval
;
136 void WaveWriter::updateHeader() {
137 qint64 pos
= file
.pos();
138 LittleEndianArray tmp
;
140 tmp
.appendUInt32(fileSize
);
141 file
.seek(fileSizeOffset
);
145 tmp
.appendUInt32(dataSize
);
146 file
.seek(dataSizeOffset
);