gearmulator

Emulation of classic VA synths of the late 90s/2000s that are based on Motorola 56300 family DSPs
Log | Files | Refs | Submodules | README | LICENSE

commit 2892b0f44571f8cb83b404875c96a337e894b5ed
parent 0caae856225131947316c649e292cdd9becef97e
Author: dsp56300 <dsp56300@users.noreply.github.com>
Date:   Sat, 27 Jul 2024 22:35:56 +0200

change DSP/UC sync & audio processing, now DSP A writes its out to the DSP B input again

Diffstat:
Msource/nord/n2x/n2xLib/n2xdsp.cpp | 59++++-------------------------------------------------------
Msource/nord/n2x/n2xLib/n2xdsp.h | 20--------------------
Msource/nord/n2x/n2xLib/n2xhardware.cpp | 91+++++++++++++++++++++++++++----------------------------------------------------
Msource/nord/n2x/n2xLib/n2xhardware.h | 5++++-
4 files changed, 39 insertions(+), 136 deletions(-)

diff --git a/source/nord/n2x/n2xLib/n2xdsp.cpp b/source/nord/n2x/n2xLib/n2xdsp.cpp @@ -96,11 +96,6 @@ namespace n2x clock.setEsaiDivider(&esai, 1, 0); clock.setEsaiCounter(&esai, -1, 0); } - - esai.setCallback([this](dsp56k::Audio*) - { - onEsaiCallback(); - }, 0); } auto config = m_dsp.getJit().getConfig(); @@ -144,7 +139,7 @@ namespace n2x hdi08().setRXRateLimit(0); // hdi08().setTransmitDataAlwaysEmpty(false); - m_periphX.getEsai().writeEmptyAudioIn(2048); + m_periphX.getEsai().writeEmptyAudioIn(2); m_hdiUC.setRxEmptyCallback([&](const bool _needMoreData) { @@ -184,17 +179,7 @@ namespace n2x }); } - void DSP::advanceSamples(const uint32_t _samples, const uint32_t _latency) - { - { - std::lock_guard uLockHalt(m_haltDSPmutex); - m_maxEsaiCallbacks += _samples; - m_esaiLatency = _latency; - } - m_haltDSPcv.notify_one(); - } - - void DSP::onUCRxEmpty(bool _needMoreData) + void DSP::onUCRxEmpty(const bool _needMoreData) { if(_needMoreData) { @@ -203,8 +188,7 @@ namespace n2x return dsp().hasPendingInterrupts(); }); } - const bool res = hdiTransferDSPtoUC(); -// assert(!_needMoreData || res); + hdiTransferDSPtoUC(); } void DSP::hdiTransferUCtoDSP(const uint32_t _word) @@ -216,31 +200,11 @@ namespace n2x void DSP::hdiSendIrqToDSP(const uint8_t _irq) { - // If applicable, we wait for the halt on our DSP after resuming it - /*if(m_haltDSP.halted()) - { - bool done = false; - std::mutex mutex; - std::condition_variable wait; - m_haltDSP.wakeUp([this, _irq, &mutex, &done, &wait] - { - dsp().execInterrupt(_irq); - { - std::unique_lock lock(mutex); - done = true; - } - wait.notify_one(); - }); - std::unique_lock lock(mutex); - wait.wait(lock, [&done]{return done;}); - assert(done); - } - else*/ { dsp().injectExternalInterrupt(_irq); dsp().injectExternalInterrupt(m_irqInterruptDone); - hwLib::ScopedResumeDSP r(m_hardware.getDSPB().getHaltDSP()); + hwLib::ScopedResumeDSP rB(m_hardware.getDSPB().getHaltDSP()); m_triggerInterruptDone.wait(); } @@ -262,21 +226,6 @@ namespace n2x return _isr; } - void DSP::onEsaiCallback() - { - ++m_numEsaiCallbacks; - - if(m_numEsaiCallbacks >= m_maxEsaiCallbacks + m_esaiLatency) - { - std::unique_lock uLock(m_haltDSPmutex); - m_haltDSPcv.wait(uLock, [&] - { - return (m_maxEsaiCallbacks + m_esaiLatency) > m_numEsaiCallbacks; - }); - } - m_esaiCallback(); - } - bool DSP::hdiTransferDSPtoUC() { if (m_hdiUC.canReceiveData() && hdi08().hasTX()) diff --git a/source/nord/n2x/n2xLib/n2xdsp.h b/source/nord/n2x/n2xLib/n2xdsp.h @@ -38,13 +38,6 @@ namespace n2x return m_periphX; } - void setEsaiCallback(std::function<void()>&& _func) - { - m_esaiCallback = std::move(_func); - } - - void advanceSamples(uint32_t _samples, uint32_t _latency); - dsp56k::DSPThread& getDSPThread() const { return *m_thread; } auto& getHaltDSP() { return m_haltDSP; } @@ -53,7 +46,6 @@ namespace n2x void hdiTransferUCtoDSP(uint32_t _word); void hdiSendIrqToDSP(uint8_t _irq); uint8_t hdiUcReadIsr(uint8_t _isr); - void onEsaiCallback(); bool hdiTransferDSPtoUC(); Hardware& m_hardware; @@ -69,18 +61,6 @@ namespace n2x std::unique_ptr<dsp56k::DSPThread> m_thread; - bool m_receivedMagicEsaiPacket = false; - uint32_t m_hdiHF01 = 0; - - uint64_t m_numEsaiCallbacks = 0; - uint64_t m_maxEsaiCallbacks = 0; - uint64_t m_esaiLatency = 0; - - std::function<void()> m_esaiCallback = [] {}; - - std::condition_variable m_haltDSPcv; - std::mutex m_haltDSPmutex; - baseLib::Semaphore m_triggerInterruptDone; uint32_t m_irqInterruptDone = 0; diff --git a/source/nord/n2x/n2xLib/n2xhardware.cpp b/source/nord/n2x/n2xLib/n2xhardware.cpp @@ -13,14 +13,13 @@ namespace n2x , m_dspA(*this, m_uc.getHdi08A(), 0) , m_dspB(*this, m_uc.getHdi08B(), 1) , m_samplerateInv(1.0 / g_samplerate) + , m_semDspAtoB(1) { if(!m_rom.isValid()) return; - m_dspB.setEsaiCallback([this]() - { - onEsaiCallback(); - }); + m_dspA.getPeriph().getEsai().setCallback([this](dsp56k::Audio*){ onEsaiCallbackA(); }, 0); + m_dspB.getPeriph().getEsai().setCallback([this](dsp56k::Audio*) { onEsaiCallbackB(); }, 0); } bool Hardware::isValid() const @@ -40,7 +39,6 @@ namespace n2x void Hardware::ucYieldLoop(const std::function<bool()>& _continue) { - hwLib::ScopedResumeDSP rA(m_dspA.getHaltDSP()); hwLib::ScopedResumeDSP rB(m_dspB.getHaltDSP()); while(_continue()) @@ -79,7 +77,6 @@ namespace n2x outputs[10] = m_dummyOutput.data(); outputs[11] = m_dummyOutput.data(); - auto& esaiA = m_dspA.getPeriph().getEsai(); auto& esaiB = m_dspB.getPeriph().getEsai(); // LOG("B out " << esaiB.getAudioOutputs().size() << ", A out " << esaiA.getAudioOutputs().size() << ", B in " << esaiB.getAudioInputs().size()); @@ -89,35 +86,6 @@ namespace n2x const auto processCount = std::min(_frames, static_cast<uint32_t>(64)); _frames -= processCount; - m_dspA.advanceSamples(processCount, _latency); - m_dspB.advanceSamples(processCount, _latency); - - auto* buf = m_dspAtoBBuffer.data(); - - // read data from DSP A... - esaiA.processAudioOutput<dsp56k::TWord>(processCount, [&](size_t _index, dsp56k::Audio::TxFrame& _frame) - { - *buf++ = _frame[0][0]; - *buf++ = _frame[1][0]; - *buf++ = _frame[2][0]; - *buf++ = _frame[3][0]; - }); - - buf = m_dspAtoBBuffer.data(); - - // ...and forward it to DSP B - esaiB.processAudioInput<dsp56k::TWord>(processCount, 0, [&](size_t _s, dsp56k::Audio::RxFrame& _f) - { - _f.resize(4); - _f[0] = dsp56k::Audio::RxSlot{0}; - _f[1] = dsp56k::Audio::RxSlot{0}; - _f[2] = dsp56k::Audio::RxSlot{0}; - _f[3] = dsp56k::Audio::RxSlot{0}; - buf += 4; - }); - -// esaiB.processAudioInput(m_dspAtoBBuffer.data(), processCount * 2, 4, 0); - const auto requiredSize = processCount > 8 ? processCount - 8 : 0; if(esaiB.getAudioOutputs().size() < requiredSize) @@ -138,15 +106,6 @@ namespace n2x // read output of DSP B to regular audio output esaiB.processAudioOutputInterleaved(outputs, processCount); - for(uint32_t i=0; i<processCount; ++i) - { - const auto i4 = i<<2; - outputs[0][i] += m_dspAtoBBuffer[i4+2]; - outputs[1][i] += m_dspAtoBBuffer[i4+3]; - outputs[2][i] += m_dspAtoBBuffer[i4+0]; - outputs[3][i] += m_dspAtoBBuffer[i4+1]; - } - outputs[0] += processCount; outputs[1] += processCount; outputs[2] += processCount; @@ -168,8 +127,28 @@ namespace n2x m_dspAtoBBuffer.resize(_frames * 4); } - void Hardware::onEsaiCallback() + void Hardware::onEsaiCallbackA() { + // forward DSP A output to DSP B input + const auto out = m_dspA.getPeriph().getEsai().getAudioOutputs().pop_front(); + + dsp56k::Audio::RxFrame in; + in.resize(out.size()); + + in[0] = dsp56k::Audio::RxSlot{out[0][0]}; + in[1] = dsp56k::Audio::RxSlot{out[1][0]}; + in[2] = dsp56k::Audio::RxSlot{out[2][0]}; + in[3] = dsp56k::Audio::RxSlot{out[3][0]}; + + m_dspB.getPeriph().getEsai().getAudioInputs().push_back(in); + + m_semDspAtoB.wait(); + } + + void Hardware::onEsaiCallbackB() + { + m_semDspAtoB.notify(); + ++m_esaiFrameIndex; // processMidiInput(); @@ -201,7 +180,6 @@ namespace n2x if(m_esaiFrameIndex == m_lastEsaiFrameIndex) { - m_dspHalted = false; resumeDSPs(); std::unique_lock uLock(m_esaiFrameAddedMutex); m_esaiFrameAddedCv.wait(uLock, [this]{return m_esaiFrameIndex > m_lastEsaiFrameIndex;}); @@ -226,31 +204,24 @@ namespace n2x m_remainingUcCyclesD -= static_cast<double>(m_remainingUcCycles); if(esaiDelta > g_syncHaltDspEsaiThreshold) - { - if(!m_dspHalted) - { - m_dspHalted = true; - haltDSPs(); - } - } - else if(m_dspHalted) - { - m_dspHalted = false; - resumeDSPs(); - } + haltDSPs(); m_lastEsaiFrameIndex = esaiFrameIndex; } void Hardware::haltDSPs() { -// m_dspA.getHaltDSP().haltDSP(); + if(m_dspHalted) + return; + m_dspHalted = true; m_dspB.getHaltDSP().haltDSP(); } void Hardware::resumeDSPs() { -// m_dspA.getHaltDSP().resumeDSP(); + if(!m_dspHalted) + return; + m_dspHalted = false; m_dspB.getHaltDSP().resumeDSP(); } } diff --git a/source/nord/n2x/n2xLib/n2xhardware.h b/source/nord/n2x/n2xLib/n2xhardware.h @@ -31,7 +31,8 @@ namespace n2x private: void ensureBufferSize(uint32_t _frames); - void onEsaiCallback(); + void onEsaiCallbackA(); + void onEsaiCallbackB(); void syncUCtoDSP(); Rom m_rom; @@ -57,5 +58,7 @@ namespace n2x std::condition_variable m_requestedFramesAvailableCv; size_t m_requestedFrames = 0; bool m_dspHalted = false; + dsp56k::SpscSemaphore m_semDspAtoB; + dsp56k::RingBuffer<dsp56k::Audio::RxFrame, 4, true> m_dspAtoBbuf; }; }