/* ============================================================================== This file contains the basic framework code for a JUCE plugin processor. ============================================================================== */ #include "PluginProcessor.h" #include "PluginEditor.h" // ==================== Parameter-Layout ==================== juce::AudioProcessorValueTreeState::ParameterLayout CrystalizerEQAudioProcessor::createParameterLayout() { std::vector> params; params.push_back (std::make_unique( "TestNoiseEnabled", "Test Noise Enabled", false)); params.push_back (std::make_unique("TestNoiseLevel", "Test Noise Level", juce::NormalisableRange(-60.f, 0.f, 0.1f), -18.f)); //LOW-BAND params.push_back (std::make_unique( "LowBandFreq", "LowBand Freq", juce::NormalisableRange(20.f, 20000.f, 1.f, 0.5f), 30.f)); params.push_back (std::make_unique("LowBandGain", "LowBand Gain", juce::NormalisableRange(-24.f, 24.f, 0.1f), 0.f)); params.push_back (std::make_unique("LowBandQ", "LowBand Q", juce::NormalisableRange(0.1f, 10.f, 0.01f), 1.f)); params.push_back (std::make_unique( "LowBandSlope", "LowBand Slope", // Anzeigename juce::StringArray { "12", "24", "36", "48"}, 1)); params.push_back (std::make_unique( "LowBandModes", // Parameter-ID "Low Band Modes", // Anzeigename juce::StringArray { "Off", "Cut", "Shelf", "Bell" }, // Einträge 0)); //PEAK 1 params.push_back (std::make_unique( "Peak1Freq", "Peak1 Freq", juce::NormalisableRange(100.f, 1500.f, 1.f, 0.5f), 250.f)); params.push_back (std::make_unique( "Peak1Gain", "Peak1 Gain (dB)", juce::NormalisableRange(-24.f, 24.f, 0.1f), 0.f)); params.push_back (std::make_unique( "Peak1Q", "Peak1 Q", juce::NormalisableRange(0.1f, 10.f, 0.01f), 1.f)); params.push_back (std::make_unique("Peak1Bypass", "Peak1 Bypass", false)); //PEAK 2 params.push_back (std::make_unique( "Peak2Freq", "Peak2 Freq", juce::NormalisableRange(400.f, 6000.f, 1.f, 0.5f), 1500.f)); params.push_back (std::make_unique( "Peak2Gain", "Peak2 Gain (dB)", juce::NormalisableRange(-48.f, 48.f, 0.1f), 0.f)); params.push_back (std::make_unique( "Peak2Q", "Peak2 Q", juce::NormalisableRange(0.1f, 10.f, 0.01f), 1.f)); params.push_back (std::make_unique("Peak2Bypass", "Peak2 Bypass", false)); //PEAK 3 params.push_back (std::make_unique( "Peak3Freq", "Peak3 Freq", juce::NormalisableRange(1000.f, 16000.f, 1.f, 0.5f), 6000.f)); params.push_back (std::make_unique( "Peak3Gain", "Peak3 Gain (dB)", juce::NormalisableRange(-24.f, 24.f, 0.1f), 0.f)); params.push_back (std::make_unique( "Peak3Q", "Peak3 Q", juce::NormalisableRange(0.1f, 10.f, 0.01f), 1.f)); params.push_back (std::make_unique("Peak3Bypass", "Peak3 Bypass", false)); //HIGH BAND params.push_back (std::make_unique( "HighBandFreq", "HighBand Freq", juce::NormalisableRange(20.f, 20000.f, 1.f, 0.5f), 17000.f)); params.push_back (std::make_unique("HighBandGain", "HighBand Gain", juce::NormalisableRange(-24.f, 24.f, 0.1f), 0.f)); params.push_back (std::make_unique("HighBandQ", "HighBand Q", juce::NormalisableRange(0.1f, 10.f, 0.01f), 1.f)); params.push_back (std::make_unique( "HighBandSlope", "HighBand Slope", // Anzeigename juce::StringArray { "12", "24", "36", "48"}, 1)); params.push_back (std::make_unique( "HighBandModes", // Parameter-ID "High Band Modes", // Anzeigename juce::StringArray { "Off", "Cut", "Shelf", "Bell" }, // Einträge 0)); // params.push_back (std::make_unique("InputGain", "Input Gain", juce::NormalisableRange(-30.f, 30.0f, 0.1f), 0.0f)); params.push_back (std::make_unique("OutputGain", "Output Gain", juce::NormalisableRange(-30.f, 30.0f, 0.1f), 0.0f)); params.push_back (std::make_unique("CrystalizeButton", "Crystalize Button", false)); params.push_back (std::make_unique("MasterBypass", "MasterBypass", false)); return { params.begin(), params.end() }; } //============================================================================== CrystalizerEQAudioProcessor::CrystalizerEQAudioProcessor() #ifndef JucePlugin_PreferredChannelConfigurations : AudioProcessor (BusesProperties() #if ! JucePlugin_IsMidiEffect #if ! JucePlugin_IsSynth .withInput ("Input", juce::AudioChannelSet::stereo(), true) #endif .withOutput ("Output", juce::AudioChannelSet::stereo(), true) #endif ) #endif { } CrystalizerEQAudioProcessor::~CrystalizerEQAudioProcessor() = default; //============================================================================== const juce::String CrystalizerEQAudioProcessor::getName() const { return JucePlugin_Name; } bool CrystalizerEQAudioProcessor::acceptsMidi() const { #if JucePlugin_WantsMidiInput return true; #else return false; #endif } bool CrystalizerEQAudioProcessor::producesMidi() const { #if JucePlugin_ProducesMidiOutput return true; #else return false; #endif } bool CrystalizerEQAudioProcessor::isMidiEffect() const { #if JucePlugin_IsMidiEffect return true; #else return false; #endif } double CrystalizerEQAudioProcessor::getTailLengthSeconds() const { return 0.0; } int CrystalizerEQAudioProcessor::getNumPrograms() { return 1; // NB: some hosts don't cope very well if you tell them there are 0 programs, // so this should be at least 1, even if you're not really implementing programs. } int CrystalizerEQAudioProcessor::getCurrentProgram() { return 0; } void CrystalizerEQAudioProcessor::setCurrentProgram (int index) { } const juce::String CrystalizerEQAudioProcessor::getProgramName (int index) { return {}; } void CrystalizerEQAudioProcessor::changeProgramName (int index, const juce::String& newName) { } AudioFIFO::AudioFIFO(){ } AudioFIFO::~AudioFIFO() { } //============================================================================== void CrystalizerEQAudioProcessor::prepareToPlay (double sampleRate, int samplesPerBlock) { // Use this method as the place to do any pre-playback // initialisation that you need.. juce::dsp::ProcessSpec spec; spec.sampleRate = sampleRate; spec.maximumBlockSize = static_cast (samplesPerBlock); spec.numChannels = static_cast (getTotalNumOutputChannels()); mbLowpass.prepare(spec); mbHighpass.prepare(spec); mbLowpass.reset(); mbHighpass.reset(); mbHighPeak.prepare(spec); mbHighPeak.reset(); saturator.prepare(spec); saturator.functionToUse = [] (float x) { return std::tanh(x); }; saturator.reset(); leftChain.prepare (spec); rightChain.prepare (spec); leftChain.reset(); rightChain.reset(); updateFilters(); // initiale Koeffizienten } static void setCoeffs(juce::dsp::IIR::Filter& f, juce::dsp::IIR::Coefficients::Ptr c) { f.coefficients = c; // ok // oder: *f.coefficients = *c; } void CrystalizerEQAudioProcessor::updateFilters() { const auto sr = getSampleRate(); const auto lowFreq = apvts.getRawParameterValue("LowBandFreq")->load(); const auto lowSlope = static_cast(apvts.getRawParameterValue("LowBandSlope")->load()); const auto lowGdB = apvts.getRawParameterValue("LowBandGain")->load(); // falls angelegt const auto lowQ = apvts.getRawParameterValue("LowBandQ")->load(); const int lowBandModes = static_cast(apvts.getRawParameterValue("LowBandModes")->load()); const auto peak1F = apvts.getRawParameterValue("Peak1Freq")->load(); const auto peak1GdB = apvts.getRawParameterValue("Peak1Gain")->load(); const auto peak1Q = apvts.getRawParameterValue("Peak1Q")->load(); const auto peak1Bypass = apvts.getRawParameterValue("Peak1Bypass")->load(); const auto peak2F = apvts.getRawParameterValue("Peak2Freq")->load(); const auto peak2GdB = apvts.getRawParameterValue("Peak2Gain")->load(); const auto peak2Q = apvts.getRawParameterValue("Peak2Q")->load(); const auto peak2Bypass = apvts.getRawParameterValue("Peak2Bypass")->load(); const auto peak3F = apvts.getRawParameterValue("Peak3Freq")->load(); const auto peak3GdB = apvts.getRawParameterValue("Peak3Gain")->load(); const auto peak3Q = apvts.getRawParameterValue("Peak3Q")->load(); const auto peak3Bypass = apvts.getRawParameterValue("Peak3Bypass")->load(); const auto highFreq = apvts.getRawParameterValue("HighBandFreq")->load(); const auto highSlope = static_cast(apvts.getRawParameterValue("HighBandSlope")->load()); const auto highGdB = apvts.getRawParameterValue("HighBandGain")->load(); // falls angelegt const auto highQ = apvts.getRawParameterValue("HighBandQ")->load(); const int highBandModes = static_cast(apvts.getRawParameterValue("HighBandModes")->load()); const auto inputGdB = apvts.getRawParameterValue("InputGain")->load(); const auto outputGdB = apvts.getRawParameterValue("OutputGain")->load(); auto peak1 = juce::dsp::IIR::Coefficients::makePeakFilter(sr, peak1F, peak1Q, juce::Decibels::decibelsToGain(peak1GdB)); auto peak2 = juce::dsp::IIR::Coefficients::makePeakFilter(sr, peak2F, peak2Q, juce::Decibels::decibelsToGain(peak2GdB)); auto peak3 = juce::dsp::IIR::Coefficients::makePeakFilter(sr, peak3F, peak3Q, juce::Decibels::decibelsToGain(peak3GdB)); const auto crystalized = static_cast(apvts.getRawParameterValue("CrystalizeButton")->load()); juce::dsp::IIR::Coefficients::Ptr lowBand; juce::dsp::IIR::Coefficients::Ptr highBand; // links leftChain.get<0>().setGainDecibels (inputGdB); rightChain.get<0>().setGainDecibels (inputGdB); setCoeffs (leftChain.get<5>(), peak1); setCoeffs (rightChain.get<5>(), peak1); setCoeffs (leftChain.get<6>(), peak2); setCoeffs (rightChain.get<6>(), peak2); setCoeffs (leftChain.get<7>(), peak3); setCoeffs (rightChain.get<7>(), peak3); const auto crystalizedShelf = juce::dsp::IIR::Coefficients::makeHighShelf(sr, 12000.0f, 1.0f, juce::Decibels::decibelsToGain(4.0f)); setCoeffs(leftChain.get<12>(), crystalizedShelf); setCoeffs(rightChain.get<12>(), crystalizedShelf); const auto crystalizedBell = juce::dsp::IIR::Coefficients::makePeakFilter(sr, 10000.0f, 1.0f, juce::Decibels::decibelsToGain(2.0f)); setCoeffs(leftChain.get<13>(), crystalizedBell); setCoeffs(rightChain.get<13>(), crystalizedBell); leftChain.get<14>().setGainDecibels (outputGdB); rightChain.get<14>().setGainDecibels (outputGdB); if (peak1Bypass) { leftChain.setBypassed<5>(true); rightChain.setBypassed<5>(true); } else { leftChain.setBypassed<5>(false); rightChain.setBypassed<5>(false); } if (peak2Bypass) { leftChain.setBypassed<6>(true); rightChain.setBypassed<6>(true); } else { leftChain.setBypassed<6>(false); rightChain.setBypassed<6>(false); } if (peak3Bypass) { leftChain.setBypassed<7>(true); rightChain.setBypassed<7>(true); } else { leftChain.setBypassed<7>(false); rightChain.setBypassed<7>(false); } const float lowGainLin = juce::Decibels::decibelsToGain(lowGdB); switch (lowBandModes) { case 0: leftChain.setBypassed<1>(true); rightChain.setBypassed<1>(true); leftChain.setBypassed<2>(true); rightChain.setBypassed<2>(true); leftChain.setBypassed<3>(true); rightChain.setBypassed<3>(true); leftChain.setBypassed<4>(true); rightChain.setBypassed<4>(true); break; case 1: { const auto q = lowQ; lowBand = juce::dsp::IIR::Coefficients::makeHighPass(sr, lowFreq, q); setCoeffs(leftChain .get<1>(), lowBand); setCoeffs(rightChain.get<1>(), lowBand); leftChain .setBypassed<1>(false); rightChain.setBypassed<1>(false); setCoeffs (leftChain.get<2>(), lowBand); setCoeffs(rightChain.get<2>(), lowBand); setCoeffs (leftChain.get<3>(), lowBand); setCoeffs(rightChain.get<3>(), lowBand); setCoeffs (leftChain.get<4>(), lowBand); setCoeffs(rightChain.get<4>(), lowBand); //HIER SLOPE IMPLEMENTIEREN leftChain .setBypassed<2>(lowSlope < 2); rightChain.setBypassed<2>(lowSlope < 2); leftChain .setBypassed<3>(lowSlope < 3); rightChain.setBypassed<3>(lowSlope < 3); leftChain .setBypassed<4>(lowSlope < 4); rightChain.setBypassed<4>(lowSlope < 4); break; } case 2: // Q steuert die „Güte“/Steilheit des Übergangs lowBand = juce::dsp::IIR::Coefficients::makeLowShelf(sr, lowFreq, lowQ, lowGainLin); setCoeffs(leftChain .get<1>(), lowBand); setCoeffs(rightChain.get<1>(), lowBand); leftChain .setBypassed<1>(false); rightChain.setBypassed<1>(false); break; case 3: // Bell (Glocke – optional, falls du das Low-Band als Bell nutzen willst) lowBand = juce::dsp::IIR::Coefficients::makePeakFilter(sr, lowFreq, lowQ, lowGainLin); setCoeffs(leftChain .get<1>(), lowBand); setCoeffs(rightChain.get<1>(), lowBand); leftChain .setBypassed<1>(false); rightChain.setBypassed<1>(false); break; } //HIGH-BAND const float highGainHin = juce::Decibels::decibelsToGain(highGdB); switch (highBandModes) { case 0: leftChain.setBypassed<8>(true); rightChain.setBypassed<8>(true); leftChain.setBypassed<9>(true); rightChain.setBypassed<9>(true); leftChain.setBypassed<10>(true); rightChain.setBypassed<10>(true); leftChain.setBypassed<11>(true); rightChain.setBypassed<11>(true); break; case 1: { const auto q = highQ; highBand = juce::dsp::IIR::Coefficients::makeLowPass(sr, highFreq, q); setCoeffs(leftChain .get<8>(), highBand); setCoeffs(rightChain.get<8>(), highBand); leftChain .setBypassed<8>(false); rightChain.setBypassed<8>(false); setCoeffs (leftChain.get<9>(), highBand); setCoeffs(rightChain.get<9>(), highBand); setCoeffs (leftChain.get<10>(), highBand); setCoeffs(rightChain.get<10>(), highBand); setCoeffs (leftChain.get<11>(), highBand); setCoeffs(rightChain.get<11>(), highBand); //HIER SLOPE IMPLEMENTIEREN leftChain .setBypassed<9>(highSlope < 2); rightChain.setBypassed<9>(highSlope < 2); leftChain .setBypassed<10>(highSlope < 3); rightChain.setBypassed<10>(highSlope < 3); leftChain .setBypassed<11>(highSlope < 4); rightChain.setBypassed<11>(highSlope < 4); break; } case 2: // Q steuert die „Güte“/Steilheit des Übergangs highBand = juce::dsp::IIR::Coefficients::makeHighShelf(sr, highFreq, highQ, highGainHin); setCoeffs(leftChain .get<8>(), highBand); setCoeffs(rightChain.get<8>(), highBand); leftChain .setBypassed<8>(false); rightChain.setBypassed<8>(false); break; case 3: // Bell (Glocke – optional, falls du das Low-Band als Bell nutzen willst) highBand = juce::dsp::IIR::Coefficients::makePeakFilter(sr, highFreq, highQ, highGainHin); setCoeffs(leftChain .get<8>(), highBand); setCoeffs(rightChain.get<8>(), highBand); leftChain .setBypassed<8>(false); rightChain.setBypassed<8>(false); break; } if (!crystalized) { leftChain .setBypassed<12>(true); rightChain.setBypassed<12>(true); leftChain .setBypassed<13>(true); rightChain.setBypassed<13>(true); } else { leftChain .setBypassed<12>(false); rightChain.setBypassed<12>(false); leftChain .setBypassed<13>(false); rightChain.setBypassed<13>(false); } } juce::String CrystalizerEQAudioProcessor::savePresetToFile() const { const auto nameInput = getPresetName(); auto desktop = juce::File::getSpecialLocation(juce::File::userDesktopDirectory); // Unterordner auf dem Desktop anlegen auto presetFolder = desktop.getChildFile("CrystalizerEQ_Presets"); presetFolder.createDirectory(); // Datei vorbereiten auto file = presetFolder.getNonexistentChildFile(nameInput, ".xml"); juce::ValueTree preset ("Preset"); preset.setProperty("name", nameInput, nullptr); for (auto* p : getParameters()) { if (p == nullptr) continue; if (auto* ranged = dynamic_cast(p)) { if (ranged->getParameterID() == "MasterBypass") {continue;} juce::ValueTree param ("Param"); param.setProperty("id", ranged->getParameterID(), nullptr); param.setProperty("value", ranged->getValue(), nullptr); preset.addChild(param, -1, nullptr); } } std::unique_ptr xml (preset.createXml()); xml->writeToFile(juce::File(file), {}); return file.getFileNameWithoutExtension(); } void CrystalizerEQAudioProcessor::loadPreset(const juce::String &preset){ auto desktop = juce::File::getSpecialLocation(juce::File::userDesktopDirectory); auto presetFolder = desktop.getChildFile("CrystalizerEQ_Presets"); juce::Array files = presetFolder.findChildFiles( juce::File::findFiles, // nur Dateien (nicht Ordner) false, // nicht rekursiv (kein Durchsuchen von Unterordnern) "*.xml" // Pattern: alle Dateien ); for (const auto& f : files) { if (f.getFileName() != preset) { continue; } std::unique_ptr xml (juce::XmlDocument::parse(f)); if (xml == nullptr) return; for (auto* p : getParameters()) { if (p == nullptr) continue; if (auto* ranged = dynamic_cast(p)) { if (auto* child = xml->getFirstChildElement()) { while (child != nullptr) { juce::String id = child->getStringAttribute("id"); if (id == ranged->getParameterID()) { float value = (float) child->getDoubleAttribute("value"); ranged->beginChangeGesture(); ranged->setValueNotifyingHost(value); ranged->endChangeGesture(); break; } child = child->getNextElement(); } } } } } } void CrystalizerEQAudioProcessor::deletePreset(const juce::String &preset) const{ auto desktop = juce::File::getSpecialLocation(juce::File::userDesktopDirectory); // Unterordner auf dem Desktop anlegen auto presetFolder = desktop.getChildFile("CrystalizerEQ_Presets"); juce::Array files = presetFolder.findChildFiles( juce::File::findFiles, // nur Dateien (nicht Ordner) false, // nicht rekursiv (kein Durchsuchen von Unterordnern) "*.xml" // Pattern: alle Dateien ); for (const auto& f : files) { if (f.getFileName() != preset) { continue; } f.deleteFile(); } } void CrystalizerEQAudioProcessor::resetAllParameters() const{ for (auto* p : getParameters()) { if (p == nullptr) continue; if (auto* ranged = dynamic_cast(p)) { const float def = ranged->getDefaultValue(); // normalisiert [0..1] ranged->beginChangeGesture(); ranged->setValueNotifyingHost(def); ranged->endChangeGesture(); } } } void CrystalizerEQAudioProcessor::parameterChanged (const juce::String& id, float v) { } juce::StringArray CrystalizerEQAudioProcessor::getPresetNamesArray() const{ juce::StringArray presetNames = {"Init"}; auto desktop = juce::File::getSpecialLocation(juce::File::userDesktopDirectory); // Unterordner auf dem Desktop anlegen auto presetFolder = desktop.getChildFile("CrystalizerEQ_Presets"); juce::Array files = presetFolder.findChildFiles( juce::File::findFiles, // nur Dateien (nicht Ordner) false, // nicht rekursiv (kein Durchsuchen von Unterordnern) "*.xml" // Pattern: alle Dateien ); for (const auto& f : files) { const auto presetName = f.getFileNameWithoutExtension(); presetNames.add(presetName); } return presetNames; } void CrystalizerEQAudioProcessor::releaseResources() { // When playback stops, you can use this as an opportunity to free up any // spare memory, etc. } #ifndef JucePlugin_PreferredChannelConfigurations bool CrystalizerEQAudioProcessor::isBusesLayoutSupported (const BusesLayout& layouts) const { #if JucePlugin_IsMidiEffect juce::ignoreUnused (layouts); return true; #else // This is the place where you check if the layout is supported. // In this template code we only support mono or stereo. // Some plugin hosts, such as certain GarageBand versions, will only // load plugins that support stereo bus layouts. if (layouts.getMainOutputChannelSet() != juce::AudioChannelSet::mono() && layouts.getMainOutputChannelSet() != juce::AudioChannelSet::stereo()) return false; // This checks if the input layout matches the output layout #if ! JucePlugin_IsSynth if (layouts.getMainOutputChannelSet() != layouts.getMainInputChannelSet()) return false; #endif return true; #endif } #endif void CrystalizerEQAudioProcessor::processBlock (juce::AudioBuffer& buffer, juce::MidiBuffer& midiMessages) { juce::ignoreUnused (midiMessages); juce::ScopedNoDenormals noDenormals; auto totalNumInputChannels = getTotalNumInputChannels(); auto totalNumOutputChannels = getTotalNumOutputChannels(); // In case we have more outputs than inputs, this code clears any output // channels that didn't contain input data, (because these aren't // guaranteed to be empty - they may contain garbage). // This is here to avoid people getting screaming feedback // when they first compile a plugin, but obviously you don't need to keep // this code if your algorithm always overwrites all the output channels. for (auto i = totalNumInputChannels; i < totalNumOutputChannels; ++i) buffer.clear (i, 0, buffer.getNumSamples()); juce::AudioBuffer lowBuf, highBuf; // This is the place where you'd normally do the guts of your plugin's // audio processing... // Make sure to reset the state if your inner loop is processing // the samples and the outer loop is handling the channels. // Alternatively, you can process the samples with the channels // interleaved by keeping the same state. // (Einfacher Weg) – pro Block Parameter lesen & Filter updaten const bool testNoiseOn = apvts.getRawParameterValue("TestNoiseEnabled")->load() > 0.5f; if (testNoiseOn) { buffer.clear(); const float noiseLevelDb = apvts.getRawParameterValue("TestNoiseLevel")->load(); const float gain = juce::Decibels::decibelsToGain (noiseLevelDb); const int numSamples = buffer.getNumSamples(); const int numChannels = buffer.getNumChannels(); for (int ch = 0; ch < numChannels; ++ch) { auto* write = buffer.getWritePointer (ch); auto& rng = (ch == 0 ? noiseRandL : noiseRandR); for (int n = 0; n < numSamples; ++n) { // rng.nextFloat() ∈ [0,1) → in [-1,1) const float white = 2.0f * rng.nextFloat() - 1.0f; write[n] += white * gain; // ERSETZEN des Host-Inputs } } } const bool masterBypassed = apvts.getRawParameterValue("MasterBypass")->load() > 0.5f; if (masterBypassed) return; const auto crystalized = static_cast(apvts.getRawParameterValue("CrystalizeButton")->load()); if (crystalized) { lowBuf.makeCopyOf(buffer, true); highBuf.makeCopyOf(buffer, true); buffer.makeCopyOf(processMultiBand(lowBuf, highBuf), true); } updateFilters(); juce::dsp::AudioBlock block (buffer); auto leftBlock = block.getSingleChannelBlock (0); auto rightBlock = block.getSingleChannelBlock (1); juce::dsp::ProcessContextReplacing leftCtx (leftBlock); juce::dsp::ProcessContextReplacing rightCtx(rightBlock); leftChain.process (leftCtx); rightChain.process (rightCtx); audioFIFO.loadSamplesToFIFO(buffer); } juce::AudioBuffer CrystalizerEQAudioProcessor::processMultiBand(juce::AudioBuffer& lowBuf, juce::AudioBuffer& highBuf) { const auto sr = getSampleRate(); float fc = 10000.0f; fc = juce::jlimit(20.0f, 0.49f * (float)sr, fc); auto peakGain = juce::Decibels::gainToDecibels(1.0f); auto lp = Coeff::makeLowPass (sr, fc, 0.7071f); auto hp = Coeff::makeHighPass(sr, fc, 0.7071f); mbLowpass.setType(juce::dsp::LinkwitzRileyFilterType::lowpass); mbLowpass.setCutoffFrequency(fc); mbHighpass.setType(juce::dsp::LinkwitzRileyFilterType::highpass); mbHighpass.setCutoffFrequency(fc); { juce::dsp::AudioBlock lowBlock(lowBuf); auto leftLowBlock = lowBlock.getSingleChannelBlock (0); auto rightLowBlock = lowBlock.getSingleChannelBlock (1); juce::dsp::ProcessContextReplacing leftLowCtx(leftLowBlock); juce::dsp::ProcessContextReplacing rightLowCtx(rightLowBlock); mbLowpass.process(leftLowCtx); mbLowpass.process(rightLowCtx); } { //HIGH-BAND PROCESSING juce::dsp::AudioBlock highBlock(highBuf); auto leftHighBlock = highBlock.getSingleChannelBlock (0); auto rightHighBlock = highBlock.getSingleChannelBlock (1); juce::dsp::ProcessContextReplacing leftHighCtx(leftHighBlock); juce::dsp::ProcessContextReplacing rightHighCtx(rightHighBlock); mbHighpass.process(leftHighCtx); mbHighpass.process(rightHighCtx); //WHITE NOISE ON HIGH-BAND const int numSamples = highBuf.getNumSamples(); const int numChannels = highBuf.getNumChannels(); const float rms = highBuf.getRMSLevel(0, 0, numSamples); // oder Mittelwert über beide Kanäle const float dyn = juce::jlimit(0.0f, 1.0f, rms * 2.0f); // einfacher Kompressor const float gain = dyn * juce::Decibels::decibelsToGain(.5f); for (int ch = 0; ch < numChannels; ++ch) { auto* write = highBuf.getWritePointer (ch); auto& rng = (ch == 0 ? noiseRandL : noiseRandR); for (int n = 0; n < numSamples; ++n) { // rng.nextFloat() ∈ [0,1) → in [-1,1) const float white = 2.0f * rng.nextFloat() - 1.0f; write[n] += white * gain; // ERSETZEN des Host-Inputs } } saturator.process(leftHighCtx); saturator.process(rightHighCtx); } juce::AudioBuffer out; out.makeCopyOf(lowBuf, true); const int numCh = out.getNumChannels(); const int numSm = out.getNumSamples(); for (int ch = 0; ch < numCh; ++ch) out.addFrom(ch, 0, highBuf, ch, 0, numSm, 1.0f); return out; } void CrystalizerEQAudioProcessor::setPresetName (const juce::String& name) { presetName = name; } //============================================================================== bool CrystalizerEQAudioProcessor::hasEditor() const { return true; // (change this to false if you choose to not supply an editor) } juce::AudioProcessorEditor* CrystalizerEQAudioProcessor::createEditor() { return new CrystalizerEQAudioProcessorEditor (*this); } //============================================================================== void CrystalizerEQAudioProcessor::getStateInformation (juce::MemoryBlock& destData) { // You should use this method to store your parameters in the memory block. // You could do that either as raw data, or use the XML or ValueTree classes // as intermediaries to make it easy to save and load complex data. } void CrystalizerEQAudioProcessor::setStateInformation (const void* data, int sizeInBytes) { // You should use this method to restore your parameters from this memory block, // whose contents will have been created by the getStateInformation() call. } //============================================================================== // This creates new instances of the plugin.. juce::AudioProcessor* JUCE_CALLTYPE createPluginFilter() { return new CrystalizerEQAudioProcessor(); } void AudioFIFO::loadSamplesToFIFO(const juce::AudioBuffer &samples) { const int numSamples = samples.getNumSamples(); const float* channelData = samples.getReadPointer(0); const juce::SpinLock::ScopedLockType guard(lock); // <— NEU sampleStack.ensureStorageAllocated(sampleStack.size() + numSamples); for (int i = 0; i < numSamples; ++i) { sampleStack.add(channelData[i]); } } juce::Array AudioFIFO::sendSamplesToEditor(){ const juce::SpinLock::ScopedLockType guard(lock); juce::Array copiedSamples = sampleStack; sampleStack.clear(); return copiedSamples; }