Mixxx
|
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 }