![]() |
Mixxx
|
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 "lame". 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 "lame". 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 "lame". 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