wHardware.cpp (3564B)
1 #include "wHardware.h" 2 3 #include "dsp56kEmu/audio.h" 4 5 #include "synthLib/midiBufferParser.h" 6 7 #include "hardwareLib/sciMidi.h" 8 9 #include "mc68k/mc68k.h" 10 11 namespace wLib 12 { 13 constexpr uint32_t g_syncEsaiFrameRate = 8; 14 constexpr uint32_t g_syncHaltDspEsaiThreshold = 16; 15 16 static_assert((g_syncEsaiFrameRate & (g_syncEsaiFrameRate - 1)) == 0, "esai frame sync rate must be power of two"); 17 static_assert(g_syncHaltDspEsaiThreshold >= g_syncEsaiFrameRate * 2, "esai DSP halt threshold must be greater than two times the sync rate"); 18 19 Hardware::Hardware(const double& _samplerate) : m_samplerateInv(1.0 / _samplerate) 20 { 21 } 22 23 void Hardware::haltDSP() 24 { 25 if(m_haltDSP) 26 return; 27 28 std::lock_guard uLockHalt(m_haltDSPmutex); 29 m_haltDSP = true; 30 } 31 32 void Hardware::resumeDSP() 33 { 34 if(!m_haltDSP) 35 return; 36 37 { 38 std::lock_guard uLockHalt(m_haltDSPmutex); 39 m_haltDSP = false; 40 } 41 m_haltDSPcv.notify_one(); 42 } 43 44 void Hardware::ucYieldLoop(const std::function<bool()>& _continue) 45 { 46 const auto dspHalted = m_haltDSP; 47 48 resumeDSP(); 49 50 while(_continue()) 51 { 52 if(m_processAudio) 53 { 54 std::this_thread::yield(); 55 } 56 else 57 { 58 std::unique_lock uLock(m_esaiFrameAddedMutex); 59 m_esaiFrameAddedCv.wait(uLock); 60 } 61 } 62 63 if(dspHalted) 64 haltDSP(); 65 } 66 67 void Hardware::sendMidi(const synthLib::SMidiEvent& _ev) 68 { 69 m_midiIn.push_back(_ev); 70 } 71 72 void Hardware::receiveMidi(std::vector<uint8_t>& _data) 73 { 74 getMidi().read(_data); 75 } 76 77 void Hardware::onEsaiCallback(dsp56k::Audio& _audio) 78 { 79 ++m_esaiFrameIndex; 80 81 processMidiInput(); 82 83 if((m_esaiFrameIndex & (g_syncEsaiFrameRate-1)) == 0) 84 m_esaiFrameAddedCv.notify_one(); 85 86 m_requestedFramesAvailableMutex.lock(); 87 88 if(m_requestedFrames && _audio.getAudioOutputs().size() >= m_requestedFrames) 89 { 90 m_requestedFramesAvailableMutex.unlock(); 91 m_requestedFramesAvailableCv.notify_one(); 92 } 93 else 94 { 95 m_requestedFramesAvailableMutex.unlock(); 96 } 97 98 std::unique_lock uLock(m_haltDSPmutex); 99 m_haltDSPcv.wait(uLock, [&]{ return m_haltDSP == false; }); 100 } 101 102 void Hardware::syncUcToDSP() 103 { 104 if(m_remainingUcCycles > 0) 105 return; 106 107 // we can only use ESAI to clock the uc once it has been enabled 108 if(m_esaiFrameIndex <= 0) 109 return; 110 111 if(m_esaiFrameIndex == m_lastEsaiFrameIndex) 112 { 113 resumeDSP(); 114 std::unique_lock uLock(m_esaiFrameAddedMutex); 115 m_esaiFrameAddedCv.wait(uLock, [this]{return m_esaiFrameIndex > m_lastEsaiFrameIndex;}); 116 } 117 118 const auto esaiFrameIndex = m_esaiFrameIndex; 119 120 const auto ucClock = getUc().getSim().getSystemClockHz(); 121 122 const double ucCyclesPerFrame = static_cast<double>(ucClock) * m_samplerateInv; 123 124 const auto esaiDelta = esaiFrameIndex - m_lastEsaiFrameIndex; 125 126 // if the UC consumed more cycles than it was allowed to, remove them from remaining cycles 127 m_remainingUcCyclesD += static_cast<double>(m_remainingUcCycles); 128 129 // add cycles for the ESAI time that has passed 130 m_remainingUcCyclesD += ucCyclesPerFrame * static_cast<double>(esaiDelta); 131 132 // set new remaining cycle count 133 m_remainingUcCycles = static_cast<int64_t>(m_remainingUcCyclesD); 134 135 // and consume them 136 m_remainingUcCyclesD -= static_cast<double>(m_remainingUcCycles); 137 138 if(esaiDelta > g_syncHaltDspEsaiThreshold) 139 { 140 haltDSP(); 141 } 142 else 143 { 144 resumeDSP(); 145 } 146 147 m_lastEsaiFrameIndex = esaiFrameIndex; 148 } 149 150 void Hardware::processMidiInput() 151 { 152 ++m_midiOffsetCounter; 153 154 while(!m_midiIn.empty()) 155 { 156 const auto& e = m_midiIn.front(); 157 158 if(e.offset > m_midiOffsetCounter) 159 break; 160 161 getMidi().write(e); 162 m_midiIn.pop_front(); 163 } 164 } 165 }