forked from len0rd/rockbox
rbutil: Merge rbutil with utils folder.
rbutil uses several components from the utils folder, and can be considered part of utils too. Having it in a separate folder is an arbitrary split that doesn't help anymore these days, so merge them. This also allows other utils to easily use libtools.make without the need to navigate to a different folder. Change-Id: I3fc2f4de19e3e776553efb5dea5f779dfec0dc21
This commit is contained in:
parent
6c6f0757d7
commit
c876d3bbef
494 changed files with 13 additions and 13 deletions
312
utils/rbutilqt/base/encoderlame.cpp
Normal file
312
utils/rbutilqt/base/encoderlame.cpp
Normal file
|
|
@ -0,0 +1,312 @@
|
|||
/***************************************************************************
|
||||
* __________ __ ___.
|
||||
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
|
||||
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
|
||||
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
|
||||
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
|
||||
* \/ \/ \/ \/ \/
|
||||
*
|
||||
* Copyright (C) 2012 Dominik Riebeling
|
||||
*
|
||||
* All files in this archive are subject to the GNU General Public License.
|
||||
* See the file COPYING in the source tree root for full license agreement.
|
||||
*
|
||||
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
||||
* KIND, either express or implied.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
#include <QtCore>
|
||||
#include "encoderlame.h"
|
||||
#include "rbsettings.h"
|
||||
#include "lame/lame.h"
|
||||
#include "Logger.h"
|
||||
|
||||
/** Resolve a symbol from loaded library.
|
||||
*/
|
||||
#define SYMBOLRESOLVE(symbol, type) \
|
||||
do { m_##symbol = (type)lib.resolve(#symbol); \
|
||||
if(!m_##symbol) return; \
|
||||
LOG_INFO() << "Resolved symbol " #symbol; } \
|
||||
while(0)
|
||||
|
||||
EncoderLame::EncoderLame(QObject *parent) : EncoderBase(parent),
|
||||
lib("libmp3lame", this), m_symbolsResolved(false)
|
||||
{
|
||||
lib.load();
|
||||
if (!lib.isLoaded()) {
|
||||
LOG_WARNING() << "Loading mp3lame lib failed:" << lib.errorString();
|
||||
return;
|
||||
}
|
||||
|
||||
SYMBOLRESOLVE(get_lame_short_version, const char* (*)());
|
||||
SYMBOLRESOLVE(lame_set_out_samplerate, int (*)(lame_global_flags*, int));
|
||||
SYMBOLRESOLVE(lame_set_in_samplerate, int (*)(lame_global_flags*, int));
|
||||
SYMBOLRESOLVE(lame_set_num_channels, int (*)(lame_global_flags*, int));
|
||||
SYMBOLRESOLVE(lame_set_scale, int (*)(lame_global_flags*, float));
|
||||
SYMBOLRESOLVE(lame_set_mode, int (*)(lame_global_flags*, MPEG_mode));
|
||||
SYMBOLRESOLVE(lame_set_VBR, int (*)(lame_global_flags*, vbr_mode));
|
||||
SYMBOLRESOLVE(lame_set_VBR_quality, int (*)(lame_global_flags*, float));
|
||||
SYMBOLRESOLVE(lame_set_VBR_max_bitrate_kbps, int (*)(lame_global_flags*, int));
|
||||
SYMBOLRESOLVE(lame_set_bWriteVbrTag, int (*)(lame_global_flags*, int));
|
||||
SYMBOLRESOLVE(lame_init, lame_global_flags* (*)());
|
||||
SYMBOLRESOLVE(lame_init_params, int (*)(lame_global_flags*));
|
||||
SYMBOLRESOLVE(lame_encode_buffer, int (*)(lame_global_flags*, short int*, short int*, int, unsigned char*, int));
|
||||
SYMBOLRESOLVE(lame_encode_flush, int (*)(lame_global_flags*, unsigned char*, int));
|
||||
SYMBOLRESOLVE(lame_close, int (*)(lame_global_flags*));
|
||||
|
||||
m_encoderVolume = RbSettings::subValue("lame", RbSettings::EncoderVolume).toDouble();
|
||||
m_encoderQuality = RbSettings::subValue("lame", RbSettings::EncoderQuality).toDouble();
|
||||
m_symbolsResolved = true;
|
||||
}
|
||||
|
||||
void EncoderLame::generateSettings()
|
||||
{
|
||||
// no settings for now.
|
||||
// show lame version.
|
||||
if(m_symbolsResolved) {
|
||||
double quality = RbSettings::subValue("lame",
|
||||
RbSettings::EncoderQuality).toDouble();
|
||||
// default quality is 0.999.
|
||||
if(quality < 0) {
|
||||
quality = 0.99;
|
||||
}
|
||||
insertSetting(LAMEVERSION, new EncTtsSetting(this, EncTtsSetting::eREADONLYSTRING,
|
||||
tr("LAME"), QString(m_get_lame_short_version())));
|
||||
insertSetting(VOLUME, new EncTtsSetting(this, EncTtsSetting::eDOUBLE,
|
||||
tr("Volume"),
|
||||
RbSettings::subValue("lame", RbSettings::EncoderVolume).toDouble(),
|
||||
0.0, 2.0));
|
||||
insertSetting(QUALITY, new EncTtsSetting(this, EncTtsSetting::eDOUBLE,
|
||||
tr("Quality"), quality, 0.0, 1.0));
|
||||
}
|
||||
else {
|
||||
insertSetting(LAMEVERSION, new EncTtsSetting(this, EncTtsSetting::eREADONLYSTRING,
|
||||
tr("LAME"), tr("Could not find libmp3lame!")));
|
||||
}
|
||||
}
|
||||
|
||||
void EncoderLame::saveSettings()
|
||||
{
|
||||
if(m_symbolsResolved) {
|
||||
RbSettings::setSubValue("lame", RbSettings::EncoderVolume,
|
||||
getSetting(VOLUME)->current().toDouble());
|
||||
RbSettings::setSubValue("lame", RbSettings::EncoderQuality,
|
||||
getSetting(QUALITY)->current().toDouble());
|
||||
m_encoderVolume =
|
||||
RbSettings::subValue("lame", RbSettings::EncoderVolume).toDouble();
|
||||
m_encoderQuality =
|
||||
RbSettings::subValue("lame", RbSettings::EncoderQuality).toDouble();
|
||||
}
|
||||
}
|
||||
|
||||
bool EncoderLame::start()
|
||||
{
|
||||
if(!m_symbolsResolved) {
|
||||
return false;
|
||||
}
|
||||
// try to get config from settings
|
||||
return true;
|
||||
}
|
||||
|
||||
bool EncoderLame::encode(QString input,QString output)
|
||||
{
|
||||
LOG_INFO() << "Encoding" << QDir::cleanPath(input);
|
||||
if(!m_symbolsResolved) {
|
||||
LOG_ERROR() << "Symbols not successfully resolved, cannot run!";
|
||||
return false;
|
||||
}
|
||||
|
||||
QFile fin(input);
|
||||
QFile fout(output);
|
||||
// initialize encoder
|
||||
lame_global_flags *gfp;
|
||||
unsigned char header[12];
|
||||
unsigned char chunkheader[8];
|
||||
unsigned int datalength = 0;
|
||||
unsigned int channels = 0;
|
||||
unsigned int samplerate = 0;
|
||||
unsigned int samplesize = 0;
|
||||
int num_samples = 0;
|
||||
int ret;
|
||||
unsigned char* mp3buf;
|
||||
int mp3buflen;
|
||||
short int* wavbuf;
|
||||
int wavbuflen;
|
||||
|
||||
|
||||
gfp = m_lame_init();
|
||||
m_lame_set_out_samplerate(gfp, 12000); // resample to 12kHz
|
||||
// scale input volume
|
||||
m_lame_set_scale(gfp, m_encoderVolume);
|
||||
m_lame_set_mode(gfp, MONO); // mono output mode
|
||||
m_lame_set_VBR(gfp, vbr_default); // enable default VBR mode
|
||||
// VBR quality
|
||||
m_lame_set_VBR_quality(gfp, m_encoderQuality);
|
||||
m_lame_set_VBR_max_bitrate_kbps(gfp, 64); // maximum bitrate 64kbps
|
||||
m_lame_set_bWriteVbrTag(gfp, 0); // disable LAME tag.
|
||||
|
||||
if(!fin.open(QIODevice::ReadOnly)) {
|
||||
LOG_ERROR() << "Could not open input file" << input;
|
||||
return false;
|
||||
}
|
||||
|
||||
// read RIFF header
|
||||
fin.read((char*)header, 12);
|
||||
if(memcmp("RIFF", header, 4) != 0) {
|
||||
LOG_ERROR() << "RIFF header not found!"
|
||||
<< header[0] << header[1] << header[2] << header[3];
|
||||
fin.close();
|
||||
return false;
|
||||
}
|
||||
if(memcmp("WAVE", &header[8], 4) != 0) {
|
||||
LOG_ERROR() << "WAVE FOURCC not found!"
|
||||
<< header[8] << header[9] << header[10] << header[11];
|
||||
fin.close();
|
||||
return false;
|
||||
}
|
||||
|
||||
// search for fmt chunk
|
||||
do {
|
||||
// read fmt
|
||||
fin.read((char*)chunkheader, 8);
|
||||
int chunkdatalen = chunkheader[4] | chunkheader[5]<<8
|
||||
| chunkheader[6]<<16 | chunkheader[7]<<24;
|
||||
if(memcmp("fmt ", chunkheader, 4) == 0) {
|
||||
// fmt found, read rest of chunk.
|
||||
// NOTE: This code ignores the format tag value.
|
||||
// Ideally this should be checked as well. However, rbspeex doesn't
|
||||
// check the format tag either when reading wave files, so if
|
||||
// problems arise we should notice pretty soon. Furthermore, the
|
||||
// input format used should be known. In case some TTS uses a
|
||||
// different wave encoding some time this needs to get adjusted.
|
||||
if(chunkdatalen < 16) {
|
||||
LOG_ERROR() << "fmt chunk too small!";
|
||||
}
|
||||
else {
|
||||
unsigned char *buf = new unsigned char[chunkdatalen];
|
||||
fin.read((char*)buf, chunkdatalen);
|
||||
channels = buf[2] | buf[3]<<8;
|
||||
samplerate = buf[4] | buf[5]<<8 | buf[6]<<16 | buf[7]<<24;
|
||||
samplesize = buf[14] | buf[15]<<8;
|
||||
delete[] buf;
|
||||
}
|
||||
}
|
||||
// read data
|
||||
else if(memcmp("data", chunkheader, 4) == 0) {
|
||||
datalength = chunkdatalen;
|
||||
break;
|
||||
}
|
||||
else {
|
||||
// unknown chunk, just skip its data.
|
||||
LOG_WARNING() << "unknown chunk, skipping."
|
||||
<< chunkheader[0] << chunkheader[1]
|
||||
<< chunkheader[2] << chunkheader[3];
|
||||
fin.seek(fin.pos() + chunkdatalen);
|
||||
}
|
||||
} while(!fin.atEnd());
|
||||
|
||||
// check format
|
||||
if(channels == 0 || samplerate == 0 || samplesize == 0 || datalength == 0) {
|
||||
LOG_ERROR() << "invalid format. Channels:" << channels
|
||||
<< "Samplerate:" << samplerate << "Samplesize:" << samplesize
|
||||
<< "Data chunk length:" << datalength;
|
||||
fin.close();
|
||||
return false;
|
||||
}
|
||||
num_samples = (datalength / channels / (samplesize/8));
|
||||
|
||||
// set input format values
|
||||
m_lame_set_in_samplerate(gfp, samplerate);
|
||||
m_lame_set_num_channels(gfp, channels);
|
||||
|
||||
// initialize encoder.
|
||||
ret = m_lame_init_params(gfp);
|
||||
if(ret != 0) {
|
||||
LOG_ERROR() << "lame_init_params() failed with" << ret;
|
||||
fin.close();
|
||||
return false;
|
||||
}
|
||||
|
||||
// we're dealing with rather small files here (100kB-ish), so don't care
|
||||
// about the possible output size and simply allocate the same number of
|
||||
// bytes the input file has. This wastes space but should be ok.
|
||||
// Put an upper limit of 8MiB.
|
||||
if(datalength > 8*1024*1024) {
|
||||
LOG_ERROR() << "Input file too large:" << datalength;
|
||||
fin.close();
|
||||
return false;
|
||||
}
|
||||
mp3buflen = datalength;
|
||||
wavbuflen = datalength;
|
||||
mp3buf = new unsigned char[mp3buflen];
|
||||
wavbuf = new short int[wavbuflen];
|
||||
#if defined(Q_OS_MACX)
|
||||
// handle byte order -- the host might not be LE.
|
||||
if(samplesize == 8) {
|
||||
// no need to convert.
|
||||
fin.read((char*)wavbuf, wavbuflen);
|
||||
}
|
||||
else if(samplesize == 16) {
|
||||
// read LE 16bit words. Since the input format is either mono or
|
||||
// interleaved there's no need to care for that.
|
||||
unsigned int pos = 0;
|
||||
char word[2];
|
||||
while(pos < datalength) {
|
||||
fin.read(word, 2);
|
||||
wavbuf[pos++] = (word[0]&0xff) | ((word[1]<<8)&0xff00);
|
||||
}
|
||||
}
|
||||
else {
|
||||
LOG_ERROR() << "Unknown samplesize:" << samplesize;
|
||||
fin.close();
|
||||
delete[] mp3buf;
|
||||
delete[] wavbuf;
|
||||
return false;
|
||||
}
|
||||
#else
|
||||
// all systems but OS X are considered LE.
|
||||
fin.read((char*)wavbuf, wavbuflen);
|
||||
#endif
|
||||
fin.close();
|
||||
// encode data.
|
||||
fout.open(QIODevice::ReadWrite);
|
||||
ret = m_lame_encode_buffer(gfp, wavbuf, wavbuf, num_samples, mp3buf, mp3buflen);
|
||||
if(ret < 0) {
|
||||
LOG_ERROR() << "Error during encoding:" << ret;
|
||||
}
|
||||
if(fout.write((char*)mp3buf, ret) != (unsigned int)ret) {
|
||||
LOG_ERROR() << "Writing mp3 data failed!" << ret;
|
||||
fout.close();
|
||||
delete[] mp3buf;
|
||||
delete[] wavbuf;
|
||||
return false;
|
||||
}
|
||||
// flush remaining data
|
||||
ret = m_lame_encode_flush(gfp, mp3buf, mp3buflen);
|
||||
if(fout.write((char*)mp3buf, ret) != (unsigned int)ret) {
|
||||
LOG_ERROR() << "Writing final mp3 data failed!";
|
||||
fout.close();
|
||||
delete[] mp3buf;
|
||||
delete[] wavbuf;
|
||||
return false;
|
||||
}
|
||||
// shut down encoder and clean up.
|
||||
m_lame_close(gfp);
|
||||
fout.close();
|
||||
delete[] mp3buf;
|
||||
delete[] wavbuf;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/** Check if the current configuration is usable.
|
||||
* Since we're loading a library dynamically in the constructor test if that
|
||||
* succeeded. Otherwise the "configuration" is not usable, even though the
|
||||
* problem is not necessarily related to configuration values set by the user.
|
||||
*/
|
||||
bool EncoderLame::configOk()
|
||||
{
|
||||
return m_symbolsResolved;
|
||||
}
|
||||
|
||||
Loading…
Add table
Add a link
Reference in a new issue