Wave writer can now write mono files
[skype-call-recorder.git] / wavewriter.cpp
blob88d919372a375fab6a72ae330575a963bcfd8fe7
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() {
51 bool WaveWriter::open(const QString &fn, long sr, bool s) {
52 bool b = AudioFileWriter::open(fn + ".wav", sr, s);
54 if (!b)
55 return false;
57 updateHeaderInterval = sampleRate; // update header every second
58 nextUpdateHeader = sampleRate;
60 int channels = stereo ? 2 : 1;
61 LittleEndianArray array;
62 array.reserve(44);
64 // main header
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
69 // format chunk
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
78 // data chunk
79 array.append("data"); // chunk name
80 dataSizeOffset = array.size();
81 array.appendUInt32(0); // chunk size excluding name and this size
82 // PCM data follows
84 qint64 w = file.write(array);
86 fileSize = array.size() - 8;
87 dataSize = 0;
89 if (w < 0)
90 return false;
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)
98 return true;
101 bool WaveWriter::write(QByteArray &left, QByteArray &right, int samples, bool flush) {
102 QByteArray output;
104 if (stereo) {
105 // interleave data... TODO: is this something that advanced
106 // processors instructions can handle faster?
108 output.resize(samples * 4);
109 qint16 *outputData = reinterpret_cast<qint16 *>(output.data());
110 qint16 *leftData = reinterpret_cast<qint16 *>(left.data());
111 qint16 *rightData = reinterpret_cast<qint16 *>(right.data());
113 for (int i = 0; i < samples; i++) {
114 outputData[i * 2] = leftData[i];
115 outputData[i * 2 + 1] = rightData[i];
117 } else {
118 output = left;
119 output.truncate(samples * 2);
122 bool ret = file.write(output);
124 fileSize += output.size();
125 dataSize += output.size();
127 left.remove(0, samples * 2);
128 if (stereo)
129 right.remove(0, samples * 2);
131 if (!ret)
132 return false;
134 nextUpdateHeader -= samples;
135 if (flush || nextUpdateHeader <= 0) {
136 nextUpdateHeader = updateHeaderInterval;
137 updateHeader();
140 return true;
143 void WaveWriter::updateHeader() {
144 qint64 pos = file.pos();
145 LittleEndianArray tmp;
147 tmp.appendUInt32(fileSize);
148 file.seek(fileSizeOffset);
149 file.write(tmp);
151 tmp.clear();
152 tmp.appendUInt32(dataSize);
153 file.seek(dataSizeOffset);
154 file.write(tmp);
156 file.seek(pos);