summaryrefslogtreecommitdiff
path: root/rbutil/rbutilqt/base
diff options
context:
space:
mode:
Diffstat (limited to 'rbutil/rbutilqt/base')
-rw-r--r--rbutil/rbutilqt/base/encoderbase.cpp6
-rw-r--r--rbutil/rbutilqt/base/encoderlame.cpp280
-rw-r--r--rbutil/rbutilqt/base/encoderlame.h71
-rw-r--r--rbutil/rbutilqt/base/voicefile.cpp2
4 files changed, 358 insertions, 1 deletions
diff --git a/rbutil/rbutilqt/base/encoderbase.cpp b/rbutil/rbutilqt/base/encoderbase.cpp
index e2668febd1..05ccae3011 100644
--- a/rbutil/rbutilqt/base/encoderbase.cpp
+++ b/rbutil/rbutilqt/base/encoderbase.cpp
@@ -20,6 +20,7 @@
20#include "utils.h" 20#include "utils.h"
21#include "rbsettings.h" 21#include "rbsettings.h"
22#include "encoderrbspeex.h" 22#include "encoderrbspeex.h"
23#include "encoderlame.h"
23#include "encoderexe.h" 24#include "encoderexe.h"
24 25
25/********************************************************************* 26/*********************************************************************
@@ -55,7 +56,12 @@ EncoderBase* EncoderBase::getEncoder(QObject* parent,QString encoder)
55 EncoderBase* enc; 56 EncoderBase* enc;
56 if(encoder == "lame") 57 if(encoder == "lame")
57 { 58 {
59#if defined(Q_OS_MACX)
60 /* currently not on OS X */
58 enc = new EncoderExe(encoder,parent); 61 enc = new EncoderExe(encoder,parent);
62#else
63 enc = new EncoderLame(parent);
64#endif
59 return enc; 65 return enc;
60 } 66 }
61 else // rbspeex is default 67 else // rbspeex is default
diff --git a/rbutil/rbutilqt/base/encoderlame.cpp b/rbutil/rbutilqt/base/encoderlame.cpp
new file mode 100644
index 0000000000..d85453c49b
--- /dev/null
+++ b/rbutil/rbutilqt/base/encoderlame.cpp
@@ -0,0 +1,280 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 *
9 * Copyright (C) 2012 Dominik Riebeling
10 *
11 * All files in this archive are subject to the GNU General Public License.
12 * See the file COPYING in the source tree root for full license agreement.
13 *
14 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
15 * KIND, either express or implied.
16 *
17 ****************************************************************************/
18
19#include <QtCore>
20#include "encoderlame.h"
21#include "rbsettings.h"
22#include "lame/lame.h"
23
24/** Resolve a symbol from loaded library.
25 */
26#define SYMBOLRESOLVE(symbol, type) \
27 do { m_##symbol = (type)lib->resolve(#symbol); \
28 if(!m_##symbol) return; \
29 qDebug() << "[EncoderLame] Resolved symbol " #symbol; } \
30 while(0)
31
32EncoderLame::EncoderLame(QObject *parent) : EncoderBase(parent)
33{
34 m_symbolsResolved = false;
35 lib = new QLibrary("libmp3lame", this);
36
37 SYMBOLRESOLVE(get_lame_short_version, const char* (*)());
38 SYMBOLRESOLVE(lame_set_out_samplerate, int (*)(lame_global_flags*, int));
39 SYMBOLRESOLVE(lame_set_in_samplerate, int (*)(lame_global_flags*, int));
40 SYMBOLRESOLVE(lame_set_num_channels, int (*)(lame_global_flags*, int));
41 SYMBOLRESOLVE(lame_set_scale, int (*)(lame_global_flags*, float));
42 SYMBOLRESOLVE(lame_set_mode, int (*)(lame_global_flags*, MPEG_mode));
43 SYMBOLRESOLVE(lame_set_VBR, int (*)(lame_global_flags*, vbr_mode));
44 SYMBOLRESOLVE(lame_set_VBR_quality, int (*)(lame_global_flags*, float));
45 SYMBOLRESOLVE(lame_set_VBR_max_bitrate_kbps, int (*)(lame_global_flags*, int));
46 SYMBOLRESOLVE(lame_set_bWriteVbrTag, int (*)(lame_global_flags*, int));
47 SYMBOLRESOLVE(lame_init, lame_global_flags* (*)());
48 SYMBOLRESOLVE(lame_init_params, int (*)(lame_global_flags*));
49 SYMBOLRESOLVE(lame_encode_buffer, int (*)(lame_global_flags*, short int*, short int*, int, unsigned char*, int));
50 SYMBOLRESOLVE(lame_encode_flush, int (*)(lame_global_flags*, unsigned char*, int));
51 SYMBOLRESOLVE(lame_close, int (*)(lame_global_flags*));
52
53 qDebug() << "[EncoderLame] libmp3lame loaded:" << lib->isLoaded();
54 m_symbolsResolved = true;
55}
56
57void EncoderLame::generateSettings()
58{
59 // no settings for now.
60 // show lame version.
61 if(m_symbolsResolved) {
62 insertSetting(eVOLUME, new EncTtsSetting(this, EncTtsSetting::eREADONLYSTRING,
63 tr("LAME"), QString(m_get_lame_short_version())));
64 }
65 else {
66 insertSetting(eVOLUME, new EncTtsSetting(this, EncTtsSetting::eREADONLYSTRING,
67 tr("LAME"), tr("Could not find libmp3lame!")));
68 }
69}
70
71void EncoderLame::saveSettings()
72{
73 // no user settings right now.
74}
75
76bool EncoderLame::start()
77{
78 if(!m_symbolsResolved) {
79 return false;
80 }
81 // try to get config from settings
82 return true;
83}
84
85bool EncoderLame::encode(QString input,QString output)
86{
87 qDebug() << "[EncoderLame] Encoding" << input;
88 if(!m_symbolsResolved) {
89 qDebug() << "[EncoderLame] Symbols not successfully resolved, cannot run!";
90 return false;
91 }
92
93 QFile fin(input);
94 QFile fout(output);
95 // initialize encoder
96 lame_global_flags *gfp;
97 unsigned char header[12];
98 unsigned char chunkheader[8];
99 unsigned int datalength = 0;
100 unsigned int channels = 0;
101 unsigned int samplerate = 0;
102 unsigned int samplesize = 0;
103 int num_samples = 0;
104 int ret;
105 unsigned char* mp3buf;
106 int mp3buflen;
107 short int* wavbuf;
108 int wavbuflen;
109
110
111 gfp = m_lame_init();
112 m_lame_set_out_samplerate(gfp, 12000); // resample to 12kHz
113 m_lame_set_scale(gfp, 1.0); // scale input volume
114 m_lame_set_mode(gfp, MONO); // mono output mode
115 m_lame_set_VBR(gfp, vbr_default); // enable default VBR mode
116 m_lame_set_VBR_quality(gfp, 9.999); // VBR quality
117 m_lame_set_VBR_max_bitrate_kbps(gfp, 64); // maximum bitrate 64kbps
118 m_lame_set_bWriteVbrTag(gfp, 0); // disable LAME tag.
119
120 fin.open(QIODevice::ReadOnly);
121
122 // read RIFF header
123 fin.read((char*)header, 12);
124 if(memcmp("RIFF", header, 4) != 0) {
125 qDebug() << "[EncoderLame] RIFF header not found!"
126 << header[0] << header[1] << header[2] << header[3];
127 fin.close();
128 return false;
129 }
130 if(memcmp("WAVE", &header[8], 4) != 0) {
131 qDebug() << "[EncoderLame] WAVE FOURCC not found!"
132 << header[8] << header[9] << header[10] << header[11];
133 fin.close();
134 return false;
135 }
136
137 // search for fmt chunk
138 do {
139 // read fmt
140 fin.read((char*)chunkheader, 8);
141 int chunkdatalen = chunkheader[4] | chunkheader[5]<<8
142 | chunkheader[6]<<16 | chunkheader[7]<<24;
143 if(memcmp("fmt ", chunkheader, 4) == 0) {
144 // fmt found, read rest of chunk.
145 // NOTE: This code ignores the format tag value.
146 // Ideally this should be checked as well. However, rbspeex doesn't
147 // check the format tag either when reading wave files, so if
148 // problems arise we should notice pretty soon. Furthermore, the
149 // input format used should be known. In case some TTS uses a
150 // different wave encoding some time this needs to get adjusted.
151 if(chunkdatalen < 16) {
152 qDebug() << "fmt chunk too small!";
153 }
154 else {
155 unsigned char *buf = new unsigned char[chunkdatalen];
156 fin.read((char*)buf, chunkdatalen);
157 channels = buf[2] | buf[3]<<8;
158 samplerate = buf[4] | buf[5]<<8 | buf[6]<<16 | buf[7]<<24;
159 samplesize = buf[14] | buf[15]<<8;
160 delete buf;
161 }
162 }
163 // read data
164 else if(memcmp("data", chunkheader, 4) == 0) {
165 datalength = chunkdatalen;
166 break;
167 }
168 else {
169 // unknown chunk, just skip its data.
170 qDebug() << "[EncoderLame] unknown chunk, skipping."
171 << chunkheader[0] << chunkheader[1]
172 << chunkheader[2] << chunkheader[3];
173 fin.seek(fin.pos() + chunkdatalen);
174 }
175 } while(!fin.atEnd());
176
177 // check format
178 if(channels == 0 || samplerate == 0 || samplesize == 0 || datalength == 0) {
179 qDebug() << "[EncoderLame] invalid format. Channels:" << channels
180 << "Samplerate:" << samplerate << "Samplesize:" << samplesize
181 << "Data chunk length:" << datalength;
182 fin.close();
183 return false;
184 }
185 num_samples = (datalength / channels / (samplesize/8));
186
187 // set input format values
188 m_lame_set_in_samplerate(gfp, samplerate);
189 m_lame_set_num_channels(gfp, channels);
190
191 // initialize encoder.
192 ret = m_lame_init_params(gfp);
193 if(ret != 0) {
194 qDebug() << "[EncoderLame] lame_init_params() failed with" << ret;
195 fin.close();
196 return false;
197 }
198
199 // we're dealing with rather small files here (100kB-ish), so don't care
200 // about the possible output size and simply allocate the same number of
201 // bytes the input file has. This wastes space but should be ok.
202 // Put an upper limit of 8MiB.
203 if(datalength > 8*1024*1024) {
204 qDebug() << "[EncoderLame] Input file too large:" << datalength;
205 fin.close();
206 return false;
207 }
208 mp3buflen = datalength;
209 wavbuflen = datalength;
210 mp3buf = new unsigned char[mp3buflen];
211 wavbuf = new short int[wavbuflen];
212#if defined(Q_OS_MACX)
213 // handle byte order -- the host might not be LE.
214 if(samplesize == 8) {
215 // no need to convert.
216 fin.read((char*)wavbuf, wavbuflen);
217 }
218 else if(samplesize == 16) {
219 // read LE 16bit words. Since the input format is either mono or
220 // interleaved there's no need to care for that.
221 unsigned int pos = 0;
222 char word[2];
223 while(pos < datalength) {
224 fin.read(word, 2);
225 wavbuf[pos++] = (word[0]&0xff) | ((word[1]<<8)&0xff00);
226 }
227 }
228 else {
229 qDebug() << "[EncoderLame] Unknown samplesize:" << samplesize;
230 fin.close();
231 delete mp3buf;
232 delete wavbuf;
233 return false;
234 }
235#else
236 // all systems but OS X are considered LE.
237 fin.read((char*)wavbuf, wavbuflen);
238#endif
239 fin.close();
240 // encode data.
241 fout.open(QIODevice::ReadWrite);
242 ret = m_lame_encode_buffer(gfp, wavbuf, wavbuf, num_samples, mp3buf, mp3buflen);
243 if(ret < 0) {
244 qDebug() << "[EncoderLame] Error during encoding:" << ret;
245 }
246 if(fout.write((char*)mp3buf, ret) != (unsigned int)ret) {
247 qDebug() << "[EncoderLame] Writing mp3 data failed!" << ret;
248 fout.close();
249 delete mp3buf;
250 delete wavbuf;
251 return false;
252 }
253 // flush remaining data
254 ret = m_lame_encode_flush(gfp, mp3buf, mp3buflen);
255 if(fout.write((char*)mp3buf, ret) != (unsigned int)ret) {
256 qDebug() << "[EncoderLame] Writing final mp3 data failed!";
257 fout.close();
258 delete mp3buf;
259 delete wavbuf;
260 return false;
261 }
262 // shut down encoder and clean up.
263 m_lame_close(gfp);
264 fout.close();
265 delete mp3buf;
266 delete wavbuf;
267
268 return true;
269}
270
271/** Check if the current configuration is usable.
272 * Since we're loading a library dynamically in the constructor test if that
273 * succeeded. Otherwise the "configuration" is not usable, even though the
274 * problem is not necessarily related to configuration values set by the user.
275 */
276bool EncoderLame::configOk()
277{
278 return (lib->isLoaded() && m_symbolsResolved);
279}
280
diff --git a/rbutil/rbutilqt/base/encoderlame.h b/rbutil/rbutilqt/base/encoderlame.h
new file mode 100644
index 0000000000..9f87188d9b
--- /dev/null
+++ b/rbutil/rbutilqt/base/encoderlame.h
@@ -0,0 +1,71 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 *
9 * Copyright (C) 2012 Dominik Riebeling
10 *
11 * All files in this archive are subject to the GNU General Public License.
12 * See the file COPYING in the source tree root for full license agreement.
13 *
14 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
15 * KIND, either express or implied.
16 *
17 ****************************************************************************/
18
19#ifndef ENCODERLAME_H
20#define ENCODERLAME_H
21
22#include <QtCore>
23#include "encoderbase.h"
24#include "lame/lame.h"
25
26class EncoderLame : public EncoderBase
27{
28 enum ESettings
29 {
30 eVOLUME,
31 eQUALITY,
32 eCOMPLEXITY,
33 eNARROWBAND
34 };
35
36 Q_OBJECT
37 public:
38 EncoderLame(QObject *parent = NULL);
39 bool encode(QString input,QString output);
40 bool start();
41 bool stop() {return true;}
42
43 // for settings view
44 bool configOk();
45 void generateSettings();
46 void saveSettings();
47
48 private:
49 QLibrary *lib;
50 const char*(*m_get_lame_short_version)(void);
51 int (*m_lame_set_out_samplerate)(lame_global_flags*, int);
52 int (*m_lame_set_in_samplerate)(lame_global_flags*, int);
53 int (*m_lame_set_num_channels)(lame_global_flags*, int);
54 int (*m_lame_set_scale)(lame_global_flags*, float);
55 int (*m_lame_set_mode)(lame_global_flags*, MPEG_mode);
56 int (*m_lame_set_VBR)(lame_global_flags*, vbr_mode);
57 int (*m_lame_set_VBR_quality)(lame_global_flags*, float);
58 int (*m_lame_set_VBR_max_bitrate_kbps)(lame_global_flags*, int);
59 int (*m_lame_set_bWriteVbrTag)(lame_global_flags*, int);
60 lame_global_flags*(*m_lame_init)(void);
61 int (*m_lame_init_params)(lame_global_flags*);
62 int (*m_lame_encode_buffer)(lame_global_flags*, short int[], short
63 int[], int, unsigned char*, int);
64 int (*m_lame_encode_flush)(lame_global_flags*, unsigned char*, int);
65 int (*m_lame_close)(lame_global_flags*);
66
67 bool m_symbolsResolved;
68};
69
70#endif
71
diff --git a/rbutil/rbutilqt/base/voicefile.cpp b/rbutil/rbutilqt/base/voicefile.cpp
index ba23bfea89..ebabf9e8f6 100644
--- a/rbutil/rbutilqt/base/voicefile.cpp
+++ b/rbutil/rbutilqt/base/voicefile.cpp
@@ -254,7 +254,7 @@ void VoiceFileCreator::cleanup()
254 QCoreApplication::processEvents(); 254 QCoreApplication::processEvents();
255 } 255 }
256 emit logItem(tr("Finished"),LOGINFO); 256 emit logItem(tr("Finished"),LOGINFO);
257 257
258 return; 258 return;
259} 259}
260 260