1 #include "soundio/soundmanager.h"
2 
3 #include <portaudio.h>
4 
5 #include <QLibrary>
6 #include <QtDebug>
7 #include <cstring> // for memcpy and strcmp
8 
9 #include "control/controlobject.h"
10 #include "control/controlproxy.h"
11 #include "engine/enginebuffer.h"
12 #include "engine/enginemaster.h"
13 #include "engine/sidechain/enginenetworkstream.h"
14 #include "engine/sidechain/enginesidechain.h"
15 #include "moc_soundmanager.cpp"
16 #include "soundio/sounddevice.h"
17 #include "soundio/sounddevicenetwork.h"
18 #include "soundio/sounddevicenotfound.h"
19 #include "soundio/sounddeviceportaudio.h"
20 #include "soundio/soundmanagerutil.h"
21 #include "util/cmdlineargs.h"
22 #include "util/compatibility.h"
23 #include "util/defs.h"
24 #include "util/sample.h"
25 #include "util/sleep.h"
26 #include "util/versionstore.h"
27 #include "vinylcontrol/defs_vinylcontrol.h"
28 
29 typedef PaError (*SetJackClientName)(const char *name);
30 
31 namespace {
32 
33 #define CPU_OVERLOAD_DURATION 500 // in ms
34 
35 struct DeviceMode {
36     SoundDevicePointer pDevice;
37     bool isInput;
38     bool isOutput;
39 };
40 
41 #ifdef __LINUX__
42 const unsigned int kSleepSecondsAfterClosingDevice = 5;
43 #endif
44 } // anonymous namespace
45 
SoundManager(UserSettingsPointer pConfig,EngineMaster * pMaster)46 SoundManager::SoundManager(UserSettingsPointer pConfig,
47         EngineMaster* pMaster)
48         : m_pMaster(pMaster),
49           m_pConfig(pConfig),
50           m_paInitialized(false),
51           m_config(this),
52           m_pErrorDevice(nullptr),
53           m_underflowHappened(0),
54           m_underflowUpdateCount(0) {
55     // TODO(xxx) some of these ControlObject are not needed by soundmanager, or are unused here.
56     // It is possible to take them out?
57     m_pControlObjectSoundStatusCO = new ControlObject(
58             ConfigKey("[SoundManager]", "status"));
59     m_pControlObjectSoundStatusCO->set(SOUNDMANAGER_DISCONNECTED);
60 
61     m_pControlObjectVinylControlGainCO = new ControlObject(
62             ConfigKey(VINYL_PREF_KEY, "gain"));
63 
64     m_pMasterAudioLatencyOverloadCount = new ControlProxy("[Master]",
65             "audio_latency_overload_count");
66 
67     m_pMasterAudioLatencyOverload = new ControlProxy("[Master]",
68             "audio_latency_overload");
69 
70     //Hack because PortAudio samplerate enumeration is slow as hell on Linux (ALSA dmix sucks, so we can't blame PortAudio)
71     m_samplerates.push_back(44100);
72     m_samplerates.push_back(48000);
73     m_samplerates.push_back(96000);
74 
75     m_pNetworkStream = QSharedPointer<EngineNetworkStream>(
76             new EngineNetworkStream(2, 0));
77 
78     queryDevices();
79 
80     if (!m_config.readFromDisk()) {
81         m_config.loadDefaults(this, SoundManagerConfig::ALL);
82     }
83     checkConfig();
84     m_config.writeToDisk(); // in case anything changed by applying defaults
85 }
86 
~SoundManager()87 SoundManager::~SoundManager() {
88     // Clean up devices.
89     const bool sleepAfterClosing = false;
90     clearDeviceList(sleepAfterClosing);
91 
92     if (m_paInitialized) {
93         Pa_Terminate();
94         m_paInitialized = false;
95     }
96     // vinyl control proxies and input buffers are freed in closeDevices, called
97     // by clearDeviceList -- bkgood
98 
99     delete m_pControlObjectSoundStatusCO;
100     delete m_pControlObjectVinylControlGainCO;
101     delete m_pMasterAudioLatencyOverloadCount;
102     delete m_pMasterAudioLatencyOverload;
103 }
104 
getDeviceList(const QString & filterAPI,bool bOutputDevices,bool bInputDevices) const105 QList<SoundDevicePointer> SoundManager::getDeviceList(
106         const QString& filterAPI, bool bOutputDevices, bool bInputDevices) const {
107     //qDebug() << "SoundManager::getDeviceList";
108 
109     if (filterAPI == SoundManagerConfig::kDefaultAPI) {
110         return QList<SoundDevicePointer>();
111     }
112 
113     // Create a list of sound devices filtered to match given API and
114     // input/output.
115     QList<SoundDevicePointer> filteredDeviceList;
116 
117     for (const auto& pDevice: m_devices) {
118         // Skip devices that don't match the API, don't have input channels when
119         // we want input devices, or don't have output channels when we want
120         // output devices. If searching for both input and output devices,
121         // make sure to include any devices that have >0 channels.
122         bool hasOutputs = pDevice->getNumOutputChannels() >= 0;
123         bool hasInputs = pDevice->getNumInputChannels() >= 0;
124         if (pDevice->getHostAPI() != filterAPI ||
125                 (bOutputDevices && !bInputDevices && !hasOutputs) ||
126                 (bInputDevices && !bOutputDevices && !hasInputs) ||
127                 (!hasInputs && !hasOutputs)) {
128             continue;
129         }
130         filteredDeviceList.push_back(pDevice);
131     }
132     return filteredDeviceList;
133 }
134 
getHostAPIList() const135 QList<QString> SoundManager::getHostAPIList() const {
136     QList<QString> apiList;
137 
138     for (PaHostApiIndex i = 0; i < Pa_GetHostApiCount(); i++) {
139         const PaHostApiInfo* api = Pa_GetHostApiInfo(i);
140         if (api && QString(api->name) != "skeleton implementation") {
141             apiList.push_back(api->name);
142         }
143     }
144 
145     return apiList;
146 }
147 
closeDevices(bool sleepAfterClosing)148 void SoundManager::closeDevices(bool sleepAfterClosing) {
149     //qDebug() << "SoundManager::closeDevices()";
150 
151     bool closed = false;
152     for (const auto& pDevice: qAsConst(m_devices)) {
153         if (pDevice->isOpen()) {
154             // NOTE(rryan): As of 2009 (?) it has been safe to close() a SoundDevice
155             // while callbacks are active.
156             pDevice->close();
157             closed = true;
158         }
159     }
160 
161     if (closed && sleepAfterClosing) {
162 #ifdef __LINUX__
163         // Sleep for 5 sec to allow asynchronously sound APIs like "pulse" to free
164         // its resources as well
165         sleep(kSleepSecondsAfterClosingDevice);
166 #endif
167     }
168 
169     // TODO(rryan): Should we do this before SoundDevice::close()? No! Because
170     // then the callback may be running when we call
171     // onInputDisconnected/onOutputDisconnected.
172     for (const auto& pDevice: qAsConst(m_devices)) {
173         for (const auto& in: pDevice->inputs()) {
174             // Need to tell all registered AudioDestinations for this AudioInput
175             // that the input was disconnected.
176             for (auto it = m_registeredDestinations.constFind(in);
177                  it != m_registeredDestinations.constEnd() && it.key() == in; ++it) {
178                 it.value()->onInputUnconfigured(in);
179                 m_pMaster->onInputDisconnected(in);
180             }
181         }
182         for (const auto& out: pDevice->outputs()) {
183             // Need to tell all registered AudioSources for this AudioOutput
184             // that the output was disconnected.
185             for (auto it = m_registeredSources.constFind(out);
186                  it != m_registeredSources.constEnd() && it.key() == out; ++it) {
187                 it.value()->onOutputDisconnected(out);
188             }
189         }
190     }
191 
192     while (!m_inputBuffers.isEmpty()) {
193         CSAMPLE* pBuffer = m_inputBuffers.takeLast();
194         if (pBuffer != nullptr) {
195             SampleUtil::free(pBuffer);
196         }
197     }
198     m_inputBuffers.clear();
199 
200     // Indicate to the rest of Mixxx that sound is disconnected.
201     m_pControlObjectSoundStatusCO->set(SOUNDMANAGER_DISCONNECTED);
202 }
203 
clearDeviceList(bool sleepAfterClosing)204 void SoundManager::clearDeviceList(bool sleepAfterClosing) {
205     //qDebug() << "SoundManager::clearDeviceList()";
206 
207     // Close the devices first.
208     closeDevices(sleepAfterClosing);
209 
210     // Empty out the list of devices we currently have.
211     m_devices.clear();
212     m_pErrorDevice.clear();
213 
214     if (m_paInitialized) {
215         Pa_Terminate();
216         m_paInitialized = false;
217     }
218 }
219 
getSampleRates(const QString & api) const220 QList<unsigned int> SoundManager::getSampleRates(const QString& api) const {
221     if (api == MIXXX_PORTAUDIO_JACK_STRING) {
222         // queryDevices must have been called for this to work, but the
223         // ctor calls it -bkgood
224         QList<unsigned int> samplerates;
225         if (m_jackSampleRate.isValid()) {
226             samplerates.append(m_jackSampleRate);
227         }
228         return samplerates;
229     }
230     return m_samplerates;
231 }
232 
getSampleRates() const233 QList<unsigned int> SoundManager::getSampleRates() const {
234     return getSampleRates("");
235 }
236 
queryDevices()237 void SoundManager::queryDevices() {
238     //qDebug() << "SoundManager::queryDevices()";
239     queryDevicesPortaudio();
240     queryDevicesMixxx();
241 
242     // now tell the prefs that we updated the device list -- bkgood
243     emit devicesUpdated();
244 }
245 
clearAndQueryDevices()246 void SoundManager::clearAndQueryDevices() {
247     const bool sleepAfterClosing = true;
248     clearDeviceList(sleepAfterClosing);
249     queryDevices();
250 }
251 
queryDevicesPortaudio()252 void SoundManager::queryDevicesPortaudio() {
253     PaError err = paNoError;
254     if (!m_paInitialized) {
255 #ifdef Q_OS_LINUX
256         setJACKName();
257 #endif
258         err = Pa_Initialize();
259         m_paInitialized = true;
260     }
261     if (err != paNoError) {
262         qDebug() << "Error:" << Pa_GetErrorText(err);
263         m_paInitialized = false;
264         return;
265     }
266 
267     int iNumDevices = Pa_GetDeviceCount();
268     if (iNumDevices < 0) {
269         qDebug() << "ERROR: Pa_CountDevices returned" << iNumDevices;
270         return;
271     }
272 
273     // PaDeviceInfo structs have a PaHostApiIndex member, but PortAudio
274     // unfortunately provides no good way to associate this with a persistent,
275     // unique identifier for the API. So, build a QHash to do that and pass
276     // it to the SoundDevicePortAudio constructor.
277     QHash<PaHostApiIndex, PaHostApiTypeId> paApiIndexToTypeId;
278     for (PaHostApiIndex i = 0; i < Pa_GetHostApiCount(); i++) {
279         const PaHostApiInfo* api = Pa_GetHostApiInfo(i);
280         if (api && QString(api->name) != "skeleton implementation") {
281             paApiIndexToTypeId.insert(i, api->type);
282         }
283     }
284 
285     const PaDeviceInfo* deviceInfo;
286     for (int i = 0; i < iNumDevices; i++) {
287         deviceInfo = Pa_GetDeviceInfo(i);
288         if (!deviceInfo) {
289             continue;
290         }
291         /* deviceInfo fields for quick reference:
292             int     structVersion
293             const char *    name
294             PaHostApiIndex  hostApi
295             int     maxInputChannels
296             int     maxOutputChannels
297             PaTime  defaultLowInputLatency
298             PaTime  defaultLowOutputLatency
299             PaTime  defaultHighInputLatency
300             PaTime  defaultHighOutputLatency
301             double  defaultSampleRate
302          */
303         const auto deviceTypeId = paApiIndexToTypeId.value(deviceInfo->hostApi);
304         auto currentDevice = SoundDevicePointer(new SoundDevicePortAudio(
305                 m_pConfig, this, deviceInfo, deviceTypeId, i));
306         m_devices.push_back(currentDevice);
307         if (!strcmp(Pa_GetHostApiInfo(deviceInfo->hostApi)->name,
308                     MIXXX_PORTAUDIO_JACK_STRING)) {
309             m_jackSampleRate = static_cast<mixxx::audio::SampleRate::value_t>(
310                     deviceInfo->defaultSampleRate);
311         }
312     }
313 }
314 
queryDevicesMixxx()315 void SoundManager::queryDevicesMixxx() {
316     auto currentDevice = SoundDevicePointer(new SoundDeviceNetwork(
317             m_pConfig, this, m_pNetworkStream));
318     m_devices.append(currentDevice);
319 }
320 
setupDevices()321 SoundDeviceError SoundManager::setupDevices() {
322     // NOTE(rryan): Big warning: This function is concurrent with calls to
323     // pushBuffer and onDeviceOutputCallback until closeDevices() below.
324 
325     qDebug() << "SoundManager::setupDevices()";
326     m_pControlObjectSoundStatusCO->set(SOUNDMANAGER_CONNECTING);
327     SoundDeviceError err = SOUNDDEVICE_ERROR_OK;
328     // NOTE(rryan): Do not clear m_pClkRefDevice here. If we didn't touch the
329     // SoundDevice that is the clock reference, then it is safe to leave it as
330     // it was. Clearing it causes the engine to stop being processed which
331     // results in a stuttering noise (sometimes a loud buzz noise at low
332     // latencies) when changing devices.
333     //m_pClkRefDevice = NULL;
334     m_pErrorDevice.clear();
335     int outputDevicesOpened = 0;
336     int inputDevicesOpened = 0;
337 
338     // NOTE(rryan): Documenting for future people touching this class. If you
339     // would like to remove the fact that we close all the devices first and
340     // then re-open them, I'm with you! The problem is that SoundDevicePortAudio
341     // and SoundManager are not thread safe and the way that mutual exclusion
342     // between the Qt main thread and the PortAudio callback thread is achieved
343     // is that we shut off the PortAudio callbacks for all devices by closing
344     // every device first. We then update all the SoundDevice settings
345     // (configured AudioInputs/AudioOutputs) and then we re-open them.
346     //
347     // If you want to solve this issue, you should start by separating the PA
348     // callback from the logic in SoundDevicePortAudio. They should communicate
349     // via message passing over a request/response FIFO.
350 
351     // Instead of clearing m_pClkRefDevice and then assigning it directly,
352     // compute the new one then atomically hand off below.
353     SoundDevicePointer pNewMasterClockRef;
354 
355     m_pMasterAudioLatencyOverloadCount->set(0);
356 
357     // load with all configured devices.
358     // all found devices are removed below
359     QSet<SoundDeviceId> devicesNotFound = m_config.getDevices();
360 
361     // pair is isInput, isOutput
362     QVector<DeviceMode> toOpen;
363     bool haveOutput = false;
364     // loop over all available devices
365     for (const auto& pDevice: qAsConst(m_devices)) {
366         DeviceMode mode = {pDevice, false, false};
367         pDevice->clearInputs();
368         pDevice->clearOutputs();
369         m_pErrorDevice = pDevice;
370         const auto inputs = m_config.getInputs().values(pDevice->getDeviceId());
371         for (const auto& in : inputs) {
372             mode.isInput = true;
373             // TODO(bkgood) look into allocating this with the frames per
374             // buffer value from SMConfig
375             AudioInputBuffer aib(in, SampleUtil::alloc(MAX_BUFFER_LEN));
376             err = pDevice->addInput(aib);
377             if (err != SOUNDDEVICE_ERROR_OK) {
378                 SampleUtil::free(aib.getBuffer());
379                 goto closeAndError;
380             }
381 
382             m_inputBuffers.append(aib.getBuffer());
383 
384             // Check if any AudioDestination is registered for this AudioInput
385             // and call the onInputConnected method.
386             for (auto it = m_registeredDestinations.find(in);
387                     it != m_registeredDestinations.end() && it.key() == in;
388                     ++it) {
389                 it.value()->onInputConfigured(in);
390                 m_pMaster->onInputConnected(in);
391             }
392         }
393         QList<AudioOutput> outputs =
394                 m_config.getOutputs().values(pDevice->getDeviceId());
395 
396         // Statically connect the Network Device to the Sidechain
397         if (pDevice->getDeviceId().name == kNetworkDeviceInternalName) {
398             AudioOutput out(AudioPath::RECORD_BROADCAST, 0, 2, 0);
399             outputs.append(out);
400             if (m_config.getForceNetworkClock()) {
401                 pNewMasterClockRef = pDevice;
402             }
403         }
404 
405         for (const auto& out: qAsConst(outputs)) {
406             mode.isOutput = true;
407             if (pDevice->getDeviceId().name != kNetworkDeviceInternalName) {
408                 haveOutput = true;
409             }
410             // following keeps us from asking for a channel buffer EngineMaster
411             // doesn't have -- bkgood
412             const CSAMPLE* pBuffer = m_registeredSources.value(out)->buffer(out);
413             if (pBuffer == nullptr) {
414                 qDebug() << "AudioSource returned null for" << out.getString();
415                 continue;
416             }
417 
418             AudioOutputBuffer aob(out, pBuffer);
419             err = pDevice->addOutput(aob);
420             if (err != SOUNDDEVICE_ERROR_OK) {
421                 goto closeAndError;
422             }
423 
424             if (!m_config.getForceNetworkClock()) {
425                 if (out.getType() == AudioOutput::MASTER) {
426                     pNewMasterClockRef = pDevice;
427                 } else if ((out.getType() == AudioOutput::DECK ||
428                             out.getType() == AudioOutput::BUS)
429                         && !pNewMasterClockRef) {
430                     pNewMasterClockRef = pDevice;
431                 }
432             }
433 
434             // Check if any AudioSource is registered for this AudioOutput and
435             // call the onOutputConnected method.
436             for (auto it = m_registeredSources.find(out);
437                     it != m_registeredSources.end() && it.key() == out;
438                     ++it) {
439                 it.value()->onOutputConnected(out);
440             }
441         }
442 
443         if (mode.isInput || mode.isOutput) {
444             pDevice->setSampleRate(m_config.getSampleRate());
445             pDevice->setFramesPerBuffer(m_config.getFramesPerBuffer());
446             toOpen.append(mode);
447         }
448     }
449 
450     for (const auto& mode: toOpen) {
451         SoundDevicePointer pDevice = mode.pDevice;
452         m_pErrorDevice = pDevice;
453 
454         // If we have not yet set a clock source then we use the first
455         // output pDevice
456         if (pNewMasterClockRef.isNull() &&
457                 (!haveOutput || mode.isOutput)) {
458             pNewMasterClockRef = pDevice;
459             qWarning() << "Output sound device clock reference not set! Using"
460                        << pDevice->getDisplayName();
461         }
462 
463         int syncBuffers = m_config.getSyncBuffers();
464         // If we are in safe mode and using experimental polling support, use
465         // the default of 2 sync buffers instead.
466         if (CmdlineArgs::Instance().getSafeMode() && syncBuffers == 0) {
467             syncBuffers = 2;
468         }
469         err = pDevice->open(pNewMasterClockRef == pDevice, syncBuffers);
470         if (err != SOUNDDEVICE_ERROR_OK) {
471             goto closeAndError;
472         }
473         devicesNotFound.remove(pDevice->getDeviceId());
474         if (mode.isOutput) {
475             ++outputDevicesOpened;
476         }
477         if (mode.isInput) {
478             ++inputDevicesOpened;
479         }
480     }
481 
482     if (pNewMasterClockRef) {
483         qDebug() << "Using" << pNewMasterClockRef->getDisplayName()
484                  << "as output sound device clock reference";
485     } else {
486         qWarning() << "No output devices opened, no clock reference device set";
487     }
488 
489     qDebug() << outputDevicesOpened << "output sound devices opened";
490     qDebug() << inputDevicesOpened << "input sound devices opened";
491     for (const auto& device: devicesNotFound) {
492         qWarning() << device << "not found";
493     }
494 
495     m_pControlObjectSoundStatusCO->set(
496             outputDevicesOpened > 0 ?
497                     SOUNDMANAGER_CONNECTED : SOUNDMANAGER_DISCONNECTED);
498 
499     // returns OK if we were able to open all the devices the user wanted
500     if (devicesNotFound.isEmpty()) {
501         emit devicesSetup();
502         return SOUNDDEVICE_ERROR_OK;
503     }
504     m_pErrorDevice = SoundDevicePointer(
505             new SoundDeviceNotFound(devicesNotFound.constBegin()->name));
506     return SOUNDDEVICE_ERROR_DEVICE_COUNT;
507 
508 closeAndError:
509     const bool sleepAfterClosing = false;
510     closeDevices(sleepAfterClosing);
511     return err;
512 }
513 
getErrorDevice() const514 SoundDevicePointer SoundManager::getErrorDevice() const {
515     return m_pErrorDevice;
516 }
517 
getErrorDeviceName() const518 QString SoundManager::getErrorDeviceName() const {
519     SoundDevicePointer pDevice = getErrorDevice();
520     if (pDevice) {
521         return pDevice->getDisplayName();
522     }
523     return tr("a device");
524 }
525 
getLastErrorMessage(SoundDeviceError err) const526 QString SoundManager::getLastErrorMessage(SoundDeviceError err) const {
527     QString error;
528     QString deviceName(tr("a device"));
529     QString detailedError(tr("An unknown error occurred"));
530     SoundDevicePointer pDevice = getErrorDevice();
531     if (pDevice) {
532         deviceName = pDevice->getDisplayName();
533         detailedError = pDevice->getError();
534     }
535     switch (err) {
536     case SOUNDDEVICE_ERROR_DUPLICATE_OUTPUT_CHANNEL:
537         error = tr("Two outputs cannot share channels on \"%1\"").arg(deviceName);
538         break;
539     default:
540         error = tr("Error opening \"%1\"").arg(deviceName) + "\n" + detailedError;
541         break;
542     }
543     return error;
544 }
545 
getConfig() const546 SoundManagerConfig SoundManager::getConfig() const {
547     return m_config;
548 }
549 
setConfig(const SoundManagerConfig & config)550 SoundDeviceError SoundManager::setConfig(const SoundManagerConfig& config) {
551     SoundDeviceError err = SOUNDDEVICE_ERROR_OK;
552     m_config = config;
553     checkConfig();
554 
555     // Close open devices. After this call we will not get any more
556     // onDeviceOutputCallback() or pushBuffer() calls because all the
557     // SoundDevices are closed. closeDevices() blocks and can take a while.
558     const bool sleepAfterClosing = true;
559     closeDevices(sleepAfterClosing);
560 
561     // certain parts of mixxx rely on this being here, for the time being, just
562     // letting those be -- bkgood
563     // Do this first so vinyl control gets the right samplerate -- Owen W.
564     m_pConfig->set(ConfigKey("[Soundcard]","Samplerate"),
565                    ConfigValue(static_cast<int>(m_config.getSampleRate())));
566 
567     err = setupDevices();
568     if (err == SOUNDDEVICE_ERROR_OK) {
569         m_config.writeToDisk();
570     }
571     return err;
572 }
573 
checkConfig()574 void SoundManager::checkConfig() {
575     if (!m_config.checkAPI()) {
576         m_config.setAPI(SoundManagerConfig::kDefaultAPI);
577         m_config.loadDefaults(this, SoundManagerConfig::API | SoundManagerConfig::DEVICES);
578     }
579     if (!m_config.checkSampleRate(*this)) {
580         m_config.setSampleRate(SoundManagerConfig::kFallbackSampleRate);
581         m_config.loadDefaults(this, SoundManagerConfig::OTHER);
582     }
583 
584     // Even if we have a two-deck skin, if someone has configured a deck > 2
585     // then the configuration needs to know about that extra deck.
586     m_config.setCorrectDeckCount(getConfiguredDeckCount());
587     // latency checks itself for validity on SMConfig::setLatency()
588 }
589 
onDeviceOutputCallback(const SINT iFramesPerBuffer)590 void SoundManager::onDeviceOutputCallback(const SINT iFramesPerBuffer) {
591     // Produce a block of samples for output. EngineMaster expects stereo
592     // samples so multiply iFramesPerBuffer by 2.
593     m_pMaster->process(iFramesPerBuffer * 2);
594 }
595 
pushInputBuffers(const QList<AudioInputBuffer> & inputs,const SINT iFramesPerBuffer)596 void SoundManager::pushInputBuffers(const QList<AudioInputBuffer>& inputs,
597                                     const SINT iFramesPerBuffer) {
598    for (QList<AudioInputBuffer>::ConstIterator i = inputs.begin(),
599                  e = inputs.end(); i != e; ++i) {
600         const AudioInputBuffer& in = *i;
601         CSAMPLE* pInputBuffer = in.getBuffer();
602         for (auto it = m_registeredDestinations.constFind(in);
603              it != m_registeredDestinations.constEnd() && it.key() == in; ++it) {
604             it.value()->receiveBuffer(in, pInputBuffer, iFramesPerBuffer);
605         }
606     }
607 }
608 
writeProcess() const609 void SoundManager::writeProcess() const {
610     for (const auto& pDevice: m_devices) {
611         if (pDevice) {
612             pDevice->writeProcess();
613         }
614     }
615 }
616 
readProcess() const617 void SoundManager::readProcess() const {
618     for (const auto& pDevice: m_devices) {
619         if (pDevice) {
620             pDevice->readProcess();
621         }
622     }
623 }
624 
registerOutput(const AudioOutput & output,AudioSource * src)625 void SoundManager::registerOutput(const AudioOutput& output, AudioSource* src) {
626     VERIFY_OR_DEBUG_ASSERT(!m_registeredSources.contains(output)) {
627         return;
628     }
629     m_registeredSources.insert(output, src);
630     emit outputRegistered(output, src);
631 }
632 
registerInput(const AudioInput & input,AudioDestination * dest)633 void SoundManager::registerInput(const AudioInput& input, AudioDestination* dest) {
634     // Vinyl control inputs are registered twice, once for timecode and once for
635     // passthrough, each with different outputs. So unlike outputs, do not assert
636     // that the input has not been registered yet.
637     m_registeredDestinations.insert(input, dest);
638 
639     emit inputRegistered(input, dest);
640 }
641 
registeredOutputs() const642 QList<AudioOutput> SoundManager::registeredOutputs() const {
643     return m_registeredSources.keys();
644 }
645 
registeredInputs() const646 QList<AudioInput> SoundManager::registeredInputs() const {
647     return m_registeredDestinations.keys();
648 }
649 
setJACKName() const650 void SoundManager::setJACKName() const {
651 #ifdef Q_OS_LINUX
652     typedef PaError (*SetJackClientName)(const char *name);
653     QLibrary portaudio("libportaudio.so.2");
654     if (portaudio.load()) {
655         SetJackClientName func(
656             reinterpret_cast<SetJackClientName>(
657                 portaudio.resolve("PaJack_SetClientName")));
658         if (func) {
659             // PortAudio does not make a copy of the string we provide it so we
660             // need to make sure it will last forever so we intentionally leak
661             // this string.
662             char* jackNameCopy = strdup(VersionStore::applicationName().toLocal8Bit().constData());
663             if (!func(jackNameCopy)) {
664                 qDebug() << "JACK client name set";
665             }
666         } else {
667             qWarning() << "failed to resolve JACK name method";
668         }
669     } else {
670         qWarning() << "failed to load portaudio for JACK rename";
671     }
672 #endif
673 }
674 
setConfiguredDeckCount(int count)675 void SoundManager::setConfiguredDeckCount(int count) {
676     if (getConfiguredDeckCount() == count) {
677         // Unchanged
678         return;
679     }
680     m_config.setDeckCount(count);
681     checkConfig();
682     m_config.writeToDisk();
683 }
684 
getConfiguredDeckCount() const685 int SoundManager::getConfiguredDeckCount() const {
686     return m_config.getDeckCount();
687 }
688 
processUnderflowHappened()689 void SoundManager::processUnderflowHappened() {
690     if (m_underflowUpdateCount == 0) {
691         if (atomicLoadRelaxed(m_underflowHappened)) {
692             m_pMasterAudioLatencyOverload->set(1.0);
693             m_pMasterAudioLatencyOverloadCount->set(
694                     m_pMasterAudioLatencyOverloadCount->get() + 1);
695             m_underflowUpdateCount = CPU_OVERLOAD_DURATION * m_config.getSampleRate()
696                     / m_config.getFramesPerBuffer() / 1000;
697 
698             m_underflowHappened = 0; // resetting here is not thread safe,
699                                      // but that is OK, because we count only
700                                      // 1 underflow each 500 ms
701         } else {
702             m_pMasterAudioLatencyOverload->set(0.0);
703         }
704     } else {
705         --m_underflowUpdateCount;
706     }
707 }
708