summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorBe <be@mixxx.org>2020-05-07 07:33:16 -0500
committerBe <be@mixxx.org>2020-05-07 07:33:16 -0500
commitbcf0dcbe9f57bf825326285c34cfe818578e81dc (patch)
tree9b45ca3dd394ec6f2c1ba84516278fdbf191e7ee /src
parentd5be6d44272763cfdc1b19c4a8e5099df11b82dc (diff)
parentae03e5e6f1c846eac844db8e6f1e6a8f40f1c1b3 (diff)
Merge remote-tracking branch 'upstream/2.2'
Diffstat (limited to 'src')
-rw-r--r--src/engine/enginemaster.cpp73
-rw-r--r--src/engine/enginemaster.h1
-rw-r--r--src/engine/sidechain/enginesidechain.cpp16
-rw-r--r--src/engine/sidechain/enginesidechain.h3
4 files changed, 55 insertions, 38 deletions
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;