Fix bug with calls on hold
[skype-call-recorder.git] / wavewriter.cpp
blobd7b374f12409c71b7c23c551e926a6b86fd4dfed
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>
26 #include "wavewriter.h"
27 #include "common.h"
29 // little-endian helper class
31 class LittleEndianArray : public QByteArray {
32 public:
33 void appendUInt16(int i) {
34 append((char)i);
35 append((char)(i >> 8));
38 void appendUInt32(long i) {
39 append((char)i);
40 append((char)(i >> 8));
41 append((char)(i >> 16));
42 append((char)(i >> 24));
46 // WaveWriter
48 WaveWriter::WaveWriter() :
49 hasFlushed(false)
53 WaveWriter::~WaveWriter() {
54 if (file.isOpen()) {
55 debug("WARNING: WaveWriter::~WaveWriter(): File has not been closed, closing it now");
56 close();
60 bool WaveWriter::open(const QString &fn, long sr, bool s) {
61 bool b = AudioFileWriter::open(fn + ".wav", sr, s);
63 if (!b)
64 return false;
66 updateHeaderInterval = sampleRate; // update header every second
67 nextUpdateHeader = sampleRate;
69 int channels = stereo ? 2 : 1;
70 LittleEndianArray array;
71 array.reserve(44);
73 // main header
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
78 // format chunk
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
87 // data chunk
88 array.append("data"); // chunk name
89 dataSizeOffset = array.size();
90 array.appendUInt32(0); // chunk size excluding name and this size
91 // PCM data follows
93 qint64 w = file.write(array);
95 fileSize = array.size() - 8;
96 dataSize = 0;
98 if (w < 0)
99 return false;
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)
107 return true;
110 void WaveWriter::close() {
111 if (!file.isOpen()) {
112 debug("WARNING: WaveWriter::close() called, but file not open");
113 return;
116 if (!hasFlushed) {
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) {
126 QByteArray output;
128 if (stereo) {
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];
141 } else {
142 output = left;
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);
153 if (stereo)
154 right.remove(0, samples * 2);
156 if (!ret)
157 return false;
159 nextUpdateHeader -= samples;
160 if (flush || nextUpdateHeader <= 0) {
161 nextUpdateHeader = updateHeaderInterval;
162 updateHeader();
164 if (flush)
165 hasFlushed = true;
168 return true;
171 void WaveWriter::updateHeader() {
172 qint64 pos = file.pos();
173 LittleEndianArray tmp;
175 tmp.appendUInt32(fileSize);
176 file.seek(fileSizeOffset);
177 file.write(tmp);
179 tmp.clear();
180 tmp.appendUInt32(dataSize);
181 file.seek(dataSizeOffset);
182 file.write(tmp);
184 file.seek(pos);