Default to 64kpbs MP3 and add bitrate/quality recommendations
[skype-call-recorder.git] / vorbiswriter.cpp
blobad53d4cd40c2fb254f94aa82f9aa07d20df199bb
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 // 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("output.format.vorbis.quality").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 // unfortunately, this takes a char * and not a const char *
96 vorbis_comment_add_tag(&pd->vc, "COMMENT", tagComment.toUtf8().data());
97 vorbis_comment_add_tag(&pd->vc, "DATE", tagTime.toString("yyyy-MM-dd hh:mm").toAscii().data());
98 vorbis_comment_add_tag(&pd->vc, "GENRE", "Speech (Skype Call)");
100 vorbis_analysis_init(&pd->vd, &pd->vi);
101 vorbis_block_init(&pd->vd, &pd->vb);
103 std::srand(std::time(NULL));
104 ogg_stream_init(&pd->os, std::rand());
106 ogg_packet header;
107 ogg_packet header_comm;
108 ogg_packet header_code;
110 vorbis_analysis_headerout(&pd->vd, &pd->vc, &header, &header_comm, &header_code);
111 ogg_stream_packetin(&pd->os, &header);
112 ogg_stream_packetin(&pd->os, &header_comm);
113 ogg_stream_packetin(&pd->os, &header_code);
115 while (ogg_stream_flush(&pd->os, &pd->og) != 0) {
116 file.write((const char *)pd->og.header, pd->og.header_len);
117 file.write((const char *)pd->og.body, pd->og.body_len);
120 return true;
123 void VorbisWriter::close() {
124 if (!file.isOpen()) {
125 debug("WARNING: VorbisWriter::close() called, but file not open");
126 return;
129 if (!hasFlushed) {
130 debug("WARNING: VorbisWriter::close() called but no flush happened, flushing now");
131 QByteArray dummy1, dummy2;
132 write(dummy1, dummy2, 0, true);
135 AudioFileWriter::close();
138 bool VorbisWriter::write(QByteArray &left, QByteArray &right, long samples, bool flush) {
139 const long maxChunkSize = 4096;
141 const qint16 *leftData = (const qint16 *)left.constData();
142 const qint16 *rightData = stereo ? (const qint16 *)right.constData() : NULL;
144 long todoSamples = samples;
145 int eos = 0;
147 while (!eos) {
148 long chunkSize = todoSamples > maxChunkSize ? maxChunkSize : todoSamples;
149 todoSamples -= chunkSize;
151 if (chunkSize == 0) {
152 if (!flush)
153 break;
154 hasFlushed = true;
155 vorbis_analysis_wrote(&pd->vd, 0);
156 } else {
157 float **buffer = vorbis_analysis_buffer(&pd->vd, chunkSize);
159 for (long i = 0; i < chunkSize; i++)
160 buffer[0][i] = (float)leftData[i] / 32768.0f;
161 leftData += chunkSize;
163 if (stereo) {
164 for (long i = 0; i < chunkSize; i++)
165 buffer[1][i] = (float)rightData[i] / 32768.0f;
166 rightData += chunkSize;
169 vorbis_analysis_wrote(&pd->vd, chunkSize);
172 while (vorbis_analysis_blockout(&pd->vd, &pd->vb) == 1) {
173 vorbis_analysis(&pd->vb, NULL);
174 vorbis_bitrate_addblock(&pd->vb);
176 while (vorbis_bitrate_flushpacket(&pd->vd, &pd->op)) {
177 ogg_stream_packetin(&pd->os, &pd->op);
179 while (!eos && ogg_stream_pageout(&pd->os, &pd->og) != 0) {
180 file.write((const char *)pd->og.header, pd->og.header_len);
181 file.write((const char *)pd->og.body, pd->og.body_len);
183 if (ogg_page_eos(&pd->og))
184 eos = 1;
190 samplesWritten += samples;
192 left.remove(0, samples * 2);
193 if (stereo)
194 right.remove(0, samples * 2);
196 return true;