Forráskód Böngészése

Merge branch 'main' of https://github.com/polimi-cmls-2025/Chill-Music-Low-Stress

updated
Farnoosh Rad 6 hónapja
szülő
commit
ee0efa4457

+ 2 - 2
JUCE/CMLSProject/Source/CMLSChorus.cpp

@@ -83,8 +83,8 @@ void CMLSChorus::setAmount(float value) {
 
     delayLine1.setRate(freq);
     delayLine2.setRate(freq); //+ LFO_FREQ_DELTA
-    delayLine1.setDepth(0.2);
-    delayLine2.setDepth(0.2);
+    delayLine1.setDepth(depth);
+    delayLine2.setDepth(depth);
 }
 
 const float CMLSChorus::getDryWet() {

+ 1 - 1
JUCE/CMLSProject/Source/CMLSChorus.h

@@ -37,7 +37,7 @@ class CMLSChorus : juce::dsp::ProcessorBase {
 
     private:
         float dryWetProp = 0.0;
-		    float amount = 0.0;
+		float amount = 0.0;
 
         juce::dsp::Chorus<float> delayLine1; // 1st chorus delayline
         juce::dsp::Chorus<float> delayLine2; // 2nd chorus delayline

+ 38 - 34
JUCE/CMLSProject/Source/CMLSDelay.cpp

@@ -12,14 +12,12 @@
 
 CMLSDelay::CMLSDelay() {
 	delayLengthRange = new juce::NormalisableRange<float>(0, MAX_DELAY_LENGTH);
-	feedbackRange = new juce::NormalisableRange<float>(0, MAX_FEEDBACK);
+	feedbackRange = new juce::NormalisableRange<float>(MIN_FEEDBACK, MAX_FEEDBACK);
 
 	this->delayLength = 0.0f;
-	this->feedback = 0.75f;
-	this->delayBufferLength = 1;
-
-	this->delayReadPosition = 0;
-	this->delayWritePosition = 0;
+	this->feedback = 0.0f;
+	this->amount = 0.0f;
+	this->dryWetProp = 0.0f;
 }
 
 CMLSDelay::~CMLSDelay() {}
@@ -28,16 +26,23 @@ void CMLSDelay::reset() {
 }
 
 void CMLSDelay::prepare(const juce::dsp::ProcessSpec& spec) {
-	this->delayBufferLength = (int) 2.0 * spec.sampleRate;
-	
-	if (this->delayBufferLength < 1) {
-		this->delayBufferLength = 1;
+	this->effectDelaySamples = spec.sampleRate * MAX_DELAY_LENGTH;
+	this->delayLine.setMaximumDelayInSamples(this->effectDelaySamples);
+	this->linearDelay.setMaximumDelayInSamples(this->effectDelaySamples);
+
+	this->delayLine.prepare(spec);
+	this->linearDelay.prepare(spec);
+	this->mixer.prepare(spec);
+
+	for (auto& volume : this->delayFeedbackVolume) {
+		volume.reset(spec.sampleRate, 0.05f);
 	}
 
-	this->delayBuffer.setSize(spec.numChannels, this->delayBufferLength);
-	this->delayBuffer.clear();
+	this->mixer.reset();
+	this->mixer.setMixingRule(juce::dsp::DryWetMixingRule::linear);
 
-	this->delayReadPosition = (int)(this->delayWritePosition - (this->delayLength * spec.sampleRate) + this->delayBufferLength) % this->delayBufferLength;
+	std::fill(this->delayValue.begin(), this->delayValue.end(), 0.0f);
+	std::fill(this->lastDelayOutput.begin(), this->lastDelayOutput.end(), 0.0f);
 }
 
 void CMLSDelay::process(const juce::dsp::ProcessContextReplacing<float>& context) {
@@ -45,44 +50,43 @@ void CMLSDelay::process(const juce::dsp::ProcessContextReplacing<float>& context
 	auto numChannels = audioBlock.getNumChannels();
 	const auto numSamples = audioBlock.getNumSamples();
 
-	int dpr, dpw;
+	const auto& input = context.getInputBlock();
+	const auto& output = context.getOutputBlock();
 
-	for (int channel = 0; channel < numChannels; ++channel) {
-		float* channelData = audioBlock.getChannelPointer(channel);
-		float* delayData = this->delayBuffer.getWritePointer(juce::jmin(channel, this->delayBuffer.getNumChannels() - 1));
+	this->mixer.pushDrySamples(input);
 
-		dpr = this->delayReadPosition;
-		dpw = this->delayWritePosition;
+	for (size_t channel = 0; channel < numChannels; ++channel) {
+		auto* samplesIn = input.getChannelPointer(channel);
+		auto* samplesOut = output.getChannelPointer(channel);
 
-		for (int sample = 0; sample < numSamples; sample++) {
-			const float in = channelData[sample];
-			float out = 0.0f;
+		for (size_t sample = 0; sample < input.getNumSamples(); ++sample) {
+			auto input = samplesIn[sample] - this->lastDelayOutput[channel];
+			auto delayAmount = this->delayValue[channel];
 
-			out = ((1-this->dryWetProp) * in + this->dryWetProp * delayData[dpr]);
-			delayData[dpw] = in + (this->feedback * delayData[dpr]);
+			this->linearDelay.pushSample(channel, input);
+			this->linearDelay.setDelay(delayAmount);
+			samplesOut[sample] = this->linearDelay.popSample(channel);
 
-			if (++dpr >= this->delayBufferLength) {
-				dpr = 0;
-			}
-			if (++dpw >= this->delayBufferLength) {
-				dpw = 0;
-			}
-
-			channelData[sample] = out;
+			this->lastDelayOutput[channel] = samplesOut[sample] * this->delayFeedbackVolume[channel].getNextValue();
 		}
 	}
 
-	this->delayReadPosition = dpr;
-	this->delayWritePosition = dpw;
+	this->mixer.mixWetSamples(output);
 }
 
 void CMLSDelay::setDryWet(float value) {
 	this->dryWetProp = value;
+	this->mixer.setWetMixProportion(this->dryWetProp);
 }
 
 void CMLSDelay::setAmount(float value) {
 	this->delayLength = this->delayLengthRange->convertFrom0to1(value);
 	this->feedback = this->delayLengthRange->convertFrom0to1(value);
+
+	std::fill(this->delayValue.begin(), this->delayValue.end(), this->delayLength * this->effectDelaySamples);
+	for (auto& volume : this->delayFeedbackVolume) {
+		volume.setTargetValue(this->feedback);
+	}
 }
 
 const float CMLSDelay::getDryWet() {

+ 17 - 9
JUCE/CMLSProject/Source/CMLSDelay.h

@@ -13,7 +13,8 @@
 #include <JuceHeader.h>
 
 #define MAX_DELAY_LENGTH 1.0f // Maximum delay time in seconds
-#define MAX_FEEDBACK 1.0f
+#define MIN_FEEDBACK -100.0f
+#define MAX_FEEDBACK 0.0f
 
 class CMLSDelay : public juce::dsp::ProcessorBase
 {
@@ -32,17 +33,24 @@ class CMLSDelay : public juce::dsp::ProcessorBase
 		const float getAmount();
 
 	private:
-		float dryWetProp = 0.0f;
-		float amount = 0.0f;
+		float effectDelaySamples;
 
-		float delayLength;
+		//Paramters
+		float dryWetProp;
 		float feedback;
+		float delayLength;
+		float amount;
 
-		juce::AudioSampleBuffer delayBuffer;
-		int delayBufferLength;
-		int delayReadPosition;
-		int delayWritePosition;
-
+		// Normaliosable range
 		juce::NormalisableRange<float>* delayLengthRange;
 		juce::NormalisableRange<float>* feedbackRange;
+
+		//Delay Lines
+		juce::dsp::DelayLine<float> delayLine;
+		juce::dsp::DelayLine<float, juce::dsp::DelayLineInterpolationTypes::Linear> linearDelay;
+		juce::dsp::DryWetMixer<float> mixer;
+
+		std::array<float, 2> delayValue;
+		std::array<float, 2> lastDelayOutput;
+		std::array<juce::LinearSmoothedValue<float>, 2> delayFeedbackVolume;
 };

+ 34 - 0
JUCE/CMLSProject/Source/OSCReceiverWrapper.cpp

@@ -0,0 +1,34 @@
+/*
+  ==============================================================================
+
+    OSCReceiverWrapper.cpp
+    Created: 5 May 2025 2:34:15pm
+    Author:  Luigi
+
+  ==============================================================================
+*/
+
+#include "OSCReceiverWrapper.h"
+
+OSCReceiverWrapper::OSCReceiverWrapper(int port, juce::AudioProcessor* pluginProcessor) {
+    this->connect(port);
+	this->pluginProcessor = pluginProcessor;
+}
+
+OSCReceiverWrapper::~OSCReceiverWrapper() {}
+
+void OSCReceiverWrapper::oscMessageReceived(const juce::OSCMessage& message)
+{
+    if (!message.isEmpty() && message.size() == 1) {
+	    auto address = message.getAddressPattern().toString();
+        auto argument = message[0].getString();
+
+        // Interpret each message
+		if (address == "/") {
+            //this->pluginProcessor->getCallbackLock...
+        }
+    }
+}
+
+void OSCReceiverWrapper::oscConnectionError(const juce::String& errorMessage)
+{}

+ 25 - 0
JUCE/CMLSProject/Source/OSCReceiverWrapper.h

@@ -0,0 +1,25 @@
+/*
+  ==============================================================================
+
+    OSCReceiverWrapper.h
+    Created: 5 May 2025 2:34:15pm
+    Author:  Luigi
+
+  ==============================================================================
+*/
+
+#pragma once
+#include <JuceHeader.h>
+
+class OSCReceiverWrapper : public juce::OSCReceiver,
+    private juce::OSCReceiver::ListenerWithOSCAddress<juce::String>
+{
+    public:
+        OSCReceiverWrapper(int port, juce::AudioProcessor* pluginProcessor);
+        ~OSCReceiverWrapper();
+
+		void oscMessageReceived(const juce::OSCMessage& message) override;
+		void oscConnectionError(const juce::String& errorMessage);
+    private:
+		juce::AudioProcessor* pluginProcessor;
+};

+ 187 - 0
documentation/application_workflow.md

@@ -0,0 +1,187 @@
+Application workflow
+==============
+
+
+This is a proposal for how we can control the sound synthesis and effects via a drawing application. Here will be discussed the ways for the users to interact with the synthesizer and the effects using the drawing application **iDraw OSC** for Apple devices.
+
+The two possible types of interactions are:
+
+- drawing in the drawing area;
+- choosing the tool (especially the pens) and the color to draw with.
+
+The drawing area is represented in the following figure, with the 2 parameters describing the position of the pen (X coordinate and Y coordinate) and the pressure applied to the screen. Note that if the users are drawing with the finger instead of the pen, **the pressure variable is automatically set to 1 by default**.
+
+![Drawing area](rsc/drawing_area.jpeg)
+
+The different tools available and the sound parameters they control are represented in the following figure.
+
+![Different types of pens](rsc/pens.png)
+
+
+In the following sections we will describe in detail all the parameters that can be controlled by the users using the drawing application. In particular, we will talk about:
+
+- Sound synthesis: how to control the sound timbre using colors;
+- Envelopes: how to control one or many envelopes using different pens;
+- Effects: how to apply and control the effects by drawing
+
+
+
+# 1) Sound synthesis
+
+The timbre of a sound is often referred to as its color. Using this analogy, we could modify the timbre of the synth by changing the color of the pen. The color is defined by its three RGB components (red, green, blue). Let's use R, G and B to control three different waveforms, which are the three main components of our sound.
+
+
+| Color         | Sound        |
+| :------------ | :----------- |
+| Red           | Waveform 1   |
+| Green         | Waveform 2   |
+| Blue          | Waveform 3   |
+
+
+The output sound is the mix between Waveform 1, Waveform 2 and Waveform 3, with volumes corresponding to the intensity of the color component associated. For example using triangle, sawtooth and square as the three waveforms, we can imagine a combination like this:
+
+> **(R, G, B) = (0.2, 0.5, 0.9) --> Output = (20% triangle, 50% sawtooth, 90% square)**
+
+
+
+#### - Proposal 1
+The choice of the waveforms is up to the users. The application lets the users choose which waveform they want to put for red, green and blue among a set of predefined waveforms. The GUI implements a way for the users to map the three RGB components to the desired waveform.
+
+#### - Proposal 2 (Easier)
+The users do not have the choice of the waveforms, they are predefined and can not be changed. In that case the waveforms should at least be very different from each other to allow the users create complex sounds.
+
+
+
+# 2) Envelopes
+
+Envelopes are often referred to as ADSR, standing for the four parameters Attack, Decay, Sustain, Release. While all these parameters allow the users to shape the sound as best as possible, implementing a way to control them by drawing on a pad is not an esay task, especially because the pad allows for the control of only 3 parameters: the X coordinate, the Y coordinate and the pressure. Therefore there is no way to directly map the pad parameters and the 4 parameters of the envelope.
+
+However, we can stick to a more basic envelope, called AR (Attack, Release). This way, we can imagine the following mapping:
+
+| Pad parameter | OSC address | Envelope parameter  | OSC range    | Envelope parameter range |
+| :------------ | :---------- | :------------------ | :----------- | :----------------------- |
+| X axis        | /aspectX    | Attack              | [-0.5; 0.5]  | [0; 5] seconds           |
+| Y axis        | /aspectY    | Release             | [-0.5; 0.5]  | [0; 10] seconds          |
+
+The control of the envelope can be done by selecting the appropriate pen (see below).
+
+#### - Proposal 1
+We can create 3 AR envelopes, each one controlled via a different type of pen, as follows:
+
+| Envelope name | Envelope type | Description                                     | Pen type |
+| :------------ | :----------   | :---------------------------------------------  | :------- |
+| Amp. Env.     | Amplitude     | Modulates the amplitude of the sound            | Pen      |
+| Filt. Env.    | Modulation    | Modulates the cutoff frequency of the LP filter | Monoline |
+| Pitch. Env.   | Modulation    | Modulates the pitch of the oscillators          | Marker   |
+
+
+#### - Proposal 2 (Easier)
+We only create one AR amplitude envelope, controlling the amplitude of the sound, controllable with the "pen" type of pen.
+
+
+
+# 3) Effects control
+
+The effects can change drastically the final sound. Using the drawing surface we can apply effects and modify their parameters live. By effects we mean:
+
+- **On SuperCollider side (applied DURING the synthesis):**
+  - Low Frequency Oscillator (LFO) modulating pitch or filter;
+  - Resonant filter (low-pass and high-pass)
+- **On JUCE side (applied AFTER the synthesis):**
+  - Distortion;
+  - Reverb;
+  - Chorus;
+  - EQ;
+  - Delay.
+
+
+Let's synthesize in the following table all the possible parameters that can be modified for each one of these effects:
+
+| Effect                | Parameters       | Description                                    |
+| :-------------------- | :--------------- | :--------------------------------------------- |
+| **LFO**               | LFO Rate         | Frequency of the LFO (e.g. 0.01Hz to 20Hz)
+|                       | LFO Intensity    | Controls which amount of modulation we want
+|                       | LFO Waveform     | Waveform for the LFO
+|                       | LFO Target       | Target of the LFO
+| **Filter (HP or LP)** | Cutoff frequency | Cutoff frequency
+|                       | Resonance        | Q factor of the filter
+| **Distortion**        | Input level      | Level of the input signal (before distortion)
+|                       | Drive            | Shape of the distortion (soft, hard...)
+|                       | Dry/wet          | Controls the mix between dry and wet signal
+| **Reverb**            | Room size        | How long should the reverb go
+|                       | Dry/wet          | Controls the mix between dry and wet signal
+| **Chorus**            | Amount           | Frequency and depth of the chorus 
+|                       | Dry/wet          | Controls the mix between dry and wet signal
+| **EQ**                | Type             | Type of filter (shelf, peak/notch, ...)
+|                       | Frequency        | Central frequency of the filter
+|                       | Gain             | Gain to apply
+| **Delay**             | Amount           | Controls the amount of delay we want to apply
+|                       | Dry/wet          | Controls the mix between dry and wet signal
+
+
+Since the colors are used for the sound synthesis, we are left with the following controls:
+
+- **Controls for live performance (continuous):**
+  - X coordinate of the pen;
+  - Y coordinate of the pen;
+  - Pressure applied on the screen;
+- **Controls for selection/adjusting (sent only once):**
+  - Pen type;
+  - Pen thickness;
+  - Pen opacity;
+
+A creative and fun interaction design is to create custom presets consisting of a combination of effects that can be modified simultaneously using the pen. Each preset can be selected using a different type of pen, thus offering 4 possibilities of presets (3 out of the 7 pen types are already used for the envelopes). The following sections describe a possible set of presets that can be good-sounding and fun for the users.
+
+
+
+### Preset 1 - Pencil (/pencil)
+
+This effect preset uses the LFO, the filter and the reverb as follows. The parameter ranges are given as examples but may be adjusted a posteriori.
+
+| Pad parameter | OSC address   | Effect parameter | OSC range    | Parameter range       |
+| :------------ | :------------ | :--------------- | :----------- | :-------------------- |
+| X axis        | /aspectX      | Filter cutoff    | [-0.5; 0.5]  | [20; 18000] Hz
+| X axis        | /aspectX      | LFO Rate         | [-0.5; 0.5]  | [0; 15] Hz
+| Y axis        | /aspectY      | Filter resonance | [-0.5; 0.5]  | [0; max]
+| Y axis        | /aspectY      | Reverb room size | [-0.5; 0.5]  | [min; max]
+| Pen pressure  | /pressure     | LFO intensity    | [1; 8]       | [0; max]
+
+When this preset is selected, the following parameters are set and remain the same (they are not changed in any way by the pen):
+
+| Effect parameter | Value               |
+| :--------------- | :------------------ |
+| LFO Waveform     | Decreasing sawtooth |
+| LFO Target       | Filter cutoff       |
+
+
+### Preset 2 - Crayon (/crayon)
+
+This effect preset uses the LFO, the filter, the reverb and the delay as follows. The parameter ranges are given as examples but may be adjusted a posteriori.
+
+| Pad parameter | OSC parameter | Effect parameter | OSC range    | Parameter range       |
+| :------------ | :------------ | :--------------- | :----------- | :-------------------- |
+| X axis        | /aspectX      | LFO Rate         | [-0.5; 0.5]  | [15; 1] Hz
+| X axis        | /aspectX      | LFO Intensity    | [-0.5; 0.5]  | [0; max]
+| X axis        | /aspectX      | Delay amount     | [-0.5; 0.5]  | [0; max]
+| Y axis        | /aspectY      | Filter cutoff    | [-0.5; 0.5]  | [20; 18000] Hz
+| Y axis        | /aspectY      | Reverb dry/wet   | [-0.5; 0.5]  | [dry; dry+wet]
+| Pen pressure  | /pressure     | Filter resonance | [1; 5]       | [0; max]
+
+When this preset is selected, the following parameters are set and remain the same (they are not changed in any way by the pen):
+
+| Effect parameter | Value               |
+| :--------------- | :------------------ |
+| LFO Waveform     | Square              |
+| LFO Target       | Filter cutoff       |
+
+
+
+### Preset 3 - Fountain Pen (/fountainPen)
+
+TBD
+
+
+### Preset 4 - Water Color (/waterColor)
+
+TBD
+

+ 0 - 0
resources for sc and juce/osc addresses and values for iDraw OSC.pdf → documentation/iDraw_OSC_documentation.pdf


+ 0 - 0
resources for sc and juce/midi_receiver_example.scd → documentation/midi_receiver_example.scd


+ 0 - 0
resources for sc and juce/osc addresses and values.pdf → documentation/osc addresses and values.pdf


+ 0 - 0
resources for sc and juce/osc_receiver_example.scd → documentation/osc_receiver_example.scd


BIN
documentation/rsc/drawing_area.jpeg


BIN
documentation/rsc/pens.png