processor.cpp (23587B)
1 #include "processor.h" 2 3 #include <chrono> 4 5 #include "dummydevice.h" 6 #include "pluginVersion.h" 7 #include "tools.h" 8 #include "types.h" 9 10 #include "baseLib/binarystream.h" 11 #include "baseLib/filesystem.h" 12 13 #include "bridgeLib/commands.h" 14 15 #include "client/remoteDevice.h" 16 17 #include "synthLib/deviceException.h" 18 #include "synthLib/os.h" 19 #include "synthLib/midiBufferParser.h" 20 #include "synthLib/romLoader.h" 21 22 #include "dsp56kEmu/fastmath.h" 23 #include "dsp56kEmu/logging.h" 24 25 #include "juceUiLib/messageBox.h" 26 27 namespace synthLib 28 { 29 class DeviceException; 30 } 31 32 namespace pluginLib 33 { 34 constexpr char g_saveMagic[] = "DSP56300"; 35 constexpr uint32_t g_saveVersion = 2; 36 37 bridgeLib::SessionId generateRemoteSessionId() 38 { 39 return std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::system_clock::now().time_since_epoch()).count(); 40 } 41 42 Processor::Processor(const BusesProperties& _busesProperties, Properties _properties) 43 : juce::AudioProcessor(_busesProperties) 44 , m_properties(std::move(_properties)) 45 , m_midiPorts(*this) 46 , m_remoteSessionId(generateRemoteSessionId()) 47 { 48 juce::File(getPublicRomFolder()).createDirectory(); 49 50 synthLib::RomLoader::addSearchPath(getPublicRomFolder()); 51 synthLib::RomLoader::addSearchPath(synthLib::getModulePath(true)); 52 synthLib::RomLoader::addSearchPath(synthLib::getModulePath(false)); 53 } 54 55 Processor::~Processor() 56 { 57 destroyController(); 58 m_plugin.reset(); 59 m_device.reset(); 60 } 61 62 void Processor::addMidiEvent(const synthLib::SMidiEvent& ev) 63 { 64 getPlugin().addMidiEvent(ev); 65 } 66 67 void Processor::handleIncomingMidiMessage(juce::MidiInput *_source, const juce::MidiMessage &_message) 68 { 69 synthLib::SMidiEvent sm(synthLib::MidiEventSource::PhysicalInput); 70 71 const auto* raw = _message.getSysExData(); 72 if (raw) 73 { 74 const auto count = _message.getSysExDataSize(); 75 auto syx = pluginLib::SysEx(); 76 syx.push_back(0xf0); 77 for (int i = 0; i < count; i++) 78 { 79 syx.push_back(raw[i]); 80 } 81 syx.push_back(0xf7); 82 sm.sysex = std::move(syx); 83 84 getController().enqueueMidiMessages({sm}); 85 addMidiEvent(sm); 86 } 87 else 88 { 89 const auto count = _message.getRawDataSize(); 90 const auto* rawData = _message.getRawData(); 91 if (count >= 1 && count <= 3) 92 { 93 sm.a = rawData[0]; 94 sm.b = count > 1 ? rawData[1] : 0; 95 sm.c = count > 2 ? rawData[2] : 0; 96 } 97 else 98 { 99 auto syx = SysEx(); 100 for (int i = 0; i < count; i++) 101 syx.push_back(rawData[i]); 102 sm.sysex = syx; 103 } 104 105 getController().enqueueMidiMessages({sm}); 106 addMidiEvent(sm); 107 } 108 } 109 110 Controller& Processor::getController() 111 { 112 if (m_controller == nullptr) 113 m_controller.reset(createController()); 114 115 return *m_controller; 116 } 117 118 synthLib::Plugin& Processor::getPlugin() 119 { 120 if(m_plugin) 121 return *m_plugin; 122 123 try 124 { 125 m_device.reset(createDevice()); 126 if(!m_device->isValid()) 127 throw synthLib::DeviceException(synthLib::DeviceError::Unknown, "Device initialization failed"); 128 } 129 catch(const synthLib::DeviceException& e) 130 { 131 LOG("Failed to create device: " << e.what()); 132 133 // Juce loads the LV2/VST3 versions of the plugin as part of the build process, if we open a message box in this case, the build process gets stuck 134 const auto host = juce::PluginHostType::getHostPath(); 135 if(!Tools::isHeadless()) 136 { 137 std::string msg = e.what(); 138 139 m_deviceError = e.errorCode(); 140 141 if(e.errorCode() == synthLib::DeviceError::FirmwareMissing) 142 { 143 msg += "\n\n"; 144 msg += "The firmware file needs to be copied to\n"; 145 msg += baseLib::filesystem::validatePath(getPublicRomFolder()) + "\n"; 146 msg += "\n"; 147 msg += "The target folder will be opened once you click OK. Copy the firmware to this folder and reload the plugin."; 148 #ifdef _DEBUG 149 msg += "\n\n" + std::string("[Debug] Host ") + host.toStdString() + "\n\n"; 150 #endif 151 } 152 juce::Timer::callAfterDelay(2000, [this, msg] 153 { 154 genericUI::MessageBox::showOk(juce::MessageBoxIconType::WarningIcon, 155 "Device Initialization failed", msg, 156 [this] 157 { 158 const auto path = juce::File(getPublicRomFolder()); 159 (void)path.createDirectory(); 160 path.revealToUser(); 161 } 162 ); 163 }); 164 } 165 } 166 167 if(!m_device) 168 { 169 m_device.reset(new DummyDevice({})); 170 } 171 172 m_device->setDspClockPercent(m_dspClockPercent); 173 174 m_plugin.reset(new synthLib::Plugin(m_device.get(), [this](synthLib::Device* _device) 175 { 176 return onDeviceInvalid(_device); 177 })); 178 179 return *m_plugin; 180 } 181 182 bridgeClient::RemoteDevice* Processor::createRemoteDevice(const synthLib::DeviceCreateParams& _params) 183 { 184 bridgeLib::PluginDesc desc; 185 getPluginDesc(desc); 186 return new bridgeClient::RemoteDevice(_params, std::move(desc), m_remoteHost, m_remotePort); 187 } 188 189 void Processor::getRemoteDeviceParams(synthLib::DeviceCreateParams& _params) const 190 { 191 _params.preferredSamplerate = getPreferredDeviceSamplerate(); 192 _params.hostSamplerate = getHostSamplerate(); 193 } 194 195 bridgeClient::RemoteDevice* Processor::createRemoteDevice() 196 { 197 synthLib::DeviceCreateParams params; 198 getRemoteDeviceParams(params); 199 return createRemoteDevice(params); 200 } 201 202 synthLib::Device* Processor::createDevice(const DeviceType _type) 203 { 204 switch (_type) 205 { 206 case DeviceType::Local: return createDevice(); 207 case DeviceType::Remote: return createRemoteDevice(); 208 case DeviceType::Dummy: return new DummyDevice({}); 209 } 210 return nullptr; 211 } 212 213 bool Processor::setLatencyBlocks(uint32_t _blocks) 214 { 215 if (!getPlugin().setLatencyBlocks(_blocks)) 216 return false; 217 updateLatencySamples(); 218 return true; 219 } 220 221 void Processor::updateLatencySamples() 222 { 223 if(getProperties().isSynth) 224 setLatencySamples(getPlugin().getLatencyMidiToOutput()); 225 else 226 setLatencySamples(getPlugin().getLatencyInputToOutput()); 227 } 228 229 void Processor::saveCustomData(std::vector<uint8_t>& _targetBuffer) 230 { 231 baseLib::BinaryStream s; 232 saveChunkData(s); 233 s.toVector(_targetBuffer, true); 234 } 235 236 void Processor::saveChunkData(baseLib::BinaryStream& s) 237 { 238 { 239 std::vector<uint8_t> buffer; 240 getPlugin().getState(buffer, synthLib::StateTypeGlobal); 241 242 baseLib::ChunkWriter cw(s, "MIDI", 1); 243 s.write(buffer); 244 } 245 { 246 baseLib::ChunkWriter cw(s, "GAIN", 1); 247 s.write<uint32_t>(1); // version 248 s.write(m_inputGain); 249 s.write(m_outputGain); 250 } 251 252 if(m_dspClockPercent != 100) 253 { 254 baseLib::ChunkWriter cw(s, "DSPC", 1); 255 s.write(m_dspClockPercent); 256 } 257 258 if(m_preferredDeviceSamplerate > 0) 259 { 260 baseLib::ChunkWriter cw(s, "DSSR", 1); 261 s.write(m_preferredDeviceSamplerate); 262 } 263 264 m_midiPorts.saveChunkData(s); 265 } 266 267 bool Processor::loadCustomData(const std::vector<uint8_t>& _sourceBuffer) 268 { 269 if(_sourceBuffer.empty()) 270 return true; 271 272 // In Vavra, the only data we had was the gain parameters 273 if(_sourceBuffer.size() == sizeof(float) * 2 + sizeof(uint32_t)) 274 { 275 baseLib::BinaryStream ss(_sourceBuffer); 276 readGain(ss); 277 return true; 278 } 279 280 baseLib::BinaryStream s(_sourceBuffer); 281 baseLib::ChunkReader cr(s); 282 283 loadChunkData(cr); 284 285 return _sourceBuffer.empty() || (cr.tryRead() && cr.numRead() > 0); 286 } 287 288 void Processor::loadChunkData(baseLib::ChunkReader& _cr) 289 { 290 _cr.add("MIDI", 1, [this](baseLib::BinaryStream& _binaryStream, uint32_t _version) 291 { 292 std::vector<uint8_t> buffer; 293 _binaryStream.read(buffer); 294 getPlugin().setState(buffer); 295 }); 296 297 _cr.add("GAIN", 1, [this](baseLib::BinaryStream& _binaryStream, uint32_t _version) 298 { 299 readGain(_binaryStream); 300 }); 301 302 _cr.add("DSPC", 1, [this](baseLib::BinaryStream& _binaryStream, uint32_t _version) 303 { 304 auto p = _binaryStream.read<uint32_t>(); 305 p = dsp56k::clamp<uint32_t>(p, 50, 200); 306 setDspClockPercent(p); 307 }); 308 309 _cr.add("DSSR", 1, [this](baseLib::BinaryStream& _binaryStream, uint32_t _version) 310 { 311 const auto sr = _binaryStream.read<float>(); 312 setPreferredDeviceSamplerate(sr); 313 }); 314 315 m_midiPorts.loadChunkData(_cr); 316 } 317 318 void Processor::readGain(baseLib::BinaryStream& _s) 319 { 320 const auto version = _s.read<uint32_t>(); 321 if (version != 1) 322 return; 323 m_inputGain = _s.read<float>(); 324 m_outputGain = _s.read<float>(); 325 } 326 327 bool Processor::setDspClockPercent(const uint32_t _percent) 328 { 329 if(!m_device) 330 return false; 331 if(!m_device->setDspClockPercent(_percent)) 332 return false; 333 m_dspClockPercent = _percent; 334 return true; 335 } 336 337 uint32_t Processor::getDspClockPercent() const 338 { 339 if(!m_device) 340 return m_dspClockPercent; 341 return m_device->getDspClockPercent(); 342 } 343 344 uint64_t Processor::getDspClockHz() const 345 { 346 if(!m_device) 347 return 0; 348 return m_device->getDspClockHz(); 349 } 350 351 bool Processor::setPreferredDeviceSamplerate(const float _samplerate) 352 { 353 m_preferredDeviceSamplerate = _samplerate; 354 355 if(!m_device) 356 return false; 357 358 return getPlugin().setPreferredDeviceSamplerate(_samplerate); 359 } 360 361 float Processor::getPreferredDeviceSamplerate() const 362 { 363 return m_preferredDeviceSamplerate; 364 } 365 366 std::vector<float> Processor::getDeviceSupportedSamplerates() const 367 { 368 if(!m_device) 369 return {}; 370 std::vector<float> result; 371 m_device->getSupportedSamplerates(result); 372 return result; 373 } 374 375 std::vector<float> Processor::getDevicePreferredSamplerates() const 376 { 377 if(!m_device) 378 return {}; 379 std::vector<float> result; 380 m_device->getPreferredSamplerates(result); 381 return result; 382 } 383 384 std::optional<std::pair<const char*, uint32_t>> Processor::findResource(const std::string& _filename) const 385 { 386 const auto& bd = m_properties.binaryData; 387 388 for(uint32_t i=0; i<bd.listSize; ++i) 389 { 390 if (bd.originalFileNames[i] != _filename) 391 continue; 392 393 int size = 0; 394 const auto res = bd.getNamedResourceFunc(bd.namedResourceList[i], size); 395 return {std::make_pair(res, static_cast<uint32_t>(size))}; 396 } 397 return {}; 398 } 399 400 std::string Processor::getDataFolder(const bool _useFxFolder) const 401 { 402 return Tools::getPublicDataFolder(getProperties().vendor, getProductName(_useFxFolder)); 403 } 404 405 std::string Processor::getPublicRomFolder() const 406 { 407 return baseLib::filesystem::validatePath(getDataFolder() + "roms/"); 408 } 409 410 std::string Processor::getConfigFolder(const bool _useFxFolder) const 411 { 412 return baseLib::filesystem::validatePath(getDataFolder(_useFxFolder) + "config/"); 413 } 414 415 std::string Processor::getPatchManagerDataFolder(bool _useFxFolder) const 416 { 417 return baseLib::filesystem::validatePath(getDataFolder(_useFxFolder) + "patchmanager/"); 418 } 419 420 std::string Processor::getConfigFile(const bool _useFxFolder) const 421 { 422 return getConfigFolder(_useFxFolder) + getProductName(_useFxFolder) + ".xml"; 423 } 424 425 std::string Processor::getProductName(const bool _useFxName) const 426 { 427 const auto& p = getProperties(); 428 auto name = p.name; 429 if(!_useFxName && !p.isSynth && name.substr(name.size()-2, 2) == "FX") 430 return name.substr(0, name.size() - 2); 431 return name; 432 } 433 434 void Processor::getPluginDesc(bridgeLib::PluginDesc& _desc) const 435 { 436 _desc.plugin4CC = getProperties().plugin4CC; 437 _desc.pluginName = getProperties().name; 438 _desc.pluginVersion = Version::getVersionNumber(); 439 _desc.sessionId = m_remoteSessionId; 440 } 441 442 void Processor::setDeviceType(const DeviceType _type, const bool _forceChange/* = false*/) 443 { 444 if(m_deviceType == _type && !_forceChange) 445 return; 446 447 try 448 { 449 if(auto* dev = createDevice(_type)) 450 { 451 getPlugin().setDevice(dev); 452 (void)m_device.release(); 453 m_device.reset(dev); 454 m_deviceType = _type; 455 } 456 } 457 catch(synthLib::DeviceException& e) 458 { 459 genericUI::MessageBox::showOk(juce::MessageBoxIconType::WarningIcon, 460 getName().toStdString() + " - Failed to switch device type", 461 std::string("Failed to create device:\n\n") + 462 e.what() + "\n\n"); 463 } 464 465 if(_type != DeviceType::Remote) 466 m_remoteSessionId = generateRemoteSessionId(); 467 } 468 469 void Processor::setRemoteDevice(const std::string& _host, const uint32_t _port) 470 { 471 if(m_remotePort == _port && m_remoteHost == _host && m_deviceType == DeviceType::Remote) 472 return; 473 474 m_remoteHost = _host; 475 m_remotePort = _port; 476 setDeviceType(DeviceType::Remote, true); 477 } 478 479 void Processor::destroyController() 480 { 481 m_controller.reset(); 482 } 483 484 //============================================================================== 485 void Processor::prepareToPlay(double sampleRate, int samplesPerBlock) 486 { 487 // Use this method as the place to do any pre-playback 488 // initialisation that you need 489 m_hostSamplerate = static_cast<float>(sampleRate); 490 491 getPlugin().setHostSamplerate(static_cast<float>(sampleRate), m_preferredDeviceSamplerate); 492 getPlugin().setBlockSize(samplesPerBlock); 493 494 updateLatencySamples(); 495 } 496 497 void Processor::releaseResources() 498 { 499 // When playback stops, you can use this as an opportunity to free up any 500 // spare memory, etc. 501 } 502 503 bool Processor::isBusesLayoutSupported(const BusesLayout& _busesLayout) const 504 { 505 // This is the place where you check if the layout is supported. 506 // In this template code we only support mono or stereo. 507 // Some plugin hosts, such as certain GarageBand versions, will only 508 // load plugins that support stereo bus layouts. 509 if (_busesLayout.getMainOutputChannelSet() != juce::AudioChannelSet::stereo()) 510 return false; 511 512 // This checks if the input is stereo 513 if (_busesLayout.getMainInputChannelSet() != juce::AudioChannelSet::stereo()) 514 return false; 515 516 return true; 517 } 518 519 //============================================================================== 520 void Processor::getStateInformation (juce::MemoryBlock& destData) 521 { 522 // You should use this method to store your parameters in the memory block. 523 // You could do that either as raw data, or use the XML or ValueTree classes 524 // as intermediaries to make it easy to save and load complex data. 525 #if !SYNTHLIB_DEMO_MODE 526 PluginStream ss; 527 ss.write(g_saveMagic); 528 ss.write(g_saveVersion); 529 std::vector<uint8_t> buffer; 530 saveCustomData(buffer); 531 ss.write(buffer); 532 533 std::vector<uint8_t> buf; 534 ss.toVector(buf); 535 536 destData.append(buf.data(), buf.size()); 537 #endif 538 } 539 540 void Processor::setStateInformation (const void* _data, const int _sizeInBytes) 541 { 542 #if !SYNTHLIB_DEMO_MODE 543 // You should use this method to restore your parameters from this memory block, 544 // whose contents will have been created by the getStateInformation() call. 545 setState(_data, _sizeInBytes); 546 #endif 547 } 548 549 void Processor::getCurrentProgramStateInformation(juce::MemoryBlock& destData) 550 { 551 #if !SYNTHLIB_DEMO_MODE 552 std::vector<uint8_t> state; 553 getPlugin().getState(state, synthLib::StateTypeCurrentProgram); 554 destData.append(state.data(), state.size()); 555 #endif 556 } 557 558 void Processor::setCurrentProgramStateInformation(const void* data, int sizeInBytes) 559 { 560 #if !SYNTHLIB_DEMO_MODE 561 setState(data, sizeInBytes); 562 #endif 563 } 564 565 const juce::String Processor::getName() const 566 { 567 return getProperties().name; 568 } 569 570 bool Processor::acceptsMidi() const 571 { 572 return getProperties().wantsMidiInput; 573 } 574 575 bool Processor::producesMidi() const 576 { 577 return getProperties().producesMidiOut; 578 } 579 580 bool Processor::isMidiEffect() const 581 { 582 return getProperties().isMidiEffect; 583 } 584 585 void Processor::processBlock(juce::AudioBuffer<float>& buffer, juce::MidiBuffer& midiMessages) 586 { 587 juce::ScopedNoDenormals noDenormals; 588 const auto totalNumInputChannels = getTotalNumInputChannels(); 589 const auto totalNumOutputChannels = getTotalNumOutputChannels(); 590 591 const int numSamples = buffer.getNumSamples(); 592 593 // In case we have more outputs than inputs, this code clears any output 594 // channels that didn't contain input data, (because these aren't 595 // guaranteed to be empty - they may contain garbage). 596 // This is here to avoid people getting screaming feedback 597 // when they first compile a plugin, but obviously you don't need to keep 598 // this code if your algorithm always overwrites all the output channels. 599 for (auto i = totalNumInputChannels; i < totalNumOutputChannels; ++i) 600 buffer.clear (i, 0, numSamples); 601 602 // This is the place where you'd normally do the guts of your plugin's 603 // audio processing... 604 // Make sure to reset the state if your inner loop is processing 605 // the samples and the outer loop is handling the channels. 606 // Alternatively, you can process the samples with the channels 607 // interleaved by keeping the same state. 608 609 synthLib::TAudioInputs inputs{}; 610 synthLib::TAudioOutputs outputs{}; 611 612 for (int channel = 0; channel < totalNumInputChannels; ++channel) 613 inputs[channel] = buffer.getReadPointer(channel); 614 615 for (int channel = 0; channel < totalNumOutputChannels; ++channel) 616 outputs[channel] = buffer.getWritePointer(channel); 617 618 for(const auto metadata : midiMessages) 619 { 620 const auto message = metadata.getMessage(); 621 622 synthLib::SMidiEvent ev(synthLib::MidiEventSource::Host); 623 624 if(message.isSysEx() || message.getRawDataSize() > 3) 625 { 626 ev.sysex.resize(message.getRawDataSize()); 627 memcpy(ev.sysex.data(), message.getRawData(), ev.sysex.size()); 628 629 // Juce bug? Or VSTHost bug? Juce inserts f0/f7 when converting VST3 midi packet to Juce packet, but it's already there 630 if(ev.sysex.size() > 1) 631 { 632 if(ev.sysex.front() == 0xf0 && ev.sysex[1] == 0xf0) 633 ev.sysex.erase(ev.sysex.begin()); 634 635 if(ev.sysex.size() > 1) 636 { 637 if(ev.sysex[ev.sysex.size()-1] == 0xf7 && ev.sysex[ev.sysex.size()-2] == 0xf7) 638 ev.sysex.erase(ev.sysex.begin()); 639 } 640 } 641 } 642 else 643 { 644 ev.a = message.getRawData()[0]; 645 ev.b = message.getRawDataSize() > 0 ? message.getRawData()[1] : 0; 646 ev.c = message.getRawDataSize() > 1 ? message.getRawData()[2] : 0; 647 648 const auto status = ev.a & 0xf0; 649 650 if(status == synthLib::M_CONTROLCHANGE || status == synthLib::M_POLYPRESSURE || status == synthLib::M_PROGRAMCHANGE) 651 { 652 // forward to UI to react to control input changes that should move knobs 653 getController().enqueueMidiMessages({ev}); 654 } 655 } 656 657 ev.offset = metadata.samplePosition; 658 659 getPlugin().addMidiEvent(ev); 660 } 661 662 midiMessages.clear(); 663 664 bool isPlaying = true; 665 float bpm = 0.0f; 666 float ppqPos = 0.0f; 667 668 if(const auto* playHead = getPlayHead()) 669 { 670 if(auto pos = playHead->getPosition()) 671 { 672 isPlaying = pos->getIsPlaying(); 673 674 if(pos->getBpm()) 675 { 676 bpm = static_cast<float>(*pos->getBpm()); 677 processBpm(bpm); 678 } 679 if(pos->getPpqPosition()) 680 { 681 ppqPos = static_cast<float>(*pos->getPpqPosition()); 682 } 683 } 684 } 685 686 getPlugin().process(inputs, outputs, numSamples, bpm, ppqPos, isPlaying); 687 688 applyOutputGain(outputs, numSamples); 689 690 m_midiOut.clear(); 691 getPlugin().getMidiOut(m_midiOut); 692 693 if (!m_midiOut.empty()) 694 { 695 getController().enqueueMidiMessages(m_midiOut); 696 697 for (auto& e : m_midiOut) 698 { 699 if (e.source == synthLib::MidiEventSource::Editor || e.source == synthLib::MidiEventSource::Internal) 700 continue; 701 702 auto toJuceMidiMessage = [&e]() 703 { 704 if(!e.sysex.empty()) 705 { 706 assert(e.sysex.front() == 0xf0); 707 assert(e.sysex.back() == 0xf7); 708 709 return juce::MidiMessage(e.sysex.data(), static_cast<int>(e.sysex.size()), 0.0); 710 } 711 const auto len = synthLib::MidiBufferParser::lengthFromStatusByte(e.a); 712 if(len == 1) 713 return juce::MidiMessage(e.a, 0.0); 714 if(len == 2) 715 return juce::MidiMessage(e.a, e.b, 0.0); 716 return juce::MidiMessage(e.a, e.b, e.c, 0.0); 717 }; 718 719 const juce::MidiMessage message = toJuceMidiMessage(); 720 midiMessages.addEvent(message, 0); 721 722 // additionally send to the midi output we've selected in the editor 723 if (auto* out = m_midiPorts.getMidiOutput()) 724 out->sendMessageNow(message); 725 } 726 } 727 } 728 729 void Processor::processBlockBypassed(juce::AudioBuffer<float>& _buffer, juce::MidiBuffer& _midiMessages) 730 { 731 if(getProperties().isSynth || getTotalNumInputChannels() <= 0) 732 { 733 _buffer.clear(0, _buffer.getNumSamples()); 734 return; 735 } 736 737 const auto sampleCount = static_cast<uint32_t>(_buffer.getNumSamples()); 738 const auto outCount = static_cast<uint32_t>(getTotalNumOutputChannels()); 739 const auto inCount = static_cast<uint32_t>(getTotalNumInputChannels()); 740 741 uint32_t inCh = 0; 742 743 for(uint32_t outCh=0; outCh<outCount; ++outCh) 744 { 745 auto* input = _buffer.getReadPointer(static_cast<int>(inCh)); 746 auto* output = _buffer.getWritePointer(static_cast<int>(outCh)); 747 748 m_bypassBuffer.write(input, outCh, sampleCount, getLatencySamples()); 749 m_bypassBuffer.read(output, outCh, sampleCount); 750 751 ++inCh; 752 753 if(inCh >= inCount) 754 inCh = 0; 755 } 756 757 // AudioProcessor::processBlockBypassed(_buffer, _midiMessages); 758 } 759 760 #if !SYNTHLIB_DEMO_MODE 761 void Processor::setState(const void* _data, const size_t _sizeInBytes) 762 { 763 if(_sizeInBytes < 1) 764 return; 765 766 std::vector<uint8_t> state; 767 state.resize(_sizeInBytes); 768 memcpy(state.data(), _data, _sizeInBytes); 769 770 PluginStream ss(state); 771 772 if (ss.checkString(g_saveMagic)) 773 { 774 try 775 { 776 const std::string magic = ss.readString(); 777 778 if (magic != g_saveMagic) 779 return; 780 781 const auto version = ss.read<uint32_t>(); 782 783 if (version > g_saveVersion) 784 return; 785 786 std::vector<uint8_t> buffer; 787 788 if(version == 1) 789 { 790 ss.read(buffer); 791 getPlugin().setState(buffer); 792 } 793 794 ss.read(buffer); 795 796 if(!buffer.empty()) 797 { 798 try 799 { 800 loadCustomData(buffer); 801 } 802 catch (std::range_error&) 803 { 804 } 805 } 806 } 807 catch (std::range_error& e) 808 { 809 LOG("Failed to read state: " << e.what()); 810 return; 811 } 812 } 813 else 814 { 815 getPlugin().setState(state); 816 } 817 818 if (hasController()) 819 getController().onStateLoaded(); 820 } 821 #endif 822 823 //============================================================================== 824 825 int Processor::getNumPrograms() 826 { 827 return 1; // NB: some hosts don't cope very well if you tell them there are 0 programs, 828 // so this should be at least 1, even if you're not really implementing programs. 829 } 830 831 int Processor::getCurrentProgram() 832 { 833 return 0; 834 } 835 836 void Processor::setCurrentProgram(int _index) 837 { 838 juce::ignoreUnused(_index); 839 } 840 841 const juce::String Processor::getProgramName(int _index) 842 { 843 juce::ignoreUnused(_index); 844 return "default"; 845 } 846 847 void Processor::changeProgramName(int _index, const juce::String& _newName) 848 { 849 juce::ignoreUnused(_index, _newName); 850 } 851 852 double Processor::getTailLengthSeconds() const 853 { 854 return 0.0f; 855 } 856 857 synthLib::Device* Processor::onDeviceInvalid(synthLib::Device* _device) 858 { 859 if(dynamic_cast<bridgeClient::RemoteDevice*>(_device)) 860 { 861 try 862 { 863 // attempt one reconnect 864 auto* newDevice = createRemoteDevice(); 865 if(newDevice && newDevice->isValid()) 866 { 867 m_device.reset(newDevice); 868 return newDevice; 869 } 870 } 871 catch (synthLib::DeviceException& e) 872 { 873 juce::MessageManager::callAsync([e] 874 { 875 genericUI::MessageBox::showOk(juce::MessageBoxIconType::WarningIcon, 876 "Device creation failed:", 877 std::string("The connection to the remote server has been lost and a reconnect failed. Processing mode has been switched to local processing\n\n") + 878 e.what() + "\n\n"); 879 }); 880 } 881 } 882 883 setDeviceType(DeviceType::Local); 884 885 juce::MessageManager::callAsync([this] 886 { 887 getController().onStateLoaded(); 888 }); 889 890 return m_device.get(); 891 } 892 893 bool Processor::rebootDevice() 894 { 895 try 896 { 897 synthLib::Device* device = createDevice(); 898 getPlugin().setDevice(device); 899 (void)m_device.release(); 900 m_device.reset(device); 901 902 return true; 903 } 904 catch(const synthLib::DeviceException& e) 905 { 906 genericUI::MessageBox::showOk(juce::MessageBoxIconType::WarningIcon, 907 "Device creation failed:", 908 std::string("Failed to create device:\n\n") + 909 e.what() + "\n\n"); 910 return false; 911 } 912 } 913 }