utils/snapshot: Don't remove utils/syncviewer from snapshots
[skype-call-recorder.git] / mp3writer.cpp
blob3efdc6dc32aee58c3a811b85b673aac69c6090e5
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 <lame/lame.h>
27 #include <id3/tag.h>
29 #include "mp3writer.h"
30 #include "common.h"
31 #include "preferences.h"
33 Mp3Writer::Mp3Writer() :
34 lame(NULL),
35 hasFlushed(false)
39 Mp3Writer::~Mp3Writer() {
40 if (file.isOpen()) {
41 debug("WARNING: Mp3Writer::~Mp3Writer(): File has not been closed, closing it now");
42 close();
45 if (lame)
46 lame_close(lame);
49 bool Mp3Writer::open(const QString &fn, long sr, bool s) {
50 bool b = AudioFileWriter::open(fn + ".mp3", sr, s);
52 if (!b)
53 return false;
55 lame = lame_init();
56 if (!lame)
57 return false;
59 bitRate = preferences.get(Pref::OutputFormatMp3Bitrate).toInt();
61 lame_set_in_samplerate(lame, sampleRate);
62 lame_set_num_channels(lame, stereo ? 2 : 1);
63 lame_set_out_samplerate(lame, sampleRate);
64 // TODO: do we need this?
65 lame_set_bWriteVbrTag(lame, 0);
66 lame_set_mode(lame, stereo ? STEREO : MONO);
67 lame_set_brate(lame, bitRate);
68 if (lame_init_params(lame) == -1)
69 return false;
71 return true;
74 void Mp3Writer::close() {
75 if (!file.isOpen()) {
76 debug("WARNING: Mp3Writer::close() called, but file not open");
77 return;
80 if (!hasFlushed) {
81 debug("WARNING: Mp3Writer::close() called but no flush happened, flushing now");
82 QByteArray dummy1, dummy2;
83 write(dummy1, dummy2, 0, true);
86 AudioFileWriter::close();
87 writeTags();
90 namespace {
91 ID3_Frame *getOrCreateTag(ID3_Tag &tag, ID3_FrameID id) {
92 ID3_Frame *frame = tag.Find(id);
94 if (!frame) {
95 frame = new ID3_Frame(id);
96 tag.AttachFrame(frame);
99 return frame;
103 void Mp3Writer::writeTags() {
104 if (!mustWriteTags)
105 return;
107 debug("Writing tags to MP3 file");
109 QByteArray fn = QFile::encodeName(file.fileName());
110 ID3_Tag tag(fn.constData());
112 // NOTE: we don't set ID3FID_TITLE as the file name is already meant to
113 // be a good enough description of the content
115 QString str = tagTime.toString("yyyyddMMhhmm");
116 // TODO: the following would be better but doesn't work. find out why
117 //getOrCreateTag(tag, ID3FID_COMMENT )->GetField(ID3FN_TEXT)->Set(tagComment.utf16());
118 getOrCreateTag(tag, ID3FID_COMMENT )->GetField(ID3FN_TEXT)->Set(tagComment.toAscii().constData());
119 getOrCreateTag(tag, ID3FID_CONTENTTYPE)->GetField(ID3FN_TEXT)->Set("(101)Skype Call");
120 getOrCreateTag(tag, ID3FID_YEAR )->GetField(ID3FN_TEXT)->Set(str.mid(0, 4).toAscii().constData());
121 getOrCreateTag(tag, ID3FID_DATE )->GetField(ID3FN_TEXT)->Set(str.mid(4, 4).toAscii().constData());
122 getOrCreateTag(tag, ID3FID_TIME )->GetField(ID3FN_TEXT)->Set(str.mid(8, 4).toAscii().constData());
124 tag.Update();
125 mustWriteTags = false;
128 bool Mp3Writer::write(QByteArray &left, QByteArray &right, long samples, bool flush) {
129 int ret;
130 QByteArray output;
131 // rough upper bound formula taken from lame.h
132 long size = samples + samples / 4 + 7200;
134 do {
135 output.resize(size);
137 if (stereo) {
138 ret = lame_encode_buffer(lame, reinterpret_cast<const short *>(left.constData()),
139 reinterpret_cast<const short *>(right.constData()), samples,
140 reinterpret_cast<unsigned char *>(output.data()), output.size());
141 } else {
142 // lame.h claims to write to the buffers, even though they're declared const, be safe
143 // TODO: this mixes both channels again! can lame take only mono samples?
144 ret = lame_encode_buffer(lame, reinterpret_cast<const short *>(left.data()),
145 reinterpret_cast<const short *>(left.data()), samples,
146 reinterpret_cast<unsigned char *>(output.data()), output.size());
149 if (ret == -1) {
150 // there wasn't enough space in output
151 size *= 2;
152 continue;
154 } while (false);
156 if (ret < 0) {
157 debug(QString("Error while writing MP3 file, code = %1").arg(ret));
158 return false;
161 samplesWritten += samples;
163 if (ret > 0) {
164 output.truncate(ret);
165 file.write(output);
168 left.remove(0, samples * 2);
169 if (stereo)
170 right.remove(0, samples * 2);
172 if (!flush)
173 return true;
175 // flush mp3
177 output.resize(10240);
178 ret = lame_encode_flush(lame, reinterpret_cast<unsigned char *>(output.data()), output.size());
180 lame_close(lame);
181 lame = NULL;
182 hasFlushed = true;
184 if (ret < 0) {
185 debug(QString("Error while flushing MP3 file, code = %1").arg(ret));
186 return false;
189 if (ret > 0) {
190 output.truncate(ret);
191 file.write(output);
194 return true;