Mixxx

/home/maxime/Projets/Mixxx/1.10/mixxx/src/soundsourceproxy.cpp

Go to the documentation of this file.
00001 /* -*- mode:C++; indent-tabs-mode:t; tab-width:8; c-basic-offset:4; -*- */
00002 /***************************************************************************
00003                           soundsourceproxy.cpp  -  description
00004                              -------------------
00005     begin                : Wed Oct 13 2004
00006     copyright            : (C) 2004 Tue Haste Andersen
00007     email                :
00008 ***************************************************************************/
00009 
00010 /***************************************************************************
00011 *                                                                         *
00012 *   This program is free software; you can redistribute it and/or modify  *
00013 *   it under the terms of the GNU General Public License as published by  *
00014 *   the Free Software Foundation; either version 2 of the License, or     *
00015 *   (at your option) any later version.                                   *
00016 *                                                                         *
00017 ***************************************************************************/
00018 
00019 #include "trackinfoobject.h"
00020 #include "soundsourceproxy.h"
00021 #ifdef __MAD__
00022 #include "soundsourcemp3.h"
00023 #endif
00024 #include "soundsourceoggvorbis.h"
00025 #ifdef __COREAUDIO__
00026 #include "soundsourcecoreaudio.h"
00027 #endif
00028 #ifdef __SNDFILE__
00029 #include "soundsourcesndfile.h"
00030 #endif
00031 #ifdef __FFMPEGFILE__
00032 #include "soundsourceffmpeg.h"
00033 #endif
00034 #include "soundsourceflac.h"
00035 
00036 #include <QLibrary>
00037 #include <QMutexLocker>
00038 #include <QMutex>
00039 #include <QDebug>
00040 #include <QDir>
00041 #include <QDesktopServices>
00042 #include <QCoreApplication>
00043 #include <QApplication>
00044 
00045 
00046 //Static memory allocation
00047 QRegExp SoundSourceProxy::m_supportedFileRegex;
00048 QMap<QString, QLibrary*> SoundSourceProxy::m_plugins;
00049 QMap<QString, getSoundSourceFunc> SoundSourceProxy::m_extensionsSupportedByPlugins;
00050 QMutex SoundSourceProxy::m_extensionsMutex;
00051 
00052 
00053 //Constructor
00054 SoundSourceProxy::SoundSourceProxy(QString qFilename)
00055         : Mixxx::SoundSource(qFilename),
00056           m_pSoundSource(NULL),
00057           m_pTrack() {
00058     m_pSoundSource = initialize(qFilename);
00059 }
00060 
00061 //Other constructor
00062 SoundSourceProxy::SoundSourceProxy(TrackPointer pTrack)
00063         : SoundSource(pTrack->getLocation()),
00064           m_pSoundSource(NULL) {
00065 
00066     m_pSoundSource = initialize(pTrack->getLocation());
00067     m_pTrack = pTrack;
00068 }
00069 
00070 void SoundSourceProxy::loadPlugins()
00071 {
00074     QList<QDir> pluginDirs;
00075     QStringList nameFilters;
00076 
00077     QStringList clArgs = QApplication::arguments();
00078     int pluginPath = clArgs.indexOf("--pluginPath");
00079     if (pluginPath != -1 && pluginPath + 1 < clArgs.size()) {
00080         qDebug() << "Adding plugin path from commandline arg:" << clArgs.at(pluginPath + 1);
00081         pluginDirs.append(QDir(clArgs.at(pluginPath + 1)));
00082     }
00083 #ifdef __LINUX__
00084     pluginDirs.append(QDir("/usr/local/lib/mixxx/plugins/soundsource/"));
00085     pluginDirs.append(QDir("/usr/lib/mixxx/plugins/soundsource/"));
00086     pluginDirs.append(QDir(QDesktopServices::storageLocation(QDesktopServices::HomeLocation) + "/.mixxx/plugins/soundsource/"));
00087 #elif __WINDOWS__
00088     pluginDirs.append(QDir(QCoreApplication::applicationDirPath() + "/plugins/soundsource/"));
00089 #elif __APPLE__
00090     QString bundlePluginDir = QCoreApplication::applicationDirPath(); //blah/Mixxx.app/Contents/MacOS
00091     bundlePluginDir.remove("MacOS");
00092     //blah/Mixxx.app/Contents/PlugIns/soundsource
00093     //bundlePluginDir.append("PlugIns/soundsource");  //Our SCons bundle target doesn't handle plugin subdirectories :(
00094     bundlePluginDir.append("PlugIns/");
00095     pluginDirs.append(QDir(bundlePluginDir));
00096     pluginDirs.append(QDir("/Library/Application Support/Mixxx/Plugins/soundsource/"));
00097     nameFilters << "libsoundsource*";
00098 #endif
00099 
00100     QDir dir;
00101     foreach(dir, pluginDirs)
00102     {
00103         QStringList files = dir.entryList(nameFilters, QDir::Files | QDir::NoDotAndDotDot);
00104         QString file;
00105         foreach (file, files)
00106         {
00107             getPlugin(dir.filePath(file));
00108         }
00109     }
00110 }
00111 
00112 Mixxx::SoundSource* SoundSourceProxy::initialize(QString qFilename) {
00113 
00114     Mixxx::SoundSource* sndsrc = NULL;
00115     QString extension = qFilename;
00116     extension.remove(0, (qFilename.lastIndexOf(".")+1));
00117     extension = extension.toLower();
00118 
00119 #ifdef __FFMPEGFILE__
00120     return new SoundSourceFFmpeg(qFilename);
00121 #endif
00122     if (SoundSourceOggVorbis::supportedFileExtensions().contains(extension)) {
00123             return new SoundSourceOggVorbis(qFilename);
00124 #ifdef __MAD__
00125     } else if (SoundSourceMp3::supportedFileExtensions().contains(extension)) {
00126             return new SoundSourceMp3(qFilename);
00127 #endif
00128     } else if (SoundSourceFLAC::supportedFileExtensions().contains(extension)) {
00129         return new SoundSourceFLAC(qFilename);
00130 #ifdef __COREAUDIO__
00131     } else if (SoundSourceCoreAudio::supportedFileExtensions().contains(extension)) {
00132         return new SoundSourceCoreAudio(qFilename);
00133 #endif
00134     } else if (m_extensionsSupportedByPlugins.contains(extension)) {
00135         getSoundSourceFunc getter = m_extensionsSupportedByPlugins.value(extension);
00136         if (getter)
00137         {
00138             qDebug() << "Getting SoundSource plugin object for" << extension;
00139             return getter(qFilename);
00140         }
00141         else {
00142             qDebug() << "Failed to resolve getSoundSource in plugin for" <<
00143                         extension;
00144             return NULL; //Failed to load plugin
00145         }
00146 #ifdef __SNDFILE__
00147     } else if (SoundSourceSndFile::supportedFileExtensions().contains(extension)) {
00148             return new SoundSourceSndFile(qFilename);
00149 #endif
00150     } else { //Unsupported filetype
00151         return NULL;
00152     }
00153 }
00154 
00155 SoundSourceProxy::~SoundSourceProxy()
00156 {
00157     delete m_pSoundSource;
00158 }
00159 
00160 QLibrary* SoundSourceProxy::getPlugin(QString lib_filename)
00161 {
00162     static QMutex mutex;
00163     QMutexLocker locker(&mutex);
00164     QLibrary* plugin;
00165     if (m_plugins.contains(lib_filename))
00166         plugin = m_plugins.value(lib_filename);
00167     else {
00168         plugin = new QLibrary(lib_filename);
00169         if (!plugin->load()) {
00170             qDebug() << "Failed to dynamically load" << lib_filename << plugin->errorString();
00171         } else {
00172             qDebug() << "Dynamically loaded" << lib_filename;
00173             //Add the plugin to our list of loaded QLibraries/plugins
00174             m_plugins.insert(lib_filename, plugin);
00175 
00176             bool incompatible = false;
00177             //Plugin API version check
00178             getSoundSourceAPIVersionFunc getver = (getSoundSourceAPIVersionFunc)plugin->resolve("getSoundSourceAPIVersion");
00179             if (getver) {
00180                 int pluginAPIVersion = getver();
00181                 if (pluginAPIVersion != MIXXX_SOUNDSOURCE_API_VERSION) {
00182                     //SoundSource API version mismatch
00183                     incompatible = true;
00184                 }
00185             } else {
00186                 //Missing getSoundSourceAPIVersion symbol
00187                 incompatible = true;
00188             }
00189             if (incompatible)
00190             {
00191                 //Plugin is using an older/incompatible version of the
00192                 //plugin API!
00193                 qDebug() << "Plugin" << lib_filename << "is incompatible with your version of Mixxx!";
00194                 return NULL;
00195             }
00196 
00197             //Map the file extensions this plugin supports onto a function
00198             //pointer to the "getter" function that gets a SoundSourceBlah.
00199             getSoundSourceFunc getter = (getSoundSourceFunc)plugin->resolve("getSoundSource");
00200             Q_ASSERT(getter); //Getter function not found.
00201                               //Did you export it properly in your plugin?
00202             getSupportedFileExtensionsFunc getFileExts = (getSupportedFileExtensionsFunc)plugin->resolve("supportedFileExtensions");
00203             Q_ASSERT(getFileExts);
00204             freeFileExtensionsFunc freeFileExts =
00205                 reinterpret_cast<freeFileExtensionsFunc>(
00206                     plugin->resolve("freeFileExtensions"));
00207             Q_ASSERT(freeFileExts);
00208             char** supportedFileExtensions = getFileExts();
00209             int i = 0;
00210             while (supportedFileExtensions[i] != NULL)
00211             {
00212                 qDebug() << "Plugin supports:" << supportedFileExtensions[i];
00213                 m_extensionsSupportedByPlugins.insert(QString(supportedFileExtensions[i]), getter);
00214                 i++;
00215             }
00216             freeFileExts(supportedFileExtensions);
00217             //So now we have a list of file extensions (eg. "m4a", "mp4", etc)
00218             //that map onto the getter function for this plugin (eg. the
00219             //function that returns a SoundSourceM4A object)
00220         }
00221     }
00222     return plugin;
00223 }
00224 
00225 
00226 int SoundSourceProxy::open()
00227 {
00228     if (!m_pSoundSource) {
00229         return 0;
00230     }
00231     int retVal = m_pSoundSource->open();
00232 
00233     //Update some metadata (currently only the duration)
00234     //after a song is open()'d. Eg. We don't know the length
00235     //of VBR MP3s until we've seeked through and counted all
00236     //the frames. We don't do that in ParseHeader() to keep
00237     //library scanning fast.
00238     // .... but only do this if the song doesn't already
00239     //      have a duration parsed. (Some SoundSources don't
00240     //      parse metadata on open(), so they won't have the
00241     //      duration.)
00242     // SSMP3 will set duration to -1 on VBR files,
00243     //  so we must look for that here too
00244     if (m_pTrack->getDuration() <= 0)
00245         m_pTrack->setDuration(m_pSoundSource->getDuration());
00246 
00247     return retVal;
00248 }
00249 
00250 long SoundSourceProxy::seek(long l)
00251 {
00252     if (!m_pSoundSource) {
00253         return 0;
00254     }
00255     return m_pSoundSource->seek(l);
00256 }
00257 
00258 unsigned SoundSourceProxy::read(unsigned long size, const SAMPLE * p)
00259 {
00260     if (!m_pSoundSource) {
00261         return 0;
00262     }
00263     return m_pSoundSource->read(size, p);
00264 }
00265 
00266 long unsigned SoundSourceProxy::length()
00267 {
00268     if (!m_pSoundSource) {
00269         return 0;
00270     }
00271     return m_pSoundSource->length();
00272 }
00273 
00274 int SoundSourceProxy::parseHeader()
00275 {
00276     //TODO: Reorganize code so that the static ParseHeader isn't needed, and use this function instead?
00277     return 0;
00278 }
00279 
00280 int SoundSourceProxy::ParseHeader(TrackInfoObject* p)
00281 {
00282 
00283     QString qFilename = p->getLocation();
00284     SoundSource* sndsrc = initialize(qFilename);
00285     if (sndsrc == NULL)
00286         return ERR;
00287 
00288     if (sndsrc->parseHeader() == OK) {
00289         //Dump the metadata from the soundsource into the TIO
00290         //qDebug() << "Album:" << sndsrc->getAlbum(); //Sanity check to make sure we've actually parsed metadata and not the filename
00291         p->setArtist(sndsrc->getArtist());
00292         QString title = sndsrc->getTitle();
00293         if (title.isEmpty()) {
00294             // If no title is returned, use the file name (without the extension)
00295             int start = qFilename.lastIndexOf(QRegExp("[/\\\\]"))+1;
00296             int end = qFilename.lastIndexOf('.');
00297             if (end == -1) end = qFilename.length();
00298             title = qFilename.mid(start,end-start);
00299         }
00300         p->setTitle(title);
00301         p->setAlbum(sndsrc->getAlbum());
00302         p->setType(sndsrc->getType());
00303         p->setYear(sndsrc->getYear());
00304         p->setGenre(sndsrc->getGenre());
00305         p->setComment(sndsrc->getComment());
00306         p->setTrackNumber(sndsrc->getTrackNumber());
00307         p->setReplayGain(sndsrc->getReplayGain());
00308         p->setBpm(sndsrc->getBPM());
00309         p->setDuration(sndsrc->getDuration());
00310         p->setBitrate(sndsrc->getBitrate());
00311         p->setSampleRate(sndsrc->getSampleRate());
00312         p->setChannels(sndsrc->getChannels());
00313         p->setKey(sndsrc->getKey());
00314         p->setHeaderParsed(true);
00315     }
00316     else
00317     {
00318         p->setHeaderParsed(false);
00319     }
00320     delete sndsrc;
00321 
00322     return 0;
00323 }
00324 
00325 QStringList SoundSourceProxy::supportedFileExtensions()
00326 {
00327     QMutexLocker locker(&m_extensionsMutex);
00328     QList<QString> supportedFileExtensions;
00329 #ifdef __MAD__
00330     supportedFileExtensions.append(SoundSourceMp3::supportedFileExtensions());
00331 #endif
00332     supportedFileExtensions.append(SoundSourceOggVorbis::supportedFileExtensions());
00333 #ifdef __SNDFILE__
00334     supportedFileExtensions.append(SoundSourceSndFile::supportedFileExtensions());
00335 #endif
00336 #ifdef __COREAUDIO__
00337     supportedFileExtensions.append(SoundSourceCoreAudio::supportedFileExtensions());
00338 #endif
00339     supportedFileExtensions.append(m_extensionsSupportedByPlugins.keys());
00340 
00341     return supportedFileExtensions;
00342 }
00343 
00344 QStringList SoundSourceProxy::supportedFileExtensionsByPlugins() {
00345     QMutexLocker locker(&m_extensionsMutex);
00346     QList<QString> supportedFileExtensions;
00347     supportedFileExtensions.append(m_extensionsSupportedByPlugins.keys());
00348     return supportedFileExtensions;
00349 }
00350 
00351 QString SoundSourceProxy::supportedFileExtensionsString() {
00352     QStringList supportedFileExtList = SoundSourceProxy::supportedFileExtensions();
00353     // Turn the list into a "*.mp3 *.wav *.etc" style string
00354     for (int i = 0; i < supportedFileExtList.size(); ++i) {
00355         supportedFileExtList[i] = QString("*.%1").arg(supportedFileExtList[i]);
00356     }
00357     return supportedFileExtList.join(" ");
00358 }
00359 
00360 QString SoundSourceProxy::supportedFileExtensionsRegex() {
00361     QStringList supportedFileExtList = SoundSourceProxy::supportedFileExtensions();
00362 
00363     // Escape every extension appropriately
00364     for (int i = 0; i < supportedFileExtList.size(); ++i) {
00365         supportedFileExtList[i] = QRegExp::escape(supportedFileExtList[i]);
00366     }
00367 
00368     // Turn the list into a "\\.(mp3|wav|etc)$" style regex string
00369     return QString("\\.(%1)$").arg(supportedFileExtList.join("|"));
00370 }
00371 
00372 bool SoundSourceProxy::isFilenameSupported(QString fileName) {
00373     if (m_supportedFileRegex.isValid()) {
00374         QString regex = SoundSourceProxy::supportedFileExtensionsRegex();
00375         m_supportedFileRegex = QRegExp(regex, Qt::CaseInsensitive);
00376     }
00377     return fileName.contains(m_supportedFileRegex);
00378 }
00379 
00380 
00381 unsigned int SoundSourceProxy::getSampleRate()
00382 {
00383     if (!m_pSoundSource) {
00384         return 0;
00385     }
00386     return m_pSoundSource->getSampleRate();
00387 }
00388 
00389 
00390 QString SoundSourceProxy::getFilename()
00391 {
00392     if (!m_pSoundSource) {
00393         return "";
00394     }
00395     return m_pSoundSource->getFilename();
00396 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Defines