Initial check-in
[skype-call-recorder.git] / wavewriter.cpp
blob0def93aea0e9f0ac6d547a50b046db4a1ca39744
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 // 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);
124 if (!ret)
125 return false;
127 nextUpdateHeader -= samples;
128 if (flush || nextUpdateHeader <= 0) {
129 nextUpdateHeader = updateHeaderInterval;
130 updateHeader();
133 return true;
136 void WaveWriter::updateHeader() {
137 qint64 pos = file.pos();
138 LittleEndianArray tmp;
140 tmp.appendUInt32(fileSize);
141 file.seek(fileSizeOffset);
142 file.write(tmp);
144 tmp.clear();
145 tmp.appendUInt32(dataSize);
146 file.seek(dataSizeOffset);
147 file.write(tmp);
149 file.seek(pos);