Mixxx

/home/maxime/Projets/Mixxx/1.10/mixxx/src/recording/encodermp3.cpp

Go to the documentation of this file.
00001 /****************************************************************************
00002                    encodermp3.cpp  - mp3 encoder for mixxx
00003                              -------------------
00004     copyright            : (C) 2007 by Wesley Stessens
00005                            (C) 2009 by Phillip Whelan (rewritten for mp3)
00006                            (C) 2010 by Tobias Rafreider (fixes for shoutcast, dynamic loading of lame_enc.dll, etc)
00007  ***************************************************************************/
00008 
00009 /***************************************************************************
00010  *                                                                         *
00011  *   This program is free software; you can redistribute it and/or modify  *
00012  *   it under the terms of the GNU General Public License as published by  *
00013  *   the Free Software Foundation; either version 2 of the License, or     *
00014  *   (at your option) any later version.                                   *
00015  *                                                                         *
00016  ***************************************************************************/
00017 
00018 #include <stdlib.h> // needed for random num
00019 #include <time.h> // needed for random num
00020 #include <string.h> // needed for memcpy
00021 #include <QDebug>
00022 
00023 #include "recording/encodermp3.h"
00024 #include "engine/engineabstractrecord.h"
00025 #include "controlobjectthreadmain.h"
00026 #include "controlobject.h"
00027 #include "playerinfo.h"
00028 #include "trackinfoobject.h"
00029 #include "defs_recording.h"
00030 #include "errordialoghandler.h"
00031 
00032 EncoderMp3::EncoderMp3(EngineAbstractRecord *engine) {
00033     m_pEngine = engine;
00034     m_metaDataTitle = NULL;
00035     m_metaDataArtist = NULL;
00036     m_metaDataAlbum = NULL;
00037     m_pMetaData = TrackPointer(NULL);
00038     m_bufferIn[0] = NULL;
00039     m_bufferIn[1] = NULL;
00040     m_bufferOut = NULL;
00041     m_bufferOutSize = 0;
00042     m_lameFlags = NULL;
00043     m_library = NULL;
00044     m_samplerate = NULL;
00045 
00046     //These are the function pointers for lame
00047     lame_init =  0;
00048     lame_set_num_channels = 0;
00049     lame_set_in_samplerate =  0;
00050     lame_set_out_samplerate = 0;
00051     lame_close = 0;
00052     lame_set_brate = 0;
00053     lame_set_mode = 0;
00054     lame_set_quality = 0;
00055     lame_set_bWriteVbrTag = 0;
00056     lame_encode_buffer_float = 0;
00057     lame_init_params = 0;
00058     lame_encode_flush = 0;
00059 
00060     id3tag_init= 0;
00061     id3tag_set_title = 0;
00062     id3tag_set_artist = 0;
00063     id3tag_set_album = 0;
00064 
00065     /*
00066      * @ Author: Tobias Rafreider
00067      * Nobody has initialized the field before my code review.  At runtime the
00068      * Integer field was inialized by a large random value such that the
00069      * following pointer fields were never initialized in the methods
00070      * 'bufferOutGrow()' and 'bufferInGrow()' --> Valgrind shows invalid writes
00071      * :-)
00072      *
00073      * m_bufferOut = (unsigned char *)realloc(m_bufferOut, size);
00074      * m_bufferIn[0] = (float *)realloc(m_bufferIn[0], size * sizeof(float));
00075      * m_bufferIn[1] = (float *)realloc(m_bufferIn[1], size * sizeof(float));
00076      *
00077      * This has solved many segfaults when using and even closing shoutcast
00078      * along with LAME.  This bug was detected by using Valgrind memory analyser
00079      *
00080      */
00081     m_bufferInSize = 0;
00082 
00083     /*
00084      * Load shared library
00085      */
00086     QStringList libnames;
00087     QString libname = "";
00088 #ifdef __LINUX__
00089     libnames << "/usr/lib/libmp3lame.so.0";
00090     libnames << "/usr/lib/libmp3lame.so";
00091 #elif __WINDOWS__
00092     libnames << "lame_enc.dll";
00093 #elif __APPLE__
00094     libnames << "/usr/local/lib/libmp3lame.dylib";
00095     //Using MacPorts (former DarwinPorts) results in ...
00096     libnames << "/opt/local/lib/libmp3lame.dylib";
00097 #endif
00098 
00099     foreach (QString libname, libnames) {
00100         m_library = new QLibrary(libname);
00101         if (m_library->load())
00102             break;
00103         delete m_library;
00104         m_library = NULL;
00105     }
00106 
00107     if(!m_library || !m_library->isLoaded()) {
00108         ErrorDialogProperties* props = ErrorDialogHandler::instance()->newDialogProperties();
00109         props->setType(DLG_WARNING);
00110         props->setTitle(tr("Encoder"));
00111         QString key = "";
00112 #ifdef __LINUX__
00113         key = tr("<html>Mixxx cannot record or stream in MP3 without the MP3 encoder &quot;lame&quot;. Due to licensing issues, we cannot include this with Mixxx. To record or stream in MP3, you must download <b>libmp3lame</b> and install it on your system. <p>See <a href='http://mixxx.org/wiki/doku.php/internet_broadcasting#linux'>Mixxx Wiki</a> for more information. </html>");
00114         props->setText(key);
00115 #elif __WINDOWS__
00116         key = tr("<html>Mixxx cannot record or stream in MP3 without the MP3 encoder &quot;lame&quot;. Due to licensing issues, we cannot include this with Mixxx. To record or stream in MP3, you must download <b>lame_enc.dll</b> and install it on your system. <p>See <a href='http://mixxx.org/wiki/doku.php/internet_broadcasting#windows'>Mixxx Wiki</a> for more information. </html>");
00117         props->setText(key);
00118 #elif __APPLE__
00119         key = tr("<html>Mixxx cannot record or stream in MP3 without the MP3 encoder &quot;lame&quot;. Due to licensing issues, we cannot include this with Mixxx. To record or stream in MP3, you must download <b>libmp3lame</b> and install it on your system. <p>See <a href='http://mixxx.org/wiki/doku.php/internet_broadcasting#mac_osx'>Mixxx Wiki</a> for more information. </html>");
00120         props->setText(key);
00121 #endif
00122         props->setKey(key);
00123         ErrorDialogHandler::instance()->requestErrorDialog(props);
00124         return;
00125     }
00126 
00127     typedef const char* (*get_lame_version__)(void);
00128     get_lame_version__ get_lame_version = (get_lame_version__)m_library->resolve("get_lame_version");
00129 
00130 
00131     //initalize function pointers
00132     lame_init                                   = (lame_init__)m_library->resolve("lame_init");
00133     lame_set_num_channels               = (lame_set_num_channels__)m_library->resolve("lame_set_num_channels");
00134     lame_set_in_samplerate              = (lame_set_in_samplerate__)m_library->resolve("lame_set_in_samplerate");
00135     lame_set_out_samplerate             = (lame_set_out_samplerate__)m_library->resolve("lame_set_out_samplerate");
00136     lame_close                                  = (lame_close__)m_library->resolve("lame_close");
00137     lame_set_brate                              = (lame_set_brate__)m_library->resolve("lame_set_brate");
00138     lame_set_mode                               = (lame_set_mode__)m_library->resolve("lame_set_mode");
00139     lame_set_quality                    = (lame_set_quality__)m_library->resolve("lame_set_quality");
00140     lame_set_bWriteVbrTag               = (lame_set_bWriteVbrTag__)m_library->resolve("lame_set_bWriteVbrTag");
00141     lame_encode_buffer_float    = (lame_encode_buffer_float__)m_library->resolve("lame_encode_buffer_float");
00142     lame_init_params                    = (lame_init_params__)m_library->resolve("lame_init_params");
00143     lame_encode_flush                   = (lame_encode_flush__)m_library->resolve("lame_encode_flush");
00144 
00145     id3tag_init                                 = (id3tag_init__)m_library->resolve("id3tag_init");
00146     id3tag_set_title                    = (id3tag_set_title__)m_library->resolve("id3tag_set_title");
00147     id3tag_set_artist                   = (id3tag_set_artist__)m_library->resolve("id3tag_set_artist");
00148     id3tag_set_album                    = (id3tag_set_album__)m_library->resolve("id3tag_set_album");
00149 
00150 
00151           /*
00152      * Check if all function pointers are not NULL
00153      * Otherwise, the lame_enc.dll, libmp3lame.so or libmp3lame.mylib do not comply with the official header lame.h
00154      * Indicates a modified lame version
00155      *
00156      * Should not happend on Linux, but many lame binaries for Windows are modified.
00157      */
00158     if(!lame_init ||
00159        !lame_set_num_channels ||
00160        !lame_set_in_samplerate ||
00161        !lame_set_out_samplerate ||
00162        !lame_close ||
00163        !lame_set_brate ||
00164        !lame_set_mode ||
00165        !lame_set_quality ||
00166        !lame_set_bWriteVbrTag ||
00167        !lame_encode_buffer_float ||
00168        !lame_init_params ||
00169        !lame_encode_flush ||
00170        !get_lame_version ||
00171        !id3tag_init ||
00172        !id3tag_set_title ||
00173        !id3tag_set_artist ||
00174        !id3tag_set_album) {
00175         m_library->unload();
00176         m_library = NULL;
00177         //print qDebugs to detect which function pointers are null
00178         qDebug() << "lame_init: " << lame_init;
00179         qDebug() << "lame_set_num_channels: " << lame_set_num_channels;
00180         qDebug() << "lame_set_in_samplerate: " << lame_set_in_samplerate;
00181         qDebug() << "lame_set_out_samplerate: " << lame_set_out_samplerate;
00182         qDebug() << "lame_close: " << lame_close;
00183         qDebug() << "lame_set_brate " << lame_set_brate;
00184         qDebug() << "lame_set_mode: " << lame_set_mode;
00185         qDebug() << "lame_set_quality: " << lame_set_quality;
00186         qDebug() << "lame_set_bWriteVbrTag: " << lame_set_bWriteVbrTag;
00187         qDebug() << "lame_encode_buffer_float: " << lame_encode_buffer_float;
00188         qDebug() << "lame_init_params: " << lame_init_params;
00189         qDebug() << "lame_encode_flush: " << lame_encode_flush;
00190         qDebug() << "get_lame_version: " << get_lame_version;
00191         qDebug() << "id3tag_init: " << id3tag_init;
00192         qDebug() << "id3tag_set_title : " << id3tag_set_title ;
00193         qDebug() << "id3tag_set_artist: " << id3tag_set_artist;
00194         qDebug() << "id3tag_set_album  " << id3tag_set_album ;
00195 
00196         ErrorDialogProperties* props = ErrorDialogHandler::instance()->newDialogProperties();
00197         props->setType(DLG_WARNING);
00198         props->setTitle(tr("Encoder"));
00199         QString key = tr("<html>Mixxx has detected that you use a modified version of libmp3lame. See <a href='http://mixxx.org/wiki/doku.php/internet_broadcasting'>Mixxx Wiki</a> for more information.</html>");
00200         props->setText(key);
00201         props->setKey(key);
00202         ErrorDialogHandler::instance()->requestErrorDialog(props);
00203         return;
00204     }
00205     qDebug() << "Loaded libmp3lame version " << get_lame_version();
00206     m_samplerate = new ControlObjectThread(ControlObject::getControl(ConfigKey("[Master]", "samplerate")));
00207 }
00208 
00209 // Destructor
00210 EncoderMp3::~EncoderMp3() {
00211     if(m_library != NULL && m_library->isLoaded()){
00212         flush();
00213         lame_close(m_lameFlags);
00214         m_library->unload(); //unload dll, so, ...
00215         qDebug() << "Unloaded libmp3lame ";
00216         m_library = NULL;
00217     }
00218     //free requested buffers
00219     if(m_bufferIn[0] != NULL) delete m_bufferIn[0];
00220     if(m_bufferIn[1] != NULL) delete m_bufferIn[1];
00221     if(m_bufferOut != NULL) delete m_bufferOut;
00222 
00223     lame_init =  0;
00224     lame_set_num_channels = 0;
00225     lame_set_in_samplerate =  0;
00226     lame_set_out_samplerate = 0;
00227     lame_close = 0;
00228     lame_set_brate = 0;
00229     lame_set_mode = 0;
00230     lame_set_quality = 0;
00231     lame_set_bWriteVbrTag = 0;
00232     lame_encode_buffer_float = 0;
00233     lame_init_params = 0;
00234     lame_encode_flush = 0;
00235 
00236     id3tag_init= 0;
00237     id3tag_set_title = 0;
00238     id3tag_set_artist = 0;
00239     id3tag_set_album = 0;
00240     //Delete control object
00241     if(m_samplerate) delete  m_samplerate;
00242 }
00243 
00244 /*
00245  * Grow the outBuffer if needed.
00246  */
00247 
00248 int EncoderMp3::bufferOutGrow(int size) {
00249     if ( m_bufferOutSize >= size )
00250         return 0;
00251 
00252     m_bufferOut = (unsigned char *)realloc(m_bufferOut, size);
00253     if ( m_bufferOut == NULL )
00254         return -1;
00255 
00256     m_bufferOutSize = size;
00257     return 0;
00258 }
00259 
00260 /*
00261  * Grow the inBuffer(s) if needed.
00262  */
00263 
00264 int EncoderMp3::bufferInGrow(int size) {
00265     if ( m_bufferInSize >= size )
00266         return 0;
00267 
00268     m_bufferIn[0] = (float *)realloc(m_bufferIn[0], size * sizeof(float));
00269     m_bufferIn[1] = (float *)realloc(m_bufferIn[1], size * sizeof(float));
00270     if ((m_bufferIn[0] == NULL) || (m_bufferIn[1] == NULL))
00271         return -1;
00272 
00273     m_bufferInSize = size;
00274     return 0;
00275 }
00276 
00277 //Using this method requires to call method 'write()' or 'sendPackages()'
00278 //depending on which context you use the class (shoutcast or recording to HDD)
00279 void EncoderMp3::flush() {
00280         if(m_library == NULL || !m_library->isLoaded())
00281                 return;
00282     int rc = 0;
00284     rc = lame_encode_flush(m_lameFlags, m_bufferOut, m_bufferOutSize);
00285          if (rc < 0 ){
00286         return;
00287         }
00288         //end encoded audio to shoutcast or file
00289         m_pEngine->write(NULL, m_bufferOut, 0, rc);
00290 }
00291 
00292 void EncoderMp3::encodeBuffer(const CSAMPLE *samples, const int size) {
00293         if(m_library == NULL || !m_library->isLoaded())
00294                 return;
00295     int outsize = 0;
00296     int rc = 0;
00297     int i = 0;
00298 
00299     outsize = (int)((1.25 * size + 7200) + 1);
00300     bufferOutGrow(outsize);
00301 
00302     bufferInGrow(size);
00303 
00304     // Deinterleave samples
00305     for (i = 0; i < size/2; ++i)
00306     {
00307         m_bufferIn[0][i] = samples[i*2];
00308         m_bufferIn[1][i] = samples[i*2+1];
00309     }
00310 
00311     rc = lame_encode_buffer_float(m_lameFlags, m_bufferIn[0], m_bufferIn[1],
00312             size/2, m_bufferOut, m_bufferOutSize);
00313     if (rc < 0 ){
00314         return;
00315         }
00316         //write encoded audio to shoutcast stream or file
00317         m_pEngine->write(NULL, m_bufferOut, 0, rc);
00318 }
00319 
00320 void EncoderMp3::initStream() {
00321     m_bufferOutSize = (int)((1.25 * 20000 + 7200) + 1);
00322     m_bufferOut = (unsigned char *)malloc(m_bufferOutSize);
00323 
00324     m_bufferIn[0] = (float *)malloc(m_bufferOutSize * sizeof(float));
00325     m_bufferIn[1] = (float *)malloc(m_bufferOutSize * sizeof(float));
00326     return;
00327 }
00328 
00329 int EncoderMp3::initEncoder(int bitrate) {
00330     if(m_library == NULL || !m_library->isLoaded())
00331         return -1;
00332 
00333     unsigned long samplerate_in = m_samplerate->get();
00334     unsigned long samplerate_out = (samplerate_in>48000?48000:samplerate_in);
00335 
00336     m_lameFlags = lame_init();
00337 
00338     if ( m_lameFlags == NULL ) {
00339         qDebug() << "Unable to initialize MP3";
00340         return -1;
00341     }
00342 
00343     lame_set_num_channels(m_lameFlags, 2);
00344     lame_set_in_samplerate(m_lameFlags, samplerate_in);
00345     lame_set_out_samplerate(m_lameFlags, samplerate_out);
00346     lame_set_brate(m_lameFlags, bitrate);
00347     lame_set_mode(m_lameFlags, STEREO);
00348     lame_set_quality(m_lameFlags, 2);
00349     lame_set_bWriteVbrTag(m_lameFlags, 0);
00350 
00351     //ID3 Tag if fiels are not NULL
00352     id3tag_init(m_lameFlags);
00353     if(m_metaDataTitle)
00354         id3tag_set_title(m_lameFlags, m_metaDataTitle);
00355     if(m_metaDataArtist)
00356         id3tag_set_artist(m_lameFlags, m_metaDataArtist);
00357     if(m_metaDataAlbum)
00358         id3tag_set_album(m_lameFlags,m_metaDataAlbum);
00359 
00360 
00361     if (( lame_init_params(m_lameFlags)) < 0) {
00362         qDebug() << "Unable to initialize MP3 parameters";
00363         return -1;
00364     }
00365 
00366     initStream();
00367 
00368     return 0;
00369 }
00370 
00371 void EncoderMp3::updateMetaData(char* artist, char* title, char* album){
00372     m_metaDataTitle = title;
00373     m_metaDataArtist = artist;
00374     m_metaDataAlbum = album;
00375 }
00376 
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Defines