/* ============================================================================== This file contains the basic framework code for a JUCE plugin processor. ============================================================================== */ #include "PluginProcessor.h" #include "PluginEditor.h" using namespace juce; //============================================================================== RetroXAudioProcessor::RetroXAudioProcessor() #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 { audioFormatManager.registerBasicFormats(); } RetroXAudioProcessor::~RetroXAudioProcessor() { } //============================================================================== juce::AudioProcessorValueTreeState::ParameterLayout RetroXAudioProcessor::createParameterLayout() { AudioProcessorValueTreeState :: ParameterLayout layout; layout.add(std::make_unique("gain", "Gain", NormalisableRange(-48.0f, 10.0f, 0.01f,2.0f), 0.0f)); layout.add(std::make_unique("noise", "Noise", NormalisableRange(0.0f, 1.0f, 0.01f), 0.0f)); layout.add(std::make_unique("loFi", "LoFi", NormalisableRange(0.0f, 1.0f, 0.01f), 0.0f)); layout.add(std::make_unique("distort", "Distort", NormalisableRange(0.0f, 1.0f, 0.01f), 0.0f)); layout.add(std::make_unique("reverb", "Reverb", NormalisableRange(0.0f, 1.0f, 0.01f), 0.0f)); layout.add(std::make_unique("highCut", "HighCut", NormalisableRange(20.0f, 20000.0f, 10.0f, 0.35f), 20000.0f)); layout.add(std::make_unique("lowCut", "LowCut", NormalisableRange(20.0f, 20000.0f, 10.0f, 0.35f), 20.0f)); layout.add(std::make_unique("noiseType", "NoiseType", StringArray{ "White", "Pink", "Vinyl", "Tape"}, 0)); return layout; } const juce::String RetroXAudioProcessor::getName() const { return JucePlugin_Name; } bool RetroXAudioProcessor::acceptsMidi() const { #if JucePlugin_WantsMidiInput return true; #else return false; #endif } bool RetroXAudioProcessor::producesMidi() const { #if JucePlugin_ProducesMidiOutput return true; #else return false; #endif } bool RetroXAudioProcessor::isMidiEffect() const { #if JucePlugin_IsMidiEffect return true; #else return false; #endif } double RetroXAudioProcessor::getTailLengthSeconds() const { return 0.0; } int RetroXAudioProcessor::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 RetroXAudioProcessor::getCurrentProgram() { return 0; } void RetroXAudioProcessor::setCurrentProgram (int index) { } const juce::String RetroXAudioProcessor::getProgramName (int index) { return {}; } void RetroXAudioProcessor::changeProgramName (int index, const juce::String& newName) { } //============================================================================== void RetroXAudioProcessor::prepareToPlay (double sampleRate, int samplesPerBlock) { // Use this method as the place to do any pre-playback // initialisation that you need.. lastSampleRate = sampleRate; dsp::ProcessSpec spec; spec.maximumBlockSize = samplesPerBlock; spec.sampleRate = sampleRate; spec.numChannels = getTotalNumOutputChannels(); highCutFilter.prepare(spec); highCutFilter.reset(); lowCutFilter.prepare(spec); lowCutFilter.reset(); } void RetroXAudioProcessor::releaseResources() { // When playback stops, you can use this as an opportunity to free up any // spare memory, etc. } #ifndef JucePlugin_PreferredChannelConfigurations bool RetroXAudioProcessor::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 RetroXAudioProcessor::updateHighCutFilter() { highCut = *parameters.getRawParameterValue("highCut"); *highCutFilter.state = *dsp::IIR::Coefficients::makeLowPass(lastSampleRate, highCut, 4.0f); } //method to update the low cut filter void RetroXAudioProcessor::updateLowCutFilter() { lowCut = *parameters.getRawParameterValue("lowCut"); *lowCutFilter.state = *dsp::IIR::Coefficients::makeHighPass(lastSampleRate, lowCut, 4.0f); } void RetroXAudioProcessor::Playing() { state = Play; transportSource.start(); } void RetroXAudioProcessor::Stopped() { state = Stop; transportSource.stop(); } void RetroXAudioProcessor::processBlock (juce::AudioBuffer& buffer, juce::MidiBuffer& midiMessages) { juce::AudioBuffer tempBuffer; tempBuffer.setSize(buffer.getNumChannels(), buffer.getNumSamples()); juce::File sourceFile = juce::File(__FILE__); juce::File sourceDirectory = sourceFile.getParentDirectory(); // Load Vinyl1.wav if (reader1 == nullptr) { juce::File audioFile1 = sourceDirectory.getChildFile("Vinyl1.wav"); reader1 = audioFormatManager.createReaderFor(audioFile1); if (reader1 != nullptr) { readerSource1 = std::make_unique(reader1, true); readerSource1->setLooping(true); } } if (reader2 == nullptr) { juce::File audioFile2 = sourceDirectory.getChildFile("Vinyl2.wav"); reader2 = audioFormatManager.createReaderFor(audioFile2); if (reader2 != nullptr) { readerSource2 = std::make_unique(reader2, true); readerSource2->setLooping(true); } } juce::ScopedNoDenormals noDenormals; auto totalNumInputChannels = getTotalNumInputChannels(); auto totalNumOutputChannels = getTotalNumOutputChannels(); for (auto i = totalNumInputChannels; i < totalNumOutputChannels; ++i) buffer.clear (i, 0, buffer.getNumSamples()); dsp::AudioBlock block(buffer); dsp::ProcessContextReplacing context(block); updateHighCutFilter(); updateLowCutFilter(); //get the gain from the slider gain = *parameters.getRawParameterValue("gain"); float outputGain = *parameters.getRawParameterValue("gain"); gain = pow(10, outputGain / 20); //get the noise type form the combobox noiseType = *parameters.getRawParameterValue("noiseType"); if (noiseType == 2 && readerSource1) { juce::AudioSourceChannelInfo info(tempBuffer); readerSource1->getNextAudioBlock(info); } else if (noiseType == 3 && readerSource2) { juce::AudioSourceChannelInfo info(tempBuffer); readerSource2->getNextAudioBlock(info); } for (int channel = 0; channel < totalNumInputChannels; ++channel) { auto* channelData = buffer.getWritePointer (channel); for (int sample = 0; sample < buffer.getNumSamples(); sample++) { noiseAmount = *parameters.getRawParameterValue("noise"); if (noiseType == 0) { whiteNoise = (random.nextFloat() * 0.25 - 0.2) * noiseAmount / 6; } else if (noiseType == 1) { float white = random.nextFloat() * 0.25 - 0.2; //white - noise input b0 = 0.99886 * b0 + white * 0.0555179; b1 = 0.99332 * b1 + white * 0.0750759; b2 = 0.96900 * b2 + white * 0.1538520; b3 = 0.86650 * b3 + white * 0.3104856; b4 = 0.55000 * b4 + white * 0.5329522; b5 = -0.7616 * b5 - white * 0.0168980; float pink = b0 + b1 + b2 + b3 + b4 + b5 + b6 + white * 0.5362; b6 = white * 0.115926; whiteNoise = pink * noiseAmount / 10; } channelData[sample] = (buffer.getSample(channel, sample)) * gain + whiteNoise * gain; } //check if the noise type is vinyl or tape ////check if the tempBuffer has any samples if (tempBuffer.getNumSamples() > 0) { tempBuffer.applyGain(0, tempBuffer.getNumSamples(), noiseAmount * gain); //if not, add the tempBuffer to the buffer buffer.addFrom(channel,0,tempBuffer,channel,0,tempBuffer.getNumSamples()); } } highCutFilter.process(context); lowCutFilter.process(context); } //============================================================================== bool RetroXAudioProcessor::hasEditor() const { return true; // (change this to false if you choose to not supply an editor) } juce::AudioProcessorEditor* RetroXAudioProcessor::createEditor() { return new RetroXAudioProcessorEditor (*this); } //============================================================================== void RetroXAudioProcessor::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. juce::MemoryOutputStream stream(destData, true); parameters.state.writeToStream(stream); } void RetroXAudioProcessor::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. auto tree = juce::ValueTree::readFromData(data, sizeInBytes); if(tree.isValid()) { parameters.state = tree; } } //============================================================================== // This creates new instances of the plugin.. juce::AudioProcessor* JUCE_CALLTYPE createPluginFilter() { return new RetroXAudioProcessor(); }