Krotos Modules 3
Loading...
Searching...
No Matches
KwidgetAudioProcessor.cpp
Go to the documentation of this file.
1namespace krotos
2{
4 File(AssetManager::getPluginDirectory().getFullPathName() + File::getSeparatorString() + "settings.json");
5
6 const String KwidgetAudioProcessor::autoPlayMaxDuration{"autoplay_max_duration"};
7 const String KwidgetAudioProcessor::recorderReleaseTime{"recorder_release_time"};
8 const String KwidgetAudioProcessor::DroppedFileNameFormat("dnd_filename_format");
9 const String KwidgetAudioProcessor::DefaultDroppedFileNameFormat("KS_%PRESETNAME%_%TAKECOUNT%");
10 const String KwidgetAudioProcessor::T2PStrictMode("t2p_strict_mode");
11
15 // Static kwidgets are flawed and ideally should be deprecated in the future, so think carefully before
16 // adding a new one.
17 };
18
19 const StringArray KwidgetAudioProcessor::StaticKwidgetIDs = {
21 // Static kwidgets are flawed and ideally should be deprecated in the future, so think carefully before
22 // adding a new one.
23 };
24
25 KwidgetAudioProcessor::KwidgetAudioProcessor(const BusesProperties& ioLayouts, size_t numBusses,
26 size_t channelsPerStereoBus, size_t numRecorders,
27 size_t numInputChannels, size_t numOutputChannels)
28 : AudioProcessor(ioLayouts),
29 m_autoplay{{"CoreEngine0", false}, {"CoreEngine1", false}, {"CoreEngine2", false}, {"CoreEngine3", false}},
30 m_audioOuts(numBusses), m_channelsPerStereoBus(channelsPerStereoBus), m_numRecorders(numRecorders),
31 m_numVoices(NUM_VOICES_TOTAL),
32 m_mainGraph(m_numVoices, numInputChannels,
33 numOutputChannels), // The main and mod graphs are created with enough voice references to cover
34 // poly and drone synths
35 m_modulatorGraph(m_numVoices, numInputChannels, numOutputChannels),
36 m_synthPoly(m_mainGraph, m_modulatorGraph, 0, NUM_VOICES_POLY,
37 numOutputChannels), // The poly synth uses the first NUM_VOICES_POLY voices
38 m_synthDrone(m_mainGraph, m_modulatorGraph, NUM_VOICES_POLY, NUM_VOICES_DRONE,
39 numOutputChannels), // then the drone synth uses the remaining NUM_VOICES_DRONE voices
40 parameterManager(*this)
41 {
42 initialiseStateListeners();
43
44 m_presetEventMiddleware.addHook(std::make_unique<RelativeSamplePathRewriteHook>());
45
46 presetManager.presetChanged = [this](const ValueTree& newState) {
47 // try to setState of plugin, but if incoming state load aborted, inform user
48 auto setNewStateSuccess = setState(newState);
49
50 if (!setNewStateSuccess)
51 {
52 // inform the user here of failed preset load
53 PresetManagerUI::alertStateValidation();
54 }
55 if (m_globalAutoplayOn) // for autoplay presets we must restore note and recorder after state setting
56 {
57 triggerRecordingStart();
58 m_synthDrone.noteOn(1, MIDI_NOTE_FLAG_DRONE + MIDI_NOTE_NUMBER_C3, /*velocity*/ 1.0f);
59 }
60 };
61
62 initialiseSettingsFileValues();
63 initialiseAudioOutConfiguration();
64 initialiseMasterFX();
65 initialiseRecorders();
66
67#if !UNIT_TEST // don't look for/load init state/preset when running unit tests
68 if (!host.isPluginval()) // pluginval currently not compatible with state loading
69 setInitialState(); // load the necessary init state/preset at the end of the constructor
70#endif
71 }
72
73 KwidgetAudioProcessor::~KwidgetAudioProcessor()
74 {
75 /* The call to load the init preset happens asynchronously from the constructor.
76 In the time between the call being queued and executed, the DAW might have tried to load it's own data.
77 m_shouldInit flags whether the init preset should loaded in the async callback.
78 */
79 m_shouldInit = false;
80
81 /* In certain DAWs (Pro Tools) the KwidgetAudioProcessor may be created and destroyed a number of times,
82 therefore it's possible an async call gets executed after the class which queued it has been deleted.
83
84 The WeakReference is used to determine whether the caller has been destroyed at the time of execution.
85 */
86 masterReference.clear();
87 }
88
89 void KwidgetAudioProcessor::setInitialState()
90 {
91 try
92 {
93 m_initPresetPath =
94 presetManager.getInitPresetFile()
95 .getFullPathName(); // if this throws, we are missing the init preset. fails safely.
96 m_lastLoadedPresetPath = m_initPresetPath;
97
98 // TODO: presetInfoTree/presetMenu should ideally be handled in its own class/functions
99 // store the last loaded preset
100 presetInfoTree = std::make_unique<ValueTree>(krotos::XmlType::Tag::LastLoadedPreset);
101 presetInfoTree->setProperty(krotos::XmlType::Property::LastLoadedPresetName, m_lastLoadedPresetPath,
102 nullptr);
103
104 // Logic AU requires direct setState call for autoplay operation in the initial preset
105 if (host.isLogic())
106 {
107 if (m_shouldInit) // if we are creating a new instance, not recalling a session. new instance check not
108 // needed here, but just in case!
109 {
110 // try to setState of plugin
111 auto setNewStateSuccess =
112 setState(PresetManager::getValueTreeFromFile(File(m_lastLoadedPresetPath)));
113
114 if (setNewStateSuccess)
115 {
116 m_shouldInit = false;
117 }
118 else
119 {
120 // inform the user here of failed state load
121 PresetManagerUI::alertStateValidation();
122 }
123 }
124 }
125 else
126 {
127 MessageManager::callAsync([this, target = WeakReference<KwidgetAudioProcessor>{this}] {
128 if (target && m_shouldInit)
129 {
130 presetManager.setCurrentPreset(m_lastLoadedPresetPath);
131 m_shouldInit = false;
132 }
133 });
134 }
135 }
136 catch (PresetException& ex)
137 {
138 // in the worst case, where init preset/state is missing - fail safely, without loading any state
139 std::cout << ex.what() << std::endl;
140 }
141 }
142
143 void KwidgetAudioProcessor::removeEngines()
144 {
145 suspendProcessing(true);
146 removeKwidgetsOfType({KType::CoreEngine, KType::Reformer});
147 suspendProcessing(false);
148 }
149
150 void KwidgetAudioProcessor::initialiseSettingsFileValues()
151 {
152 // create and set default setting for autoplay duration
153 settingsFile.setDefaultIfVoid(autoPlayMaxDuration, 180); // 3 minutes
154
155 // create and set default setting for recorder post roll out duration
156 settingsFile.setDefaultIfVoid(recorderReleaseTime, 1.5); // 1.5 seconds
157
158 // create and set default setting for the recorder output filename
159 settingsFile.setDefaultIfVoid(DroppedFileNameFormat, DefaultDroppedFileNameFormat);
160
161 // Default setting for text to preset strict mode: false - always generate
162 settingsFile.setDefaultIfVoid(T2PStrictMode, false);
163 }
164
165 void KwidgetAudioProcessor::initialiseAudioOutConfiguration()
166 {
167 // Create the audio output Kwidgets, and connect them to the appropriate output nodes
168 const int channelIncrement = m_channelsPerStereoBus;
169 for (int i = 0; i < m_audioOuts.size(); ++i)
170 {
171 // Set AudioOut 0 ID to "Master". Name the rest incrementally from 1.
172 String audioOutID = i == 0 ? m_masterAudioOutID : KType::AudioOut + String(i);
173
174 m_audioOuts[i] = addKwidgetWithID(KType::AudioOut, audioOutID, true);
175 m_mainGraph.connectToOutputNode(m_audioOuts[i], i * channelIncrement, m_channelsPerStereoBus);
176
177 // Hotfix KST-1903 - Static kwidgets once created must retain the same
178 // number of parameters due to a design flaw in the parameter allocation
179 // See https://bitbucket.org/krotos/krotos_modules_3/commits/210fc7d7462c69287d5ac50f32bc06e9996fc178
180 // Todo: Update once new solution for static kwidgets is found.
181 if (i > 0)
182 {
183 jassert(m_audioOuts[i]->getParameters().size() == 2);
184 }
185 }
186
187 // TODO: This code forces the number of buses and channels to be set at compile time in the PluginProcessor with
188 // constexpr Assign master audio out ptr to "Master"
189 jassert(m_audioOuts[0] != nullptr);
190 m_masterAudioOut = m_audioOuts[0];
191
192 // Set up master autoplay parameter which only exists in the "Master" audio out.
193 m_masterAudioOut->addParameterCallback(Kwidget_AudioOut::Parameters::Autoplay, [this](float newValue) {
194 autoPlayGlobalChanged(newValue > ParameterManager::OnOffThreshold);
195 });
196
197#if ENABLE_SANDBOX
198 // creates the kwidget - adds to main Graph and the lists
199 m_audioOutSandBox =
200 addKwidgetWithID(KType::AudioOut, KwidgetFactory::KwidgetModifiers::SandBox + KType::AudioOut, true);
201#endif
202 // Hotfix KST-1903 - Static kwidgets once created must retain the same
203 // number of parameters due to backwards compatibility issues with parameter allocation
204 // See https://bitbucket.org/krotos/krotos_modules_3/commits/210fc7d7462c69287d5ac50f32bc06e9996fc178
205 // Do not add new parameters to static kwidgets until this is addressed.
206 // Todo: Update once new solution for static kwidgets is found.
207 jassert(m_masterAudioOut->getParameters().size() == 4);
208 }
209
210 void KwidgetAudioProcessor::initialiseRecorders()
211 {
212 for (int i = 0; i < m_numRecorders; i++)
213 {
214 // Set Recorder 0 ID to "Recorder". Name the rest incrementally from 1.
215 String recorderID = KType::Recorder + (i > 0 ? String(i) : "");
216 std::unique_ptr<Kwidget> m_recorder_tmp = std::make_unique<Kwidget_Recorder>(recorderID);
217 m_recorders[i] = std::move(m_recorder_tmp);
218 parameterManager.addKwidget(m_recorders[i].get());
219 m_kwidgets.add(m_recorders[i].get());
220 m_recorderPtrs.add(dynamic_cast<Kwidget_Recorder*>(m_recorders[i].get()));
221 m_kwidgetLookup[m_recorders[i]->getKwidgetID()] = m_recorders[i].get();
222 }
223 }
224
225 void KwidgetAudioProcessor::initialiseMasterFX()
226 {
227 String masterFilterID = KMod::MasterFX + KType::Filter;
228 m_masterFilter = std::make_unique<Kwidget_Filter>(masterFilterID);
229 parameterManager.addKwidget(m_masterFilter.get(), true, false);
230 m_kwidgets.add(m_masterFilter.get());
231 m_kwidgetLookup[m_masterFilter->getKwidgetID()] = m_masterFilter.get();
232
233 String masterReverbID = KMod::MasterFX + KType::ConvolutionReverb;
234 m_masterReverb = std::make_unique<Kwidget_ConvolutionReverb>(masterReverbID);
235 parameterManager.addKwidget(m_masterReverb.get(), true, false);
236 m_kwidgets.add(m_masterReverb.get());
237 m_kwidgetLookup[m_masterReverb->getKwidgetID()] = m_masterReverb.get();
238 m_masterReverb->getCustomParameter(Kwidget_ConvolutionReverb::Parameters::ImpulsePath)
239 ->setValue(utils::StringsIntoPath(Kwidget_ConvolutionReverb::getDefaultImpulsePath(), "D_Equipment",
240 "Classic Hall.wav"));
241
242 String macroSliderID = KMod::MasterFX + KType::MacroSlider;
243 m_intensityMacro = std::make_unique<Kwidget_MacroSlider>(macroSliderID);
244 parameterManager.addKwidget(m_intensityMacro.get(), true, false);
245 m_kwidgets.add(m_intensityMacro.get());
246 m_kwidgetLookup[m_intensityMacro->getKwidgetID()] = m_intensityMacro.get();
247
248 // Hotfix KST-1903 - Static kwidgets once created must retain the same
249 // number of parameters due to backwards compatibility issues with parameter allocation
250 // See https://bitbucket.org/krotos/krotos_modules_3/commits/210fc7d7462c69287d5ac50f32bc06e9996fc178
251 // Do not add new parameters to static kwidgets until this is addressed.
252 // Todo: Update once new solution for static kwidgets is found.
253 jassert(m_masterReverb->getParameters().size() == 4);
254 jassert(m_masterFilter->getParameters().size() == 6);
255 jassert(m_intensityMacro->getParameters().size() == 1);
256
257 // Set up parameter defaults
258 m_masterAudioOut->getParameter(Kwidget_AudioOut::Parameters::FXActivation)->set(0.0f);
259
260 // Cache the parameter pointer for the processBlock bypass.
261 m_audioOutFxEnabledParameter = m_masterAudioOut->getParameter(Kwidget_AudioOut::Parameters::FXActivation);
262
263 // Hardcode modulation destinations for intensity macroSlider
264 parameterManager.addModulation(macroSliderID, 0, masterFilterID, Kwidget_Filter::Parameters::Cutoff, -0.8f,
265 KAttachment::Polarity::Unipolar);
266 parameterManager.addModulation(macroSliderID, 0, masterReverbID, Kwidget_ConvolutionReverb::Parameters::Mix,
267 1.0f, KAttachment::Polarity::Unipolar);
268 }
269
270 void KwidgetAudioProcessor::soloActivated(Kwidget_CoreEngine* engine)
271 {
272 soloActivatedAllEngines(engine->getKwidgetID());
273 }
274
275 void KwidgetAudioProcessor::soloActivated(Kwidget_Reformer* engine)
276 {
277 soloActivatedAllEngines(engine->getKwidgetID());
278 }
279
280 void KwidgetAudioProcessor::soloDeActivated(Kwidget_CoreEngine* engine)
281 {
282 soloDeActivatedAllEngines(engine->getKwidgetID());
283 }
284
285 void KwidgetAudioProcessor::soloDeActivated(Kwidget_Reformer* engine)
286 {
287 soloDeActivatedAllEngines(engine->getKwidgetID());
288 }
289
290 void KwidgetAudioProcessor::soloActivatedAllEngines(const String& callingID)
291 {
292 // Iterate through all kwidgets, finding Reformers and CoreEngines
293 // and setting their mutes and solos appropriately
294 // Solo is effectively simply all qualifying kwidgets being muted
295 // except for the calling kwidget [SOLO]
296 for (auto& k : m_kwidgets)
297 {
298 if (k->isAChildKwidget())
299 {
300 continue; // Ignore child kwidgets
301 }
302 else if (k->getKwidgetType() == KType::Reformer)
303 {
304 // ensure calling core engine being soloed is unmuted
305 if (k->getKwidgetID() == callingID)
306 {
307 k->getParameter(Kwidget_Reformer::Parameters::Mute)->set(0.0f);
308 }
309 else // mute / unsolo all other core engines
310 {
311 k->getParameter(Kwidget_Reformer::Parameters::Mute)->set(1.0f);
312 k->getParameter(Kwidget_Reformer::Parameters::Solo)->set(0.0f);
313 }
314 }
315 else if (k->getKwidgetType() == KType::CoreEngine)
316 {
317 // ensure calling core engine being soloed is unmuted
318 if (k->getKwidgetID() == callingID)
319 {
320 k->getParameter(Kwidget_CoreEngine::Parameters::Mute)->set(0.0f);
321 }
322 else // mute / unsolo all other core engines
323 {
324 k->getParameter(Kwidget_CoreEngine::Parameters::Mute)->set(1.0f);
325 k->getParameter(Kwidget_CoreEngine::Parameters::Solo)->set(0.0f);
326 }
327 }
328 }
329 }
330
331 void KwidgetAudioProcessor::soloDeActivatedAllEngines(const String& callingID)
332 {
333 // Iterate through all kwidgets, finding Reformers and CoreEngines
334 // and unmuting them, except the calling kwidget which by definition, having
335 // been previously soloed, will already be unmuted [SOLO]
336 for (auto& k : m_kwidgets)
337 {
338 if (k->isAChildKwidget() || k->getKwidgetID() == callingID)
339 {
340 continue; // Ignore calling kwidget & child kwidgets
341 }
342 else if (k->getKwidgetType() == KType::Reformer)
343 {
344 k->getParameter(Kwidget_Reformer::Parameters::Mute)->set(0.0f);
345 }
346 else if (k->getKwidgetType() == KType::CoreEngine)
347 {
348 k->getParameter(Kwidget_CoreEngine::Parameters::Mute)->set(0.0f);
349 }
350 }
351 }
352
353 void KwidgetAudioProcessor::triggerRecordingStart()
354 {
355 if (isTimerRunning() == false)
356 {
357 // start autoplay limit timer
358 auto autoplayMaxDurationValue = settingsFile.getSetting(autoPlayMaxDuration);
359
360 // 180 second/3 minute fallback value
361 int maxDuration = autoplayMaxDurationValue.isVoid() ? 180 : static_cast<int>(autoplayMaxDurationValue);
362
363 startTimer(maxDuration * 1000);
364 }
365
366 m_recordingTriggerStarted = true;
367
368 for (auto& m_recorderTmp : m_recorders)
369 {
370 m_recorderTmp->getActiveVoice()->noteOn(MIDI_NOTE_NUMBER_C3, 1.0f);
371 }
372 }
373
374 void KwidgetAudioProcessor::triggerRecordingStop()
375 {
376 m_recordingTriggerStarted = false;
377
378 for (auto& m_recorderTmp : m_recorders)
379 {
380 m_recorderTmp->getActiveVoice()->noteOff(1.0f);
381 }
382 }
383
384 void KwidgetAudioProcessor::parameterValueChanged(int /*parameterIndex*/, float /*newValue*/)
385 {
386 // TODO: This is a realtime function called on the audio thread when any parameter value changes.
387 // It is looking up ALL kwidgets and ALL of their parameters in order to
388 // control the xypad trigger behaviour. This is far too much overhead for realtime processing.
389 // Refactor with a more efficient approach.
390 for (auto* kw : m_kwidgets)
391 {
392 if (kw->getKwidgetType().contains(KType::XyPad))
393 {
394 for (auto kwParam : kw->getParameters())
395 {
396 if (kwParam->paramID == Kwidget_XyPad::Parameters::toggleOn)
397 {
398 m_xypadTriggerOn = kw->getParameter(Kwidget_XyPad::Parameters::toggleOn)->getValue() >
399 ParameterManager::OnOffThreshold;
400 break;
401 }
402 if (kwParam->paramID == Kwidget_XyPad::Parameters::onClick)
403 {
404 m_xypadTriggerOn = kw->getParameter(Kwidget_XyPad::Parameters::toggleOn)->getValue() >
405 ParameterManager::OnOffThreshold;
406
407 if (m_xypadTriggerOn == true && m_globalAutoplayOn == false)
408 {
409 if (kwParam->getValue() > ParameterManager::OnOffThreshold &&
410 m_previousTriggerValue <= ParameterManager::OnOffThreshold)
411 {
412 m_synthPoly.noteOn(1, MIDI_NOTE_NUMBER_C3, 1.0f);
413 triggerRecordingStart();
414 }
415 else if (kwParam->getValue() <= ParameterManager::OnOffThreshold &&
416 m_previousTriggerValue > ParameterManager::OnOffThreshold)
417 {
418 m_synthPoly.noteOff(1, MIDI_NOTE_NUMBER_C3, 1.0f, true);
419 triggerRecordingStop();
420 }
421 m_previousTriggerValue = kwParam->getValue();
422 }
423 break;
424 }
425 }
426 }
427 }
428 }
429
430 AudioBuffer<float> KwidgetAudioProcessor::extractStereoBuffer(int numChannel1, int numChannel2,
431 AudioBuffer<float>& multiChannelBuffer)
432 {
433 float* channels[2] = {multiChannelBuffer.getWritePointer(numChannel1),
434 multiChannelBuffer.getWritePointer(numChannel2)};
435 AudioBuffer<float> buffer(channels, 2, 0, multiChannelBuffer.getNumSamples());
436 return buffer;
437 }
438
439 void KwidgetAudioProcessor::autoPlayGlobalChanged(bool enableAutoplay)
440 {
441 if (m_globalAutoplayOn == enableAutoplay)
442 return;
443
444 if (enableAutoplay && !m_globalAutoplayOn)
445 {
446 // start autoplay limit timer
447 auto autoplayMaxDurationValue = settingsFile.getSetting(autoPlayMaxDuration);
448
449 // 180 second/3 minute fallback value
450 int maxDuration = autoplayMaxDurationValue.isVoid() ? 180 : static_cast<int>(autoplayMaxDurationValue);
451
452 startTimer(maxDuration * 1000);
453
454 triggerRecordingStart();
455
456 // trigger drone synth
457 m_synthDrone.noteOn(1, MIDI_NOTE_FLAG_DRONE + MIDI_NOTE_NUMBER_C3, 1.0f);
458 }
459 else if (!enableAutoplay)
460 {
461 // stop recorder limit timer
462 stopTimer();
463
464 // Flush any playing drones
465 m_synthDrone.noteOn(
466 1, MIDI_NOTE_FLAG_DRONE_NOTEUP + MIDI_NOTE_FLAG_FLUSH + MIDI_NOTE_FLAG_DRONE + MIDI_NOTE_NUMBER_C3,
467 1.0f);
468
469 triggerRecordingStop();
470 }
471
472 m_globalAutoplayOn = enableAutoplay;
473 }
474
475 void KwidgetAudioProcessor::timerCallback()
476 {
477 autoPlayGlobalChanged(false);
478
479 if (onAutoplayTimerStop != nullptr)
480 {
481 onAutoplayTimerStop();
482 }
483 }
484
485 void KwidgetAudioProcessor::autoPlayChanged(Kwidget_CoreEngine* engine, float newValue)
486 {
487 auto thisID = engine->getKwidgetID();
488 m_autoplay[thisID.toRawUTF8()] = (bool)(newValue > ParameterManager::OnOffThreshold);
489 }
490
491 void KwidgetAudioProcessor::loopEnableChanged(Kwidget_CoreEngine* /*engine*/, float /* newValue */) {}
492
493 void KwidgetAudioProcessor::addKListener(KListener* l) { m_listeners.add(l); }
494
495 void KwidgetAudioProcessor::removeKListener(KListener* l) { m_listeners.remove(l); }
496
497 void KwidgetAudioProcessor::prepareToPlay(double sampleRate, int samplesPerBlock)
498 {
499 m_synthPoly.setCurrentPlaybackSampleRate(sampleRate);
500 m_synthPoly.setSamePitchNoteStealingEnable(false);
501 m_synthDrone.setCurrentPlaybackSampleRate(sampleRate);
502 m_synthDrone.setNoteStealingEnabled(true);
503 m_mainGraph.prepareToPlay(sampleRate, samplesPerBlock);
504 m_modulatorGraph.prepareToPlay(sampleRate, samplesPerBlock);
505 notifyAllKwidgetsParametersListeners(); // Refreshes all params, which will kickstart autoplay where selected
506
507 for (auto& m_recorderTmp : m_recorders)
508 {
509 m_recorderTmp->getActiveVoice()->prepareToPlay(sampleRate, samplesPerBlock);
510 }
511
512 // prepare the Filter Processor
513 m_masterFilter.get()->getActiveVoice()->prepareToPlay(sampleRate, samplesPerBlock);
514
515 // prepare the Convolution Reverb Processor
516 m_masterReverb.get()->getActiveVoice()->prepareToPlay(sampleRate, samplesPerBlock);
517
518 if (m_globalAutoplayOn) // preparing the synth will turn off the note. Restore that now
519 {
520 m_synthDrone.noteOn(1, MIDI_NOTE_FLAG_DRONE + MIDI_NOTE_NUMBER_C3, /*velocity*/ 1.0f);
521 }
522 }
523
524 void KwidgetAudioProcessor::processBlock(AudioBuffer<float>& buffer, MidiBuffer& midiMessages)
525 {
526 // We are not expecting audio input, clearing the buffer prevents
527 // uninitialised audio buffers from being summed into our output.
528 // For reference this was the fix for ticket "Additional output busses unusable/clipping VST3"
529 // https://krotosltd.atlassian.net/browse/KST-2467
530 buffer.clear();
531
532 // render the synth
533 m_synthPoly.processBlock(buffer, midiMessages);
534
535 if (m_globalAutoplayOn == true) // XYPad and MIDI note
536 {
537 // clear input MIDIBuffer
538 midiMessages.clear();
539
540 // render the drone synth
541 m_synthDrone.processBlock(buffer, midiMessages);
542 }
543
544 buffer.applyGain(Decibels::decibelsToGain<float>(
545 3.f)); // Compensate for 3dB drop in output KST-824 TODO: Investigate further the root cause of the drop
546
547 if (!bypassPostFx && m_audioOutFxEnabledParameter->get() > ParameterManager::OnOffThreshold)
548 {
549 // TODO: Address this properly by converting all KwidgetProcessors to multichannel instead of hardcoded
550 // stereo and make krotos_dsp modules independent of number of channels when possible.
551 auto stereoBufferTmp = extractStereoBuffer(0, 1, buffer);
552 m_masterFilter.get()->getActiveVoice()->processBlock(stereoBufferTmp, midiMessages);
553 m_masterReverb.get()->getActiveVoice()->processBlock(stereoBufferTmp, midiMessages);
554 }
555
556 // Record
557 recordOutput(buffer, midiMessages);
558 }
559
560 void KwidgetAudioProcessor::recordOutput(AudioBuffer<float>& buffer, MidiBuffer& midiMessages)
561 {
562 // Split the main 10 channel Buffer into smaller 5 2-channel Buffers , without copy
563 int m_activeRecorders = 1; // just one active recorder (main out)
564 for (int i = 0; i < m_activeRecorders; i++)
565 {
566 int chan = 2 * i;
567 auto bufferTmp = extractStereoBuffer(chan, chan + 1, buffer);
568 if (i > 1)
569 {
570 String s = String(KType::CoreEngine) + String(i - 1);
571
572 if (m_autoplay[s.toRawUTF8()] == false)
573 {
574 bufferTmp.clear();
575 }
576 }
577 Kwidget_Recorder* recorder = m_recorderPtrs[i];
578 recorder->handleMidiMessages(midiMessages);
579 m_recorders[i]->getActiveVoice()->processBlock(bufferTmp, midiMessages);
580 }
581 }
582
583 Kwidget* KwidgetAudioProcessor::addKwidgetWithID(const String& kwidgetType, const String& kwidgetID,
584 bool useEndParams, bool isChildKwidget)
585 {
586#if ENABLE_SANDBOX
587 m_addedFromState = false;
588#endif
589 auto newKwidget = KwidgetFactory::createKwidget(kwidgetType, kwidgetID);
590
591 auto newKwidgetPtr = newKwidget.get();
592
593 if (!parameterManager.addKwidget(newKwidgetPtr, useEndParams, isChildKwidget))
594 return nullptr;
595
596 addKwidgetInternal(std::move(newKwidget));
597 return newKwidgetPtr;
598 }
599
600 Kwidget* KwidgetAudioProcessor::addChildKwidget(const String& kwidgetType, const String& parentID,
601 const String& childID, int typeLimit)
602 {
603#if ENABLE_SANDBOX
604 m_addedFromState = false;
605#endif
606 String baseKwidgetID = parentID + Kwidget::Constants::ChildDelimiter +
607 childID; // ... we indicate that this is a child by prefixing the supplied child ID with
608 // our parent's ID, seperated by a ":"
609 auto autoGeneratedkwidgetID = parameterManager.generateKwidgetID(
610 baseKwidgetID, "", typeLimit); // This will modify baseKwidgetID to make it unique
611
612 auto newKwidget = KwidgetFactory::createKwidget(kwidgetType, autoGeneratedkwidgetID);
613 auto newKwidgetPtr = newKwidget.get();
614
615 if (!parameterManager.addKwidget(newKwidgetPtr, false, true))
616 return nullptr;
617
618 addKwidgetInternal(std::move(newKwidget));
619 newKwidgetPtr = m_kwidgets.getLast();
620
621 return newKwidgetPtr;
622 }
623
624 Kwidget* KwidgetAudioProcessor::addUserKwidget(const String& kwidgetType, const String& idModifier,
625 int typeInstanceLimit)
626 {
627#if ENABLE_SANDBOX
628 m_addedFromState = false;
629#endif
630 // Auto generate an ID for this Kwidget, based on its type, and a count of how many times this type has been
631 // used
632 String kwidgetID = parameterManager.generateKwidgetID(kwidgetType, idModifier, typeInstanceLimit);
633
634 if (kwidgetID == "") // If a unique Kwidget ID meeting our requirements couldn't be generated ...
635 {
636 return nullptr; // ... then we return null kwidget
637 }
638
639 auto newKwidget = KwidgetFactory::createKwidget(kwidgetType, kwidgetID);
640
641 if (newKwidget == nullptr)
642 {
643 jassertfalse;
644 return nullptr; // Failed to create kwidget
645 }
646
647 auto newKwidgetPtr = newKwidget.get();
648
649 if (!parameterManager.addKwidget(newKwidgetPtr))
650 {
651 jassertfalse;
652 return nullptr; // Failed to add kwidget
653 }
654
655 if (auto coreEngineKwidget = dynamic_cast<Kwidget_CoreEngine*>(newKwidgetPtr))
656 {
657 coreEngineKwidget->canReadSpecificFiles(m_readSpecificFiles);
658 }
659 else if (auto reformerKwidget = dynamic_cast<Kwidget_Reformer*>(newKwidgetPtr))
660 {
661 reformerKwidget->canReadSpecificFiles(m_readSpecificFiles);
662 }
663
664 addKwidgetInternal(std::move(newKwidget));
665
666 // fetch updated ptr after std::move
667 newKwidgetPtr = m_kwidgets.getLast();
668
669 makeStaticConnectionsForKwidget(newKwidgetPtr);
670 // Call this method in the kwidget we just created to allow it to create child kwidgets
671 newKwidgetPtr->createNestedKwidgets(*this);
672
673 // Register this processor as Listener to the CoreEgine solo
674 if (auto coreEngineKwidget = dynamic_cast<Kwidget_CoreEngine*>(newKwidgetPtr))
675 {
676 // Register the KwidgetAudioProcessor as a Listener to the mute
677 coreEngineKwidget->addListener(this);
678 }
679 else if (auto reformerKwidget = dynamic_cast<Kwidget_Reformer*>(newKwidgetPtr))
680 {
681 // Register the KwidgetAudioProcessor as a Listener to the mute
682 reformerKwidget->addListener(this);
683 }
684
685 return newKwidgetPtr;
686 }
687
688 void KwidgetAudioProcessor::removeKwidget(const String& kwidgetID)
689 {
690 if (m_kwidgetLookup.count(kwidgetID) == 1)
691 {
692 auto kwidget = m_kwidgetLookup.at(kwidgetID);
693
694 // Remove listener from the XyPad's Kwidget_XyPad::Parameters::onClick parameter Listeners list
695 if (auto xyPadKwidget = dynamic_cast<Kwidget_XyPad*>(kwidget))
696 {
697 xyPadKwidget->getParameter(Kwidget_XyPad::Parameters::onClick)->removeListener(this);
698 xyPadKwidget->getParameter(Kwidget_XyPad::Parameters::toggleOn)->removeListener(this);
699 xyPadKwidget->setKwidgetAudioProcessorReference(nullptr);
700 }
701 else if (auto triggerButtonKwidget = dynamic_cast<Kwidget_TriggerButton*>(kwidget))
702 {
703 triggerButtonKwidget->removeListener(this);
704 }
705
706 // Unregister this processor as listener to the CoreEngine solo
707 if (auto coreEngineKwidget = dynamic_cast<Kwidget_CoreEngine*>(kwidget))
708 coreEngineKwidget->removeListener(this);
709
710 // Remove all the kwidgets children
711 std::vector<Kwidget*>& children = kwidget->getChildKwidgets();
712
713 // change to remove backwards so that it doesn't fail checking an already deleted kwidget
714 for (int i = static_cast<int>(children.size()) - 1; i >= 0; i--)
715 {
716 removeKwidget(children.at(i)->getKwidgetID());
717 }
718
719 children.clear(); // Clear the refences as they no longer exist
720
721 parameterManager.removeKwidget(kwidgetID);
722
723 m_listeners.call([kwidget](KListener& l) { l.kwidgetRemoved(kwidget); });
724
725 m_kwidgets.removeFirstMatchingValue(kwidget);
726
727 m_kwidgetLookup.erase(kwidgetID);
728
729 if (kwidget->isModulator())
730 m_modulatorGraph.removeKwidget(kwidget);
731 else
732 m_mainGraph.removeKwidget(kwidget);
733
734 // Tell the host to recheck the names, values, etc. of its parameters
735 // to make it stop showing the old Kwidget's parameters.
736 updateHostDisplay();
737 }
738 else
739 {
740 jassertfalse; // hit this assert trying to remove a kwidget which doesn't exist in m_kwidgetLookup
741 }
742 }
743
744 void KwidgetAudioProcessor::removeDynamicKwidgets() { removeKwidgetsExcluding({}); }
745
746 void KwidgetAudioProcessor::removeKwidgetsExcluding(const StringArray& kwidgetsToExclude)
747 {
748 std::vector<Kwidget*> kwidgetsToRemove;
749
750 for (auto& k : m_kwidgets)
751 {
752 auto id = k->getKwidgetID();
753 auto type = k->getKwidgetType();
754
755 if (!kwidgetsToExclude.contains(id) && !k->isAChildKwidget() && !isAStaticKwidget(*k))
756 {
757 // Don't remove excluded or child kwidgets
758 kwidgetsToRemove.push_back(k);
759 }
760 }
761
762 for (auto& k : kwidgetsToRemove)
763 {
764 removeKwidget(k->getKwidgetID());
765 }
766 }
767
768 void KwidgetAudioProcessor::removeKwidgetsOfType(const StringArray& kwidgetTypesToRemove)
769 {
770 std::vector<Kwidget*> kwidgetsToRemove;
771
772 for (auto& k : m_kwidgets)
773 {
774 auto kType = k->getKwidgetType();
775 if ((kwidgetTypesToRemove.contains(kType)) && !isAStaticKwidget(*k) &&
776 (!k->isAChildKwidget())) // Don't remove excluded or child kwidgets
777 kwidgetsToRemove.push_back(k);
778 }
779
780 for (auto& k : kwidgetsToRemove)
781 {
782 removeKwidget(k->getKwidgetID());
783 }
784 }
785
786 const bool KwidgetAudioProcessor::isAStaticKwidget(const Kwidget& kwidget)
787 {
788 bool isStatic = isAStaticKwidgetType(kwidget.getKwidgetType());
789 isStatic |= StaticKwidgetIDs.contains(kwidget.getKwidgetID());
790 return isStatic;
791 }
792
793 bool KwidgetAudioProcessor::addConnection(const String& sourceID, const String& destID)
794 {
795 if (!m_kwidgetLookup.count(sourceID))
796 {
797 // Couldn't find source Kwidget
798 jassertfalse;
799 return false;
800 }
801
802 if (!m_kwidgetLookup.count(destID))
803 {
804 // Couldn't find destination Kwidget
805 jassertfalse;
806 return false;
807 }
808
809 auto src = m_kwidgetLookup.at(sourceID);
810 auto dest = m_kwidgetLookup.at(destID);
811
812 // Modulator Kwidgets should not have any audio connections.
813 jassert(!src->isModulator() && !dest->isModulator());
814
815 if (!m_mainGraph.canConnect(src, dest))
816 {
817 jassertfalse;
818 return false;
819 }
820
821 parameterManager.addConnection(sourceID, destID);
822
823 return true;
824 }
825
826 bool KwidgetAudioProcessor::makeStaticConnectionsForKwidget(Kwidget* kwidget)
827 {
828 // Define any static audio connections that a specific kwidget requires here...
829
830 bool retVal = true;
831
832 if (kwidget != nullptr)
833 {
834 auto kwidgetID = kwidget->getKwidgetID();
835
836 // Core engine multi-out configuration based on "Tab" ID prefix
837
838#if ENABLE_SANDBOX
839 if (kwidgetID.contains(KwidgetFactory::KwidgetModifiers::SandBox))
840 return false;
841#endif
842#if ENABLE_REFORMER
843 if ((kwidget->getKwidgetType() == KType::CoreEngine) || (kwidget->getKwidgetType() == KType::Reformer))
844#else
845 if (kwidget->getKwidgetType() == KType::CoreEngine)
846#endif
847 {
848 auto masterOutID = m_masterAudioOut->getKwidgetID();
849
850 // Kwidget IDs containing a "TabX" prefix are routed to X audio
851 // out e.g. "Tab2CoreEngine1" would be routed to audio out 2
852 if (kwidgetID.startsWith("Tab"))
853 {
854 int tabIndex = kwidgetID.substring(3, 4).getIntValue();
855
856 retVal &= addConnection(kwidgetID, masterOutID); // Connection to main stereo bus
857 retVal &= addConnection(kwidgetID,
858 m_audioOuts[tabIndex]->getKwidgetID()); // Connection to individual bus
859 }
860 // Backwards compatability with older presets. As a fallback,
861 // route to master and bus 1.
862 else
863 {
864 retVal &= addConnection(kwidgetID, masterOutID);
865 retVal &= addConnection(kwidgetID, m_audioOuts[1]->getKwidgetID());
866 }
867 }
868 }
869 return retVal;
870 }
871
872 /* TODO: this function is a bit problematic in that its a special case where we don't want to clear modulation for
873 QuickFX It would be much better if this was its own contained unit, instead of mixed in with the dynamic
874 connections I have added a nullptr check to the QuickFX related Kwidget for now in case it doesn't exist */
875 void KwidgetAudioProcessor::clearModulation()
876 {
877 if (m_intensityMacro != nullptr)
878 {
879 parameterManager.clearModulationExcept(m_intensityMacro.get()->getKwidgetID());
880 }
881 else
882 {
883 parameterManager.clearModulation();
884 }
885 }
886
887 void KwidgetAudioProcessor::loadBundle(const File& bundleDir)
888 {
889 auto presetFile = PresetBundler::getPresetFromBundleDirectory(bundleDir);
890 presetManager.setCurrentPreset(presetFile);
891 }
892
893 bool KwidgetAudioProcessor::setState(const ValueTree& newState)
894 {
895 PresetLoadRequest loadRequest;
896 loadRequest.newState = const_cast<ValueTree&>(newState);
897 loadRequest.presetFile = presetManager.getCurrentPreset().path;
898 auto loadResponse = m_presetEventMiddleware.processPresetLoad(loadRequest);
899 if (!loadResponse.success)
900 {
901 DBG(loadResponse.message);
902 jassertfalse;
903 }
904
905 // validate incoming state - aborts detected failed state loads that cannot be repaired
906 auto validationSuccess = validateState(newState, /*enable incoming state repair*/ true);
907
908 if (!validationSuccess)
909 return false;
910
911 // stop kwidget_recorder recordings
912 for (int i = 0; i < m_numRecorders; i++)
913 {
914 auto recorderProcessor = dynamic_cast<KwidgetProcessor_Recorder*>(m_recorderPtrs[i]->getActiveVoice());
915 recorderProcessor->stopRecording();
916 }
917
918 suspendProcessing(true);
919
920 parameterManager.clearConnections();
921 clearModulation();
922 removeDynamicKwidgets();
923
924 auto kwidgetTree = newState.getChildWithName(XmlType::Tag::kwidgets);
925
926 // Cache the master AudioOut tree state for later - this is a static kwidget whose state we do want to recall
927 // manually...
928 auto masterAudioOutTree =
929 kwidgetTree.getChildWithProperty(XmlType::Property::id, m_masterAudioOut->getKwidgetID());
930 // cache master Filter tree state for later
931 auto masterFilterTree =
932 kwidgetTree.getChildWithProperty(XmlType::Property::id, m_masterFilter.get()->getKwidgetID());
933 // cache master Reverb tree state for later
934 auto masterReverbTree =
935 kwidgetTree.getChildWithProperty(XmlType::Property::id, m_masterReverb.get()->getKwidgetID());
936
937 // Remove static Kwidgets from the incoming state tree
938 for (int i = 0; i < StaticKwidgetTypes.size(); ++i)
939 {
940 auto staticKwidgetTree = kwidgetTree.getChildWithProperty(XmlType::Property::type, StaticKwidgetTypes[i]);
941 kwidgetTree.removeChild(staticKwidgetTree, nullptr);
942 }
943
944 auto intensityMacroTree =
945 kwidgetTree.getChildWithProperty(XmlType::Property::id, m_intensityMacro.get()->getKwidgetID());
946 kwidgetTree.removeChild(intensityMacroTree, nullptr);
947
948 // PresetMenu last loaded preset logic TODO: this stuff needs its own class/functions, required for KS preset
949 // menu!
950 ValueTree customDataTree = newState.getChildWithName(XmlType::Tag::customData);
951 if (customDataTree.isValid())
952 {
953 auto lastLoadedPreset = customDataTree.getChildWithName(XmlType::Tag::LastLoadedPreset);
954 if (lastLoadedPreset.isValid())
955 {
956 m_lastLoadedPresetPath =
957 lastLoadedPreset.getProperty(XmlType::Property::LastLoadedPresetName).toString();
958 if (m_lastLoadedPresetPath.isEmpty())
959 {
960 m_lastLoadedPresetPath = m_initPresetPath;
961 }
962 presetInfoTree->setProperty(krotos::XmlType::Property::LastLoadedPresetName, m_lastLoadedPresetPath,
963 nullptr);
964 }
965 }
966
967 parameterManager.getCustomDataTree().copyPropertiesAndChildrenFrom(
968 newState.getChildWithName(XmlType::Tag::customData), nullptr);
969
970 // Add new Kwidgets for each saved state remaining in the tree
971 for (const auto& kwidget : kwidgetTree)
972 {
973 // TODO: factor out this exception to which kwidgets get added - it's one of those special cases we want to
974 // get rid of
975
976 String kwidgetType = kwidget.getProperty(XmlType::Property::type);
977 // Don't add static kwidgets as these are instantiated manually.
978 if (!isAStaticKwidgetType(kwidgetType))
979 {
980 addKwidgetFromState(kwidget);
981 }
982 }
983
984 parameterManager.setCurrentState(newState);
985
986 // Manually set state of master audio out, as it contains the autoplay parameters.
987 m_masterAudioOut->setState(masterAudioOutTree, *this);
988 m_masterFilter->setState(masterFilterTree, *this);
989 m_masterReverb->setState(masterReverbTree, *this);
990 m_intensityMacro->setState(intensityMacroTree, *this);
991
992#if ENABLE_SANDBOX
993 addConnection(m_audioOutSandBox->getKwidgetID(), m_masterAudioOut->getKwidgetID());
994#endif
995 // Update recorder kwidgets to have the latest preset name for drag and drop file naming
996 const String presetName =
997 File::createFileWithoutCheckingPath(m_lastLoadedPresetPath).getFileNameWithoutExtension();
998 setRecordersNameData(presetName);
999
1000 // Resume processing
1001 suspendProcessing(false);
1002
1003 // By the time we get here we have created all the kwidgets which are represented in the state
1004 // and all the kparams created by these kwidgets will have had their values set from state
1005 // When the value is set from state a value changed callback will be made to any listeners which have registered
1006 // The modulation system means that any given kwidget processor may be listening for changes made to parameters
1007 // owned by another kwidget Kwidgets are created in order - any kwidget processor set up to listen to another
1008 // kwidget's param will not get the initial value changed callback if it was created after the sending kwidget
1009 // was created For that reason we send out another value changed message from every param, once all kwidgets and
1010 // connections are made:
1011
1012 notifyAllKwidgetsParametersListeners();
1013
1014 return true;
1015 }
1016
1017 bool KwidgetAudioProcessor::validateState(const ValueTree& newState, bool repairEnabled)
1018 {
1019 auto paramTreeGeneric = newState.getChildWithName(XmlType::Tag::params);
1020
1021 bool noFailures = parameterManager.initialiseLinkIndexUsageTable(newState, repairEnabled);
1022
1023 noFailures &= parameterManager.initialiseKwidgetIDUsageTable(newState, repairEnabled);
1024
1025 auto kwidgetTree = newState.getChildWithName(XmlType::Tag::kwidgets);
1026
1027 // test that kwidget sub-trees in incoming state are valid
1028 for (auto kwidget : kwidgetTree)
1029 {
1030 noFailures &= parameterManager.validateKwidgetState(kwidget, paramTreeGeneric, repairEnabled);
1031 auto childKwidgetTree = kwidget.getChildWithName(XmlType::Tag::kwidgets);
1032 for (auto ckwidget : childKwidgetTree) // For every child kwidget
1033 {
1034 noFailures &= parameterManager.validateKwidgetState(ckwidget, paramTreeGeneric, repairEnabled);
1035 }
1036 }
1037
1038 return noFailures;
1039 }
1040
1041 void KwidgetAudioProcessor::notifyAllKwidgetsParametersListeners()
1042 {
1043 for (auto& kwidget : m_kwidgets) // For every kwidget ...
1044 {
1045 kwidget->notifyAllParametersListeners();
1046 }
1047 }
1048
1049 ValueTree KwidgetAudioProcessor::getState()
1050 {
1051 for (const auto& k : m_kwidgetLookup)
1052 k.second->flushState();
1053
1054 m_masterAudioOut->flushState();
1055
1056 return parameterManager.getState().createCopy();
1057 }
1058
1059 const Array<Kwidget*>& KwidgetAudioProcessor::getKwidgets() { return m_kwidgets; }
1060
1061 Kwidget* KwidgetAudioProcessor::getKwidget(const String& kwidgetID)
1062 {
1063 if (kwidgetID.contains(":") == true)
1064 {
1065 // this is a child kwidget we must find the parent and then get the kwidget
1066 int parentIDIndex = kwidgetID.indexOf(Kwidget::Constants::ChildDelimiter);
1067 if (parentIDIndex > 0)
1068 {
1069 String parentID = kwidgetID.substring(0, parentIDIndex);
1070
1071 if (m_kwidgetLookup.count(parentID) > 0)
1072 {
1073 return m_kwidgetLookup.at(parentID)->findChildKwidget(kwidgetID);
1074 }
1075 }
1076 }
1077 // jassert(m_kwidgetLookup.count(kwidgetID) == 1);
1078 if (m_kwidgetLookup.count(kwidgetID) > 0)
1079 {
1080 return m_kwidgetLookup.at(kwidgetID);
1081 }
1082 else
1083 {
1084 return nullptr;
1085 }
1086 }
1087
1088#if ENABLE_REFORMER
1089 const Array<Kwidget*> KwidgetAudioProcessor::getKwidgetsOfType(const String kwidgetType)
1090 {
1091 Array<Kwidget*> typedKwidgets;
1092
1093 // Loop though all the kwidgets in the system
1094 for (auto& kwidget : m_kwidgets)
1095 {
1096 if (kwidget->getKwidgetType() == kwidgetType)
1097 {
1098 typedKwidgets.add(kwidget);
1099 }
1100 }
1101
1102 return typedKwidgets;
1103 }
1104
1105 const Array<Kwidget*> KwidgetAudioProcessor::getKwidgetsOfTypeWithAccessPermission(const StringRef kwidgetType,
1106 const StringRef reqId)
1107 {
1108 Array<Kwidget*> typedKwidgets;
1109
1110 // Loop though all the kwidgets in the system
1111 for (auto& kwidget : m_kwidgets)
1112 {
1113 if (kwidget->getKwidgetType() == kwidgetType)
1114 {
1115 // If we are graanted access
1116 if (kwidget->requestAccess(reqId))
1117 {
1118 typedKwidgets.add(kwidget);
1119 }
1120 }
1121 }
1122
1123 return typedKwidgets;
1124 }
1125
1126 Kwidget* KwidgetAudioProcessor::getKwidgetWithAccessPermission(const String& kwidgetID, const StringRef reqId)
1127 {
1128 // Critical section here in case kwidget deleted on another thread before
1129 // access granted
1130 const juce::ScopedLock scopedLock(m_lock);
1131
1132 auto kwidget = getKwidget(kwidgetID);
1133
1134 // If the kwidget was found
1135 if (kwidget != nullptr)
1136 {
1137 // And we are graanted access
1138 if (kwidget->requestAccess(reqId))
1139 {
1140 return kwidget;
1141 }
1142 }
1143
1144 return nullptr;
1145 }
1146#endif
1147
1148 void KwidgetAudioProcessor::getStateInformation(MemoryBlock& destData)
1149 {
1150 auto currentState = getState();
1151
1152 // get custom data tree
1153 auto customData = currentState.getChildWithName(XmlType::Tag::customData);
1154 if (customData.isValid())
1155 {
1156 // see if last preset field exists
1157 auto lastPresetField = customData.getChildWithName(XmlType::Tag::LastLoadedPreset);
1158 if (lastPresetField.isValid())
1159 {
1160 // update its property
1161 lastPresetField.setProperty(krotos::XmlType::Property::LastLoadedPresetName, m_lastLoadedPresetPath,
1162 nullptr);
1163 }
1164 else
1165 {
1166 // inject if does not exist
1167 if (presetInfoTree != nullptr) // error check in case there is no preset
1168 {
1169 auto m_vtCopy = presetInfoTree->createCopy();
1170 m_vtCopy.setProperty(XmlType::Property::LastLoadedPresetName, m_lastLoadedPresetPath, nullptr);
1171 customData.appendChild(m_vtCopy, nullptr);
1172 }
1173 }
1174 }
1175 std::unique_ptr<XmlElement> xml(currentState.createXml());
1176 copyXmlToBinary(*xml, destData);
1177 }
1178
1179 void KwidgetAudioProcessor::setStateInformation(const void* incomingData, int sizeInBytes)
1180 {
1181 std::unique_ptr<XmlElement> xmlState(getXmlFromBinary(incomingData, sizeInBytes));
1182 if (xmlState.get() != nullptr)
1183 {
1184 auto stateFromXML = ValueTree::fromXml(*xmlState);
1185
1186 auto setNewStateSuccess = setState(stateFromXML);
1187
1188 // lets test incoming state from DAW
1189 if (!setNewStateSuccess)
1190 {
1191 // notify user if session state fails validation
1192 PresetManagerUI::alertStateValidation();
1193 }
1194 else
1195 {
1196 // only stop init.ksp loading if validation successful
1197 m_shouldInit = false;
1198 }
1199 }
1200 }
1201
1202 void KwidgetAudioProcessor::setLastPresetPath(String lastLoadedPresetPath)
1203 {
1204 m_lastLoadedPresetPath = lastLoadedPresetPath;
1205 }
1206
1207 String& KwidgetAudioProcessor::getLastPresetPath() { return m_lastLoadedPresetPath; }
1208
1209 const String KwidgetAudioProcessor::getName() const { return JucePlugin_Name; }
1210 bool KwidgetAudioProcessor::acceptsMidi() const { return true; }
1211 bool KwidgetAudioProcessor::producesMidi() const { return true; }
1212 bool KwidgetAudioProcessor::isMidiEffect() const { return false; }
1213 double KwidgetAudioProcessor::getTailLengthSeconds() const { return 0.0; }
1214 int KwidgetAudioProcessor::getNumPrograms() { return 1; }
1215 int KwidgetAudioProcessor::getCurrentProgram() { return 0; }
1216 void KwidgetAudioProcessor::setCurrentProgram(int) {}
1217 const String KwidgetAudioProcessor::getProgramName(int) { return {}; }
1218 void KwidgetAudioProcessor::changeProgramName(int, const String&) {}
1219 bool KwidgetAudioProcessor::isBusesLayoutSupported(const BusesLayout&) const { return true; }
1220
1221 Kwidget* KwidgetAudioProcessor::addKwidgetFromState(const ValueTree& kwidgetToAdd)
1222 {
1223#if ENABLE_SANDBOX
1224 m_addedFromState = true;
1225#endif
1232 auto kwidgetTree = kwidgetToAdd.createCopy();
1233
1234 KwidgetLoadRequest loadRequest;
1235 loadRequest.kwidgetTree = kwidgetTree;
1236 auto loadResponse = m_presetEventMiddleware.processKwidgetLoad(loadRequest);
1237 if (loadResponse.success)
1238 {
1239 kwidgetTree = loadRequest.kwidgetTree;
1240 }
1241 else
1242 {
1243 DBG("Event middleware failed to execute: \n\t" << loadResponse.message);
1244 jassertfalse;
1245 }
1246
1247 String kwidgetType = kwidgetTree[XmlType::Property::type].toString();
1248
1249 // Create the Kwidget object, which will have a default state
1250 auto kwidgetID = kwidgetTree[XmlType::Property::id].toString();
1251 auto newKwidget = KwidgetFactory::createKwidget(kwidgetType, kwidgetID);
1252
1253 // Get the raw pointer for use where raw ptrs are required as arguments.
1254 auto newKwidgetPtr = newKwidget.get();
1255
1256 if (auto coreEngineKwidget = dynamic_cast<Kwidget_CoreEngine*>(newKwidgetPtr))
1257 {
1258 coreEngineKwidget->canReadSpecificFiles(m_readSpecificFiles);
1259 }
1260 else if (auto reformerKwidget = dynamic_cast<Kwidget_Reformer*>(newKwidgetPtr))
1261 {
1262 reformerKwidget->canReadSpecificFiles(m_readSpecificFiles);
1263 }
1264
1265 // Copy the values from the saved state in to the Kwidget state.
1266 newKwidget->setState(kwidgetTree, *this);
1267
1268 // Is the Kwidget being added a Kwidget_CoreEngine ?
1269 if (auto coreEngineKwidget = dynamic_cast<Kwidget_CoreEngine*>(newKwidgetPtr))
1270 {
1271 // If so, we register as a Listener to the Kwidget_CoreEngine's solo button [SOLO]
1272 coreEngineKwidget->addListener(this);
1273 }
1274
1275 // Is the Kwidget being added a Kwidget_Reformer ?
1276 if (auto reformerKwidget = dynamic_cast<Kwidget_Reformer*>(newKwidgetPtr))
1277 {
1278 // If so, we register as a Listener to the Kwidget_Reformer's solo button [SOLO]
1279 reformerKwidget->addListener(this);
1280 }
1281
1282 parameterManager.addKwidget(newKwidgetPtr, false, newKwidget->isAChildKwidget());
1283
1284 // Add to the processor, transferring ownership
1285 addKwidgetInternal(std::move(newKwidget));
1286
1287 newKwidgetPtr = m_kwidgets.getLast(); // update ptr after move
1288
1289 makeStaticConnectionsForKwidget(newKwidgetPtr);
1290
1291 newKwidgetPtr->createNestedKwidgets(*this);
1292
1293 return newKwidgetPtr;
1294 }
1295
1296 void KwidgetAudioProcessor::addKwidgetInternal(std::unique_ptr<Kwidget> kwidget)
1297 {
1298 auto kwidgetPtr = kwidget.get();
1299
1300 // Set up XY pad listeners for note trigger etc.
1301 if (kwidget->getKwidgetType().contains(KType::XyPad))
1302 {
1303 Kwidget_XyPad* xyPadKwidget = dynamic_cast<Kwidget_XyPad*>(kwidgetPtr);
1304 xyPadKwidget->getParameter(Kwidget_XyPad::Parameters::onClick)->addListener(this);
1305 xyPadKwidget->getParameter(Kwidget_XyPad::Parameters::toggleOn)->addListener(this);
1306 xyPadKwidget->setKwidgetAudioProcessorReference(this);
1307 }
1308 else if (kwidget->getKwidgetType().contains(KType::TriggerButton))
1309 {
1310 Kwidget_TriggerButton* triggerButton = dynamic_cast<Kwidget_TriggerButton*>(kwidgetPtr);
1311 triggerButton->addListener(this);
1312 }
1313
1314 if (kwidget->isModulator())
1315 m_modulatorGraph.addKwidget(std::move(kwidget));
1316 else
1317 m_mainGraph.addKwidget(std::move(kwidget));
1318
1319 m_kwidgets.add(kwidgetPtr);
1320 m_kwidgetLookup[kwidgetPtr->getKwidgetID()] = kwidgetPtr;
1321
1322 m_listeners.call([kwidgetPtr](KListener& l) { l.kwidgetAdded(kwidgetPtr); });
1323
1324 // Tell the host to recheck the names, values, etc. of its parameters
1325 // to make it show the new Kwidget's parameters
1326 updateHostDisplay();
1327 }
1328
1329 void KwidgetAudioProcessor::initialiseStateListeners()
1330 {
1332 const auto& connectionTree = parameterManager.getState().getChildWithName(XmlType::Tag::connections);
1333
1334 m_connectionAttachment.reset(new ValueTreeAttachment(connectionTree));
1335
1336 m_connectionAttachment->onChildAdded = [this](ValueTree& /*parent*/, ValueTree& child) {
1337 // Connect together the audio from two Kwidgets
1338
1339 auto src = getKwidget(child[XmlType::Property::source].toString());
1340 auto dest = getKwidget(child[XmlType::Property::destination].toString());
1341
1342 jassert(src != NULL);
1343 jassert(dest != NULL);
1344
1345 if (MessageManager::getInstance()->isThisTheMessageThread())
1346 {
1347 // Connections between kwidgets are implied to be stereo
1348 // so the number of channels is hardwired here
1349 // to make this hard wired value appear nearer the start of this chain of events
1350 // would involve giving the paramter manager knowledge of kwidget channel needs
1351 // I'm going to leave it here from the moment before I end up refactoring the entire plugin with
1352 // no design. TODO look at where the number of channels in a kwidget is defined when we do the big
1353 // refactor
1354 m_mainGraph.addConnection(src, dest, 0, m_channelsPerStereoBus);
1355 }
1356 else
1357 {
1358 MessageManager::callAsync(
1359 [this, src, dest]() { m_mainGraph.addConnection(src, dest, 0, m_channelsPerStereoBus); });
1360 }
1361 };
1362
1363 m_connectionAttachment->onChildRemoved = [this](ValueTree& /*parent*/, ValueTree& child) {
1364 auto src = getKwidget(child[XmlType::Property::source].toString());
1365 auto dest = getKwidget(child[XmlType::Property::destination].toString());
1366 m_mainGraph.removeConnection(src, dest);
1367 };
1368
1370 const auto& modulationTree = parameterManager.getState().getChildWithName(XmlType::Tag::modulations);
1371 m_modulationAttachment.reset(new ValueTreeAttachment(modulationTree));
1372
1373 m_modulationAttachment->onChildAdded = [this](ValueTree& /*parent*/, ValueTree& child) {
1374 auto src = getKwidget(child[XmlType::Property::source].toString());
1375 auto dest = getKwidget(child[XmlType::Property::destination].toString());
1376 auto paramID = child[XmlType::Property::param].toString();
1377 auto idx = int(child[XmlType::Property::modulatorIndex]);
1378 auto depth = child[XmlType::Property::depth];
1379 auto polarity = int(child[XmlType::Property::polarity]);
1380
1381 dest->addModulator(paramID, src, idx, depth, static_cast<KAttachment::Polarity>(polarity));
1382
1383 auto depthParam = new CustomParameter(child, XmlType::Property::depth, parameterManager.getUndoManager());
1384 auto polarityParam =
1385 new CustomParameter(child, XmlType::Property::polarity, parameterManager.getUndoManager());
1386
1387 depthParam->valueChanged = [=](const var& newDepth) {
1388 dest->setModulatorDepth(paramID, src, idx, newDepth);
1389 };
1390
1391 polarityParam->valueChanged = [=](const var& newPolarity) {
1392 dest->setModulatorPolarity(paramID, src, idx, static_cast<KAttachment::Polarity>((int)newPolarity));
1393 };
1394
1395 m_modulationInstances.add(depthParam);
1396 m_modulationInstances.add(polarityParam);
1397 };
1398
1399 m_modulationAttachment->onChildRemoved = [this](ValueTree& /*parent*/, ValueTree& child) {
1400 auto src = getKwidget(child[XmlType::Property::source].toString());
1401 auto dest = getKwidget(child[XmlType::Property::destination].toString());
1402 auto paramID = child[XmlType::Property::param].toString();
1403 auto idx = int(child[XmlType::Property::modulatorIndex]);
1404
1405 for (auto inst : m_modulationInstances)
1406 {
1407 if (inst->getParameterTree() == child)
1408 {
1409 m_modulationInstances.removeObject(inst);
1410 break;
1411 }
1412 }
1413
1414 dest->removeModulator(paramID, src, idx);
1415 };
1416 }
1417
1418 bool KwidgetAudioProcessor::isRecording()
1419 {
1420 m_isRecording = false;
1421
1422 for (int i = 0; i < m_numRecorders; i++)
1423 {
1424 if (m_recorderPtrs[i]->getActive() == true)
1425 {
1426 m_isRecording = true;
1427 break;
1428 }
1429 }
1430 return m_isRecording;
1431 }
1432
1433 void KwidgetAudioProcessor::onClickChanged(Kwidget_TriggerButton* /*triggerButton*/, float newValue,
1434 MIDIMode midiModeValue)
1435 {
1436 if (newValue > ParameterManager::OnOffThreshold)
1437 {
1438 // Account for all notes enum value
1439 m_synthPoly.noteOn(1, MIDI_NOTE_NUMBER_C3 + static_cast<int>(midiModeValue) - 1, 1.0f);
1440 triggerRecordingStart();
1441 }
1442 else
1443 {
1444 // Account for all notes enum value
1445 m_synthPoly.noteOff(1, MIDI_NOTE_NUMBER_C3 + static_cast<int>(midiModeValue) - 1, 1.0f, true);
1446
1447 // only stop recording if autoplay is not currently in use
1448 if (m_masterAudioOut->getParameter(Kwidget_AudioOut::Parameters::Autoplay)->get() <=
1449 ParameterManager::OnOffThreshold)
1450 {
1451 triggerRecordingStop();
1452 }
1453 }
1454 }
1455
1456 void KwidgetAudioProcessor::setReadSpecificFiles(bool canRead)
1457 {
1458 for (auto* kwidget : getKwidgets())
1459 {
1460 if (kwidget->getKwidgetType() == KType::CoreEngine)
1461 {
1462 dynamic_cast<Kwidget_CoreEngine*>(kwidget)->canReadSpecificFiles(canRead);
1463 }
1464#if ENABLE_REFORMER
1465 else if (kwidget->getKwidgetType() == KType::Reformer)
1466 {
1467 dynamic_cast<Kwidget_Reformer*>(kwidget)->canReadSpecificFiles(canRead);
1468 }
1469#endif
1470 }
1471
1472 m_readSpecificFiles = canRead;
1473 }
1474
1475 void KwidgetAudioProcessor::setRecordersNameData(String newPresetName)
1476 {
1477 for (auto& recorder : m_recorderPtrs)
1478 {
1479 if (recorder)
1480 {
1481 recorder->setPresetName(newPresetName);
1482 }
1483 }
1484 }
1485} // namespace krotos
static File getPluginDirectory()
Definition AssetManager.cpp:392
A wrapper around juce::ValueTree designed to store custom plugin state (strings, arrays,...
Definition CustomParameter.h:9
Polarity
Definition KAttachment.h:120
Definition Kwidget_CoreEngine.h:4
Definition Kwidget_Recorder.h:4
void handleMidiMessages(MidiBuffer &midiMessages)
Definition Kwidget_Recorder.cpp:92
Definition Kwidget_Reformer.h:4
Definition Kwidget_TriggerButton.h:4
void addListener(Listener *listenerToAdd)
Definition Kwidget_TriggerButton.cpp:74
Definition Kwidget_XyPad.h:4
void setKwidgetAudioProcessorReference(KwidgetAudioProcessor *ref)
Definition Kwidget_XyPad.h:52
static const String autoPlayMaxDuration
Definition KwidgetAudioProcessor.h:228
static const StringArray StaticKwidgetIDs
Definition KwidgetAudioProcessor.h:26
static const StringArray StaticKwidgetTypes
Definition KwidgetAudioProcessor.h:25
static SettingsFile settingsFile
Definition KwidgetAudioProcessor.h:221
static const String DroppedFileNameFormat
Definition KwidgetAudioProcessor.h:230
static const String DefaultDroppedFileNameFormat
Definition KwidgetAudioProcessor.h:231
static const String recorderReleaseTime
Definition KwidgetAudioProcessor.h:229
static const String T2PStrictMode
Definition KwidgetAudioProcessor.h:232
KwidgetAudioProcessor(const BusesProperties &ioLayouts, size_t numBusses, size_t channelsPerStereoBus, size_t numRecorders, size_t numInputChannels, size_t numOutputChannels)
Definition KwidgetAudioProcessor.cpp:25
Definition Kwidget.h:8
KParameter * getParameter(const String &parameterID) const noexcept
Get a raw pointer to a KParameter belonging to this Kwidget's KwidgetProcessor. Useful for low overhe...
Definition Kwidget.cpp:168
const String & getKwidgetType() const
Definition Kwidget.cpp:365
const String & getKwidgetID() const
Definition Kwidget.cpp:367
The processing power of the KwidgetRecorder that performs the recording of the plugins output and wri...
Definition KwidgetProcessor_Recorder.h:18
void stopRecording()
Definition KwidgetProcessor_Recorder.cpp:139
An exception for preset manager.
Definition PresetManager.h:148
char const * what() const noexcept override
Definition PresetManager.cpp:339
Definition ValueTreeAttachment.h:4
Definition AirAbsorptionFilter.cpp:2
MIDIMode
Definition SampleEngine.h:54
Definition KwidgetAudioProcessor.h:34
virtual void kwidgetAdded(Kwidget *)=0
virtual void kwidgetRemoved(Kwidget *)=0
static const String MasterFX
Definition KwidgetFactory.h:40
static const String ConvolutionReverb
Definition KwidgetFactory.h:15
static const String MacroSlider
Definition KwidgetFactory.h:22
static const String AudioOut
Definition KwidgetFactory.h:11
static const String Recorder
Definition KwidgetFactory.h:24
static const String Filter
Definition KwidgetFactory.h:18
A middleware request object for a Kwidget load event.
Definition PresetEventMiddleware.h:36
ValueTree kwidgetTree
The value tree representing the state of the Kwidget to be loaded.
Definition PresetEventMiddleware.h:40
A middleware request object for a preset load event.
Definition PresetEventMiddleware.h:7
File presetFile
Definition PresetEventMiddleware.h:12
ValueTree newState
The new preset state value tree.
Definition PresetEventMiddleware.h:11
static const Identifier LastLoadedPresetName
Definition XmlType.h:51
static const Identifier LastLoadedPreset
Definition XmlType.h:29