1 /***************************************************************************
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
9 * Copyright (C) 2012 Dominik Riebeling
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.
14 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
15 * KIND, either express or implied.
17 ****************************************************************************/
20 #include "encoderlame.h"
21 #include "rbsettings.h"
22 #include "lame/lame.h"
24 /** Resolve a symbol from loaded library.
26 #define SYMBOLRESOLVE(symbol, type) \
27 do { m_##symbol = (type)lib->resolve(#symbol); \
28 if(!m_##symbol) return; \
29 qDebug() << "[EncoderLame] Resolved symbol " #symbol; } \
32 EncoderLame::EncoderLame(QObject
*parent
) : EncoderBase(parent
)
34 m_symbolsResolved
= false;
35 lib
= new QLibrary("libmp3lame", this);
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
*));
53 qDebug() << "[EncoderLame] libmp3lame loaded:" << lib
->isLoaded();
54 m_symbolsResolved
= true;
57 void EncoderLame::generateSettings()
59 // no settings for now.
61 if(m_symbolsResolved
) {
62 double quality
= RbSettings::subValue("lame",
63 RbSettings::EncoderQuality
).toDouble();
64 // default quality is 0.999.
68 insertSetting(LAMEVERSION
, new EncTtsSetting(this, EncTtsSetting::eREADONLYSTRING
,
69 tr("LAME"), QString(m_get_lame_short_version())));
70 insertSetting(VOLUME
, new EncTtsSetting(this, EncTtsSetting::eDOUBLE
,
72 RbSettings::subValue("lame", RbSettings::EncoderVolume
).toDouble(),
74 insertSetting(QUALITY
, new EncTtsSetting(this, EncTtsSetting::eDOUBLE
,
75 tr("Quality"), quality
, 0.0, 1.0));
78 insertSetting(LAMEVERSION
, new EncTtsSetting(this, EncTtsSetting::eREADONLYSTRING
,
79 tr("LAME"), tr("Could not find libmp3lame!")));
83 void EncoderLame::saveSettings()
85 if(m_symbolsResolved
) {
86 RbSettings::setSubValue("lame", RbSettings::EncoderVolume
,
87 getSetting(VOLUME
)->current().toDouble());
88 RbSettings::setSubValue("lame", RbSettings::EncoderQuality
,
89 getSetting(QUALITY
)->current().toDouble());
93 bool EncoderLame::start()
95 if(!m_symbolsResolved
) {
98 // try to get config from settings
102 bool EncoderLame::encode(QString input
,QString output
)
104 qDebug() << "[EncoderLame] Encoding" << QDir::cleanPath(input
);
105 if(!m_symbolsResolved
) {
106 qDebug() << "[EncoderLame] Symbols not successfully resolved, cannot run!";
112 // initialize encoder
113 lame_global_flags
*gfp
;
114 unsigned char header
[12];
115 unsigned char chunkheader
[8];
116 unsigned int datalength
= 0;
117 unsigned int channels
= 0;
118 unsigned int samplerate
= 0;
119 unsigned int samplesize
= 0;
122 unsigned char* mp3buf
;
129 m_lame_set_out_samplerate(gfp
, 12000); // resample to 12kHz
130 // scale input volume
131 m_lame_set_scale(gfp
,
132 RbSettings::subValue("lame", RbSettings::EncoderVolume
).toDouble());
133 m_lame_set_mode(gfp
, MONO
); // mono output mode
134 m_lame_set_VBR(gfp
, vbr_default
); // enable default VBR mode
136 m_lame_set_VBR_quality(gfp
,
137 RbSettings::subValue("lame", RbSettings::EncoderQuality
).toDouble());
138 m_lame_set_VBR_max_bitrate_kbps(gfp
, 64); // maximum bitrate 64kbps
139 m_lame_set_bWriteVbrTag(gfp
, 0); // disable LAME tag.
141 if(!fin
.open(QIODevice::ReadOnly
)) {
142 qDebug() << "[EncoderLame] Could not open input file" << input
;
147 fin
.read((char*)header
, 12);
148 if(memcmp("RIFF", header
, 4) != 0) {
149 qDebug() << "[EncoderLame] RIFF header not found!"
150 << header
[0] << header
[1] << header
[2] << header
[3];
154 if(memcmp("WAVE", &header
[8], 4) != 0) {
155 qDebug() << "[EncoderLame] WAVE FOURCC not found!"
156 << header
[8] << header
[9] << header
[10] << header
[11];
161 // search for fmt chunk
164 fin
.read((char*)chunkheader
, 8);
165 int chunkdatalen
= chunkheader
[4] | chunkheader
[5]<<8
166 | chunkheader
[6]<<16 | chunkheader
[7]<<24;
167 if(memcmp("fmt ", chunkheader
, 4) == 0) {
168 // fmt found, read rest of chunk.
169 // NOTE: This code ignores the format tag value.
170 // Ideally this should be checked as well. However, rbspeex doesn't
171 // check the format tag either when reading wave files, so if
172 // problems arise we should notice pretty soon. Furthermore, the
173 // input format used should be known. In case some TTS uses a
174 // different wave encoding some time this needs to get adjusted.
175 if(chunkdatalen
< 16) {
176 qDebug() << "fmt chunk too small!";
179 unsigned char *buf
= new unsigned char[chunkdatalen
];
180 fin
.read((char*)buf
, chunkdatalen
);
181 channels
= buf
[2] | buf
[3]<<8;
182 samplerate
= buf
[4] | buf
[5]<<8 | buf
[6]<<16 | buf
[7]<<24;
183 samplesize
= buf
[14] | buf
[15]<<8;
188 else if(memcmp("data", chunkheader
, 4) == 0) {
189 datalength
= chunkdatalen
;
193 // unknown chunk, just skip its data.
194 qDebug() << "[EncoderLame] unknown chunk, skipping."
195 << chunkheader
[0] << chunkheader
[1]
196 << chunkheader
[2] << chunkheader
[3];
197 fin
.seek(fin
.pos() + chunkdatalen
);
199 } while(!fin
.atEnd());
202 if(channels
== 0 || samplerate
== 0 || samplesize
== 0 || datalength
== 0) {
203 qDebug() << "[EncoderLame] invalid format. Channels:" << channels
204 << "Samplerate:" << samplerate
<< "Samplesize:" << samplesize
205 << "Data chunk length:" << datalength
;
209 num_samples
= (datalength
/ channels
/ (samplesize
/8));
211 // set input format values
212 m_lame_set_in_samplerate(gfp
, samplerate
);
213 m_lame_set_num_channels(gfp
, channels
);
215 // initialize encoder.
216 ret
= m_lame_init_params(gfp
);
218 qDebug() << "[EncoderLame] lame_init_params() failed with" << ret
;
223 // we're dealing with rather small files here (100kB-ish), so don't care
224 // about the possible output size and simply allocate the same number of
225 // bytes the input file has. This wastes space but should be ok.
226 // Put an upper limit of 8MiB.
227 if(datalength
> 8*1024*1024) {
228 qDebug() << "[EncoderLame] Input file too large:" << datalength
;
232 mp3buflen
= datalength
;
233 wavbuflen
= datalength
;
234 mp3buf
= new unsigned char[mp3buflen
];
235 wavbuf
= new short int[wavbuflen
];
236 #if defined(Q_OS_MACX)
237 // handle byte order -- the host might not be LE.
238 if(samplesize
== 8) {
239 // no need to convert.
240 fin
.read((char*)wavbuf
, wavbuflen
);
242 else if(samplesize
== 16) {
243 // read LE 16bit words. Since the input format is either mono or
244 // interleaved there's no need to care for that.
245 unsigned int pos
= 0;
247 while(pos
< datalength
) {
249 wavbuf
[pos
++] = (word
[0]&0xff) | ((word
[1]<<8)&0xff00);
253 qDebug() << "[EncoderLame] Unknown samplesize:" << samplesize
;
260 // all systems but OS X are considered LE.
261 fin
.read((char*)wavbuf
, wavbuflen
);
265 fout
.open(QIODevice::ReadWrite
);
266 ret
= m_lame_encode_buffer(gfp
, wavbuf
, wavbuf
, num_samples
, mp3buf
, mp3buflen
);
268 qDebug() << "[EncoderLame] Error during encoding:" << ret
;
270 if(fout
.write((char*)mp3buf
, ret
) != (unsigned int)ret
) {
271 qDebug() << "[EncoderLame] Writing mp3 data failed!" << ret
;
277 // flush remaining data
278 ret
= m_lame_encode_flush(gfp
, mp3buf
, mp3buflen
);
279 if(fout
.write((char*)mp3buf
, ret
) != (unsigned int)ret
) {
280 qDebug() << "[EncoderLame] Writing final mp3 data failed!";
286 // shut down encoder and clean up.
295 /** Check if the current configuration is usable.
296 * Since we're loading a library dynamically in the constructor test if that
297 * succeeded. Otherwise the "configuration" is not usable, even though the
298 * problem is not necessarily related to configuration values set by the user.
300 bool EncoderLame::configOk()
302 return (lib
->isLoaded() && m_symbolsResolved
);