commit ae6453874de8412a487911f8f28ba22ea975fd07
parent a0d8f30701a876d569cea96ace37c50086cb648b
Author: dsp56300 <dsp56300@users.noreply.github.com>
Date: Wed, 24 Jul 2024 23:51:35 +0200
optimizations: replace yield by condition variable / reduce thread contention in processAudio() / support latency
Diffstat:
8 files changed, 85 insertions(+), 14 deletions(-)
diff --git a/source/nord/n2x/n2xLib/n2xdsp.cpp b/source/nord/n2x/n2xLib/n2xdsp.cpp
@@ -176,6 +176,11 @@ namespace n2x
m_thread.reset(new dsp56k::DSPThread(dsp(), m_name.c_str()));
m_thread->setLogToStdout(false);
+
+ m_vbaInterruptDone = dsp().registerInterruptFunc([this]
+ {
+ m_triggerInterruptDone.notify();
+ });
}
void DSP::advanceSamples(const uint32_t _samples, const uint32_t _latency)
@@ -211,15 +216,12 @@ namespace n2x
void DSP::hdiSendIrqToDSP(const uint8_t _irq)
{
// waitDspRxEmpty();
- const auto& rxData = hdi08().rxData();
-
- assert(rxData.size() <= 1);
-
dsp().injectExternalInterrupt(_irq);
- m_hardware.ucYieldLoop([&]
+ dsp().injectExternalInterrupt(m_vbaInterruptDone);
+ m_hardware.resumeDSPsForFunc([&]
{
- return dsp().hasPendingInterrupts() || hdi08().hasRXData();
+ m_triggerInterruptDone.wait();
});
hdiTransferDSPtoUC();
diff --git a/source/nord/n2x/n2xLib/n2xdsp.h b/source/nord/n2x/n2xLib/n2xdsp.h
@@ -5,6 +5,7 @@
#include "dsp56kEmu/dsp.h"
#include "dsp56kEmu/dspthread.h"
+#include "synthLib/trigger.h"
namespace mc68k
{
@@ -80,5 +81,8 @@ namespace n2x
std::condition_variable m_haltDSPcv;
std::mutex m_haltDSPmutex;
+
+ synthLib::Trigger<> m_triggerInterruptDone;
+ uint32_t m_vbaInterruptDone = 0;
};
}
diff --git a/source/nord/n2x/n2xLib/n2xhardware.cpp b/source/nord/n2x/n2xLib/n2xhardware.cpp
@@ -67,7 +67,7 @@ namespace n2x
haltDSP();
}
- void Hardware::processAudio(uint32_t _frames, uint32_t _latency)
+ void Hardware::processAudio(uint32_t _frames, const uint32_t _latency)
{
ensureBufferSize(_frames);
@@ -92,11 +92,11 @@ namespace n2x
while (_frames)
{
- const auto processCount = std::min(_frames, static_cast<uint32_t>(16));
+ const auto processCount = std::min(_frames, static_cast<uint32_t>(64));
_frames -= processCount;
- m_dspA.advanceSamples(processCount, 0);
- m_dspB.advanceSamples(processCount, 0);
+ m_dspA.advanceSamples(processCount, _latency);
+ m_dspB.advanceSamples(processCount, _latency);
auto* buf = m_dspAtoBBuffer.data();
@@ -124,6 +124,23 @@ namespace n2x
// esaiB.processAudioInput(m_dspAtoBBuffer.data(), processCount * 2, 4, 0);
+ const auto requiredSize = processCount > 8 ? processCount - 8 : 0;
+
+ if(esaiB.getAudioOutputs().size() < requiredSize)
+ {
+ // reduce thread contention by waiting for output buffer to be full enough to let us grab the data without entering the read mutex too often
+
+ std::unique_lock uLock(m_requestedFramesAvailableMutex);
+ m_requestedFrames = requiredSize;
+ m_requestedFramesAvailableCv.wait(uLock, [&]()
+ {
+ if(esaiB.getAudioOutputs().size() < requiredSize)
+ return false;
+ m_requestedFrames = 0;
+ return true;
+ });
+ }
+
// read output of DSP B to regular audio output
esaiB.processAudioOutputInterleaved(outputs, processCount);
diff --git a/source/nord/n2x/n2xLib/n2xhardware.h b/source/nord/n2x/n2xLib/n2xhardware.h
@@ -21,13 +21,19 @@ namespace n2x
const auto& getAudioOutputs() const { return m_audioOutputs; }
- void processAudio(const uint32_t _frames)
+ void processAudio(uint32_t _frames, uint32_t _latency);
+
+ void resumeDSPsForFunc(const std::function<void()>& _callback)
{
- return processAudio(_frames, _frames);
+ const auto halted = m_haltDSP;
+ if(halted)
+ resumeDSP();
+ _callback();
+ if(halted)
+ haltDSP();
}
private:
- void processAudio(uint32_t _frames, uint32_t _latency);
void ensureBufferSize(uint32_t _frames);
void onEsaiCallback();
void syncUCtoDSP();
diff --git a/source/nord/n2x/n2xTestConsole/n2xTestConsole.cpp b/source/nord/n2x/n2xTestConsole/n2xTestConsole.cpp
@@ -50,7 +50,7 @@ int main()
while(true)
{
- hw->processAudio(blockSize);
+ hw->processAudio(blockSize, blockSize);
auto& outs = hw->getAudioOutputs();
diff --git a/source/synthLib/CMakeLists.txt b/source/synthLib/CMakeLists.txt
@@ -28,6 +28,7 @@ set(SOURCES
resamplerInOut.cpp resamplerInOut.h
romLoader.cpp romLoader.h
sysexToMidi.cpp sysexToMidi.h
+ trigger.cpp trigger.h
wavReader.cpp wavReader.h
wavTypes.h
wavWriter.cpp wavWriter.h
diff --git a/source/synthLib/trigger.cpp b/source/synthLib/trigger.cpp
@@ -0,0 +1 @@
+#include "trigger.h"
diff --git a/source/synthLib/trigger.h b/source/synthLib/trigger.h
@@ -0,0 +1,40 @@
+#pragma once
+
+#include <mutex>
+#include <condition_variable>
+#include <cstdint>
+
+namespace synthLib
+{
+ template<typename TCounter = uint32_t>
+ class Trigger
+ {
+ public:
+ void wait()
+ {
+ std::unique_lock uLock(m_haltMutex);
+
+ m_haltcv.wait(uLock, [&]
+ {
+ return m_notifyCounter > m_waitCounter;
+ });
+
+ ++m_waitCounter;
+ }
+
+ void notify()
+ {
+ {
+ std::lock_guard uLockHalt(m_haltMutex);
+ ++m_notifyCounter;
+ }
+ m_haltcv.notify_one();
+ }
+
+ private:
+ std::condition_variable m_haltcv;
+ std::mutex m_haltMutex;
+ TCounter m_waitCounter = 0;
+ TCounter m_notifyCounter = 0;
+ };
+}