mqhardware.cpp (10247B)
1 #include "mqhardware.h" 2 3 #include "synthLib/midiBufferParser.h" 4 #include "synthLib/deviceException.h" 5 6 #include <cstring> // memcpy 7 8 namespace mqLib 9 { 10 Hardware::Hardware(const ROM& _rom) 11 : wLib::Hardware(44100) 12 , m_rom(_rom) 13 , m_uc(m_rom) 14 #if MQ_VOICE_EXPANSION 15 , m_dsps{MqDsp(*this, m_uc.getHdi08A().getHdi08(), 0), MqDsp(*this, m_uc.getHdi08B().getHdi08(), 1) , MqDsp(*this, m_uc.getHdi08C().getHdi08(), 2)} 16 #else 17 , m_dsps{MqDsp(*this, m_uc.getHdi08A().getHdi08(), 0)} 18 #endif 19 , m_midi(m_uc.getQSM(), 44100) 20 { 21 if(!m_rom.isValid()) 22 throw synthLib::DeviceException(synthLib::DeviceError::FirmwareMissing); 23 24 m_uc.getPortF().setDirectionChangeCallback([&](const mc68k::Port& _port) 25 { 26 if(_port.getDirection() == 0xff) 27 setGlobalDefaultParameters(); 28 }); 29 } 30 31 Hardware::~Hardware() 32 { 33 m_dsps.front().getPeriph().getEsai().setCallback({}, 0); 34 } 35 36 void Hardware::process() 37 { 38 processUcCycle(); 39 } 40 41 void Hardware::setBootMode(const BootMode _mode) 42 { 43 auto setButton = [&](const Buttons::ButtonType _type, const bool _pressed = true) 44 { 45 m_uc.getButtons().setButton(_type, _pressed); 46 }; 47 48 switch (_mode) 49 { 50 case BootMode::Default: 51 setButton(Buttons::ButtonType::Inst1, false); 52 setButton(Buttons::ButtonType::Inst3, false); 53 setButton(Buttons::ButtonType::Shift, false); 54 setButton(Buttons::ButtonType::Global, false); 55 setButton(Buttons::ButtonType::Multi, false); 56 setButton(Buttons::ButtonType::Play, false); 57 break; 58 case BootMode::FactoryTest: 59 setButton(Buttons::ButtonType::Inst1); 60 setButton(Buttons::ButtonType::Global); 61 break; 62 case BootMode::EraseFlash: 63 setButton(Buttons::ButtonType::Inst3); 64 setButton(Buttons::ButtonType::Global); 65 break; 66 case BootMode::WaitForSystemDump: 67 setButton(Buttons::ButtonType::Shift); 68 setButton(Buttons::ButtonType::Global); 69 break; 70 case BootMode::DspClockResetAndServiceMode: 71 setButton(Buttons::ButtonType::Multi); 72 break; 73 case BootMode::ServiceMode: 74 setButton(Buttons::ButtonType::Global); 75 break; 76 case BootMode::MemoryGame: 77 setButton(Buttons::ButtonType::Global); 78 setButton(Buttons::ButtonType::Play); 79 break; 80 } 81 } 82 83 void Hardware::resetMidiCounter() 84 { 85 // wait for DSP to enter blocking state 86 87 const auto& esai = m_dsps.front().getPeriph().getEsai(); 88 89 auto& inputs = esai.getAudioInputs(); 90 auto& outputs = esai.getAudioOutputs(); 91 92 while(inputs.size() > 2 && !outputs.full()) 93 std::this_thread::yield(); 94 95 m_midiOffsetCounter = 0; 96 } 97 98 void Hardware::hdiProcessUCtoDSPNMIIrq() 99 { 100 // QS6 is connected to DSP NMI pin but I've never seen this being triggered 101 #if SUPPORT_NMI_INTERRUPT 102 const uint8_t requestNMI = m_uc.requestDSPinjectNMI(); 103 104 if(m_requestNMI && !requestNMI) 105 { 106 // LOG("uc request DSP NMI"); 107 m_dsps.front().hdiSendIrqToDSP(dsp56k::Vba_NMI); 108 109 m_requestNMI = requestNMI; 110 } 111 #endif 112 } 113 114 void Hardware::initVoiceExpansion() 115 { 116 if (m_dsps.size() < 3) 117 { 118 setupEsaiListener(); 119 return; 120 } 121 /* 122 m_dsps[1].getPeriph().getPortC().hostWrite(0x10); // set bit 4 of GPIO Port C, vexp DSPs are waiting for this 123 m_dsps[2].getPeriph().getPortC().hostWrite(0x10); // set bit 4 of GPIO Port C, vexp DSPs are waiting for this 124 125 bool done = false; 126 127 auto& esaiA = m_dsps[0].getPeriph().getEsai(); 128 auto& esaiB = m_dsps[1].getPeriph().getEsai(); 129 auto& esaiC = m_dsps[2].getPeriph().getEsai(); 130 131 esaiA.setCallback([&](dsp56k::Audio*) 132 { 133 }, 0); 134 135 esaiB.setCallback([&](dsp56k::Audio*) 136 { 137 }, 0); 138 139 esaiC.setCallback([&](dsp56k::Audio*) 140 { 141 }, 0); 142 143 while (!m_dsps.front().receivedMagicEsaiPacket()) 144 { 145 // vexp1 only needs the audio input 146 esaiB.getAudioInputs().push_back({0}); 147 148 // transfer output from vexp1 to vexp2 149 auto out = esaiB.getAudioOutputs().pop_front(); 150 std::array<dsp56k::TWord, 4> in{ out[0], out[1], out[2], 0}; 151 esaiC.getAudioInputs().push_back(in); 152 153 // read output of vexp2 and send to main 154 out = esaiC.getAudioOutputs().pop_front(); 155 156 // this should consist of RX0 = audio input, RX1/2 = vexp2 output TX1/TX2 157 in = {0, out[1], out[2]}; 158 esaiA.getAudioInputs().push_back(in); 159 160 // final output 0,1,2 = audio outs 161 out = esaiA.getAudioOutputs().pop_front(); 162 } 163 LOG("Voice Expansion initialization completed"); 164 setupEsaiListener(); 165 */ 166 } 167 168 void Hardware::setupEsaiListener() 169 { 170 auto& esaiA = m_dsps.front().getPeriph().getEsai(); 171 172 esaiA.setCallback([&](dsp56k::Audio*) 173 { 174 onEsaiCallback(esaiA); 175 }, 0); 176 } 177 178 void Hardware::processUcCycle() 179 { 180 syncUcToDSP(); 181 182 const auto deltaCycles = m_uc.exec(); 183 if(m_esaiFrameIndex > 0) 184 m_remainingUcCycles -= static_cast<int64_t>(deltaCycles); 185 186 for (auto& dsp : m_dsps) 187 dsp.transferHostFlagsUc2Dsdp(); 188 189 hdiProcessUCtoDSPNMIIrq(); 190 191 for (auto& dsp : m_dsps) 192 dsp.hdiTransferDSPtoUC(); 193 194 if(m_uc.requestDSPReset()) 195 { 196 for (auto& dsp : m_dsps) 197 { 198 if(dsp.haveSentTXToDSP()) 199 { 200 m_uc.dumpMemory("DSPreset"); 201 assert(false && "DSP needs reset even though it got data already. Needs impl"); 202 } 203 } 204 m_uc.notifyDSPBooted(); 205 } 206 } 207 208 void Hardware::setGlobalDefaultParameters() 209 { 210 m_midi.write({0xf0,0x3e,0x10,0x7f,0x24,0x00,0x07,0x02,0xf7}); // Control Send = SysEx 211 m_midi.write({0xf0,0x3e,0x10,0x7f,0x24,0x00,0x08,0x01,0xf7}); // Control Receive = on 212 m_bootCompleted = true; 213 } 214 215 void Hardware::processAudio(uint32_t _frames, uint32_t _latency) 216 { 217 ensureBufferSize(_frames); 218 219 if(m_esaiFrameIndex == 0) 220 return; 221 222 m_midi.process(_frames); 223 224 m_processAudio = true; 225 226 auto& esai = m_dsps.front().getPeriph().getEsai(); 227 228 const dsp56k::TWord* inputs[16]{nullptr}; 229 dsp56k::TWord* outputs[16]{nullptr}; 230 231 // TODO: Right audio input channel needs to be delayed by one frame 232 233 inputs[0] = &m_audioInputs[0].front(); 234 inputs[1] = &m_audioInputs[1].front(); 235 inputs[2] = m_dummyInput.data(); 236 inputs[3] = m_dummyInput.data(); 237 inputs[4] = m_dummyInput.data(); 238 inputs[5] = m_dummyInput.data(); 239 inputs[6] = m_dummyInput.data(); 240 inputs[7] = m_dummyInput.data(); 241 242 outputs[1] = &m_audioOutputs[0].front(); 243 outputs[0] = &m_audioOutputs[1].front(); 244 outputs[3] = &m_audioOutputs[2].front(); 245 outputs[2] = &m_audioOutputs[3].front(); 246 outputs[5] = &m_audioOutputs[4].front(); 247 outputs[4] = &m_audioOutputs[5].front(); 248 outputs[6] = m_dummyOutput.data(); 249 outputs[7] = m_dummyOutput.data(); 250 outputs[8] = m_dummyOutput.data(); 251 outputs[9] = m_dummyOutput.data(); 252 outputs[10] = m_dummyOutput.data(); 253 outputs[11] = m_dummyOutput.data(); 254 255 const auto totalFrames = _frames; 256 257 while (_frames) 258 { 259 const auto processCount = std::min(_frames, static_cast<uint32_t>(1024)); 260 _frames -= processCount; 261 262 if constexpr (g_useVoiceExpansion) 263 { 264 auto& esaiA = m_dsps[0].getPeriph().getEsai(); 265 auto& esaiB = m_dsps[1].getPeriph().getEsai(); 266 auto& esaiC = m_dsps[2].getPeriph().getEsai(); 267 /* 268 const auto tccrA = esaiA.getTccrAsString(); const auto rccrA = esaiA.getRccrAsString(); 269 const auto tcrA = esaiA.getTcrAsString(); const auto rcrA = esaiA.getRcrAsString(); 270 271 const auto tccrB = esaiB.getTccrAsString(); const auto rccrB = esaiB.getRccrAsString(); 272 const auto tcrB = esaiB.getTcrAsString(); const auto rcrB = esaiB.getRcrAsString(); 273 274 const auto tccrC = esaiC.getTccrAsString(); const auto rccrC = esaiC.getRccrAsString(); 275 const auto tcrC = esaiC.getTcrAsString(); const auto rcrC = esaiC.getRcrAsString(); 276 277 LOG("ESAI DSPmain:\n" << tccrA << '\n' << tcrA << '\n' << rccrA << '\n' << rcrA << '\n'); 278 LOG("ESAI VexpA:\n" << tccrB << '\n' << tcrB << '\n' << rccrB << '\n' << rcrB << '\n'); 279 LOG("ESAI VexpB:\n" << tccrC << '\n' << tcrC << '\n' << rccrC << '\n' << rcrC << '\n'); 280 */ 281 // vexp1 only needs the audio input 282 esaiB.processAudioInputInterleaved(inputs, processCount); 283 284 // transfer output from vexp1 to vexp2 285 esaiB.processAudioOutputInterleaved(outputs, processCount); 286 287 const dsp56k::TWord* in[] = { outputs[0], outputs[1], outputs[2], outputs[3], outputs[4], outputs[5], nullptr, nullptr }; 288 esaiC.processAudioInputInterleaved(in, processCount); 289 290 // read output of vexp2 and send to main 291 esaiC.processAudioOutputInterleaved(outputs, processCount); 292 293 // RX1/2 = vexp2 output TX1/TX2 294 const dsp56k::TWord* inA[] = { inputs[1], inputs[0], outputs[2], outputs[3], outputs[4], outputs[5], nullptr, nullptr }; 295 esaiA.processAudioInputInterleaved(inA, processCount); 296 297 // final output 0,1,2 = audio outs below 298 } 299 else 300 { 301 esai.processAudioInputInterleaved(inputs, processCount, _latency); 302 } 303 304 const auto requiredSize = processCount > 8 ? processCount - 8 : 0; 305 306 if(esai.getAudioOutputs().size() < requiredSize) 307 { 308 // 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 309 310 std::unique_lock uLock(m_requestedFramesAvailableMutex); 311 m_requestedFrames = requiredSize; 312 m_requestedFramesAvailableCv.wait(uLock, [&]() 313 { 314 if(esai.getAudioOutputs().size() < requiredSize) 315 return false; 316 m_requestedFrames = 0; 317 return true; 318 }); 319 } 320 321 esai.processAudioOutputInterleaved(outputs, processCount); 322 323 if constexpr (g_useVoiceExpansion) 324 { 325 for (uint32_t i = 1; i < 3; ++i) 326 { 327 auto& e = m_dsps[i].getPeriph().getEsai(); 328 329 dsp56k::TWord* outs[16]{ nullptr }; 330 if (e.getAudioOutputs().size() >= 512) 331 e.processAudioOutputInterleaved(outs, static_cast<uint32_t>(e.getAudioOutputs().size() >> 1)); 332 } 333 } 334 335 inputs[0] += processCount; 336 inputs[1] += processCount; 337 338 outputs[0] += processCount; 339 outputs[1] += processCount; 340 outputs[2] += processCount; 341 outputs[3] += processCount; 342 outputs[4] += processCount; 343 outputs[5] += processCount; 344 } 345 346 m_processAudio = false; 347 } 348 349 void Hardware::ensureBufferSize(uint32_t _frames) 350 { 351 if(m_audioInputs.front().size() < _frames) 352 { 353 for (auto& input : m_audioInputs) 354 input.resize(_frames); 355 } 356 357 if(m_audioOutputs.front().size() < _frames) 358 { 359 for (auto& output : m_audioOutputs) 360 output.resize(_frames); 361 } 362 363 if(m_dummyInput.size() < _frames) 364 m_dummyInput.resize(_frames); 365 if(m_dummyOutput.size() < _frames) 366 m_dummyOutput.resize(_frames); 367 } 368 }