Add 2009 to copyright and fix its format
[skype-call-recorder.git] / vorbiswriter.cpp
blob5874a9a8b379e86f047d040a9a8a9891e7877bba
1 /*
2 Skype Call Recorder
3 Copyright 2008 - 2009 by 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 // Note: this file doesn't include comments related to writing Ogg Vorbis
25 // files. it is mostly based on the examples/encoder_example.c from the vorbis
26 // library, so have a look there if you're curious about how this works.
28 // TODO: currently, this only writes tags while opening the file, but doesn't
29 // update them if they've been changed before close(). for now this is ok, as
30 // it never happens.
32 #include <QByteArray>
33 #include <QString>
34 #include <cstdlib>
35 #include <ctime>
36 #include <vorbis/vorbisenc.h>
38 #include "vorbiswriter.h"
39 #include "common.h"
40 #include "preferences.h"
42 struct VorbisWriterPrivateData {
43 ogg_stream_state os;
44 ogg_page og;
45 ogg_packet op;
46 vorbis_info vi;
47 vorbis_comment vc;
48 vorbis_dsp_state vd;
49 vorbis_block vb;
52 VorbisWriter::VorbisWriter() :
53 pd(NULL),
54 hasFlushed(false)
58 VorbisWriter::~VorbisWriter() {
59 if (file.isOpen()) {
60 debug("WARNING: VorbisWriter::~VorbisWriter(): File has not been closed, closing it now");
61 close();
64 if (pd) {
65 ogg_stream_clear(&pd->os);
66 vorbis_block_clear(&pd->vb);
67 vorbis_dsp_clear(&pd->vd);
68 vorbis_comment_clear(&pd->vc);
69 vorbis_info_clear(&pd->vi);
70 delete pd;
74 bool VorbisWriter::open(const QString &fn, long sr, bool s) {
75 bool b = AudioFileWriter::open(fn + ".ogg", sr, s);
77 if (!b)
78 return false;
80 int quality = preferences.get(Pref::OutputFormatVorbisQuality).toInt();
82 pd = new VorbisWriterPrivateData;
83 vorbis_info_init(&pd->vi);
85 if (vorbis_encode_init_vbr(&pd->vi, stereo ? 2 : 1, sampleRate, (float)quality / 10.0f) != 0) {
86 delete pd;
87 pd = NULL;
88 return false;
91 // TODO: the docs vaguely mention that stereo coupling can be disabled
92 // with vorbis_encode_ctl(), but I didn't find anything concrete
94 vorbis_comment_init(&pd->vc);
95 // vorbis_comment_add_tag() in libvorbis up to version 1.2.0
96 // incorrectly takes a char * instead of a const char *. to prevent
97 // compiler warnings we use const_cast<>(), since it's known that
98 // libvorbis does not change the arguments.
99 vorbis_comment_add_tag(&pd->vc, const_cast<char *>("COMMENT"), const_cast<char *>(tagComment.toUtf8().constData()));
100 vorbis_comment_add_tag(&pd->vc, const_cast<char *>("DATE"), const_cast<char *>(tagTime.toString("yyyy-MM-dd hh:mm").toAscii().constData()));
101 vorbis_comment_add_tag(&pd->vc, const_cast<char *>("GENRE"), const_cast<char *>("Speech (Skype Call)"));
103 vorbis_analysis_init(&pd->vd, &pd->vi);
104 vorbis_block_init(&pd->vd, &pd->vb);
106 std::srand(std::time(NULL));
107 ogg_stream_init(&pd->os, std::rand());
109 ogg_packet header;
110 ogg_packet header_comm;
111 ogg_packet header_code;
113 vorbis_analysis_headerout(&pd->vd, &pd->vc, &header, &header_comm, &header_code);
114 ogg_stream_packetin(&pd->os, &header);
115 ogg_stream_packetin(&pd->os, &header_comm);
116 ogg_stream_packetin(&pd->os, &header_code);
118 while (ogg_stream_flush(&pd->os, &pd->og) != 0) {
119 file.write((const char *)pd->og.header, pd->og.header_len);
120 file.write((const char *)pd->og.body, pd->og.body_len);
123 return true;
126 void VorbisWriter::close() {
127 if (!file.isOpen()) {
128 debug("WARNING: VorbisWriter::close() called, but file not open");
129 return;
132 if (!hasFlushed) {
133 debug("WARNING: VorbisWriter::close() called but no flush happened, flushing now");
134 QByteArray dummy1, dummy2;
135 write(dummy1, dummy2, 0, true);
138 AudioFileWriter::close();
141 bool VorbisWriter::write(QByteArray &left, QByteArray &right, long samples, bool flush) {
142 const long maxChunkSize = 4096;
144 const qint16 *leftData = (const qint16 *)left.constData();
145 const qint16 *rightData = stereo ? (const qint16 *)right.constData() : NULL;
147 long todoSamples = samples;
148 int eos = 0;
150 while (!eos) {
151 long chunkSize = todoSamples > maxChunkSize ? maxChunkSize : todoSamples;
152 todoSamples -= chunkSize;
154 if (chunkSize == 0) {
155 if (!flush)
156 break;
157 hasFlushed = true;
158 vorbis_analysis_wrote(&pd->vd, 0);
159 } else {
160 float **buffer = vorbis_analysis_buffer(&pd->vd, chunkSize);
162 for (long i = 0; i < chunkSize; i++)
163 buffer[0][i] = (float)leftData[i] / 32768.0f;
164 leftData += chunkSize;
166 if (stereo) {
167 for (long i = 0; i < chunkSize; i++)
168 buffer[1][i] = (float)rightData[i] / 32768.0f;
169 rightData += chunkSize;
172 vorbis_analysis_wrote(&pd->vd, chunkSize);
175 while (vorbis_analysis_blockout(&pd->vd, &pd->vb) == 1) {
176 vorbis_analysis(&pd->vb, NULL);
177 vorbis_bitrate_addblock(&pd->vb);
179 while (vorbis_bitrate_flushpacket(&pd->vd, &pd->op)) {
180 ogg_stream_packetin(&pd->os, &pd->op);
182 while (!eos && ogg_stream_pageout(&pd->os, &pd->og) != 0) {
183 file.write((const char *)pd->og.header, pd->og.header_len);
184 file.write((const char *)pd->og.body, pd->og.body_len);
186 if (ogg_page_eos(&pd->og))
187 eos = 1;
193 samplesWritten += samples;
195 left.remove(0, samples * 2);
196 if (stereo)
197 right.remove(0, samples * 2);
199 return true;