📜 ⬆️ ⬇️

Creating audio plug-ins, part 4

All posts series:
Part 1. Introduction and setup
Part 2. Learning Code
Part 3. VST and AU
Part 4. Digital Distortion
Part 5. Presets and GUI
Part 6. Signal synthesis
Part 7. Receive MIDI Messages
Part 8. Virtual Keyboard
Part 9. Envelopes
Part 10. Refinement GUI
Part 11. Filter
Part 12. Low-frequency oscillator
Part 13. Redesign
Part 14. Polyphony 1
Part 15. Polyphony 2
Part 16. Antialiasing



It's time to start writing our first plugin. This will be a dirty digital distortion. More precisely, the plugin will simply cut off the peaks of the amplitude of the audio signal.

Digital distortion



The signal values ​​exceeding a certain threshold, we limit so that they do not go beyond it:
')


By saying “exceeding”, I mean “exceeding a certain positive threshold or falling below a certain negative threshold”.

With the help of the good old script duplicate you can copy any project by giving it a new name. And for each new project we don’t have to make all the changes that I described before.
Open a terminal, go to the IPlugExamples directory in it and enter this:

./duplicate.py MyFirstPlugin/ DigitalDistortion YourName

If you have not read the previous posts, the results of them can be downloaded from here . If you do this on a Mac, make sure that no other project is open in Xcode. In the newly created DigitalDistortion folder there is a DigitalDistortion.xcodeproj file. Open it, check that the APP targt build runs without errors. Edit the schemas as I described earlier so that REAPER runs for VST and AU. Don't forget that Arguments Passed On Launch should point to the correct .rpp file.

Now when REAPER starts, it loads not DigitalDistortion , but MyFirstPlugin . Miracles. This is because the project files in REAPER are simply structured text files in which the duplicate script replaces all “MyFirstPlugin” with “DigitalDistortion”.

Let's first rename the mGain parameter to mThreshold . Open DigitalDistortion.h and rename the private variable:

 private: double mThreshold; 


Now in DigitalDistortion.cpp replace (Cmd + Alt + F) all occurring Gain with Threshold . No errors should pop up during assembly. In the constructor, in the parameter initialization string, specify 0.01 as the minimum value, and 100.0 as the default value:

 GetParam(kThreshold)->InitDouble("Threshold", 100.0, 0.01, 100.0, 0.01, "%"); 


Now let's write the digital signal processing directly:

 void DigitalDistortion::ProcessDoubleReplacing( double** inputs, double** outputs, int nFrames) { // Mutex is already locked for us. int const channelCount = 2; for (int i = 0; i < channelCount; i++) { double* input = inputs[i]; double* output = outputs[i]; for (int s = 0; s < nFrames; ++s, ++input, ++output) { if(*input >= 0) { // Make sure positive values can't go above the threshold: *output = fmin(*input, mThreshold); } else { // Make sure negative values can't go below the threshold: *output = fmax(*input, -mThreshold); } } } } 


If you get an error saying that fmin and fmax not defined, try renaming them simply to min and max . If this did not help, add the following to the DigitalDistortion.cpp header :

 #include <math.h> 


If this does not solve the problem, add this instead of the previous line:

 #include <algorithm> 


And replace fmin with std::min , and fmax with std::max .

Despite the fact that channelCount hard-coded, we removed some redundancy by using an external for loop to iterate over the channels. That is, at first the plugin processes several samples from one channel, and then does the same with the samples of the second one.
An interesting point with the conditional if . For positive amplitude values, we choose the smaller of the two: either the input value or the threshold value. For negative ones, on the contrary, choose a larger one: either *input or a negative threshold value. In short, we always choose the value that is closer to zero.
Run the plugin in REAPER and drive it on the test sound. When the knob is turned all the way to the right, a clear sound will be heard. The more counterclockwise the knob is turned, the more distorted the signal becomes.
As the signal is distorted, it becomes quieter - this is because we lower the threshold value closer and closer to zero, and, accordingly, we cut off the amplitude to more and more silent values. To compensate for this, divide the input value by the threshold:

 if(*input >= 0) { *output = fmin(*input, mThreshold); } else { *output = fmax(*input, -mThreshold); } *output /= mThreshold; 


Slightly above, we set the minimum value for the parameter to 0.01 . Thus, we will never divide by zero, even if we turn the knob all the way to the left.
Now, if you run the plugin again, the amplitude will remain at the same level. But the volume will seem higher: clipping the amplitude brings our sine wave closer in shape closer to the meander , which has a larger rms value .

So far, I deliberately try not to climb into the wilds of digital signal processing. In my opinion, a good plugin is not just a signal processing algorithm. It is a mixture that includes



So before diving into the sound processing algorithms in the following posts, we will add presets and a more pleasant interface.

Original article:
martin-finke.de/blog/articles/audio-plugins-005-digital-distortion

Source: https://habr.com/ru/post/225751/


All Articles