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

xtHardware.cpp (6931B)


      1 #include "xtHardware.h"
      2 
      3 #include "synthLib/midiBufferParser.h"
      4 #include "synthLib/deviceException.h"
      5 
      6 #include <cstring>	// memcpy
      7 
      8 #include "xtRomLoader.h"
      9 
     10 namespace xt
     11 {
     12 	Rom initializeRom(const std::vector<uint8_t>& _romData, const std::string& _romName)
     13 	{
     14 		if(_romData.empty())
     15 			return RomLoader::findROM();
     16 		return Rom{_romName, _romData};
     17 	}
     18 
     19 	Hardware::Hardware(const std::vector<uint8_t>& _romData, const std::string& _romName)
     20 		: wLib::Hardware(40000)
     21 		, m_rom(initializeRom(_romData, _romName))
     22 		, m_uc(m_rom)
     23 		, m_dsps{DSP(*this, m_uc.getHdi08A().getHdi08(), 0)}
     24 		, m_midi(m_uc)
     25 	{
     26 		if(!m_rom.isValid())
     27 			throw synthLib::DeviceException(synthLib::DeviceError::FirmwareMissing);
     28 	}
     29 
     30 	Hardware::~Hardware()
     31 	{
     32 		m_dsps.front().getPeriph().getEssi0().setCallback({}, 0);
     33 	}
     34 
     35 	void Hardware::process()
     36 	{
     37 		processUcCycle();
     38 	}
     39 	
     40 	void Hardware::resetMidiCounter()
     41 	{
     42 		// wait for DSP to enter blocking state
     43 
     44 		const auto& esai = m_dsps.front().getPeriph().getEssi0();
     45 
     46 		auto& inputs = esai.getAudioInputs();
     47 		auto& outputs = esai.getAudioOutputs();
     48 
     49 		while(inputs.size() > 2 && !outputs.full())
     50 			std::this_thread::yield();
     51 
     52 		m_midiOffsetCounter = 0;
     53 	}
     54 
     55 	void Hardware::initVoiceExpansion()
     56 	{
     57 		if (m_dsps.size() < 3)
     58 		{
     59 			setupEsaiListener();
     60 			return;
     61 		}
     62 	}
     63 
     64 	void Hardware::setupEsaiListener()
     65 	{
     66 		auto& esaiA = m_dsps.front().getPeriph().getEssi0();
     67 
     68 		esaiA.setCallback([&](dsp56k::Audio*)
     69 		{
     70 			m_bootCompleted = true;
     71 
     72 			onEsaiCallback(esaiA);
     73 		}, 0);
     74 	}
     75 
     76 	void Hardware::processUcCycle()
     77 	{
     78 		syncUcToDSP();
     79 
     80 		const auto deltaCycles = m_uc.exec();
     81 		if(m_esaiFrameIndex > 0)
     82 			m_remainingUcCycles -= static_cast<int64_t>(deltaCycles);
     83 
     84 		for (auto& dsp : m_dsps)
     85 			dsp.transferHostFlagsUc2Dsdp();
     86 
     87 		for (auto& dsp : m_dsps)
     88 			dsp.hdiTransferDSPtoUC();
     89 
     90 		if(m_uc.requestDSPReset())
     91 		{
     92 			for (auto& dsp : m_dsps)
     93 			{
     94 				if(dsp.haveSentTXToDSP())
     95 				{
     96 //					m_uc.dumpMemory("DSPreset");
     97 					assert(false && "DSP needs reset even though it got data already. Needs impl");
     98 				}
     99 			}
    100 			m_uc.notifyDSPBooted();
    101 		}
    102 	}
    103 
    104 	void Hardware::processAudio(uint32_t _frames, uint32_t _latency)
    105 	{
    106 		ensureBufferSize(_frames);
    107 
    108 		if(m_esaiFrameIndex == 0)
    109 			return;
    110 
    111 		m_midi.process(_frames);
    112 
    113 		m_processAudio = true;
    114 
    115 		auto& esai = m_dsps.front().getPeriph().getEssi0();
    116 
    117 		const dsp56k::TWord* inputs[16]{nullptr};
    118 		dsp56k::TWord* outputs[16]{nullptr};
    119 
    120 		inputs[0] = &m_audioInputs[0].front();
    121 		inputs[1] = &m_audioInputs[1].front();
    122 		inputs[2] = m_dummyInput.data();
    123 		inputs[3] = m_dummyInput.data();
    124 		inputs[4] = m_dummyInput.data();
    125 		inputs[5] = m_dummyInput.data();
    126 		inputs[6] = m_dummyInput.data();
    127 		inputs[7] = m_dummyInput.data();
    128 
    129 		outputs[0] = &m_audioOutputs[0].front();
    130 		outputs[1] = &m_audioOutputs[1].front();
    131 		outputs[2] = &m_audioOutputs[2].front();
    132 		outputs[3] = &m_audioOutputs[3].front();
    133 		outputs[4] = m_dummyOutput.data();
    134 		outputs[5] = m_dummyOutput.data();
    135 		outputs[6] = m_dummyOutput.data();
    136 		outputs[7] = m_dummyOutput.data();
    137 		outputs[8] = m_dummyOutput.data();
    138 		outputs[9] = m_dummyOutput.data();
    139 		outputs[10] = m_dummyOutput.data();
    140 		outputs[11] = m_dummyOutput.data();
    141 
    142 		const auto totalFrames = _frames;
    143 
    144 		while (_frames)
    145 		{
    146 			const auto processCount = std::min(_frames, static_cast<uint32_t>(1024));
    147 			_frames -= processCount;
    148 /*
    149 			if constexpr (g_useVoiceExpansion)
    150 			{
    151 				auto& esaiA = m_dsps[0].getPeriph().getEsai();
    152 				auto& esaiB = m_dsps[1].getPeriph().getEsai();
    153 				auto& esaiC = m_dsps[2].getPeriph().getEsai();
    154 				
    155 				const auto tccrA = esaiA.getTccrAsString();	const auto rccrA = esaiA.getRccrAsString();
    156 				const auto tcrA = esaiA.getTcrAsString();	const auto rcrA = esaiA.getRcrAsString();
    157 
    158 				const auto tccrB = esaiB.getTccrAsString();	const auto rccrB = esaiB.getRccrAsString();
    159 				const auto tcrB = esaiB.getTcrAsString();	const auto rcrB = esaiB.getRcrAsString();
    160 
    161 				const auto tccrC = esaiC.getTccrAsString();	const auto rccrC = esaiC.getRccrAsString();
    162 				const auto tcrC = esaiC.getTcrAsString();	const auto rcrC = esaiC.getRcrAsString();
    163 
    164 				LOG("ESAI DSPmain:\n" << tccrA << '\n' << tcrA << '\n' << rccrA << '\n' << rcrA << '\n');
    165 				LOG("ESAI VexpA:\n"   << tccrB << '\n' << tcrB << '\n' << rccrB << '\n' << rcrB << '\n');
    166 				LOG("ESAI VexpB:\n"   << tccrC << '\n' << tcrC << '\n' << rccrC << '\n' << rcrC << '\n');
    167 
    168 				// vexp1 only needs the audio input
    169 				esaiB.processAudioInputInterleaved(inputs, processCount);
    170 
    171 				// transfer output from vexp1 to vexp2
    172 				esaiB.processAudioOutputInterleaved(outputs, processCount);
    173 
    174 				const dsp56k::TWord* in[] = { outputs[0], outputs[1], outputs[2], outputs[3], outputs[4], outputs[5], nullptr, nullptr };
    175 				esaiC.processAudioInputInterleaved(in, processCount);
    176 
    177 				// read output of vexp2 and send to main
    178 				esaiC.processAudioOutputInterleaved(outputs, processCount);
    179 
    180 				// RX1/2 = vexp2 output TX1/TX2
    181 				const dsp56k::TWord* inA[] = { inputs[1], inputs[0], outputs[2], outputs[3], outputs[4], outputs[5], nullptr, nullptr };
    182 				esaiA.processAudioInputInterleaved(inA, processCount);
    183 
    184 				// final output 0,1,2 = audio outs below
    185 			}
    186 			else
    187 */			{
    188 				esai.processAudioInputInterleaved(inputs, processCount, _latency);
    189 			}
    190 
    191 			const auto requiredSize = processCount > 8 ? processCount - 8 : 0;
    192 
    193 			if(esai.getAudioOutputs().size() < requiredSize)
    194 			{
    195 				// 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
    196 
    197 				std::unique_lock uLock(m_requestedFramesAvailableMutex);
    198 				m_requestedFrames = requiredSize;
    199 				m_requestedFramesAvailableCv.wait(uLock, [&]()
    200 				{
    201 					if(esai.getAudioOutputs().size() < requiredSize)
    202 						return false;
    203 					m_requestedFrames = 0;
    204 					return true;
    205 				});
    206 			}
    207 
    208 			esai.processAudioOutputInterleaved(outputs, processCount);
    209 			/*
    210 			if constexpr (g_useVoiceExpansion)
    211 			{
    212 				for (uint32_t i = 1; i < 3; ++i)
    213 				{
    214 					auto& e = m_dsps[i].getPeriph().getEsai();
    215 
    216 					dsp56k::TWord* outs[16]{ nullptr };
    217 					if (e.getAudioOutputs().size() >= 512)
    218 						e.processAudioOutputInterleaved(outs, static_cast<uint32_t>(e.getAudioOutputs().size() >> 1));
    219 				}
    220 			}
    221 			*/
    222 			inputs[0] += processCount;
    223 			inputs[1] += processCount;
    224 
    225 			outputs[0] += processCount;
    226 			outputs[1] += processCount;
    227 			outputs[2] += processCount;
    228 			outputs[3] += processCount;
    229 			outputs[4] += processCount;
    230 			outputs[5] += processCount;
    231 		}
    232 
    233 		m_processAudio = false;
    234 	}
    235 
    236 	void Hardware::ensureBufferSize(const uint32_t _frames)
    237 	{
    238 		if(m_audioInputs.front().size() < _frames)
    239 		{
    240 			for (auto& input : m_audioInputs)
    241 				input.resize(_frames);
    242 		}
    243 
    244 		if(m_audioOutputs.front().size() < _frames)
    245 		{
    246 			for (auto& output : m_audioOutputs)
    247 				output.resize(_frames);
    248 		}
    249 
    250 		if(m_dummyInput.size() < _frames)
    251 			m_dummyInput.resize(_frames);
    252 		if(m_dummyOutput.size() < _frames)
    253 			m_dummyOutput.resize(_frames);
    254 	}
    255 }