LinearSmoothedValue not smoothing Slider Value for Sine Generator

Hi If anyone could point me in the right direction on this, would be great !

I m trying to create a simple sine generator with frequency as a parameter that can be changed by a slider that uses LinearSmoothedValue Class for the smoothing between value jumps.

the sine generator create a sinewave sound ( albeit with some artifacts, but thats a different question ). The slider controls the frequency. But when moving the slider their is crackling.

I’ve tried to use the LinearSmoothedValue class ( creating this Value “smoothedFrequency2”). I’ve looked at how it’s implemented in the Reverb.h class inside Juce. I tried to implement it in the same way and many other wise without success it is still crackling when moving the slider.

I’ve inserted some text code to check if my LinearSmoothedValue object is smoothing at all using the smoothedFrequency2.isSmoothing() … and it looks like its working …

i’ve tried since 2 days but im surely missing something very obvious … below is my code if anyone knows what im doing wrong let me know … best :slight_smile:

`PluginProcessor.h

/*																											
==============================================================================																											
																											
This file was auto-generated!																											
																											
It contains the basic framework code for a JUCE plugin processor.																											
																											
=============================================================================='																											
*/																											
																											
#ifndef PLUGINPROCESSOR_H_INCLUDED																											
#define PLUGINPROCESSOR_H_INCLUDED																											
																											
#include "../JuceLibraryCode/JuceHeader.h"																											
																											
																											
//==============================================================================																											
/**																											
*/																											
class SinePlugIn2AudioProcessor  : public AudioProcessor//,																											
																											
																											
{																											
public:																											
//==============================================================================																											
SinePlugIn2AudioProcessor();																											
~SinePlugIn2AudioProcessor();																											
																											
//==============================================================================																											
void prepareToPlay (double sampleRate, int samplesPerBlock) override;																											
void releaseResources() override;																											
																											
#ifndef JucePlugin_PreferredChannelConfigurations																											
bool setPreferredBusArrangement (bool isInput, int bus, const AudioChannelSet& preferredSet) override;																											
#endif																											
																											
void processBlock (AudioSampleBuffer&, MidiBuffer&) override;																											
																											
//==============================================================================																											
AudioProcessorEditor* createEditor() override;																											
bool hasEditor() const override;																											
																											
//==============================================================================																											
const String getName() const override;																											
																											
bool acceptsMidi() const override;																											
bool producesMidi() const override;																											
double getTailLengthSeconds() const override;																											
																											
//==============================================================================																											
int getNumPrograms() override;																											
int getCurrentProgram() override;																											
void setCurrentProgram (int index) override;																											
const String getProgramName (int index) override;																											
void changeProgramName (int index, const String& newName) override;																											
																											
//==============================================================================																											
void getStateInformation (MemoryBlock& destData) override;																											
void setStateInformation (const void* data, int sizeInBytes) override;																											
																											
	double currentSampleRate, angleDelta, currentAngle;																										
	double currentFrequency;																										
																			//double currentFrequency, targetFrequency;								// Smoothing -
	int offset = 0;																										
																											
																											
	void setCurrentFrequency(double LinearSmoothedValue);																										
																											
																											
	LinearSmoothedValue<double>smoothedFrequency;																										
																											
	inline void updateAngleDelta() noexcept;																										
																											
	AudioProcessorParameter *pitchParameter;																										
	AudioProcessorParameter *volumeParameter;																										
																											
private:																											
																											
																											
	//==============================================================================																										
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SinePlugIn2AudioProcessor)																											
};																											
																											
																											
																											
#endif  // PLUGINPROCESSOR_H_INCLUDED																											

PluginProcessor.cpp

/*																									
==============================================================================																									
																									
This file was auto-generated!																									
																									
It contains the basic framework code for a JUCE plugin processor.																									
																									
==============================================================================																									
*/																									
																									
#include "PluginProcessor.h"																									
#include "PluginEditor.h"																									
																									
																									
																									
																									
//==============================================================================																									
SinePlugIn2AudioProcessor::SinePlugIn2AudioProcessor() :																									
	currentAngle(0.0),													// 5) initialize class with default values											
	angleDelta(0.0),																								
	currentSampleRate(0.0),																								
	smoothedPitchValue(100.0),																								
	currentFrequency(500.0)												// Smoothing -												
	//targetFrequency(currentFrequency)																								
																									
{																									
																									
																		// 9) add parameter							
	addParameter(pitchParameter = new AudioParameterFloat("PitchSliderParameter", "Pitch_Slider", 0.0, 1.0, 0.5));																								
	addParameter(volumeParameter = new AudioParameterFloat("VolumeSliderParameter", "Volume_Slider", 0.0, 1.0, 0.5));																								
																									
}																									
																									
SinePlugIn2AudioProcessor::~SinePlugIn2AudioProcessor()																									
{																									
}																									
																									
//==============================================================================																									
const String SinePlugIn2AudioProcessor::getName() const																									
{																									
return JucePlugin_Name;																									
}																									
																									
bool SinePlugIn2AudioProcessor::acceptsMidi() const																									
{																									
#if JucePlugin_WantsMidiInput																									
return true;																									
#else																									
return false;																									
#endif																									
}																									
																									
bool SinePlugIn2AudioProcessor::producesMidi() const																									
{																									
#if JucePlugin_ProducesMidiOutput																									
return true;																									
#else																									
return false;																									
#endif																									
}																									
																									
double SinePlugIn2AudioProcessor::getTailLengthSeconds() const																									
{																									
return 0.0;																									
}																									
																									
int SinePlugIn2AudioProcessor::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 SinePlugIn2AudioProcessor::getCurrentProgram()																									
{																									
return 0;																									
}																									
																									
void SinePlugIn2AudioProcessor::setCurrentProgram (int index)																									
{																									
}																									
																									
const String SinePlugIn2AudioProcessor::getProgramName (int index)																									
{																									
return String();																									
}																									
																									
void SinePlugIn2AudioProcessor::changeProgramName (int index, const String& newName)																									
{																									
}																									
																									
//==============================================================================																									
void SinePlugIn2AudioProcessor::prepareToPlay (double sampleRate, int samplesPerBlock)																									
{																									
// Use this method as the place to do any pre-playback																									
// initialisation that you need..																									
																						// 6)  override void prepareToPlayfunction() function			
																									
																									
																									
	currentSampleRate = sampleRate;																								
	updateAngleDelta();																	// setting currentSamplerate to sampleRate(which is from set by AudioDevice / Daw)							
																						// update angleDelta();			
}																									
																									
void SinePlugIn2AudioProcessor::releaseResources()																									
{																									
// When playback stops, you can use this as an opportunity to free up any																									
// spare memory, etc.																									
}																									
																									
#ifndef JucePlugin_PreferredChannelConfigurations																									
bool SinePlugIn2AudioProcessor::setPreferredBusArrangement (bool isInput, int bus, const AudioChannelSet& preferredSet)																									
{																									
// Reject any bus arrangements that are not compatible with your plugin																									
																									
const int numChannels = preferredSet.size();																									
																									
#if JucePlugin_IsMidiEffect																									
if (numChannels != 0)																									
return false;																									
#elif JucePlugin_IsSynth																									
if (isInput || (numChannels != 1 && numChannels != 2))																									
return false;																									
#else																									
if (numChannels != 1 && numChannels != 2)																									
return false;																									
																									
if (! AudioProcessor::setPreferredBusArrangement (! isInput, bus, preferredSet))																									
return false;																									
#endif																									
																									
return AudioProcessor::setPreferredBusArrangement (isInput, bus, preferredSet);																									
}																									
#endif																									
																									
void SinePlugIn2AudioProcessor::processBlock (AudioSampleBuffer& buffer, MidiBuffer& midiMessages)																									
{																									
																									
																									
	const int totalNumInputChannels  = getTotalNumInputChannels();																								
const int 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.																									
																									
																									
// This is the place where you'd normally do the guts of your plugin's																									
// audio processing...																									
	int const numSamples = buffer.getNumSamples();																								
																									
	for (int channel = 0; channel < totalNumOutputChannels; ++channel)																								
	{																								
		const float level = 0.25f;																							
		float* const channelBuffer = buffer.getWritePointer(channel);																							
																									
																									
																									
																									
			for (int sample = 0; sample < buffer.getNumSamples(); ++sample)																						
			{																						
				const float currentSample = sinf(float(currentAngle*(sample + offset)));																					
				//smoothedSample.setValue(currentSample);																					
				//const float currentSample = (float)std::sin(currentAngle);  // Smoothing -																					
				//currentFrequency += frequencyIncrement;						// Smoothing -															
				//updateAngleDelta();											// Smoothing -										
				updateAngleDelta();																					
				currentAngle = angleDelta;																					
																									
				channelBuffer[sample] = currentSample * level;																					
																									
			}																						
		}																							
	}																								
	offset += numSamples;																								
}																									
																									
//==============================================================================																									
																									
																									
inline void SinePlugIn2AudioProcessor::updateAngleDelta() noexcept																									
{																									
	const double cyclesPerSample = currentFrequency / currentSampleRate; //xxx //currentFrequency / currentSampleRate;// double(smoothedPitchValue.getNextValue()) / currentSampleRate;																								
	angleDelta = cyclesPerSample * 2.0 * double_Pi;																								
	counter++; // debug counter #1																								
}																									
																									
void SinePlugIn2AudioProcessor::setCurrentFrequency(double LinearSmoothedValue)																									
{																									
	currentFrequency = LinearSmoothedValue;																								
																									
}																									
																									
//=============================================================================																									
																									
																									
//==============================================================================																									
bool SinePlugIn2AudioProcessor::hasEditor() const																									
{																									
return true; // (change this to false if you choose to not supply an editor)																									
}																									
																									
AudioProcessorEditor* SinePlugIn2AudioProcessor::createEditor()																									
{																									
return new SinePlugIn2AudioProcessorEditor (*this);																									
}																									
																									
//==============================================================================																									
void SinePlugIn2AudioProcessor::getStateInformation (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 SinePlugIn2AudioProcessor::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..																									
AudioProcessor* JUCE_CALLTYPE createPluginFilter()																									
{																									
return new SinePlugIn2AudioProcessor();																									
}																									

PluginEditor.h

/*																									
																								
																									
This file was auto-generated!																									
																									
It contains the basic framework code for a JUCE plugin editor.																									
																									
																									
*/																									
																									
																									
#ifndef PLUGINEDITOR_H_INCLUDED																									
#define PLUGINEDITOR_H_INCLUDED																									
																									
#include "../JuceLibraryCode/JuceHeader.h"																									
#include "PluginProcessor.h"												// 11) make sure PluginProcessor.h is included													
#include "PluginEditor.h"																									
																									
//==============================================================================																									
/**																									
*/																									
class SinePlugIn2AudioProcessorEditor  : public AudioProcessorEditor,																									
			public Slider::Listener											// 9a) derrive from Slider::Listener Class											
																	// Debug - LinearSmoothed								
																									
																									
																									
{																									
public:																									
SinePlugIn2AudioProcessorEditor (SinePlugIn2AudioProcessor&);																									
~SinePlugIn2AudioProcessorEditor();																									
																									
//==============================================================================																									
void paint (Graphics&) override;																									
void resized() override;																									
																									
	void sliderValueChanged(Slider *slider) override;																								
																									
	Label test;																		// debug label						
																									
	LinearSmoothedValue<double>smoothedFrequency2;																								
																									
																									
	// Sliders																		// 7) create Slider Objects						
																									
	Slider pitchSlider;																								
	Slider volumeSlider;																								
																									
																									
																									
																									
																									
private:																									
// This reference is provided as a quick way for your editor to																									
// access the processor object that created it.																									
SinePlugIn2AudioProcessor& processor;																									
																									
																									
																									
																									
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SinePlugIn2AudioProcessorEditor)																									
};																									
																									
																									
																									
#endif  // PLUGINEDITOR_H_INCLUDED																									

PluginEditor.cpp

/*																									
==============================================================================																									
																									
This file was auto-generated!																									
																									
It contains the basic framework code for a JUCE plugin editor.																									
																									
==============================================================================																									
*/																									
																									
#include "PluginProcessor.h"																									
#include "PluginEditor.h"																									
																									
																									
//==============================================================================																									
SinePlugIn2AudioProcessorEditor::SinePlugIn2AudioProcessorEditor (SinePlugIn2AudioProcessor& p)																									
: AudioProcessorEditor (&p), processor (p)																									
{																									
// Make sure that before the constructor has finished, you've set the																									
// editor's size to whatever you need it to be.																									
																									
	addAndMakeVisible(pitchSlider);																								
																									
																									
	pitchSlider.setRange(20.0, 5000.0, 1.0);		//																						
	pitchSlider.setSkewFactorFromMidPoint(200.0);																								
	pitchSlider.addListener(this);																								
																									
																									
																									
	addAndMakeVisible(volumeSlider);																								
	volumeSlider.setRange(0.0, 1.0, 0.0);																								
	volumeSlider.addListener(this);																								
																									
	addAndMakeVisible(test);																								
	test.setText("test", dontSendNotification);																								
	test.setColour(Label::textColourId, Colours::black);																								
																									
	// editor's size to whatever you need it to be.																								
	const int numberOfRows = p.getNumParameters()+1;																								
	const int height = numberOfRows * 48 + 24;																								
																									
	setSize(600, height);																								
																									
	// set smoothedFrequency															// xxx									
	smoothedFrequency2.reset(processor.currentSampleRate, 0.01);																								
}																									
																									
SinePlugIn2AudioProcessorEditor::~SinePlugIn2AudioProcessorEditor()																									
{																									
}																									
																									
//==============================================================================																									
void SinePlugIn2AudioProcessorEditor::paint (Graphics& g)																									
{																									
g.fillAll (Colours::white);																									
																									
}																									
																									
void SinePlugIn2AudioProcessorEditor::resized()																									
{																									
// This is generally where you'll want to lay out the positions of any																									
// subcomponents in your editor..																									
																									
	const int unit = 8;																	// 10b) (this is optional) layout Sliders based on a unit grid.							
	const int margin = 2 * unit;																								
	const int offsetAdd = 4 * unit;																								
	const int offsetMult = 6 * unit;																								
	const int sliderWidth = 60 * unit;																								
	const int sliderHeight = 3 * unit;																								
																									
																									
	pitchSlider.setBounds(margin, offsetAdd + 0 * offsetMult, sliderWidth, sliderHeight);																								
	volumeSlider.setBounds(margin, offsetAdd + 1 * offsetMult, sliderWidth, sliderHeight);																								
																									
																									
	test.setBounds(margin, offsetAdd + 2 * offsetMult, sliderWidth, sliderHeight);		// bug test #1																						
																									
}																									
																									
//																									
void SinePlugIn2AudioProcessorEditor::sliderValueChanged(Slider* slider)   // 12b) define SliderValueChanged																									
{																									
	if (slider == &pitchSlider)																								
	{																								
																									
																									
		processor.setSmoothedPitchValue(pitchSlider.getValue());																							
		processor.pitchParameter->setValue(pitchSlider.getValue());																							
																									
		smoothedFrequency2.setValue(pitchSlider.getValue());																							
		processor.setCurrentFrequency(smoothedFrequency2.getNextValue());																							
																									
		String string;																							
																									
																									
		// Debug - LinearSmoothedValue - if the LinearSmoothedValue is working																							
																									
		if(smoothedFrequency2.isSmoothing())																							
		{																							
			string = "smoothedFrequency is smoothing";																						
		}																							
		else if (smoothedFrequency2.isSmoothing())																							
		{																							
			string = "smoothedFrequency is not smoothing";																						
		}																							
		else																							
		{																							
			string = "nothing happening";																						
		}																							
																									
		test.setText(string += (int(smoothedFrequency2.getTargetValue())), dontSendNotification); // bug test #1																							
	}																								
																									
}																									
//==========================================================================

set your LinearSmoothedValue as private membervariable, call reset(…) in the prepare to play function, set it in the slider callback with setValue(xy) and get the returned value once! every! sample in the process block with getNextValue()

hi thanks for your suggestion … i really appreciate it as im pulling my hair a bit on this :slight_smile:

so i set a LinearSmoothedValue as a private member in the processor.cpp ; ive reset it in the prepare to play ; in the sliderValueChanged() ive set it like this processor.smoothedFrequency.setValue(pitchSlider.getValue() ) and then i got the value inside the processBlock … smoothedFrequency.getNextValue() …

However it is still crackling, but the crackling is a highpitched sound that replaced the pop sound before … did i do the first 3 steps right ? or is it something in my processor that could be the issue ?

thanks for any advice

b.

Have a look at the juce reverb:

The idea is to set the setValue function every time the value has changed (e.g. slider moved) and the getNextValue function for every sample in the process function.

I have the LinearSmoothed as private, because I’m changing it from the parameterChanged callback within the AudioProcessor. But if you want to access it from the editor directly, you have to declare it public

I think LinearSmoothedValue doesn’t help here. The problem is, that if you change the pitch, the phase will jump.
You have two options to overcome that problem, either calculate the phase of the sine with old pitch setting and shift the new sine wave to continue at the same phase, or you create the buffer twice with old and new value and create a crossfade between the two sines.

shouldn’t be a problem, since he’s only updating the angleDelta not the absolute angle…

b_thats_me if you haven’t changed it: in your code you have this line wrong.

currentAngle += angleDelta;

you’re missing the +

that is correct, sorry…
But then I still doubt that LinearSmoothedValue is necessary for the frequency.

1 Like

hi thanks @daniel and @john_henry

this should be “simple”, im starting to suspect my approach is the issue ?generally, how do you guys set up a controller ( slider ) that should smoothly transition a value when moved ?

@john_henry
being a newbie im not familiar with the parameterChanged callback … but i assume it this https://www.juce.com/doc/classAudioProcessorListener#aa08a28d958759256aecd0f4af8a826f4

if thats the case how do i implement the parameterChanged ? if there is a thread or source where i can study it myself that would be great … ( ive tried to understand the threads but it s not clear )

i wanted to avoid using parameter so far because i thought i get a sine generator running and then move on to the topic of parameter. do I need to understand how parameter work before i can get a smooth fading slider for my sine generator … ?

this is my updated code i’ve tried to go by your suggestions ( @john_henry @daniel )

i did not however change the = to += because that broke my sine into some weird wave

also now when i compile the slider doesnt change the frequency anymore … the sine is fixed on the initial frequency

editor.h

/*
  ==============================================================================

    This file was auto-generated!

    It contains the basic framework code for a JUCE plugin editor.

  ==============================================================================
*/


#ifndef PLUGINEDITOR_H_INCLUDED
#define PLUGINEDITOR_H_INCLUDED

#include "../JuceLibraryCode/JuceHeader.h"
#include "PluginProcessor.h"												
#include "PluginEditor.h"

//==============================================================================
/**
*/
class SinePlugIn2AudioProcessorEditor  : public AudioProcessorEditor,
										 public Slider::Listener		
								
	
{
public:
    SinePlugIn2AudioProcessorEditor (SinePlugIn2AudioProcessor&);
    ~SinePlugIn2AudioProcessorEditor();

    //==============================================================================
    void paint (Graphics&) override;
    void resized() override;

	void sliderValueChanged(Slider *slider) override;								
																	
	
	Slider pitchSlider;
	Slider volumeSlider;
	


private:
    // This reference is provided as a quick way for your editor to
    // access the processor object that created it.
    SinePlugIn2AudioProcessor& processor;

	

    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SinePlugIn2AudioProcessorEditor)
};



#endif  // PLUGINEDITOR_H_INCLUDED

editor.cpp

/*
  ==============================================================================

    This file was auto-generated!

    It contains the basic framework code for a JUCE plugin editor.

  ==============================================================================
*/

#include "PluginProcessor.h"
#include "PluginEditor.h"


//==============================================================================
SinePlugIn2AudioProcessorEditor::SinePlugIn2AudioProcessorEditor (SinePlugIn2AudioProcessor& p)
    : AudioProcessorEditor (&p), processor (p)
{
    // Make sure that before the constructor has finished, you've set the
    // editor's size to whatever you need it to be.

	addAndMakeVisible(pitchSlider);					
	pitchSlider.setRange(20.0, 5000.0, 1.0);		
	pitchSlider.setSkewFactorFromMidPoint(200.0);	
	pitchSlider.addListener(this);					



	addAndMakeVisible(volumeSlider);
	volumeSlider.setRange(0.0, 1.0, 0.0);
	volumeSlider.addListener(this);

	addAndMakeVisible(test);						
	test.setText("test", dontSendNotification);
	test.setColour(Label::textColourId, Colours::black);
	
	
																

	
	

	// editor's size to whatever you need it to be.									
	const int numberOfRows = p.getNumParameters()+1;									
	const int height = numberOfRows * 48 + 24;

	setSize(600, height);

}

SinePlugIn2AudioProcessorEditor::~SinePlugIn2AudioProcessorEditor()
{
}

//==============================================================================
void SinePlugIn2AudioProcessorEditor::paint (Graphics& g)
{
    g.fillAll (Colours::white);

}

void SinePlugIn2AudioProcessorEditor::resized()
{
    // This is generally where you'll want to lay out the positions of any
    // subcomponents in your editor..

	const int unit = 8;																
	const int margin = 2 * unit;
	const int offsetAdd = 4 * unit;
	const int offsetMult = 6 * unit;
	const int sliderWidth = 60 * unit;
	const int sliderHeight = 3 * unit;


	pitchSlider.setBounds(margin, offsetAdd + 0 * offsetMult, sliderWidth, sliderHeight);
	volumeSlider.setBounds(margin, offsetAdd + 1 * offsetMult, sliderWidth, sliderHeight);

	
	test.setBounds(margin, offsetAdd + 2 * offsetMult, sliderWidth, sliderHeight);		

}

//
void SinePlugIn2AudioProcessorEditor::sliderValueChanged(Slider* slider)  
{
	if (slider == &pitchSlider)
	{
																		 
	
		processor.pitchParameter->setValue(pitchSlider.getValue());			
		
		//processor.setSmoothedFrequency(pitchSlider.getValue());
		
		
	}

}

processor.h

/*
  ==============================================================================

    This file was auto-generated!

    It contains the basic framework code for a JUCE plugin processor.

  ==============================================================================
*/

#ifndef PLUGINPROCESSOR_H_INCLUDED
#define PLUGINPROCESSOR_H_INCLUDED

#include "../JuceLibraryCode/JuceHeader.h"





//==============================================================================
/**
*/
class SinePlugIn2AudioProcessor  : public AudioProcessor,	
								   public AudioProcessorListener    
								   
{
public:
    //==============================================================================
    SinePlugIn2AudioProcessor();
    ~SinePlugIn2AudioProcessor();

    //==============================================================================
    void prepareToPlay (double sampleRate, int samplesPerBlock) override;
    void releaseResources() override;

   #ifndef JucePlugin_PreferredChannelConfigurations
    bool setPreferredBusArrangement (bool isInput, int bus, const AudioChannelSet& preferredSet) override;
   #endif

    void processBlock (AudioSampleBuffer&, MidiBuffer&) override;

    //==============================================================================
    AudioProcessorEditor* createEditor() override;
    bool hasEditor() const override;

    //==============================================================================
    const String getName() const override;

    bool acceptsMidi() const override;
    bool producesMidi() const override;
    double getTailLengthSeconds() const override;

    //==============================================================================
    int getNumPrograms() override;
    int getCurrentProgram() override;
    void setCurrentProgram (int index) override;
    const String getProgramName (int index) override;
    void changeProgramName (int index, const String& newName) override;

    //==============================================================================
    void getStateInformation (MemoryBlock& destData) override;
    void setStateInformation (const void* data, int sizeInBytes) override;

	// parameters
	double currentSampleRate, angleDelta, currentAngle;						
	double currentFrequency;
																			
	
	int offset = 0;
	
	void setSmoothedFrequency(double SliderValue); 								
					

	LinearSmoothedValue<float> smoothedPitchValue;								


	AudioProcessorParameter *pitchParameter;						
	AudioProcessorParameter *volumeParameter;

	
	void audioProcessorParameterChanged(AudioProcessor *processor, int parameterIndex, float newValue) override; //xxx

	void audioProcessorChanged(AudioProcessor *processor) override {};

private:
	LinearSmoothedValue<double>smoothedFrequency;									
	
	//==============================================================================
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SinePlugIn2AudioProcessor)
};



#endif  // PLUGINPROCESSOR_H_INCLUDED

processor.cpp

/*
  ==============================================================================

    This file was auto-generated!

    It contains the basic framework code for a JUCE plugin processor.

  ==============================================================================
*/

#include "PluginProcessor.h"
#include "PluginEditor.h"




//==============================================================================
SinePlugIn2AudioProcessor::SinePlugIn2AudioProcessor() :
	currentAngle(0.0),  													
	angleDelta(0.0),
	currentSampleRate(0.0),
	smoothedPitchValue(100.0),
	currentFrequency(500.0),												 
	smoothedFrequency(200.0)												
	
																			
{
																		
	addParameter(pitchParameter = new AudioParameterFloat("PitchSliderParameter", "Pitch_Slider", 0.0, 1.0, 0.5));
	addParameter(volumeParameter = new AudioParameterFloat("VolumeSliderParameter", "Volume_Slider", 0.0, 1.0, 0.5));

	
}

SinePlugIn2AudioProcessor::~SinePlugIn2AudioProcessor()
{
}

//==============================================================================
const String SinePlugIn2AudioProcessor::getName() const
{
    return JucePlugin_Name;
}

bool SinePlugIn2AudioProcessor::acceptsMidi() const
{
   #if JucePlugin_WantsMidiInput
    return true;
   #else
    return false;
   #endif
}

bool SinePlugIn2AudioProcessor::producesMidi() const
{
   #if JucePlugin_ProducesMidiOutput
    return true;
   #else
    return false;
   #endif
}

double SinePlugIn2AudioProcessor::getTailLengthSeconds() const
{
    return 0.0;
}

int SinePlugIn2AudioProcessor::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 SinePlugIn2AudioProcessor::getCurrentProgram()
{
    return 0;
}

void SinePlugIn2AudioProcessor::setCurrentProgram (int index)
{
}

const String SinePlugIn2AudioProcessor::getProgramName (int index)
{
    return String();
}

void SinePlugIn2AudioProcessor::changeProgramName (int index, const String& newName)
{
}

//==============================================================================
void SinePlugIn2AudioProcessor::prepareToPlay (double sampleRate, int samplesPerBlock)
{
    // Use this method as the place to do any pre-playback
    // initialisation that you need..
																	
	
	currentSampleRate = sampleRate;									
	smoothedFrequency.reset(currentSampleRate, 0.01);		
}

void SinePlugIn2AudioProcessor::releaseResources()
{
    // When playback stops, you can use this as an opportunity to free up any
    // spare memory, etc.
}

#ifndef JucePlugin_PreferredChannelConfigurations
bool SinePlugIn2AudioProcessor::setPreferredBusArrangement (bool isInput, int bus, const AudioChannelSet& preferredSet)
{
    // Reject any bus arrangements that are not compatible with your plugin

    const int numChannels = preferredSet.size();

   #if JucePlugin_IsMidiEffect
    if (numChannels != 0)
        return false;
   #elif JucePlugin_IsSynth
    if (isInput || (numChannels != 1 && numChannels != 2))
        return false;
   #else
    if (numChannels != 1 && numChannels != 2)
        return false;

    if (! AudioProcessor::setPreferredBusArrangement (! isInput, bus, preferredSet))
        return false;
   #endif

    return AudioProcessor::setPreferredBusArrangement (isInput, bus, preferredSet);
}
#endif

void SinePlugIn2AudioProcessor::processBlock (AudioSampleBuffer& buffer, MidiBuffer& midiMessages)	  // 1) 
{
	
	
	const int totalNumInputChannels  = getTotalNumInputChannels();
    const int 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.
   

    // This is the place where you'd normally do the guts of your plugin's
    // audio processing...
	int const numSamples = buffer.getNumSamples();

	for (int channel = 0; channel < totalNumOutputChannels; ++channel)
	{
		const float level = 0.25f;
		float* const channelBuffer = buffer.getWritePointer(channel);
			

			for (int sample = 0; sample < buffer.getNumSamples(); ++sample)
			{
				const double cyclesPerSample = smoothedFrequency.getNextValue() / currentSampleRate; //xxx 
				angleDelta = cyclesPerSample * 2.0 * double_Pi;
				const float currentSample = std::sin(angleDelta*(sample + offset));
				
				channelBuffer[sample] = currentSample * level;
				
			}
	}
	offset += numSamples;
}

//==============================================================================


void SinePlugIn2AudioProcessor::setSmoothedFrequency(double SliderValue)	// dont need this i guess
{
	smoothedFrequency.setValue(SliderValue);
	
}

void SinePlugIn2AudioProcessor::audioProcessorParameterChanged(AudioProcessor *processor, int parameterIndex, float newValue)
{
	if (parameterIndex == pitchParameter->getParameterIndex())
	{
		smoothedFrequency.setValue(newValue);
	}
}



//=============================================================================




//==============================================================================
bool SinePlugIn2AudioProcessor::hasEditor() const
{
    return true; // (change this to false if you choose to not supply an editor)
}

AudioProcessorEditor* SinePlugIn2AudioProcessor::createEditor()
{
    return new SinePlugIn2AudioProcessorEditor (*this);
}

//==============================================================================
void SinePlugIn2AudioProcessor::getStateInformation (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 SinePlugIn2AudioProcessor::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..
AudioProcessor* JUCE_CALLTYPE createPluginFilter()
{
    return new SinePlugIn2AudioProcessor();
}

look at the Tutorials on the Juce website, I just noticed, it covers a sine generator. After that you can look at the JUCE demo project and demo plugin, which covers a lot of JUCE’s functionality…

Ok, there are a few notes:

  1. If you are using the audioProcessor as audioProcessorListener, you have to add it as such, in the constructor:

    addListener (this);
    But I’d rather use a AudioProcessorValueTreeState, create the parameters there and being listener to the object. Big benefit: You can use a AudioProcessorValueTreeState::SliderAttachment which controls your slider and the parameter, gui and processor stay in sync.
    Please have a look, there are some examples in the forum, I don’t know if meanwhile there exists a tutorial…

2.) The slider and the processor change the variable in your processor. This works for live, but when it comes to automation the dataflow should be gui => processor->getParameter()->setValue() => calls valueChanged either on your processorListener or the AudioProcessorValueTreeState. Only then you can record the gui movements in the DAW. This again happens automatically, if you use the AudioProcessorValueTreeState.

3.) Now the more interesting: maths
Your computation of the sample’s value depends on the loop variable. This is obviously an error, because the sample will start 0 at arbitrary points (your output should be the same no mattter which blocksize you chose!)
Also the frequency setting looks improvable to me. I think what you meant to do was:

float radiansPerSample = 2.0f * juce::float_Pi / (frequency * currentRate);
for (int channel = 0; channel < totalNumOutputChannels; ++channel) {
    const float level = 0.25f;
    float* const channelBuffer = buffer.getWritePointer(channel);
    for (int sample=0, float pos = offset; sample < buffer.getNumSamples(); ++sample) {
        channelBuffer[sample] = level * std::sin (pos);
        pos += radiansPerSample;
    }
}
offset = fmod (offset + radiansPerSample * buffer.getNumSamples(), 2.0f * juce::float_Pi);

Here you see, what @john_henry meant, that there are no jumps in the phase (=pos). So that’s why you don’t need to smoothen the value. It is however essential for the level.

EDIT:
4.) The sin method is a little too slow, that’s why jules or timur encouraged somewhere in the forum to use a lookup table instead. However for you to learn at the moment it should be fine.

Hope that helps…

1 Like