diff --git a/CMakeLists.txt b/CMakeLists.txt index 5ac1aba8..aa9f750d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -16,46 +16,47 @@ add_subdirectory(common) add_subdirectory(lib/vst3sdk) smtg_enable_vst3_sdk() -add_subdirectory(AccumulativeRingMod) -add_subdirectory(BasicLimiter) -add_subdirectory(BasicLimiterAutoMake) +# add_subdirectory(AccumulativeRingMod) +# add_subdirectory(BasicLimiter) +# add_subdirectory(BasicLimiterAutoMake) add_subdirectory(ClangCymbal) -add_subdirectory(ClangSynth) -add_subdirectory(CollidingCombSynth) -add_subdirectory(CombDistortion) -add_subdirectory(CubicPadSynth) -add_subdirectory(EnvelopedSine) -add_subdirectory(EsPhaser) -add_subdirectory(FDN64Reverb) -add_subdirectory(FDNCymbal) -add_subdirectory(FeedbackPhaser) -add_subdirectory(FoldShaper) -add_subdirectory(GenericDrum) -add_subdirectory(IterativeSinCluster) -add_subdirectory(L3Reverb) -add_subdirectory(L4Reverb) -add_subdirectory(LatticeReverb) -add_subdirectory(LightPadSynth) -add_subdirectory(LongPhaser) -add_subdirectory(MatrixShifter) -add_subdirectory(MaybeSnare) -add_subdirectory(MembraneSynth) -add_subdirectory(MiniCliffEQ) -add_subdirectory(ModuloShaper) -add_subdirectory(NarrowingDelay) -add_subdirectory(OddPowShaper) -add_subdirectory(OrdinaryPhaser) -add_subdirectory(ParallelComb) -add_subdirectory(ParallelDetune) -add_subdirectory(PitchShiftDelay) -add_subdirectory(RingModSpacer) -add_subdirectory(SevenDelay) -add_subdirectory(SoftClipper) -add_subdirectory(SyncSawSynth) -add_subdirectory(TrapezoidSynth) -add_subdirectory(UltraSynth) -add_subdirectory(UltrasonicRingMod) -add_subdirectory(WaveCymbal) + +# add_subdirectory(ClangSynth) +# add_subdirectory(CollidingCombSynth) +# add_subdirectory(CombDistortion) +# add_subdirectory(CubicPadSynth) +# add_subdirectory(EnvelopedSine) +# add_subdirectory(EsPhaser) +# add_subdirectory(FDN64Reverb) +# add_subdirectory(FDNCymbal) +# add_subdirectory(FeedbackPhaser) +# add_subdirectory(FoldShaper) +# add_subdirectory(GenericDrum) +# add_subdirectory(IterativeSinCluster) +# add_subdirectory(L3Reverb) +# add_subdirectory(L4Reverb) +# add_subdirectory(LatticeReverb) +# add_subdirectory(LightPadSynth) +# add_subdirectory(LongPhaser) +# add_subdirectory(MatrixShifter) +# add_subdirectory(MaybeSnare) +# add_subdirectory(MembraneSynth) +# add_subdirectory(MiniCliffEQ) +# add_subdirectory(ModuloShaper) +# add_subdirectory(NarrowingDelay) +# add_subdirectory(OddPowShaper) +# add_subdirectory(OrdinaryPhaser) +# add_subdirectory(ParallelComb) +# add_subdirectory(ParallelDetune) +# add_subdirectory(PitchShiftDelay) +# add_subdirectory(RingModSpacer) +# add_subdirectory(SevenDelay) +# add_subdirectory(SoftClipper) +# add_subdirectory(SyncSawSynth) +# add_subdirectory(TrapezoidSynth) +# add_subdirectory(UltraSynth) +# add_subdirectory(UltrasonicRingMod) +# add_subdirectory(WaveCymbal) # ## Below are prototype plugins. Breaking changes will be introduced. # add_subdirectory(TestBedSynth) diff --git a/ClangCymbal/source/editor.cpp b/ClangCymbal/source/editor.cpp index 631a3e5d..c86da35c 100644 --- a/ClangCymbal/source/editor.cpp +++ b/ClangCymbal/source/editor.cpp @@ -16,6 +16,7 @@ // along with ClangCymbal. If not, see . #include "editor.hpp" +#include "gui/randomizebutton.hpp" #include "version.hpp" #include @@ -188,8 +189,18 @@ bool Editor::prepareUI() miscLeft1, miscTop3, labelWidth, labelHeight, uiTextSize, ID::slideType, slideTypeItems); addCheckbox( - miscLeft0 + int(labelWidth / 2), miscTop4, labelWidth, labelHeight, uiTextSize, - "2x Sampling", ID::overSampling); + miscLeft1, miscTop4, labelWidth, labelHeight, uiTextSize, "2x Sampling", + ID::overSampling); + + // Randomize button. + const auto randomButtonTop = miscTop4; + const auto randomButtonLeft = miscLeft0; + auto panicButton = new RandomizeButton( + CRect( + randomButtonLeft, randomButtonTop, randomButtonLeft + labelWidth - 2 * margin, + randomButtonTop + labelHeight), + this, 0, "Random", getFont(uiTextSize), palette, this); + frame->addView(panicButton); // Oscillator. constexpr auto oscLeft0 = gainLeft0 + 2 * labelWidth + 4 * margin; diff --git a/ClangCymbal/source/gui/randomizebutton.hpp b/ClangCymbal/source/gui/randomizebutton.hpp new file mode 100644 index 00000000..c552b309 --- /dev/null +++ b/ClangCymbal/source/gui/randomizebutton.hpp @@ -0,0 +1,311 @@ +// (c) 2023 Takamitsu Endo +// +// This file is part of ClangCymbal. +// +// ClangCymbal is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// ClangCymbal is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with ClangCymbal. If not, see . + +#pragma once + +#include "public.sdk/source/vst/vsteditcontroller.h" +#include "vstgui/vstgui.h" + +#include "../../../common/gui/plugeditor.hpp" +#include "../../../common/gui/style.hpp" +#include "../parameter.hpp" + +#include +#include +#include +#include +#include + +namespace VSTGUI { + +class RandomizeButton : public CControl { +public: + std::string label; + + RandomizeButton( + const CRect &size, + IControlListener *listener, + int32_t tag, + std::string label, + const SharedPointer &fontId, + Uhhyou::Palette &palette, + Steinberg::Vst::PlugEditor *editor) + : CControl(size, listener, tag) + , label(label) + , fontId(fontId) + , pal(palette) + , editor(editor) + { + if (editor) editor->remember(); + } + + ~RandomizeButton() + { + if (editor) editor->forget(); + } + + void draw(CDrawContext *pContext) override + { + pContext->setDrawMode(CDrawMode(CDrawModeFlags::kAntiAliasing)); + CDrawContext::Transform t( + *pContext, CGraphicsTransform().translate(getViewSize().getTopLeft())); + + // Border. + const double borderW = isMouseEntered ? 2 * borderWidth : borderWidth; + const double halfBorderWidth = int(borderW / 2.0); + pContext->setFillColor(isPressed ? pal.highlightButton() : pal.boxBackground()); + pContext->setFrameColor( + isMouseEntered && !isPressed ? pal.highlightButton() : pal.border()); + pContext->setLineWidth(borderW); + pContext->drawRect( + CRect( + halfBorderWidth, halfBorderWidth, getWidth() - halfBorderWidth, + getHeight() - halfBorderWidth), + kDrawFilledAndStroked); + + // Text + pContext->setFont(fontId); + pContext->setFontColor(pal.foreground()); + pContext->drawString( + label.c_str(), CRect(0, 0, getWidth(), getHeight()), kCenterText); + } + + void onMouseEnterEvent(MouseEnterEvent &event) override + { + isMouseEntered = true; + invalid(); + event.consumed = true; + } + + void onMouseExitEvent(MouseExitEvent &event) override + { + if (value == 1.0f) { + value = 0.0f; + } + isPressed = false; + isMouseEntered = false; + invalid(); + event.consumed = true; + } + + void onMouseDownEvent(MouseDownEvent &event) override + { + using ID = Steinberg::Synth::ParameterID::ID; + using Pv = Steinberg::Vst::ParamValue; + + if (!event.buttonState.isLeft()) return; + isPressed = true; + value = 1.0f; + + if (editor) { + using Rng = std::mt19937_64; + + std::random_device source; + std::random_device::result_type + random_data[(Rng::state_size - 1) / sizeof(source()) + 1]; + std::generate(std::begin(random_data), std::end(random_data), std::ref(source)); + std::seed_seq seeds(std::begin(random_data), std::end(random_data)); + Rng rng(seeds); + + std::uniform_real_distribution uniform{Pv(0), Pv(1)}; + std::uniform_real_distribution uniformUpperHalf{Pv(0.5), Pv(1)}; + std::uniform_real_distribution uniformLowerHalf{Pv(0), Pv(0.5)}; + setParam(ID::impulseGain, uniform(rng)); + // setParam(ID::oscGain, uniform(rng)); + setParam(ID::oscNoisePulseRatio, uniform(rng)); + setParam(ID::oscAttack, uniformLowerHalf(rng)); + setParam(ID::oscDecay, uniformUpperHalf(rng)); + setParam(ID::oscDensityHz, uniformUpperHalf(rng)); + setParam(ID::oscDensityKeyFollow, uniform(rng)); + setParam(ID::oscNoiseDecay, uniform(rng)); + setParam(ID::oscBounce, uniform(rng)); + setParam(ID::oscBounceCurve, uniform(rng)); + setParam(ID::oscJitter, uniform(rng)); + setParam(ID::oscPulseAmpRandomness, uniform(rng)); + // setParam(ID::oscLowpassCutoffSemi, uniform(rng)); + setParam(ID::oscLowpassQ, uniform(rng)); + setParam(ID::oscLowpassKeyFollow, uniform(rng)); + + setParam(ID::fdnMatrixIdentityAmount, uniform(rng)); + setParam(ID::fdnFeedback, uniform(rng)); + setParam(ID::fdnOvertoneAdd, uniform(rng)); + setParam(ID::fdnOvertoneMul, uniform(rng)); + setParam(ID::fdnOvertoneOffset, uniform(rng)); + setParam(ID::fdnOvertoneModulo, uniform(rng)); + // setParam(ID::fdnOvertoneRandomness, uniform(rng)); + setParam(ID::fdnInterpRate, uniform(rng)); + setParam(ID::fdnInterpLowpassSecond, uniform(rng)); + setParam(ID::fdnSeed, uniform(rng)); + // setParam(ID::fdnRandomizeRatio, uniform(rng)); + + auto lpCut = generateFilterTable(rng); + auto lpQ = generateFilterTable(rng); + auto hpCut = generateFilterTable(rng); + auto hpQ = generateFilterTable(rng); + for (int idx = 0; idx < fdnMatrixSize; ++idx) { + setParam(ID::fdnLowpassCutoffSemiOffset0 + idx, lpCut[idx]); + setParam(ID::fdnLowpassQOffset0 + idx, lpQ[idx]); + setParam(ID::fdnHighpassCutoffSemiOffset0 + idx, hpCut[idx]); + setParam(ID::fdnHighpassQOffset0 + idx, hpQ[idx]); + } + + auto lpSemi = uniform(rng); + auto hpSemi = uniform(rng); + if (lpSemi < hpSemi) std::swap(lpSemi, hpSemi); + + setParam(ID::fdnLowpassCutoffSemi, lpSemi); + setParam(ID::fdnLowpassCutoffSlope, uniform(rng)); + setParam(ID::fdnLowpassQ, uniform(rng)); + setParam(ID::fdnLowpassQSlope, uniform(rng)); + // setParam(ID::fdnLowpassKeyFollow, uniform(rng)); + setParam(ID::fdnHighpassCutoffSemi, hpSemi); + setParam(ID::fdnHighpassCutoffSlope, uniform(rng)); + setParam(ID::fdnHighpassQ, uniform(rng)); + setParam(ID::fdnHighpassQSlope, uniform(rng)); + // setParam(ID::fdnHighpassKeyFollow, uniform(rng)); + + auto table = generateEnvTable(rng); + for (int idx = 0; idx < nModEnvelopeWavetable; ++idx) { + setParam(ID::modEnvelopeWavetable0 + idx, table[idx]); + } + setParam(ID::modEnvelopeTime, uniform(rng)); + setParam(ID::modEnvelopeToFdnLowpassCutoff, uniform(rng)); + setParam(ID::modEnvelopeToFdnHighpassCutoff, uniform(rng)); + setParam(ID::modEnvelopeToFdnPitch, uniform(rng)); + setParam(ID::modEnvelopeToFdnOvertoneAdd, uniform(rng)); + setParam(ID::modEnvelopeToOscJitter, uniform(rng)); + setParam(ID::modEnvelopeToOscNoisePulseRatio, uniform(rng)); + + // setParam(ID::tremoloMix, uniform(rng)); + setParam(ID::tremoloDepth, uniform(rng)); + setParam(ID::tremoloDelayTime, uniform(rng)); + setParam(ID::tremoloModulationToDelayTimeOffset, uniform(rng)); + setParam(ID::tremoloModulationRateHz, uniform(rng)); + } + + invalid(); + event.consumed = true; + } + + void onMouseUpEvent(MouseUpEvent &event) override + { + if (isPressed) { + isPressed = false; + value = 0.0f; + invalid(); + } + event.consumed = true; + } + + void onMouseCancelEvent(MouseCancelEvent &event) override + { + if (isPressed) { + isPressed = false; + value = 0; + invalid(); + } + isMouseEntered = false; + event.consumed = true; + } + + void setBorderWidth(CCoord width) { borderWidth = width < 0 ? 0 : width; } + + CLASS_METHODS(RandomizeButton, CControl); + +private: + // Before calling check if `editor` is not nullptr. + inline void setParam(Steinberg::Vst::ParamID id, Steinberg::Vst::ParamValue value) + { + editor->valueChanged(id, value); + editor->updateUI(id, value); + } + + template inline auto generateFilterTable(Rng &rng) + { + using Pv = Steinberg::Vst::ParamValue; + constexpr int length = int(fdnMatrixSize); + + std::array table{}; + + std::uniform_real_distribution uniform{Pv(-1), Pv(1)}; + std::generate(table.begin(), table.end(), [&]() { return uniform(rng); }); + + // Bidirectional filtering. + Pv v1 = 0; + for (int idx = 0; idx < length; ++idx) { + v1 += Pv(0.3) * (table[idx] - v1); + table[idx] = v1; + } + v1 = 0; + for (int idx = length - 1; idx >= 0; --idx) { + v1 += Pv(0.3) * (table[idx] - v1); + table[idx] = v1; + } + + // Random scaling. + Pv max = 0; + for (int i = 0; i < length; ++i) max = std::max(max, std::abs(table[i])); + std::uniform_real_distribution uniform01{Pv(0), Pv(1)}; + if (max != 0) { + auto scaler = uniform01(rng) / max; + for (int i = 0; i < length; ++i) table[i] *= scaler; + } + + // Normalze range from [-1, 1] to [0, 1]. + for (int i = 0; i < length; ++i) table[i] = Pv(0.5) * (Pv(1) + table[i]); + + return table; + } + + template inline auto generateEnvTable(Rng &rng) + { + using Pv = Steinberg::Vst::ParamValue; + + std::array table{}; + + std::uniform_real_distribution uniform{Pv(0), Pv(1)}; + std::generate(table.begin(), table.end(), [&]() { return uniform(rng); }); + + Pv v1 = 0; + Pv v2 = 0; + for (int idx = nModEnvelopeWavetable - 1; idx >= 0; --idx) { + v1 += Pv(0.3) * (table[idx] - v1); + v2 += Pv(0.3) * (v1 - v2); + table[idx] = v2; + } + + const auto iter = std::max_element(table.begin(), table.end()); + if (iter == table.end()) return table; + auto max = *iter; + if (max == 0) return table; + for (int idx = 0; idx < nModEnvelopeWavetable; ++idx) table[idx] /= max; + + return table; + } + + Steinberg::Vst::PlugEditor *editor = nullptr; + + SharedPointer fontId; + Uhhyou::Palette &pal; + + CCoord borderWidth = 1.0; + + bool isPressed = false; + bool isMouseEntered = false; +}; + +} // namespace VSTGUI diff --git a/ClangCymbal/source/version.hpp b/ClangCymbal/source/version.hpp index d73a8b9f..6606f4e3 100644 --- a/ClangCymbal/source/version.hpp +++ b/ClangCymbal/source/version.hpp @@ -29,11 +29,11 @@ #define SUB_VERSION_STR "1" #define SUB_VERSION_INT 1 -#define RELEASE_NUMBER_STR "8" -#define RELEASE_NUMBER_INT 8 +#define RELEASE_NUMBER_STR "9" +#define RELEASE_NUMBER_INT 9 -#define BUILD_NUMBER_STR "11" -#define BUILD_NUMBER_INT 11 +#define BUILD_NUMBER_STR "12" +#define BUILD_NUMBER_INT 12 #define FULL_VERSION_STR \ MAJOR_VERSION_STR "." SUB_VERSION_STR "." RELEASE_NUMBER_STR "." BUILD_NUMBER_STR