diff options
author | Be <be@mixxx.org> | 2020-05-07 07:33:16 -0500 |
---|---|---|
committer | Be <be@mixxx.org> | 2020-05-07 07:33:16 -0500 |
commit | bcf0dcbe9f57bf825326285c34cfe818578e81dc (patch) | |
tree | 9b45ca3dd394ec6f2c1ba84516278fdbf191e7ee | |
parent | d5be6d44272763cfdc1b19c4a8e5099df11b82dc (diff) | |
parent | ae03e5e6f1c846eac844db8e6f1e6a8f40f1c1b3 (diff) |
Merge remote-tracking branch 'upstream/2.2'
-rw-r--r-- | CHANGELOG | 1 | ||||
-rw-r--r-- | src/engine/enginemaster.cpp | 73 | ||||
-rw-r--r-- | src/engine/enginemaster.h | 1 | ||||
-rw-r--r-- | src/engine/sidechain/enginesidechain.cpp | 16 | ||||
-rw-r--r-- | src/engine/sidechain/enginesidechain.h | 3 |
5 files changed, 56 insertions, 38 deletions
@@ -17,6 +17,7 @@ * Add controller mapping for Hercules DJControl Inpulse 300 #2465 * Add controller mapping for Denon MC7000 #2546 * Add controller mapping for Stanton DJC.4 #2607 +* Fix broadcasting via broadcast/recording input lp:1876222 #2743 ==== 2.2.3 2019-11-24 ==== * Don't make users reconfigure sound hardware when it has not changed #2253 diff --git a/src/engine/enginemaster.cpp b/src/engine/enginemaster.cpp index 94ebc0335a..cfbf89bf05 100644 --- a/src/engine/enginemaster.cpp +++ b/src/engine/enginemaster.cpp @@ -153,7 +153,9 @@ EngineMaster::EngineMaster(UserSettingsPointer pConfig, } // Starts a thread for recording and broadcast - m_pEngineSideChain = bEnableSidechain ? new EngineSideChain(pConfig) : NULL; + m_pEngineSideChain = + bEnableSidechain ? + new EngineSideChain(pConfig, m_pSidechainMix) : nullptr; // X-Fader Setup m_pXFaderMode = new ControlPushButton( @@ -565,7 +567,7 @@ void EngineMaster::process(const int iBufferSize) { m_masterGainOld = master_gain; // Record/broadcast signal is the same as the master output - if (!m_bExternalRecordBroadcastInputConnected) { + if (sidechainMixRequired()) { SampleUtil::copy(m_pSidechainMix, m_pMaster, m_iBufferSize); } } else if (configuredMicMonitorMode == MicMonitorMode::MASTER_AND_BOOTH) { @@ -602,7 +604,7 @@ void EngineMaster::process(const int iBufferSize) { m_masterGainOld = master_gain; // Record/broadcast signal is the same as the master output - if (!m_bExternalRecordBroadcastInputConnected) { + if (sidechainMixRequired()) { SampleUtil::copy(m_pSidechainMix, m_pMaster, m_iBufferSize); } } else if (configuredMicMonitorMode == MicMonitorMode::DIRECT_MONITOR) { @@ -635,41 +637,42 @@ void EngineMaster::process(const int iBufferSize) { SampleUtil::applyRampingGain(m_pMaster, m_masterGainOld, master_gain, m_iBufferSize); m_masterGainOld = master_gain; - if (!m_bExternalRecordBroadcastInputConnected) { + if (sidechainMixRequired()) { SampleUtil::copy(m_pSidechainMix, m_pMaster, m_iBufferSize); - } - // The talkover signal Mixxx receives is delayed by the round trip latency. - // There is an output latency between the time Mixxx processes the audio - // and the user hears it. So if the microphone user plays on beat with - // what they hear, they will be playing out of sync with the engine's - // processing by the output latency. Additionally, Mixxx gets input signals - // delayed by the input latency. By the time Mixxx receives the input signal, - // a full round trip through the signal chain has elapsed since Mixxx - // processed the output signal. - // Although Mixxx receives the input signal delayed, the user hears it mixed - // in hardware with the master & booth outputs without that - // latency, so to record/broadcast the same signal that is heard - // on the master & booth outputs, the master mix must be delayed before - // mixing the talkover signal for the record/broadcast mix. - // If not using microphone inputs or recording/broadcasting from - // a sound card input, skip unnecessary processing here. - if (m_pNumMicsConfigured->get() > 0 - && !m_bExternalRecordBroadcastInputConnected) { - // Copy the master mix to a separate buffer before delaying it - // to avoid delaying the master output. - m_pLatencyCompensationDelay->process(m_pSidechainMix, m_iBufferSize); - SampleUtil::add(m_pSidechainMix, m_pTalkover, m_iBufferSize); + if (m_pNumMicsConfigured->get() > 0) { + // The talkover signal Mixxx receives is delayed by the round trip latency. + // There is an output latency between the time Mixxx processes the audio + // and the user hears it. So if the microphone user plays on beat with + // what they hear, they will be playing out of sync with the engine's + // processing by the output latency. Additionally, Mixxx gets input signals + // delayed by the input latency. By the time Mixxx receives the input signal, + // a full round trip through the signal chain has elapsed since Mixxx + // processed the output signal. + // Although Mixxx receives the input signal delayed, the user hears it mixed + // in hardware with the master & booth outputs without that + // latency, so to record/broadcast the same signal that is heard + // on the master & booth outputs, the master mix must be delayed before + // mixing the talkover signal for the record/broadcast mix. + // If not using microphone inputs or recording/broadcasting from + // a sound card input, skip unnecessary processing here. + + // Copy the master mix to a separate buffer before delaying it + // to avoid delaying the master output. + m_pLatencyCompensationDelay->process(m_pSidechainMix, m_iBufferSize); + SampleUtil::add(m_pSidechainMix, m_pTalkover, m_iBufferSize); + } } } - // Submit buffer to the side chain to do broadcasting, recording, - // etc. (CPU intensive non-realtime tasks) - // If recording/broadcasting from a sound card input, - // SoundManager will send the input buffer from the sound card to m_pSidechain - // so skip sending a buffer to m_pSidechain here. - if (!m_bExternalRecordBroadcastInputConnected - && m_pEngineSideChain != nullptr) { + // Submit buffer to the side chain to do CPU intensive non-realtime + // tasks like recording. The SoundDeviceNetwork, responsible for + // passing samples to the network reads directly from m_pSidechainMix, + // registering it with SoundDevice::addOutput(). + // Note: In case the broadcast/recording input is configured, + // EngineSideChain::receiveBuffer has copied the input buffer to m_pSidechainMix + // via before (called by SoundManager::pushInputBuffers()) + if (m_pEngineSideChain) { m_pEngineSideChain->writeSamples(m_pSidechainMix, iFrames); } @@ -966,3 +969,7 @@ void EngineMaster::registerNonEngineChannelSoundIO(SoundManager* pSoundManager) } pSoundManager->registerOutput(AudioOutput(AudioOutput::RECORD_BROADCAST, 0, 2), this); } + +bool EngineMaster::sidechainMixRequired() const { + return m_pEngineSideChain && !m_bExternalRecordBroadcastInputConnected; +} diff --git a/src/engine/enginemaster.h b/src/engine/enginemaster.h index c1996ac807..094e358bfb 100644 --- a/src/engine/enginemaster.h +++ b/src/engine/enginemaster.h @@ -273,6 +273,7 @@ class EngineMaster : public QObject, public AudioSource { ChannelHandleFactory* m_pChannelHandleFactory; void applyMasterEffects(); void processHeadphones(const double masterMixGainInHeadphones); + bool sidechainMixRequired() const; EngineEffectsManager* m_pEngineEffectsManager; diff --git a/src/engine/sidechain/enginesidechain.cpp b/src/engine/sidechain/enginesidechain.cpp index eab8c42b1e..fe9c562641 100644 --- a/src/engine/sidechain/enginesidechain.cpp +++ b/src/engine/sidechain/enginesidechain.cpp @@ -28,17 +28,23 @@ #include <QMutexLocker> #include "engine/sidechain/sidechainworker.h" +#include "engine/engine.h" #include "util/counter.h" #include "util/event.h" #include "util/sample.h" #include "util/timer.h" #include "util/trace.h" -EngineSideChain::EngineSideChain(UserSettingsPointer pConfig) +#define SIDECHAIN_BUFFER_SIZE 65536 + +EngineSideChain::EngineSideChain( + UserSettingsPointer pConfig, + CSAMPLE* sidechainMix) : m_pConfig(pConfig), m_bStopThread(false), m_sampleFifo(SIDECHAIN_BUFFER_SIZE), - m_pWorkBuffer(SampleUtil::alloc(SIDECHAIN_BUFFER_SIZE)) { + m_pWorkBuffer(SampleUtil::alloc(SIDECHAIN_BUFFER_SIZE)), + m_pSidechainMix(sidechainMix) { // We use HighPriority to prevent starvation by lower-priority processes (Qt // main thread, analysis, etc.). This used to be LowPriority but that is not // a suitable choice since we do semi-realtime tasks @@ -76,11 +82,13 @@ void EngineSideChain::addSideChainWorker(SideChainWorker* pWorker) { void EngineSideChain::receiveBuffer(AudioInput input, const CSAMPLE* pBuffer, unsigned int iFrames) { - if (input.getType() != AudioInput::RECORD_BROADCAST) { + VERIFY_OR_DEBUG_ASSERT(input.getType() == AudioInput::RECORD_BROADCAST) { qDebug() << "WARNING: AudioInput type is not RECORD_BROADCAST. Ignoring incoming buffer."; return; } - writeSamples(pBuffer, iFrames); + // Just copy the received samples form the sound card input to the + // engine. After processing we get it back via writeSamples() + SampleUtil::copy(m_pSidechainMix, pBuffer, iFrames * mixxx::kEngineChannelCount); } void EngineSideChain::writeSamples(const CSAMPLE* pBuffer, int iFrames) { diff --git a/src/engine/sidechain/enginesidechain.h b/src/engine/sidechain/enginesidechain.h index 6cfa6a1adc..ad37821da8 100644 --- a/src/engine/sidechain/enginesidechain.h +++ b/src/engine/sidechain/enginesidechain.h @@ -32,7 +32,7 @@ class EngineSideChain : public QThread, public AudioDestination { Q_OBJECT public: - EngineSideChain(UserSettingsPointer pConfig); + EngineSideChain(UserSettingsPointer pConfig, CSAMPLE* sidechainMix); virtual ~EngineSideChain(); // Not thread-safe, wait-free. Submit buffer of samples to the sidechain for @@ -60,6 +60,7 @@ class EngineSideChain : public QThread, public AudioDestination { FIFO<CSAMPLE> m_sampleFifo; CSAMPLE* m_pWorkBuffer; + CSAMPLE* m_pSidechainMix; // Provides thread safety around the wait condition below. QMutex m_waitLock; |