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