PluginProcessor.cpp (18882B)
1 /* 2 ============================================================================== 3 4 This file was auto-generated! 5 6 It contains the basic framework code for a JUCE plugin processor. 7 8 ============================================================================== 9 */ 10 11 #include "PluginProcessor.h" 12 #include "PluginEditor.h" 13 #include <iostream> 14 #include <fstream> 15 16 //============================================================================== 17 NeuralPiAudioProcessor::NeuralPiAudioProcessor() 18 #ifndef JucePlugin_PreferredChannelConfigurations 19 : AudioProcessor(BusesProperties() 20 #if ! JucePlugin_IsMidiEffect 21 #if ! JucePlugin_IsSynth 22 .withInput("Input", AudioChannelSet::stereo(), true) 23 #endif 24 .withOutput("Output", AudioChannelSet::stereo(), true) 25 #endif 26 ) 27 28 #endif 29 { 30 setupDataDirectories(); 31 installTones(); 32 resetDirectory(userAppDataDirectory_tones); 33 // Sort jsonFiles alphabetically 34 std::sort(jsonFiles.begin(), jsonFiles.end()); 35 if (jsonFiles.size() > 0) { 36 loadConfig(jsonFiles[current_model_index]); 37 } 38 39 resetDirectoryIR(userAppDataDirectory_irs); 40 // Sort irFiles alphabetically 41 std::sort(irFiles.begin(), irFiles.end()); 42 if (irFiles.size() > 0) { 43 loadIR(irFiles[current_ir_index]); 44 } 45 46 // initialize parameters: 47 addParameter(gainParam = new AudioParameterFloat(GAIN_ID, GAIN_NAME, NormalisableRange<float>(0.0f, 1.0f, 0.01f), 0.5f)); 48 addParameter(masterParam = new AudioParameterFloat(MASTER_ID, MASTER_NAME, NormalisableRange<float>(0.0f, 1.0f, 0.01f), 0.5f)); 49 addParameter(bassParam = new AudioParameterFloat(BASS_ID, BASS_NAME, NormalisableRange<float>(0.0f, 1.0f, 0.01f), 0.5f)); 50 addParameter(midParam = new AudioParameterFloat(MID_ID, MID_NAME, NormalisableRange<float>(0.0f, 1.0f, 0.01f), 0.5f)); 51 addParameter(trebleParam = new AudioParameterFloat(TREBLE_ID, TREBLE_NAME, NormalisableRange<float>(0.0f, 1.0f, 0.01f), 0.5f)); 52 addParameter(presenceParam = new AudioParameterFloat(PRESENCE_ID, PRESENCE_NAME, NormalisableRange<float>(0.0f, 1.0f, 0.01f), 0.5f)); 53 addParameter(modelParam = new AudioParameterFloat(MODEL_ID, MODEL_NAME, NormalisableRange<float>(0.0f, 1.0f, 0.001f), 0.0f)); 54 addParameter(irParam = new AudioParameterFloat(IR_ID, IR_NAME, NormalisableRange<float>(0.0f, 1.0f, 0.001f), 0.0f)); 55 addParameter(delayParam = new AudioParameterFloat(DELAY_ID, DELAY_NAME, NormalisableRange<float>(0.0f, 1.0f, 0.001f), 0.0f)); 56 addParameter(reverbParam = new AudioParameterFloat(REVERB_ID, REVERB_NAME, NormalisableRange<float>(0.0f, 1.0f, 0.001f), 0.0f)); 57 } 58 59 60 NeuralPiAudioProcessor::~NeuralPiAudioProcessor() 61 { 62 } 63 64 //============================================================================== 65 const String NeuralPiAudioProcessor::getName() const 66 { 67 return JucePlugin_Name; 68 } 69 70 bool NeuralPiAudioProcessor::acceptsMidi() const 71 { 72 #if JucePlugin_WantsMidiInput 73 return true; 74 #else 75 return false; 76 #endif 77 } 78 79 bool NeuralPiAudioProcessor::producesMidi() const 80 { 81 #if JucePlugin_ProducesMidiOutput 82 return true; 83 #else 84 return false; 85 #endif 86 } 87 88 bool NeuralPiAudioProcessor::isMidiEffect() const 89 { 90 #if JucePlugin_IsMidiEffect 91 return true; 92 #else 93 return false; 94 #endif 95 } 96 97 double NeuralPiAudioProcessor::getTailLengthSeconds() const 98 { 99 return 0.0; 100 } 101 102 int NeuralPiAudioProcessor::getNumPrograms() 103 { 104 return 1; // NB: some hosts don't cope very well if you tell them there are 0 programs, 105 // so this should be at least 1, even if you're not really implementing programs. 106 } 107 108 int NeuralPiAudioProcessor::getCurrentProgram() 109 { 110 return 0; 111 } 112 113 void NeuralPiAudioProcessor::setCurrentProgram (int index) 114 { 115 } 116 117 const String NeuralPiAudioProcessor::getProgramName (int index) 118 { 119 return {}; 120 } 121 122 void NeuralPiAudioProcessor::changeProgramName (int index, const String& newName) 123 { 124 } 125 126 //============================================================================== 127 void NeuralPiAudioProcessor::prepareToPlay (double sampleRate, int samplesPerBlock) 128 { 129 // Use this method as the place to do any pre-playback 130 // initialisation that you need.. 131 LSTM.reset(); 132 133 // set up DC blocker 134 dcBlocker.coefficients = dsp::IIR::Coefficients<float>::makeHighPass(sampleRate, 35.0f); 135 dsp::ProcessSpec spec{ sampleRate, static_cast<uint32> (samplesPerBlock), 2 }; 136 dcBlocker.prepare(spec); 137 138 // Set up IR 139 cabSimIR.prepare(spec); 140 141 // fx chain 142 fxChain.prepare(spec); 143 } 144 145 void NeuralPiAudioProcessor::releaseResources() 146 { 147 // When playback stops, you can use this as an opportunity to free up any 148 // spare memory, etc. 149 } 150 151 #ifndef JucePlugin_PreferredChannelConfigurations 152 bool NeuralPiAudioProcessor::isBusesLayoutSupported (const BusesLayout& layouts) const 153 { 154 #if JucePlugin_IsMidiEffect 155 ignoreUnused (layouts); 156 return true; 157 #else 158 // This is the place where you check if the layout is supported. 159 // In this template code we only support mono or stereo. 160 if (layouts.getMainOutputChannelSet() != AudioChannelSet::mono() 161 && layouts.getMainOutputChannelSet() != AudioChannelSet::stereo()) 162 return false; 163 164 // This checks if the input layout matches the output layout 165 #if ! JucePlugin_IsSynth 166 if (layouts.getMainOutputChannelSet() != layouts.getMainInputChannelSet()) 167 return false; 168 #endif 169 170 return true; 171 #endif 172 } 173 #endif 174 175 176 void NeuralPiAudioProcessor::processBlock (AudioBuffer<float>& buffer, MidiBuffer& midiMessages) 177 { 178 ScopedNoDenormals noDenormals; 179 180 // Setup Audio Data 181 const int numSamples = buffer.getNumSamples(); 182 const int numInputChannels = getTotalNumInputChannels(); 183 const int sampleRate = getSampleRate(); 184 185 auto block = dsp::AudioBlock<float>(buffer).getSingleChannelBlock(0); 186 auto context = juce::dsp::ProcessContextReplacing<float>(block); 187 188 // Amp ============================================================================= 189 if (amp_state == 1) { 190 auto gain = static_cast<float> (gainParam->get()); 191 auto master = static_cast<float> (masterParam->get()); 192 // Note: Default 0.0 -> 1.0 param range is converted to +-12.0 here 193 auto bass = (static_cast<float> (bassParam->get() - 0.5) * 24.0); 194 auto mid = (static_cast<float> (midParam->get() - 0.5) * 24.0); 195 auto treble = (static_cast<float> (trebleParam->get() - 0.5) * 24.0); 196 auto presence = (static_cast<float> (presenceParam->get() - 0.5) * 24.0); 197 198 auto delay = (static_cast<float> (delayParam->get())); 199 auto reverb = (static_cast<float> (reverbParam->get())); 200 201 auto model = static_cast<float> (modelParam->get()); 202 model_index = getModelIndex(model); 203 204 auto ir = static_cast<float> (irParam->get()); 205 ir_index = getIrIndex(ir); 206 207 // Applying gain adjustment for snapshot models 208 if (LSTM.input_size == 1) { 209 buffer.applyGain(gain * 2.0); 210 } 211 212 // Process EQ 213 eq4band.setParameters(bass, mid, treble, presence);// Better to move this somewhere else? Only need to set when value changes 214 eq4band.process(buffer.getReadPointer(0), buffer.getWritePointer(0), midiMessages, numSamples, numInputChannels, sampleRate); 215 216 // Apply LSTM model 217 if (model_loaded == 1 && lstm_state == true) { 218 if (current_model_index != model_index) { 219 loadConfig(jsonFiles[model_index]); 220 current_model_index = model_index; 221 } 222 223 // Process LSTM based on input_size (snapshot model or conditioned model) 224 if (LSTM.input_size == 1) { 225 LSTM.process(buffer.getReadPointer(0), buffer.getWritePointer(0), numSamples); 226 } 227 else if (LSTM.input_size == 2) { 228 LSTM.process(buffer.getReadPointer(0), gain, buffer.getWritePointer(0), numSamples); 229 } 230 else if (LSTM.input_size == 3) { 231 LSTM.process(buffer.getReadPointer(0), gain, master, buffer.getWritePointer(0), numSamples); 232 } 233 } 234 235 // Process IR 236 if (ir_state == true && num_irs > 0) { 237 if (current_ir_index != ir_index) { 238 loadIR(irFiles[ir_index]); 239 current_ir_index = ir_index; 240 } 241 auto block = dsp::AudioBlock<float>(buffer).getSingleChannelBlock(0); 242 auto context = juce::dsp::ProcessContextReplacing<float>(block); 243 cabSimIR.process(context); 244 245 // IR generally makes output quieter, add volume here to make ir on/off volume more even 246 buffer.applyGain(2.0); 247 } 248 249 // Master Volume 250 if (LSTM.input_size == 1 || LSTM.input_size == 2) { 251 buffer.applyGain(master * 2.0); // Adding volume range (2x) mainly for clean models 252 } 253 254 // Process Delay, and Reverb 255 set_delayParams(delay); 256 set_reverbParams(reverb); 257 fxChain.process(context); 258 } 259 260 // process DC blocker 261 auto monoBlock = dsp::AudioBlock<float>(buffer).getSingleChannelBlock(0); 262 dcBlocker.process(dsp::ProcessContextReplacing<float>(monoBlock)); 263 264 for (int ch = 1; ch < buffer.getNumChannels(); ++ch) 265 buffer.copyFrom(ch, 0, buffer, 0, 0, buffer.getNumSamples()); 266 } 267 268 //============================================================================== 269 bool NeuralPiAudioProcessor::hasEditor() const 270 { 271 return true; // (change this to false if you choose to not supply an editor) 272 } 273 274 AudioProcessorEditor* NeuralPiAudioProcessor::createEditor() 275 { 276 return new NeuralPiAudioProcessorEditor (*this); 277 } 278 279 //============================================================================== 280 void NeuralPiAudioProcessor::getStateInformation(MemoryBlock& destData) 281 { 282 MemoryOutputStream stream(destData, true); 283 284 stream.writeFloat(*gainParam); 285 stream.writeFloat(*masterParam); 286 stream.writeFloat(*bassParam); 287 stream.writeFloat(*midParam); 288 stream.writeFloat(*trebleParam); 289 stream.writeFloat(*presenceParam); 290 stream.writeFloat(*modelParam); 291 stream.writeFloat(*irParam); 292 stream.writeFloat(*delayParam); 293 stream.writeFloat(*reverbParam); 294 } 295 296 void NeuralPiAudioProcessor::setStateInformation(const void* data, int sizeInBytes) 297 { 298 MemoryInputStream stream(data, static_cast<size_t> (sizeInBytes), false); 299 300 gainParam->setValueNotifyingHost(stream.readFloat()); 301 masterParam->setValueNotifyingHost(stream.readFloat()); 302 bassParam->setValueNotifyingHost(stream.readFloat()); 303 midParam->setValueNotifyingHost(stream.readFloat()); 304 trebleParam->setValueNotifyingHost(stream.readFloat()); 305 presenceParam->setValueNotifyingHost(stream.readFloat()); 306 modelParam->setValueNotifyingHost(stream.readFloat()); 307 irParam->setValueNotifyingHost(stream.readFloat()); 308 delayParam->setValueNotifyingHost(stream.readFloat()); 309 reverbParam->setValueNotifyingHost(stream.readFloat()); 310 } 311 312 int NeuralPiAudioProcessor::getModelIndex(float model_param) 313 { 314 int a = static_cast<int>(round(model_param * (num_models - 1.0))); 315 if (a > num_models - 1) { 316 a = num_models - 1; 317 } 318 else if (a < 0) { 319 a = 0; 320 } 321 return a; 322 } 323 324 int NeuralPiAudioProcessor::getIrIndex(float ir_param) 325 { 326 int a = static_cast<int>(round(ir_param * (num_irs - 1.0))); 327 if (a > num_irs - 1) { 328 a = num_irs - 1; 329 } 330 else if (a < 0) { 331 a = 0; 332 } 333 return a; 334 } 335 336 void NeuralPiAudioProcessor::loadConfig(File configFile) 337 { 338 this->suspendProcessing(true); 339 String path = configFile.getFullPathName(); 340 char_filename = path.toUTF8(); 341 342 try { 343 // Load the JSON file into the correct model 344 LSTM.load_json(char_filename); 345 346 // Check what the input size is and then update the GUI appropirately 347 if (LSTM.input_size == 1) { 348 params = 0; 349 } 350 else if (LSTM.input_size == 2) { 351 params = 1; 352 } 353 else if (LSTM.input_size == 3) { 354 params = 2; 355 } 356 357 // If we are good: let's say so 358 model_loaded = 1; 359 } 360 catch (const std::exception& e) { 361 DBG("Unable to load json file: " + configFile.getFullPathName()); 362 std::cout << e.what(); 363 } 364 365 this->suspendProcessing(false); 366 } 367 368 void NeuralPiAudioProcessor::loadIR(File irFile) 369 { 370 this->suspendProcessing(true); 371 372 try { 373 cabSimIR.load(irFile); 374 ir_loaded = 1; 375 } 376 catch (const std::exception& e) { 377 DBG("Unable to load IR file: " + irFile.getFullPathName()); 378 std::cout << e.what(); 379 } 380 this->suspendProcessing(false); 381 } 382 383 void NeuralPiAudioProcessor::resetDirectory(const File& file) 384 { 385 jsonFiles.clear(); 386 if (file.isDirectory()) 387 { 388 juce::Array<juce::File> results; 389 file.findChildFiles(results, juce::File::findFiles, false, "*.json"); 390 for (int i = results.size(); --i >= 0;) 391 jsonFiles.push_back(File(results.getReference(i).getFullPathName())); 392 } 393 } 394 395 void NeuralPiAudioProcessor::resetDirectoryIR(const File& file) 396 { 397 irFiles.clear(); 398 if (file.isDirectory()) 399 { 400 juce::Array<juce::File> results; 401 file.findChildFiles(results, juce::File::findFiles, false, "*.wav"); 402 for (int i = results.size(); --i >= 0;) 403 irFiles.push_back(File(results.getReference(i).getFullPathName())); 404 } 405 } 406 407 void NeuralPiAudioProcessor::addDirectory(const File& file) 408 { 409 if (file.isDirectory()) 410 { 411 juce::Array<juce::File> results; 412 file.findChildFiles(results, juce::File::findFiles, false, "*.json"); 413 for (int i = results.size(); --i >= 0;) 414 { 415 jsonFiles.push_back(File(results.getReference(i).getFullPathName())); 416 num_models = num_models + 1.0; 417 } 418 } 419 } 420 421 void NeuralPiAudioProcessor::addDirectoryIR(const File& file) 422 { 423 if (file.isDirectory()) 424 { 425 juce::Array<juce::File> results; 426 file.findChildFiles(results, juce::File::findFiles, false, "*.wav"); 427 for (int i = results.size(); --i >= 0;) 428 { 429 irFiles.push_back(File(results.getReference(i).getFullPathName())); 430 num_irs = num_irs + 1.0; 431 } 432 } 433 } 434 435 void NeuralPiAudioProcessor::setupDataDirectories() 436 { 437 // User app data directory 438 File userAppDataTempFile = userAppDataDirectory.getChildFile("tmp.pdl"); 439 440 File userAppDataTempFile_tones = userAppDataDirectory_tones.getChildFile("tmp.pdl"); 441 442 File userAppDataTempFile_irs = userAppDataDirectory_irs.getChildFile("tmp.pdl"); 443 444 // Create (and delete) temp file if necessary, so that user doesn't have 445 // to manually create directories 446 if (!userAppDataDirectory.exists()) { 447 userAppDataTempFile.create(); 448 } 449 if (userAppDataTempFile.existsAsFile()) { 450 userAppDataTempFile.deleteFile(); 451 } 452 453 if (!userAppDataDirectory_tones.exists()) { 454 userAppDataTempFile_tones.create(); 455 } 456 if (userAppDataTempFile_tones.existsAsFile()) { 457 userAppDataTempFile_tones.deleteFile(); 458 } 459 460 if (!userAppDataDirectory_irs.exists()) { 461 userAppDataTempFile_irs.create(); 462 } 463 if (userAppDataTempFile_irs.existsAsFile()) { 464 userAppDataTempFile_irs.deleteFile(); 465 } 466 467 468 // Add the tones directory and update tone list 469 addDirectory(userAppDataDirectory_tones); 470 addDirectoryIR(userAppDataDirectory_irs); 471 } 472 473 void NeuralPiAudioProcessor::installTones() 474 //==================================================================== 475 // Description: Checks that the default tones 476 // are installed to the NeuralPi directory, and if not, 477 // copy them from the binary data in the plugin to that directory. 478 // 479 //==================================================================== 480 { 481 // Default tones 482 File ts9_tone = userAppDataDirectory_tones.getFullPathName() + "/TS9.json"; 483 File bjdirty_tone = userAppDataDirectory_tones.getFullPathName() + "/BluesJR.json"; 484 File ht40od_tone = userAppDataDirectory_tones.getFullPathName() + "/HT40_Overdrive.json"; 485 486 if (ts9_tone.existsAsFile() == false) { 487 std::string string_command = ts9_tone.getFullPathName().toStdString(); 488 const char* char_ts9_tone = &string_command[0]; 489 490 std::ofstream myfile; 491 myfile.open(char_ts9_tone); 492 myfile << BinaryData::TS9_json; 493 494 myfile.close(); 495 } 496 497 if (bjdirty_tone.existsAsFile() == false) { 498 std::string string_command = bjdirty_tone.getFullPathName().toStdString(); 499 const char* char_bjdirty = &string_command[0]; 500 501 std::ofstream myfile; 502 myfile.open(char_bjdirty); 503 myfile << BinaryData::BluesJr_json; 504 505 myfile.close(); 506 } 507 508 if (ht40od_tone.existsAsFile() == false) { 509 std::string string_command = ht40od_tone.getFullPathName().toStdString(); 510 const char* char_ht40od = &string_command[0]; 511 512 std::ofstream myfile; 513 myfile.open(char_ht40od); 514 myfile << BinaryData::HT40_Overdrive_json; 515 516 myfile.close(); 517 } 518 519 } 520 521 void NeuralPiAudioProcessor::set_ampEQ(float bass_slider, float mid_slider, float treble_slider, float presence_slider) 522 { 523 eq4band.setParameters(bass_slider, mid_slider, treble_slider, presence_slider); 524 } 525 526 void NeuralPiAudioProcessor::set_delayParams(float paramValue) 527 { 528 auto& del = fxChain.template get<delayIndex>(); 529 del.setWetLevel(paramValue); 530 // Setting delay time as larger steps to minimize clicking, and to start delay time at a reasonable value 531 if (paramValue < 0.25) { 532 del.setDelayTime(0, 0.25); 533 } else if (paramValue < 0.5) { 534 del.setDelayTime(0, 0.5); 535 } else if (paramValue < 0.75) { 536 del.setDelayTime(0, 0.75); 537 } else { 538 del.setDelayTime(0, 1.0); 539 } 540 del.setFeedback(0.8-paramValue/2); 541 } 542 543 544 void NeuralPiAudioProcessor::set_reverbParams(float paramValue) 545 { 546 auto& rev = fxChain.template get<reverbIndex>(); 547 rev_params = rev.getParameters(); 548 549 // Sets reverb params as a function of a single reverb param value ( 0.0 to 1.0) 550 rev_params.wetLevel = paramValue; 551 rev_params.damping = 0.6 - paramValue/2; // decay is inverse of damping 552 rev_params.roomSize = 0.8 - paramValue/2; 553 //rev_params.width = paramValue; 554 rev.setParameters(rev_params); 555 } 556 557 float NeuralPiAudioProcessor::convertLogScale(float in_value, float x_min, float x_max, float y_min, float y_max) 558 { 559 float b = log(y_max / y_min) / (x_max - x_min); 560 float a = y_max / exp(b * x_max); 561 float converted_value = a * exp(b * in_value); 562 return converted_value; 563 } 564 565 566 float NeuralPiAudioProcessor::decibelToLinear(float dbValue) 567 { 568 return powf(10.0, dbValue/20.0); 569 } 570 571 572 //============================================================================== 573 // This creates new instances of the plugin.. 574 AudioProcessor* JUCE_CALLTYPE createPluginFilter() 575 { 576 return new NeuralPiAudioProcessor(); 577 }