Все посты серии:
Часть 1. Введение и настройка
Часть 2. Изучение кода
Часть 3. VST и AU
Часть 4. Цифровой дисторшн
______________________________________________________________
Пора приступать к написанию нашего первого плагина. Это будет грязный цифровой дисторшн. Если говорить точнее, плагин будет просто обрезать пики амплитуды звукового сигнала.
Цифровой дисторшн
Значения сигнала, превышающие определенный порог, мы ограничим так, чтобы они не выходили за него:
Говоря «превышающие» я имею ввиду «превышающие определенный положительный порог или опускающиеся ниже определенного отрицательного порога».
При помощи старого доброго скрипта duplicate
можно скопировать любой проект, дав ему новое имя. И нам не придется для каждого нового проекта вносить все те изменения, которые я описывал раньше.
Откройте терминал, в нем зайдите в директорию IPlugExamples и введите вот это:
./duplicate.py MyFirstPlugin/ DigitalDistortion YourName
Если вы не читали предыдущие посты, результаты из них можно скачать отсюда. Если вы делаете это на Маке, убедитесь, что в Xcode не открыто никаких других проектов. В свежесозданной папке DigitalDistortion лежит файл DigitalDistortion.xcodeproj. Откройте его, проверьте, чтобы сборка таргета APP запускалась без ошибок. Отредактируйте схемы, как я описывал раньше, чтобы REAPER запускался для VST и AU. Не забудьте, что Arguments Passed On Launch должен указывать на нужный .rpp файл.
Теперь при запуске REAPER он загружает не MyFirstPlugin, а DigitalDistortion. Чудеса. Это потому что файлы проектов в REAPER – это просто структурированные текстовые файлы, в которых скрипт duplicate
заменил все “MyFirstPlugin” на “DigitalDistortion”.
Давайте для начала переименуем параметр mGain
в mThreshold
. Откройте DigitalDistortion.h и переименуйте private
переменную:
private:
double mThreshold;
Теперь в DigitalDistortion.cpp замените (Cmd+Alt+F) все встречающиеся Gain
на Threshold
. При сборке не должно выскочить никаких ошибок. В конструкторе, в строке инициализации параметра укажите 0.01
как минимальное значение, и 100.0
как значение по умолчанию:
GetParam(kThreshold)->InitDouble("Threshold", 100.0, 0.01, 100.0, 0.01, "%");
Теперь давайте непосредственно напишем цифровую обработку сигнала:
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);
}
}
}
}
Если вылезает ошибка про то, что fmin
и fmax
не определены, попробуйте переименовать их просто в min
и max
. Если и это не помогло, добавьте в шапку DigitalDistortion.cpp следующее:
#include <math.h>
Если это не решило проблему, вместо предыдущей строки добавьте эту:
#include <algorithm>
И замените fmin
на std::min
, и fmax
на std::max
.
Несмотря на то, что значение channelCount
жестко задано, мы убрали некоторую избыточность, использовав внешний цикл for
для итерации над каналами. То есть, сначала плагин обрабатывает несколько семплов из одного канала, а затем делает то же самое с семплами второго.
Интересный момент с условным оператором if
. Для положительных значений амплитуды мы выбираем меньшее из двух: либо входное значение, либо пороговое. Для отрицательных — наоборот, выбираем большее: либо *input
, либо отрицательное пороговое значение. Короче, мы всегда выбираем то значение, которое ближе к нулю.
Запустите плагин в REAPER и погоняйте его на тестовом звуке. Когда ручка повернута до упора направо, будет слышен чистый звук. Чем больше против часовой стрелки поворачивается ручка, тем более искаженный становится сигнал.
По мере искажения сигнал становится и тише — это потому, что мы опускаем пороговое значение все ближе и ближе к нулю, и, соответственно, обрезаем амплитуду до все более тихих величин. Чтобы это компенсировать, разделим входное значение на пороговое:
if(*input >= 0) {
*output = fmin(*input, mThreshold);
} else {
*output = fmax(*input, -mThreshold);
}
*output /= mThreshold;
Чуть выше мы задали минимальное значение для параметра равным 0.01
. Таким образом, мы никогда не будем делить на ноль, даже если до упора повернем ручку налево.
Теперь, если снова запустить плагин, амплитуда будет оставаться на том же уровне. А вот громкость будет казаться выше: обрезание амплитуды приближает нашу синусоидальную волну по форме ближе к меандру, который имеет большее среднеквадратичное значение.
Пока что я намеренно стараюсь не лезть в дебри цифровой обработки сигналов. На мой взгляд, хороший плагин — это не просто алгоритмы обработки сигнала. Это смесь, включающая в себя
- надежную совместимость с хостом (настройки, стабильная работа)
- хорошее звучание (вот тут уже цифровая обработка в чистом виде)
- понятный пользовательский интерфейс
- красивый внешний вид
Так что перед погружением в алгоритмы обработки звука в следующих постах добавим пресеты и более приятный интерфейс.
Автор: 1eqinfinity