From 9d938c0df644185c2a570836d22871b010e7d35d Mon Sep 17 00:00:00 2001 From: alper gungormusler Date: Sun, 18 Feb 2024 17:48:32 -0500 Subject: [PATCH] implement simple lfo effect + might need to support some sort of note start/reset in effect definition --- barelymusician/effects/BUILD.bazel | 13 ++++ barelymusician/effects/CMakeLists.txt | 17 +++++ barelymusician/effects/high_pass_effect.cpp | 8 +- barelymusician/effects/lfo_effect.cpp | 75 +++++++++++++++++++ barelymusician/effects/lfo_effect.h | 72 ++++++++++++++++++ barelymusician/effects/low_pass_effect.cpp | 8 +- .../Scripts/Effects/LfoEffect.cs | 40 ++++++++++ .../Scripts/Effects/LfoEffect.cs.meta | 11 +++ .../Assets/BarelyMusician/Scripts/Musician.cs | 6 ++ platforms/unity/BUILD.bazel | 3 +- platforms/unity/CMakeLists.txt | 1 + platforms/unity/unity_android.lds | 1 + platforms/unity/unity_windows.def | 1 + 13 files changed, 247 insertions(+), 9 deletions(-) create mode 100644 barelymusician/effects/lfo_effect.cpp create mode 100644 barelymusician/effects/lfo_effect.h create mode 100644 platforms/unity/Assets/BarelyMusician/Scripts/Effects/LfoEffect.cs create mode 100644 platforms/unity/Assets/BarelyMusician/Scripts/Effects/LfoEffect.cs.meta diff --git a/barelymusician/effects/BUILD.bazel b/barelymusician/effects/BUILD.bazel index be0723c2..10a9969c 100644 --- a/barelymusician/effects/BUILD.bazel +++ b/barelymusician/effects/BUILD.bazel @@ -25,6 +25,19 @@ cc_library( alwayslink = True, ) +cc_library( + name = "lfo_effect", + srcs = ["lfo_effect.cpp"], + hdrs = ["lfo_effect.h"], + defines = ["BARELYMUSICIAN_EXPORTS"], + deps = [ + ":custom_effect", + "//barelymusician", + "//barelymusician/dsp:oscillator", + ], + alwayslink = True, +) + cc_library( name = "low_pass_effect", srcs = ["low_pass_effect.cpp"], diff --git a/barelymusician/effects/CMakeLists.txt b/barelymusician/effects/CMakeLists.txt index 7a05b826..57983cdc 100644 --- a/barelymusician/effects/CMakeLists.txt +++ b/barelymusician/effects/CMakeLists.txt @@ -24,6 +24,23 @@ target_link_libraries( barely_one_pole_filter ) +add_library( + barely_lfo_effect + lfo_effect.cpp + lfo_effect.h +) +target_compile_definitions( + barely_lfo_effect + PUBLIC BARELYMUSICIAN_EXPORTS +) +target_link_libraries( + barely_lfo_effect + barely_custom_effect + barelymusician + barely_oscillator +) + + add_library( barely_low_pass_effect low_pass_effect.cpp diff --git a/barelymusician/effects/high_pass_effect.cpp b/barelymusician/effects/high_pass_effect.cpp index 3e3cd49f..e5620d70 100644 --- a/barelymusician/effects/high_pass_effect.cpp +++ b/barelymusician/effects/high_pass_effect.cpp @@ -34,16 +34,16 @@ void HighPassEffect::Process(double* output_samples, int output_channel_count, int output_frame_count) noexcept { assert(output_channel_count <= kMaxChannelCount); for (int frame = 0; frame < output_frame_count; ++frame) { + for (int channel = 0; channel < output_channel_count; ++channel) { + auto& sample = output_samples[output_channel_count * frame + channel]; + sample = filters_[channel].Next(sample); + } if (cutoff_frequency_.second != 0.0) { cutoff_frequency_.first += cutoff_frequency_.second; for (auto& filter : filters_) { filter.SetCoefficient(GetFilterCoefficient(frame_rate_, cutoff_frequency_.first)); } } - for (int channel = 0; channel < output_channel_count; ++channel) { - auto& sample = output_samples[output_channel_count * frame + channel]; - sample = filters_[channel].Next(sample); - } } } diff --git a/barelymusician/effects/lfo_effect.cpp b/barelymusician/effects/lfo_effect.cpp new file mode 100644 index 00000000..bdfcdcfc --- /dev/null +++ b/barelymusician/effects/lfo_effect.cpp @@ -0,0 +1,75 @@ +#include "barelymusician/effects/lfo_effect.h" + +#include +#include + +#include "barelymusician/barelymusician.h" +#include "barelymusician/dsp/oscillator.h" +#include "barelymusician/effects/custom_effect.h" + +BarelyEffectDefinition BarelyLfoEffect_GetDefinition() { + return barely::LfoEffect::GetDefinition(); +} + +namespace barely { + +EffectDefinition LfoEffect::GetDefinition() noexcept { + static const std::array(Control::kCount)> + control_definitions = { + // Oscillator type. + ControlDefinition{static_cast(OscillatorType::kSine), 0.0, + static_cast(OscillatorType::kNoise)}, + // Oscillator frequency. + ControlDefinition{1.0, 0.0, 32.0}, + // Intensity. + ControlDefinition{1.0, 0.0, 1.0}, + }; + return CustomEffect::GetDefinition(control_definitions); +} + +LfoEffect::LfoEffect(int frame_rate) noexcept : lfo_(frame_rate) { assert(frame_rate > 0); } + +void LfoEffect::Process(double* output_samples, int output_channel_count, + int output_frame_count) noexcept { + for (int frame = 0; frame < output_frame_count; ++frame) { + const double gain = intensity_.first * lfo_.Next(); + for (int channel = 0; channel < output_channel_count; ++channel) { + auto& sample = output_samples[output_channel_count * frame + channel]; + sample *= gain; + } + if (frequency_.second != 0.0) { + frequency_.first += frequency_.second; + lfo_.SetFrequency(frequency_.first); + } + if (intensity_.second != 0.0) { + intensity_.first += intensity_.second; + } + } +} + +void LfoEffect::SetControl(int index, double value, double slope_per_frame) noexcept { + switch (static_cast(index)) { + case Control::kOscillatorType: + lfo_.SetType(static_cast(static_cast(value))); + break; + case Control::kOscillatorFrequency: + if (value != frequency_.first) { + frequency_.first = value; + lfo_.SetFrequency(value); + } + frequency_.second = slope_per_frame; + break; + case Control::kIntensity: + if (value != intensity_.first) { + intensity_.first = value; + lfo_.SetFrequency(value); + } + intensity_.second = slope_per_frame; + break; + default: + assert(false); + break; + } +} + +} // namespace barely diff --git a/barelymusician/effects/lfo_effect.h b/barelymusician/effects/lfo_effect.h new file mode 100644 index 00000000..98491527 --- /dev/null +++ b/barelymusician/effects/lfo_effect.h @@ -0,0 +1,72 @@ +#ifndef BARELYMUSICIAN_EFFECTS_LFO_EFFECT_H_ +#define BARELYMUSICIAN_EFFECTS_LFO_EFFECT_H_ + +// NOLINTBEGIN +#include "barelymusician/barelymusician.h" + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +/// Returns the low-frequency oscillator effect definition. +/// +/// @return Effect definition. +BARELY_EXPORT BarelyEffectDefinition BarelyLfoEffect_GetDefinition(); + +#ifdef __cplusplus +} // extern "C" +#endif // __cplusplus +// NOLINTEND + +#ifdef __cplusplus + +#include "barelymusician/dsp/oscillator.h" +#include "barelymusician/effects/custom_effect.h" + +namespace barely { + +/// Simple amplitude low-frequency oscillator effect. +class LfoEffect : public CustomEffect { + public: + /// Control enum. + enum class Control : int { + /// Oscillator type. + kOscillatorType = 0, + /// Oscillator frequency. + kOscillatorFrequency = 1, + /// Intensity. + kIntensity = 2, + /// Number of controls. + kCount, + }; + + /// Returns the effect definition. + /// + /// @return Effect definition. + static EffectDefinition GetDefinition() noexcept; + + protected: + /// Constructs new `LfoEffect`. + explicit LfoEffect(int frame_rate) noexcept; + + /// Implements `CustomEffect`. + void Process(double* output_samples, int output_channel_count, + int output_frame_count) noexcept final; + void SetControl(int index, double value, double slope_per_frame) noexcept final; + void SetData(const void* /*data*/, int /*size*/) noexcept final {} + + private: + // Low-frequency oscillator. + Oscillator lfo_; + + // Frequency-slope pair. + std::pair frequency_ = {220.0, 0.0}; + + // Intensity-slope pair. + std::pair intensity_ = {1.0, 0.0}; +}; + +} // namespace barely +#endif // __cplusplus + +#endif // BARELYMUSICIAN_EFFECTS_LFO_EFFECT_H_ diff --git a/barelymusician/effects/low_pass_effect.cpp b/barelymusician/effects/low_pass_effect.cpp index 770124b4..7739efc4 100644 --- a/barelymusician/effects/low_pass_effect.cpp +++ b/barelymusician/effects/low_pass_effect.cpp @@ -34,16 +34,16 @@ void LowPassEffect::Process(double* output_samples, int output_channel_count, int output_frame_count) noexcept { assert(output_channel_count <= kMaxChannelCount); for (int frame = 0; frame < output_frame_count; ++frame) { + for (int channel = 0; channel < output_channel_count; ++channel) { + auto& sample = output_samples[output_channel_count * frame + channel]; + sample = filters_[channel].Next(sample); + } if (cutoff_frequency_.second != 0.0) { cutoff_frequency_.first += cutoff_frequency_.second; for (auto& filter : filters_) { filter.SetCoefficient(GetFilterCoefficient(frame_rate_, cutoff_frequency_.first)); } } - for (int channel = 0; channel < output_channel_count; ++channel) { - auto& sample = output_samples[output_channel_count * frame + channel]; - sample = filters_[channel].Next(sample); - } } } diff --git a/platforms/unity/Assets/BarelyMusician/Scripts/Effects/LfoEffect.cs b/platforms/unity/Assets/BarelyMusician/Scripts/Effects/LfoEffect.cs new file mode 100644 index 00000000..923c1971 --- /dev/null +++ b/platforms/unity/Assets/BarelyMusician/Scripts/Effects/LfoEffect.cs @@ -0,0 +1,40 @@ +using UnityEngine; + +namespace Barely { + /// A representation of a simple amplitude low-frequency oscillator effect. + public class LfoEffect : Effect { + /// Type. + public OscillatorType Type { + get { return (OscillatorType)GetControl(0); } + set { SetControl(0, (double)value, 0.0); } + } + + /// Frequency. + public double Frequency { + get { return GetControl(1); } + set { SetControl(1, value, 0.0); } + } + + /// Intensity. + public double Intensity { + get { return GetControl(2); } + set { SetControl(2, value, 0.0); } + } + + /// Sets the oscillator frequency with a slope. + /// + /// @param frequency Oscillator frequency. + /// @param slopePerBeat Slope in value change per beat. + public void SetFrequency(double frequency, double slopePerBeat) { + SetControl(0, frequency, slopePerBeat); + } + + /// Sets the intensity with a slope. + /// + /// @param intensity Intensity. + /// @param slopePerBeat Slope in value change per beat. + public void SetIntensity(double intensity, double slopePerBeat) { + SetControl(0, intensity, slopePerBeat); + } + } +} // namespace Barely diff --git a/platforms/unity/Assets/BarelyMusician/Scripts/Effects/LfoEffect.cs.meta b/platforms/unity/Assets/BarelyMusician/Scripts/Effects/LfoEffect.cs.meta new file mode 100644 index 00000000..f146d772 --- /dev/null +++ b/platforms/unity/Assets/BarelyMusician/Scripts/Effects/LfoEffect.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 64bbbf018cc15164fa492ea4898a9737 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/platforms/unity/Assets/BarelyMusician/Scripts/Musician.cs b/platforms/unity/Assets/BarelyMusician/Scripts/Musician.cs index 4aa3ea63..bea2aac2 100644 --- a/platforms/unity/Assets/BarelyMusician/Scripts/Musician.cs +++ b/platforms/unity/Assets/BarelyMusician/Scripts/Musician.cs @@ -318,6 +318,9 @@ public static void Effect_Create(Instrument instrument, Effect effect, case HighPassEffect highPass: definition = BarelyHighPassEffect_GetDefinition(); break; + case LfoEffect lfoEffect: + definition = BarelyLfoEffect_GetDefinition(); + break; case LowPassEffect lowPass: definition = BarelyLowPassEffect_GetDefinition(); break; @@ -1875,6 +1878,9 @@ private static extern bool BarelyRepeater_Create(IntPtr musician, Int32 processO [DllImport(pluginName, EntryPoint = "BarelyHighPassEffect_GetDefinition")] private static extern EffectDefinition BarelyHighPassEffect_GetDefinition(); + [DllImport(pluginName, EntryPoint = "BarelyLfoEffect_GetDefinition")] + private static extern EffectDefinition BarelyLfoEffect_GetDefinition(); + [DllImport(pluginName, EntryPoint = "BarelyLowPassEffect_GetDefinition")] private static extern EffectDefinition BarelyLowPassEffect_GetDefinition(); diff --git a/platforms/unity/BUILD.bazel b/platforms/unity/BUILD.bazel index fe307dbf..ace5007b 100644 --- a/platforms/unity/BUILD.bazel +++ b/platforms/unity/BUILD.bazel @@ -6,8 +6,9 @@ BARELYMUSICIAN_DEPS = [ "//barelymusician", "//barelymusician/components:arpeggiator", "//barelymusician/components:repeater", - "//barelymusician/effects:low_pass_effect", "//barelymusician/effects:high_pass_effect", + "//barelymusician/effects:lfo_effect", + "//barelymusician/effects:low_pass_effect", "//barelymusician/instruments:percussion_instrument", "//barelymusician/instruments:sampler_instrument", "//barelymusician/instruments:synth_instrument", diff --git a/platforms/unity/CMakeLists.txt b/platforms/unity/CMakeLists.txt index f47ef009..e26b3967 100644 --- a/platforms/unity/CMakeLists.txt +++ b/platforms/unity/CMakeLists.txt @@ -11,6 +11,7 @@ target_link_libraries( barelymusician barely_arpeggiator barely_high_pass_effect + barely_lfo_effect barely_low_pass_effect barely_percussion_instrument barely_sampler_instrument diff --git a/platforms/unity/unity_android.lds b/platforms/unity/unity_android.lds index 99f64baf..b4bf219b 100644 --- a/platforms/unity/unity_android.lds +++ b/platforms/unity/unity_android.lds @@ -88,6 +88,7 @@ VERS_1.0 { BarelyRepeater_Stop; BarelyHighPassEffect_GetDefinition; + BarelyLfoEffect_GetDefinition; BarelyLowPassEffect_GetDefinition; BarelyPercussionInstrument_GetDefinition; diff --git a/platforms/unity/unity_windows.def b/platforms/unity/unity_windows.def index ade00890..5b37fb17 100644 --- a/platforms/unity/unity_windows.def +++ b/platforms/unity/unity_windows.def @@ -89,6 +89,7 @@ EXPORTS BarelyRepeater_Stop BarelyHighPassEffect_GetDefinition + BarelyLfoEffect_GetDefinition BarelyLowPassEffect_GetDefinition BarelyPercussionInstrument_GetDefinition