Automatically convert old stereo preferences to new ones
[skype-call-recorder.git] / wavewriter.cpp
blob07a532e168675e1105f6816d3a08bfe83e583c30
1 /*
2 Skype Call Recorder
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
21 http://www.fsf.org/
24 #include <QByteArray>
25 #include <QString>
27 #include "wavewriter.h"
28 #include "common.h"
30 // little-endian helper class
32 class LittleEndianArray : public QByteArray {
33 public:
34 void appendUInt16(int i) {
35 append((char)i);
36 append((char)(i >> 8));
39 void appendUInt32(long i) {
40 append((char)i);
41 append((char)(i >> 8));
42 append((char)(i >> 16));
43 append((char)(i >> 24));
47 // WaveWriter
49 WaveWriter::WaveWriter() :
50 hasFlushed(false)
54 WaveWriter::~WaveWriter() {
55 if (file.isOpen()) {
56 debug("WARNING: WaveWriter::~WaveWriter(): File has not been closed, closing it now");
57 close();
61 bool WaveWriter::open(const QString &fn, long sr, bool s) {
62 bool b = AudioFileWriter::open(fn + ".wav", sr, s);
64 if (!b)
65 return false;
67 updateHeaderInterval = sampleRate; // update header every second
68 nextUpdateHeader = sampleRate;
70 int channels = stereo ? 2 : 1;
71 LittleEndianArray array;
72 array.reserve(44);
74 // main header
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
79 // format chunk
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
88 // data chunk
89 array.append("data"); // chunk name
90 dataSizeOffset = array.size();
91 array.appendUInt32(0); // chunk size excluding name and this size
92 // PCM data follows
94 qint64 w = file.write(array);
96 fileSize = array.size() - 8;
97 dataSize = 0;
99 if (w < 0)
100 return false;
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)
108 return true;
111 void WaveWriter::close() {
112 if (!file.isOpen()) {
113 debug("WARNING: WaveWriter::close() called, but file not open");
114 return;
117 if (!hasFlushed) {
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) {
127 QByteArray output;
129 if (stereo) {
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];
142 } else {
143 output = left;
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);
154 if (stereo)
155 right.remove(0, samples * 2);
157 if (!ret)
158 return false;
160 nextUpdateHeader -= samples;
161 if (flush || nextUpdateHeader <= 0) {
162 nextUpdateHeader = updateHeaderInterval;
163 updateHeader();
165 if (flush)
166 hasFlushed = true;
169 return true;
172 void WaveWriter::updateHeader() {
173 qint64 pos = file.pos();
174 LittleEndianArray tmp;
176 tmp.appendUInt32(fileSize);
177 file.seek(fileSizeOffset);
178 file.write(tmp);
180 tmp.clear();
181 tmp.appendUInt32(dataSize);
182 file.seek(dataSizeOffset);
183 file.write(tmp);
185 file.seek(pos);