Mixxx

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

Go to the documentation of this file.
00001 
00007 /***************************************************************************
00008  *                                                                         *
00009  *   This program is free software; you can redistribute it and/or modify  *
00010  *   it under the terms of the GNU General Public License as published by  *
00011  *   the Free Software Foundation; either version 2 of the License, or     *
00012  *   (at your option) any later version.                                   *
00013  *                                                                         *
00014  ***************************************************************************/
00015 
00016 #include "soundmanagerconfig.h"
00017 #include "soundmanagerutil.h"
00018 #include "sounddevice.h"
00019 #include "soundmanager.h"
00020 
00021 // this (7) represents latency values from 1 ms to about 80 ms -- bkgood
00022 const unsigned int SoundManagerConfig::kMaxLatency = 7;
00023 
00024 const QString SoundManagerConfig::kDefaultAPI = QString("None");
00025 const unsigned int SoundManagerConfig::kDefaultSampleRate = 48000;
00026 // latency=5 means about 21 ms of latency which is default in trunk r2453 -- bkgood
00027 const int SoundManagerConfig::kDefaultLatency = 5;
00028 
00029 SoundManagerConfig::SoundManagerConfig()
00030     : m_api("None")
00031     , m_sampleRate(kDefaultSampleRate)
00032     , m_latency(kDefaultLatency) {
00033     m_configFile = QFileInfo(QString("%1/%2/%3")
00034             .arg(QDir::homePath())
00035             .arg(SETTINGS_PATH)
00036             .arg(SOUNDMANAGERCONFIG_FILENAME));
00037 }
00038 
00039 SoundManagerConfig::~SoundManagerConfig() {
00040     // don't write to disk here, it's SoundManager's responsibility
00041     // to save its own configuration -- bkgood
00042 }
00043 
00049 bool SoundManagerConfig::readFromDisk() {
00050     QFile file(m_configFile.absoluteFilePath());
00051     QDomDocument doc;
00052     QDomElement rootElement;
00053     if (!file.open(QIODevice::ReadOnly)) {
00054         return false;
00055     }
00056     if (!doc.setContent(&file)) {
00057         file.close();
00058         return false;
00059     }
00060     file.close();
00061     rootElement = doc.documentElement();
00062     setAPI(rootElement.attribute("api"));
00063     setSampleRate(rootElement.attribute("samplerate", "0").toUInt());
00064     setLatency(rootElement.attribute("latency", "0").toUInt());
00065     clearOutputs();
00066     clearInputs();
00067     QDomNodeList devElements(rootElement.elementsByTagName("SoundDevice"));
00068     for (int i = 0; i < devElements.count(); ++i) {
00069         QDomElement devElement(devElements.at(i).toElement());
00070         if (devElement.isNull()) continue;
00071         QString device(devElement.attribute("name"));
00072         if (device.isEmpty()) continue;
00073         QDomNodeList outElements(devElement.elementsByTagName("output"));
00074         QDomNodeList inElements(devElement.elementsByTagName("input"));
00075         for (int j = 0; j < outElements.count(); ++j) {
00076             QDomElement outElement(outElements.at(j).toElement());
00077             if (outElement.isNull()) continue;
00078             AudioOutput out(AudioOutput::fromXML(outElement));
00079             if (out.getType() == AudioPath::INVALID) continue;
00080             bool dupe(false);
00081             foreach (AudioOutput otherOut, m_outputs) {
00082                 if (out == otherOut
00083                         && out.getChannelGroup() == otherOut.getChannelGroup()) {
00084                     dupe = true;
00085                     break;
00086                 }
00087             }
00088             if (dupe) continue;
00089             addOutput(device, out);
00090         }
00091         for (int j = 0; j < inElements.count(); ++j) {
00092             QDomElement inElement(inElements.at(j).toElement());
00093             if (inElement.isNull()) continue;
00094             AudioInput in(AudioInput::fromXML(inElement));
00095             if (in.getType() == AudioPath::INVALID) continue;
00096             bool dupe(false);
00097             foreach (AudioInput otherIn, m_inputs) {
00098                 if (in == otherIn
00099                         && in.getChannelGroup() == otherIn.getChannelGroup()) {
00100                     dupe = true;
00101                     break;
00102                 }
00103             }
00104             if (dupe) continue;
00105             addInput(device, in);
00106         }
00107     }
00108     return true;
00109 }
00110 
00111 bool SoundManagerConfig::writeToDisk() const {
00112     QDomDocument doc("SoundManagerConfig");
00113     QDomElement docElement(doc.createElement("SoundManagerConfig"));
00114     docElement.setAttribute("api", m_api);
00115     docElement.setAttribute("samplerate", m_sampleRate);
00116     docElement.setAttribute("latency", m_latency);
00117     doc.appendChild(docElement);
00118     foreach (QString device, m_outputs.keys().toSet().unite(m_inputs.keys().toSet())) {
00119         QDomElement devElement(doc.createElement("SoundDevice"));
00120         devElement.setAttribute("name", device);
00121         foreach (AudioInput in, m_inputs.values(device)) {
00122             QDomElement inElement(doc.createElement("input"));
00123             in.toXML(&inElement);
00124             devElement.appendChild(inElement);
00125         }
00126         foreach (AudioOutput out, m_outputs.values(device)) {
00127             QDomElement outElement(doc.createElement("output"));
00128             out.toXML(&outElement);
00129             devElement.appendChild(outElement);
00130         }
00131         docElement.appendChild(devElement);
00132     }
00133     QFile file(m_configFile.absoluteFilePath());
00134     if (!file.open(QIODevice::Truncate | QIODevice::WriteOnly)) {
00135         return false;
00136     }
00137     file.write(doc.toString().toUtf8());
00138     file.close();
00139     return true;
00140 }
00141 
00142 QString SoundManagerConfig::getAPI() const {
00143     return m_api;
00144 }
00145 
00146 void SoundManagerConfig::setAPI(const QString &api) {
00147     // SoundManagerConfig doesn't necessarily have access to a SoundManager
00148     // instance, so I can't check for input validity here -- bkgood
00149     m_api = api;
00150 }
00151 
00158 bool SoundManagerConfig::checkAPI(const SoundManager &soundManager) {
00159     if (!soundManager.getHostAPIList().contains(m_api) && m_api != "None") {
00160         return false;
00161     }
00162     return true;
00163 }
00164 
00165 unsigned int SoundManagerConfig::getSampleRate() const {
00166     return m_sampleRate;
00167 }
00168 
00169 void SoundManagerConfig::setSampleRate(unsigned int sampleRate) {
00170     // making sure we don't divide by zero elsewhere
00171     m_sampleRate = sampleRate != 0 ? sampleRate : kDefaultSampleRate;
00172 }
00173 
00180 bool SoundManagerConfig::checkSampleRate(const SoundManager &soundManager) {
00181     if (!soundManager.getSampleRates(m_api).contains(m_sampleRate)) {
00182         return false;
00183     }
00184     return true;
00185 }
00186 
00187 unsigned int SoundManagerConfig::getLatency() const {
00188     return m_latency;
00189 }
00190 
00191 unsigned int SoundManagerConfig::getFramesPerBuffer() const {
00192     Q_ASSERT(m_latency > 0); // endless loop otherwise
00193     unsigned int framesPerBuffer = 1;
00194     double sampleRate = m_sampleRate; // need this to avoid int division
00195     // first, get to the framesPerBuffer value corresponding to latency index 1
00196     for (; framesPerBuffer / sampleRate * 1000 < 1.0; framesPerBuffer *= 2);
00197     // then, keep going until we get to our desired latency index (if not 1)
00198     for (unsigned int latencyIndex = 1; latencyIndex < m_latency; ++latencyIndex) {
00199         framesPerBuffer <<= 1; // *= 2
00200     }
00201     return framesPerBuffer;
00202 }
00203 
00212 void SoundManagerConfig::setLatency(unsigned int latency) {
00213     // latency should be either the min of kMaxLatency and the passed value
00214     // if it's 0, pretend it was 1 -- bkgood
00215     m_latency = latency != 0 ? math_min(latency, kMaxLatency) : 1;
00216 }
00217 
00218 void SoundManagerConfig::addOutput(const QString &device, const AudioOutput &out) {
00219     m_outputs.insert(device, out);
00220 }
00221 
00222 void SoundManagerConfig::addInput(const QString &device, const AudioInput &in) {
00223     m_inputs.insert(device, in);
00224 }
00225 
00226 QMultiHash<QString, AudioOutput> SoundManagerConfig::getOutputs() const {
00227     return m_outputs;
00228 }
00229 
00230 QMultiHash<QString, AudioInput> SoundManagerConfig::getInputs() const {
00231     return m_inputs;
00232 }
00233 
00234 void SoundManagerConfig::clearOutputs() {
00235     m_outputs.clear();
00236 }
00237 
00238 void SoundManagerConfig::clearInputs() {
00239     m_inputs.clear();
00240 }
00241 
00246 void SoundManagerConfig::filterOutputs(SoundManager *soundManager) {
00247     QSet<QString> deviceNames;
00248     QSet<QString> toDelete;
00249     foreach (SoundDevice *device, soundManager->getDeviceList(m_api, true, false)) {
00250         deviceNames.insert(device->getInternalName());
00251     }
00252     foreach (QString deviceName, m_outputs.uniqueKeys()) {
00253         if (!deviceNames.contains(deviceName)) {
00254             toDelete.insert(deviceName);
00255         }
00256     }
00257     foreach (QString del, toDelete) {
00258         m_outputs.remove(del);
00259     }
00260 }
00261 
00266 void SoundManagerConfig::filterInputs(SoundManager *soundManager) {
00267     QSet<QString> deviceNames;
00268     QSet<QString> toDelete;
00269     foreach (SoundDevice *device, soundManager->getDeviceList(m_api, false, true)) {
00270         deviceNames.insert(device->getInternalName());
00271     }
00272     foreach (QString deviceName, m_inputs.uniqueKeys()) {
00273         if (!deviceNames.contains(deviceName)) {
00274             toDelete.insert(deviceName);
00275         }
00276     }
00277     foreach (QString del, toDelete) {
00278         m_inputs.remove(del);
00279     }
00280 }
00281 
00289 void SoundManagerConfig::loadDefaults(SoundManager *soundManager, unsigned int flags) {
00290     if (flags & SoundManagerConfig::API) {
00291         QList<QString> apiList = soundManager->getHostAPIList();
00292         if (!apiList.isEmpty()) {
00293 #ifdef __LINUX__
00294             //Check for JACK and use that if it's available, otherwise use ALSA
00295             if (apiList.contains(MIXXX_PORTAUDIO_JACK_STRING)) {
00296                 m_api = MIXXX_PORTAUDIO_JACK_STRING;
00297             } else {
00298                 m_api = MIXXX_PORTAUDIO_ALSA_STRING;
00299             }
00300 #endif
00301 #ifdef __WINDOWS__
00302             //Existence of ASIO doesn't necessarily mean you've got ASIO devices
00303             //Do something more advanced one day if you like - Adam
00304             // hoping this counts as more advanced, tests if ASIO is an option
00305             // and then that we have at least one ASIO output device -- bkgood
00306             if (apiList.contains(MIXXX_PORTAUDIO_ASIO_STRING)
00307                    && !soundManager->getDeviceList(
00308                        MIXXX_PORTAUDIO_ASIO_STRING, true, false).isEmpty()) {
00309                 m_api = MIXXX_PORTAUDIO_ASIO_STRING;
00310             } else {
00311                 m_api = MIXXX_PORTAUDIO_DIRECTSOUND_STRING;
00312             }
00313 #endif
00314 #ifdef __APPLE__
00315             m_api = MIXXX_PORTAUDIO_COREAUDIO_STRING;
00316 #endif
00317         }
00318     }
00319     if (flags & SoundManagerConfig::DEVICES) {
00320         clearOutputs();
00321         clearInputs();
00322         QList<SoundDevice*> outputDevices = soundManager->getDeviceList(m_api, true, false);
00323         if (!outputDevices.isEmpty()) {
00324             foreach (SoundDevice *device, outputDevices) {
00325                 if (device->getNumOutputChannels() < 2) continue;
00326                 AudioOutput masterOut(AudioPath::MASTER, 0);
00327                 addOutput(device->getInternalName(), masterOut);
00328                 break;
00329             }
00330         }
00331     }
00332     if (flags & SoundManagerConfig::OTHER) {
00333         QList<unsigned int> sampleRates = soundManager->getSampleRates(m_api);
00334         if (sampleRates.contains(kDefaultSampleRate)) {
00335             m_sampleRate = kDefaultSampleRate;
00336         } else if (!sampleRates.isEmpty()) {
00337             m_sampleRate = sampleRates.first();
00338         } else {
00339             qWarning() << "got empty sample rate list from SoundManager, this is a bug";
00340             Q_ASSERT(false);
00341         }
00342         m_latency = kDefaultLatency;
00343     }
00344 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Defines