-
-
Notifications
You must be signed in to change notification settings - Fork 142
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #169 from OTTO-project/feature/delay-fx
Delay effect
- Loading branch information
Showing
14 changed files
with
692 additions
and
7 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Submodule Gamma
updated
35 files
+1 −1 | Gamma/Access.h | |
+40 −49 | Gamma/Containers.h | |
+2 −2 | Gamma/Conversion.h | |
+1 −1 | Gamma/DFT.h | |
+3 −3 | Gamma/Delay.h | |
+1 −1 | Gamma/Effects.h | |
+7 −7 | Gamma/Envelope.h | |
+3 −3 | Gamma/Filter.h | |
+3 −35 | Gamma/HRFilter.h | |
+52 −69 | Gamma/Noise.h | |
+2 −2 | Gamma/Oscillator.h | |
+17 −54 | Gamma/SamplePlayer.h | |
+0 −1 | Gamma/SoundFile.h | |
+68 −61 | Gamma/Strategy.h | |
+9 −18 | Gamma/Types.h | |
+19 −15 | Gamma/arr.h | |
+12 −25 | Gamma/ipl.h | |
+5 −5 | Gamma/rnd.h | |
+8 −8 | Gamma/scl.h | |
+2 −2 | Gamma/tbl.h | |
+5 −6 | Makefile | |
+5 −7 | Makefile.common | |
+3 −2 | Makefile.config | |
+12 −22 | Makefile.rules | |
+12 −10 | README.md | |
+31 −46 | examples/function/random.cpp | |
+0 −33 | examples/source/plotNoise.cpp | |
+ − | sounds/count.wav | |
+106 −192 | src/SoundFile.cpp | |
+0 −882 | src/SoundFileIO.h | |
+5 −1 | tests/unitTests.cpp | |
+4 −0 | tests/ut/utAccess.cpp | |
+1 −24 | tests/ut/utGenerators.cpp | |
+0 −51 | tests/ut/utStrategy.cpp | |
+0 −1 | tests/ut/ut_arr.cpp |
Submodule nanovg
updated
9 files
+0 −7 | README.md | |
+21 −23 | example/demo.c | |
+11 −11 | example/perf.c | |
+49 −108 | src/fontstash.h | |
+49 −84 | src/nanovg.c | |
+3 −15 | src/nanovg.h | |
+15 −27 | src/nanovg_gl.h | |
+1 −12 | src/nanovg_gl_utils.h | |
+165 −1,927 | src/stb_truetype.h |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
#include "audio.hpp" | ||
|
||
#include "services/audio_manager.hpp" | ||
|
||
namespace otto::engines::pingpong { | ||
|
||
Audio::Audio() noexcept | ||
{ | ||
for (auto& dl : delay_line) dl.maxDelay(6.f); | ||
output_delay.maxDelay(spread_max); | ||
input_filter.set_position(0.6f); | ||
} | ||
|
||
void Audio::action(itc::prop_change<&Props::delaytime>, float t) noexcept | ||
{ | ||
for (auto& d : delay_line) d.delay(t); | ||
} | ||
|
||
void Audio::action(itc::prop_change<&Props::feedback>, float f) noexcept | ||
{ | ||
feedback_ = f * 1.5; | ||
} | ||
|
||
void Audio::action(itc::prop_change<&Props::filter>, float f) noexcept | ||
{ | ||
input_filter.set_position(f); | ||
for (auto& lf : loop_filter) lf.set_position(f); | ||
} | ||
|
||
void Audio::action(itc::prop_change<&Props::stereo>, float s) noexcept | ||
{ | ||
spread_ = std::max(1.f - 2.f * s, 0.f); | ||
spread_ *= spread_; | ||
output_delay.delaySamplesR(1.f + spread_ * spread_max_samples); | ||
pingpong_ = std::max(0.f, 2.f * s - 1.f); | ||
} | ||
|
||
void Audio::action(itc::prop_change<&Props::stereo_invert>, bool inv) noexcept | ||
{ | ||
invert_ = inv; | ||
} | ||
|
||
audio::ProcessData<2> Audio::process(audio::ProcessData<1> data) noexcept | ||
{ | ||
auto buf = services::AudioManager::current().buffer_pool().allocate_multi<2>(); | ||
for (auto&& [dat, bufL, bufR] : util::zip(data.audio, buf[0], buf[1])) { | ||
auto in = input_filter(dat); // Filter | ||
// Read next value from first delay line - don't input new one yet | ||
auto out0 = delay_line[0](); | ||
auto out1 = delay_line[1](feedback_ * loop_filter[0](out0)); | ||
delay_line[0](in + feedback_ * loop_filter[1](out1)); | ||
|
||
// Transform outputs to left/right signals | ||
bufL = output_delay(out0 + (1.f - pingpong_) * out1); | ||
bufR = (1.f - pingpong_) * out0 + out1; | ||
|
||
if (invert_) std::swap(bufL, bufR); | ||
} | ||
return data.with(buf); | ||
} | ||
|
||
} // namespace otto::engines::pingpong |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
#pragma once | ||
|
||
#include "Gamma/Delay.h" | ||
#include "pingpong.hpp" | ||
#include "services/application.hpp" | ||
#include "util/dsp/dj_filter.hpp" | ||
|
||
namespace otto::engines::pingpong { | ||
|
||
using namespace core; | ||
|
||
struct Audio { | ||
Audio() noexcept; | ||
audio::ProcessData<2> process(audio::ProcessData<1>) noexcept; | ||
|
||
|
||
void action(itc::prop_change<&Props::filter>, float f) noexcept; | ||
|
||
void action(itc::prop_change<&Props::stereo>, float s) noexcept; | ||
void action(itc::prop_change<&Props::stereo_invert>, bool inv) noexcept; | ||
|
||
void action(itc::prop_change<&Props::delaytime>, float t) noexcept; | ||
|
||
void action(itc::prop_change<&Props::feedback>, float f) noexcept; | ||
|
||
private: | ||
/// Feeds into each other | ||
std::array<gam::Delay<>, 2> delay_line; | ||
/// Gets output of delay line 0. Used for stereo spread. | ||
gam::Delay<> output_delay; | ||
/// Filters | ||
DJFilter<gam::Biquad<>> input_filter{400, 10000}; | ||
std::array<DJFilter<gam::OnePole<>>, 2> loop_filter = {{{300, 15000}, {300, 15000}}}; | ||
|
||
|
||
float feedback_ = 0.5; | ||
float pingpong_ = 0; | ||
float spread_ = 0; | ||
const float spread_max = 0.100f; | ||
const float spread_max_samples = spread_max * gam::sampleRate() - 2.f; | ||
bool invert_ = false; | ||
}; | ||
} // namespace otto::engines::pingpong |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
#include "pingpong.hpp" | ||
|
||
#include "core/service.hpp" | ||
#include "services/application.hpp" | ||
#include "services/audio_manager.hpp" | ||
#include "services/clock_manager.hpp" | ||
#include "services/log_manager.hpp" | ||
#include "services/ui_manager.hpp" | ||
#include "util/math.hpp" | ||
|
||
namespace otto::engines::pingpong { | ||
|
||
PingPong::PingPong() | ||
: audio(std::make_unique<Audio>()), screen_(std::make_unique<Screen>()), props{{*audio, *screen_}} | ||
{ | ||
props.timetype.on_change() | ||
.connect([this](bool tt) { | ||
props.delaytime.set(calculate_delaytime(props.timetype, props.free_time, props.subdivision)); | ||
}) | ||
.call_now(); | ||
} | ||
|
||
using namespace core::input; | ||
|
||
bool PingPong::keypress(Key key) | ||
{ | ||
switch (key) { | ||
case Key::blue_click: props.timetype = !props.timetype; break; | ||
case Key::red_click: props.stereo_invert = !props.stereo_invert; break; | ||
default: return false; ; | ||
} | ||
return true; | ||
} | ||
|
||
void PingPong::encoder(EncoderEvent ev) | ||
{ | ||
switch (ev.encoder) { | ||
case Encoder::blue: { | ||
if (props.timetype) { | ||
props.subdivision.step(util::math::sgn(ev.steps)); | ||
} else { | ||
props.free_time.step(ev.steps); | ||
} | ||
props.delaytime.set(calculate_delaytime(props.timetype, props.free_time, props.subdivision)); | ||
break; | ||
} | ||
case Encoder::green: props.feedback.step(ev.steps); break; | ||
case Encoder::yellow: props.filter.step(ev.steps); break; | ||
case Encoder::red: props.stereo.step(ev.steps); break; | ||
} | ||
} | ||
|
||
core::ui::ScreenAndInput PingPong::screen() | ||
{ | ||
return {*screen_, *this}; | ||
} | ||
|
||
// TODO: This will NOT update when the bpm changes. It should do that. | ||
float PingPong::calculate_delaytime(bool type, float free, SubdivisionEnum sd) | ||
{ | ||
if (type) { | ||
// Using subdivisions. Result is sec. per bar (whole note) | ||
auto res = 240.f / services::ClockManager::current().bpm(); | ||
// Identification of the subdivisions | ||
switch (sd) { | ||
case SubdivisionEnum::thirtysecondth: res *= 1.f/32.f; break; | ||
case SubdivisionEnum::sixteenth: res *= 1.f/16.f; break; | ||
case SubdivisionEnum::eighthtriplet: res *= 1.f/12.f; break; | ||
case SubdivisionEnum::eighth: res *= 1.f/8.f; break; | ||
case SubdivisionEnum::doteighth: res *= 3.f/16.f; break; | ||
case SubdivisionEnum::quartertriplet: res *= 1.f / 6.f; break; | ||
case SubdivisionEnum::quarter: res *= 1.f/4.f; break; | ||
case SubdivisionEnum::dotquarter: res *= 3.f/8.f; break; | ||
case SubdivisionEnum::halftriplet: res *= 1.f/3.f; break; | ||
case SubdivisionEnum::half: res *= 1.f/2.f; break; | ||
case SubdivisionEnum::dothalf: res *= 3.f/4.f; break; | ||
case SubdivisionEnum::whole: break; | ||
default: OTTO_UNREACHABLE; | ||
} | ||
return res; | ||
} else { | ||
// Using free time control | ||
auto fff = free * free * free; | ||
return 0.05f * (1.f - fff) + 6.f * fff; | ||
} | ||
} | ||
|
||
} // namespace otto::engines::pingpong |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
#pragma once | ||
|
||
#include "core/audio/clock.hpp" | ||
#include "core/engine/engine.hpp" | ||
#include "core/input.hpp" | ||
#include "itc/itc.hpp" | ||
|
||
namespace otto::engines::pingpong { | ||
|
||
using namespace core::props; | ||
|
||
struct Screen; | ||
struct Audio; | ||
|
||
using Sender = core::engine::EngineSender<Audio, Screen>; | ||
|
||
BETTER_ENUM(SubdivisionEnum, std::int8_t, thirtysecondth, sixteenth, eighthtriplet, eighth, quartertriplet, doteighth, quarter, halftriplet, dotquarter, half, dothalf, whole); | ||
|
||
struct Props { | ||
Sender sender; | ||
|
||
/// Changes between free delay time and subdivisions | ||
Sender::Prop<struct timetype_tag, bool> timetype = {sender, false}; | ||
/// Only changed when timetype is false. | ||
Sender::Prop<struct free_time_tag, float> free_time = {sender, 0, limits(0, 1), step_size(0.01)}; | ||
/// Only changed when timetype is true | ||
Sender::Prop<struct subdivision_tag, SubdivisionEnum> subdivision = {sender, SubdivisionEnum::quarter}; | ||
/// Actual delaytime in ms. Sent to the audio thread | ||
/// | ||
/// 6 seconds is the length of a measure at 40 bpm | ||
Sender::Prop<struct delaytime_tag, float> delaytime = {sender, 0.5, limits(0, 6)}; | ||
|
||
/// 0.5 is neutral | ||
Sender::Prop<struct filter_tag, float> filter = {sender, 0.5, limits(0, 1), step_size(0.01)}; | ||
|
||
/// 0.5 is neutral | ||
Sender::Prop<struct stereo_tag, float> stereo = {sender, 0.5, limits(0, 1), step_size(0.01)}; | ||
Sender::Prop<struct stereo_invert_tag, bool> stereo_invert = {sender, false}; | ||
|
||
Sender::Prop<struct feedback_tag, float> feedback = {sender, 0.2, limits(0, 0.99), step_size(0.01)}; | ||
|
||
DECL_REFLECTION(Props, free_time, delaytime, subdivision, timetype, filter, stereo, stereo_invert, feedback); | ||
}; | ||
|
||
struct PingPong : core::engine::EffectEngine<PingPong> { | ||
static constexpr util::string_ref name = "PingPong"; | ||
PingPong(); | ||
|
||
float calculate_delaytime(bool type, float free, SubdivisionEnum sd); | ||
|
||
bool keypress(core::input::Key k) override; | ||
void encoder(core::input::EncoderEvent e) override; | ||
|
||
core::ui::ScreenAndInput screen() override; | ||
|
||
std::unique_ptr<Audio> audio; | ||
std::unique_ptr<Screen> screen_; | ||
|
||
Props props; | ||
}; | ||
|
||
} // namespace otto::engines::pingpong | ||
|
||
#include "audio.hpp" | ||
#include "screen.hpp" |
Oops, something went wrong.