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

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 }