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

plugin.cpp (7509B)


      1 #include "plugin.h"
      2 #include "device.h"
      3 
      4 #include <cmath>
      5 
      6 #include "os.h"
      7 
      8 using namespace synthLib;
      9 
     10 namespace synthLib
     11 {
     12 	constexpr uint8_t g_stateVersion = 1;
     13 
     14 	Plugin::Plugin(Device* _device, CallbackDeviceInvalid _callbackDeviceInvalid)
     15 	: m_resampler(_device->getChannelCountIn(), _device->getChannelCountOut())
     16 	, m_device(_device)
     17 	, m_midiClock(*this)
     18 	, m_deviceSamplerate(_device->getSamplerate())
     19 	, m_callbackDeviceInvalid(std::move(_callbackDeviceInvalid))
     20 	{
     21 	}
     22 
     23 	void Plugin::addMidiEvent(const SMidiEvent& _ev)
     24 	{
     25 		std::lock_guard lock(m_lockAddMidiEvent);
     26 
     27 		if(m_midiInRingBuffer.full())
     28 		{
     29 			std::lock_guard l(m_lock);
     30 			processMidiInEvent(m_midiInRingBuffer.pop_front());
     31 		}
     32 		m_midiInRingBuffer.push_back(_ev);
     33 	}
     34 
     35 	bool Plugin::setPreferredDeviceSamplerate(const float _samplerate)
     36 	{
     37 		std::lock_guard lock(m_lock);
     38 
     39 		const auto sr = m_device->getDeviceSamplerate(_samplerate, m_hostSamplerate);
     40 
     41 		if(sr == m_deviceSamplerate)  // NOLINT(clang-diagnostic-float-equal)
     42 			return true;
     43 
     44 		if(!m_device->setSamplerate(sr))
     45 			return false;
     46 
     47 		m_deviceSamplerate = sr;
     48 		m_resampler.setSamplerates(m_hostSamplerate, m_deviceSamplerate);
     49 
     50 		updateDeviceLatency();
     51 		return true;
     52 	}
     53 
     54 	void Plugin::setHostSamplerate(const float _hostSamplerate, const float _preferredDeviceSamplerate)
     55 	{
     56 		std::lock_guard lock(m_lock);
     57 
     58 		m_deviceSamplerate = m_device->getDeviceSamplerate(_preferredDeviceSamplerate, _hostSamplerate);
     59 		m_device->setSamplerate(m_deviceSamplerate);
     60 		m_resampler.setSamplerates(_hostSamplerate, m_deviceSamplerate);
     61 
     62 		m_hostSamplerate = _hostSamplerate;
     63 		m_hostSamplerateInv = _hostSamplerate > 0 ? 1.0f / _hostSamplerate : 0.0f;
     64 
     65 		updateDeviceLatency();
     66 	}
     67 
     68 	void Plugin::process(const TAudioInputs& _inputs, const TAudioOutputs& _outputs, size_t _count, const float _bpm, const float _ppqPos, const bool _isPlaying)
     69 	{
     70 		setFlushDenormalsToZero();
     71 
     72 		TAudioInputs inputs(_inputs);
     73 		TAudioOutputs outputs(_outputs);
     74 
     75 		for(size_t i=0; i<inputs.size(); ++i)
     76 			inputs[i] = _inputs[i] ? _inputs[i] : getDummyBuffer(_count);
     77 
     78 		for(size_t i=0; i<outputs.size(); ++i)
     79 			outputs[i] = _outputs[i] ? _outputs[i] : getDummyBuffer(_count);
     80 
     81 		std::lock_guard lock(m_lock);
     82 
     83 		if(!m_device->isValid())
     84 		{
     85 			m_device = m_callbackDeviceInvalid(m_device);
     86 
     87 			if(!m_device || !m_device->isValid())
     88 				return;
     89 		}
     90 
     91 		processMidiInEvents();
     92 		processMidiClock(_bpm, _ppqPos, _isPlaying, _count);
     93 
     94 		m_resampler.process(inputs, outputs, m_midiIn, m_midiOut, static_cast<uint32_t>(_count), 
     95 			[&](const TAudioInputs& _ins, const TAudioOutputs& _outs, size_t _c, const ResamplerInOut::TMidiVec& _midiIn, ResamplerInOut::TMidiVec& _midiOut)
     96 		{
     97 			m_device->process(_ins, _outs, _c, _midiIn, _midiOut);
     98 		});
     99 
    100 		m_midiIn.clear();
    101 	}
    102 
    103 	void Plugin::getMidiOut(std::vector<SMidiEvent>& _midiOut)
    104 	{
    105 		std::swap(_midiOut, m_midiOut);
    106 		m_midiOut.clear();
    107 	}
    108 
    109 	bool Plugin::isValid() const
    110 	{
    111 		return m_device->isValid();
    112 	}
    113 
    114 	void Plugin::setDevice(Device* _device)
    115 	{
    116 		if(!_device)
    117 			return;
    118 
    119 		std::lock_guard lock(m_lock);
    120 
    121 		std::vector<uint8_t> deviceState;
    122 		getState(deviceState, StateTypeGlobal);
    123 
    124 		delete m_device;
    125 
    126 		m_device = _device;
    127 
    128 		m_device->setSamplerate(m_deviceSamplerate);
    129 		if(!deviceState.empty())
    130 			setState(deviceState);
    131 
    132 		// MIDI clock has to send the start event again, some device find it confusing and do strange things if there isn't any
    133 		m_midiClock.restart();
    134 
    135 		updateDeviceLatency();
    136 	}
    137 
    138 #if !SYNTHLIB_DEMO_MODE
    139 	bool Plugin::getState(std::vector<uint8_t>& _state, StateType _type) const
    140 	{
    141 		if(!m_device)
    142 			return false;
    143 
    144 		_state.push_back(g_stateVersion);
    145 		_state.push_back(_type);
    146 
    147 		return m_device->getState(_state, _type);
    148 	}
    149 
    150 	bool Plugin::setState(const std::vector<uint8_t>& _state) const
    151 	{
    152 		if(!m_device)
    153 			return false;
    154 
    155 		if(_state.empty())
    156 			return false;
    157 
    158 		if(_state.size() < 2)
    159 			return m_device->setStateFromUnknownCustomData(_state);
    160 
    161 		const auto version = _state[0];
    162 
    163 		if(version != g_stateVersion)
    164 			return m_device->setStateFromUnknownCustomData(_state);
    165 
    166 		const auto stateType = static_cast<StateType>(_state[1]);
    167 
    168 		auto state = _state;
    169 		state.erase(state.begin(), state.begin() + 2);
    170 
    171 		return m_device->setState(state, stateType);
    172 	}
    173 #endif
    174 	void Plugin::insertMidiEvent(const SMidiEvent& _ev)
    175 	{
    176 		if(m_midiIn.empty() || m_midiIn.back().offset <= _ev.offset)
    177 		{
    178 			m_midiIn.push_back(_ev);
    179 			return;
    180 		}
    181 
    182 		for (auto it = m_midiIn.begin(); it != m_midiIn.end(); ++it)
    183 		{
    184 			if (it->offset > _ev.offset)
    185 			{
    186 				m_midiIn.insert(it, _ev);
    187 				return;
    188 			}
    189 		}
    190 
    191 		m_midiIn.push_back(_ev);
    192 	}
    193 
    194 	bool Plugin::setLatencyBlocks(uint32_t _latencyBlocks)
    195 	{
    196 		std::lock_guard lock(m_lock);
    197 
    198 		if(m_extraLatencyBlocks == _latencyBlocks)
    199 			return false;
    200 
    201 		m_extraLatencyBlocks = _latencyBlocks;
    202 		updateDeviceLatency();
    203 		return true;
    204 	}
    205 
    206 	void Plugin::processMidiClock(const float _bpm, const float _ppqPos, const bool _isPlaying, const size_t _sampleCount)
    207 	{
    208 		m_midiClock.process(_bpm, _ppqPos, _isPlaying, _sampleCount);
    209 	}
    210 
    211 	float* Plugin::getDummyBuffer(size_t _minimumSize)
    212 	{
    213 		if(m_dummyBuffer.size() < _minimumSize)
    214 			m_dummyBuffer.resize(_minimumSize);
    215 
    216 		return m_dummyBuffer.data();
    217 	}
    218 
    219 	void Plugin::updateDeviceLatency()
    220 	{
    221 		if(m_blockSize <= 0 || m_hostSamplerate <= 0)
    222 			return;
    223 
    224 		const auto latency = static_cast<uint32_t>(std::ceil(static_cast<float>(m_blockSize * m_extraLatencyBlocks) * m_device->getSamplerate() * m_hostSamplerateInv));
    225 		m_device->setExtraLatencySamples(latency);
    226 
    227 		m_deviceLatencyMidiToOutput = static_cast<uint32_t>(static_cast<float>(m_device->getInternalLatencyMidiToOutput()) * m_hostSamplerate / m_device->getSamplerate());
    228 		m_deviceLatencyInputToOutput = static_cast<uint32_t>(static_cast<float>(m_device->getInternalLatencyInputToOutput()) * m_hostSamplerate / m_device->getSamplerate());
    229 	}
    230 
    231 	void Plugin::processMidiInEvents()
    232 	{
    233 		while (!m_midiInRingBuffer.empty())
    234 		{
    235 			const auto ev = m_midiInRingBuffer.pop_front();
    236 
    237 			processMidiInEvent(ev);
    238 		}
    239 	}
    240 
    241 	void Plugin::processMidiInEvent(const SMidiEvent& _ev)
    242 	{
    243 		// sysex might be sent in multiple chunks. Happens if coming from hardware
    244 		if (!_ev.sysex.empty())
    245 		{
    246 			const bool isComplete = _ev.sysex.front() == M_STARTOFSYSEX && _ev.sysex.back() == M_ENDOFSYSEX;
    247 
    248 			if (isComplete)
    249 			{
    250 				m_midiIn.push_back(_ev);
    251 				return;
    252 			}
    253 
    254 			const bool isStart = _ev.sysex.front() == M_STARTOFSYSEX && _ev.sysex.back() != M_ENDOFSYSEX;
    255 			const bool isEnd = _ev.sysex.front() != M_STARTOFSYSEX && _ev.sysex.back() == M_ENDOFSYSEX;
    256 
    257 			if (isStart)
    258 			{
    259 				m_pendingSysexInput = _ev;
    260 				return;
    261 			}
    262 
    263 			if (!m_pendingSysexInput.sysex.empty())
    264 			{
    265 				m_pendingSysexInput.sysex.insert(m_pendingSysexInput.sysex.end(), _ev.sysex.begin(), _ev.sysex.end());
    266 
    267 				if (isEnd)
    268 				{
    269 					m_midiIn.push_back(m_pendingSysexInput);
    270 					m_pendingSysexInput.sysex.clear();
    271 				}
    272 			}
    273 		}
    274 
    275 		m_midiIn.push_back(_ev);
    276 	}
    277 
    278 	void Plugin::setBlockSize(const uint32_t _blockSize)
    279 	{
    280 		std::lock_guard lock(m_lock);
    281 		m_blockSize = _blockSize;
    282 		updateDeviceLatency();
    283 	}
    284 
    285 	uint32_t Plugin::getLatencyMidiToOutput() const
    286 	{
    287 		std::lock_guard lock(m_lock);
    288 		return m_blockSize * m_extraLatencyBlocks + m_deviceLatencyMidiToOutput + m_resampler.getOutputLatency();
    289 	}
    290 
    291 	uint32_t Plugin::getLatencyInputToOutput() const
    292 	{
    293 		std::lock_guard lock(m_lock);
    294 		return m_blockSize * m_extraLatencyBlocks + m_deviceLatencyInputToOutput + m_resampler.getOutputLatency() + m_resampler.getInputLatency();
    295 	}
    296 }