Skip to content

Commit

Permalink
WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
stellar-aria committed Jan 12, 2025
1 parent 2cfbe0e commit 3b4f0f0
Show file tree
Hide file tree
Showing 3 changed files with 97 additions and 51 deletions.
108 changes: 60 additions & 48 deletions src/deluge/dsp/delay/simple/delay.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,18 +39,15 @@ class Delay {
constexpr static size_t kNumSamplesMax =
kNumSamplesMainDelay + (kNumSamplesMainDelay / 3.f); // 6.666 seconds maximum
public:
/// @brief The mode of the Delay
enum class Mode {
Repitch,
Fade,
Jump,
Repitch, ///< Repitch on delay length change (akin to tape speed)
Fade, ///< Fade previous delay on length change
Jump, ///< Hard cut to new delay
};

enum class ChannelMode {
Sync, // beat-synced
Time, // strict timing
};
/// @brief The
struct ChannelConfig {
ChannelMode mode;
Milliseconds duration; // ms for time, beat divisions for sync, internally stored as ms
Percentage<float> nudge; // up to 0.33 (33%)

Expand All @@ -60,11 +57,13 @@ class Delay {
};

struct Config {
bool channel_link = true; // uses first config for all settings
bool channel_link = true; // uses first config for both channels
ChannelConfig l_channel; // stereo
ChannelConfig r_channel;
Percentage<float> feedback = 0.5f; // 50% for linear decay rate by default
bool freeze = false; // disables new input and feedback, cycling the current buffer contents infinitely

Percentage<float> feedback = 0.5f; // Defaults to 50% for linear decay rate

bool freeze = false; //< Disable new input and feedback, cycling the current buffer contents infinitely

// Standard 2 param bandpass
Frequency<float> filter_cutoff = 1000.f; // Hz
Expand All @@ -76,11 +75,15 @@ class Delay {
// float lfo_time_depth = 0.f;

bool ping_pong = false;

Percentage<float> dry_wet = 0.5f;
};

void setConfig(const Config& config) { config_ = config; }
void setConfig(const Config& config) {
this->config_ = config;
if (this->config_.channel_link) {
this->config_.r_channel = this->config_.l_channel;
}
}

void processBlock(std::span<q31_t> buffer) {
interpolated::Parameter feedback{old_config_.feedback.value, config_.feedback.value, buffer.size()};

Expand All @@ -94,78 +97,87 @@ class Delay {
// interpolated::Parameter lfo_time_depth{old_config_.lfo_time_depth, new_config_.lfo_time_depth,
// buffer.size()};

interpolated::Parameter dry_wet{old_config_.dry_wet.value, config_.dry_wet.value, buffer.size()};

// l channel config has changed, so swap
if (old_config_.l_channel != config_.l_channel) {
auto& old_buffer = bufferLeft();
swapBufferLeft();
auto& new_buffer = bufferLeft();
auto& buffer = bufferLeft();

new_buffer.set_size(config_.l_channel.duration // main
+ static_cast<size_t>(config_.l_channel.duration * config_.l_channel.nudge)); // nudge
buffer.set_size(config_.l_channel.duration // main
+ static_cast<size_t>(config_.l_channel.duration * config_.l_channel.nudge)); // nudge

if (mode_ == Mode::Repitch) {
new_buffer.RepitchCopyFrom(old_buffer);
buffer.RepitchCopyFrom(old_buffer);
}
else if (mode_ == Mode::Fade) {
new_buffer.CopyFrom(old_buffer);
new_buffer.ApplyGainRamp(blocks::GainRamp{1.f, 0.f});
buffer.CopyFrom(old_buffer);
buffer.ApplyGainRamp(blocks::GainRamp{1.f, 0.f});
}
else if (mode_ == Mode::Jump) {
buffer.CopyFrom(old_buffer);
}
// Jump discards delay state
}

if (old_config_.r_channel != config_.r_channel) {
// r channel config has changed, so swap
auto& old_buffer = bufferRight();
swapBufferRight();
auto& new_buffer = bufferRight();
auto& buffer = bufferRight();

new_buffer.set_size(config_.r_channel.duration // main
+ static_cast<size_t>(config_.r_channel.duration * config_.r_channel.nudge)); // nudge
buffer.set_size(config_.r_channel.duration // main
+ static_cast<size_t>(config_.r_channel.duration * config_.r_channel.nudge)); // nudge

if (mode_ == Mode::Repitch) {
new_buffer.RepitchCopyFrom(old_buffer);
buffer.RepitchCopyFrom(old_buffer);
}
else if (mode_ == Mode::Fade) {
new_buffer.CopyFrom(old_buffer);
new_buffer.ApplyGainRamp(blocks::GainRamp{1.f, 0.f});
buffer.CopyFrom(old_buffer);
buffer.ApplyGainRamp(blocks::GainRamp{1.f, 0.f});
}
else if (mode_ == Mode::Jump) {
buffer.CopyFrom(old_buffer);
}
// Jump discards delay state
}

// when the buffer is frozen, no writing is peformed, only advancement of the rec/play head
if (config_.freeze) {
for (auto& [left, right] : argon::vectorize_interleaved<2, int32_t>(buffer)) {
left = bufferLeft().ReadSIMD().ConvertTo<q31_t, 31>(); // To Q31
bufferLeft().Advance(4);
for (auto& [left_out, right_out] : argon::vectorize_interleaved<2, q31_t>(buffer)) {
auto&& [buffer_left, buffer_right] = buffers();

right = bufferRight().ReadSIMD().ConvertTo<q31_t, 31>(); // To Q31
bufferRight().Advance(4);
left_out = buffer_left.ReadSIMD().ConvertTo<q31_t, 31>(); // To Q31
right_out = buffer_right.ReadSIMD().ConvertTo<q31_t, 31>(); // To Q31

buffer_left.Advance(4);
buffer_right.Advance(4);
}
return;
}

// Otherwise...
for (auto& [left, right] : argon::vectorize_interleaved<2, int32_t>(buffer)) {
feedback.Next();
filter_cutoff.Next();
filter_width.Next();
lfo_rate.Next();
lfo_filter_depth.Next();
dry_wet.Next();

/** TODO: continue work here */
for (auto& [left, right] : argon::vectorize_interleaved<2, q31_t>(buffer)) {
Argon<float> feedback_value = feedback.NextSIMD();
Argon<float> filter_cutoff_value = filter_cutoff.NextSIMD();
Argon<float> filter_width_value = filter_width.NextSIMD();
Argon<float> lfo_rate_value = lfo_rate.NextSIMD();
Argon<float> lfo_filter_depth_value = lfo_filter_depth.NextSIMD();

auto&& [buffer_left, buffer_right] = buffers();
auto [input_left, input_right] = {left, right};

left = buffer_left.ReadSIMD().ConvertTo<q31_t, 31>(); // To Q31
buffer_left.Advance(4);
buffer_left.WriteSIMD(input_left, feedback_value);

right = buffer_right.ReadSIMD().ConvertTo<q31_t, 31>(); // To Q31
buffer_right.Advance(4);
buffer_right.WriteSIMD(input_right, feedback_value);
}
old_config_ = config_;
}

void process(std::array<Argon<int32_t>, 2> samples) {}

std::pair<Buffer<kNumSamplesMax>&, Buffer<kNumSamplesMax>&> buffers() { return {bufferLeft(), bufferRight()}; }
std::tuple<Buffer<kNumSamplesMax>&, Buffer<kNumSamplesMax>&> buffers() { return {bufferLeft(), bufferRight()}; }

Buffer<kNumSamplesMax>& bufferLeft() { return l_buffers_[l_buffer_idx_]; }

Buffer<kNumSamplesMax>& bufferRight() { return l_buffers_[l_buffer_idx_]; }

private:
Expand Down
20 changes: 17 additions & 3 deletions src/deluge/dsp/interpolate/parameter.hpp
Original file line number Diff line number Diff line change
@@ -1,22 +1,36 @@
#pragma once
#include <argon.hpp>
#include <cstddef>

namespace deluge::dsp::interpolated {
class Parameter {
public:
Parameter(float& state, float new_value, size_t size)
: state_(state), value_(new_value), increment_((new_value - state) / static_cast<float>(size)) {}
Parameter(float old_value, float new_value, size_t size)
: value_(old_value), target_(new_value), increment_((new_value - old_value) / static_cast<float>(size)) {}

constexpr float Next() {
if (value_ >= target_) {
return target_;
}
value_ += increment_;
return value_;
}

constexpr Argon<float> NextSIMD() {
Argon<float> value = Argon{value_}.MultiplyAdd(increment_, {1.f, 2.f, 3.f, 4.f});
value = (((value <= target_) & value.As<uint32_t>()) // keep lanes less than or equal to target
| ((value > target_) & Argon{target_}.As<uint32_t>())) // replace lanes greater than target with target
.As<float>();

value_ = value.LastLane();
return value;
}

constexpr float subsample(float t) { return value_ + (increment_ * t); }

private:
float& state_;
float value_;
float target_;
float increment_;
};
} // namespace deluge::dsp::interpolated
20 changes: 20 additions & 0 deletions src/deluge/model/types.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,16 @@ struct Value {
T value = 0;
operator T() { return value; }
auto operator<=>(const Value<T>& o) const = default;
bool operator==(const Value<T>& o) const = default;
};

template <typename T>
struct Frequency : Value<T> {
Frequency() : Value<T>() {}
Frequency(T value) : Value<T>{value} {}

auto operator<=>(const Frequency& o) const { return this->value <=> o.value; };

static constexpr std::string_view unit = "Hz";
};

Expand All @@ -29,6 +33,20 @@ struct Percentage<float> : Value<float> {
Percentage(float value, float lower_bound, float upper_bound)
: Value<float>{value}, lower_bound{lower_bound}, upper_bound{upper_bound} {}

bool operator==(const Percentage& o) const {
if (this->lower_bound == o.lower_bound && this->upper_bound == o.upper_bound) {
return this->value == o.value;
}

float diff = upper_bound - lower_bound;
float val_scaled = (this->value - lower_bound) / diff;

float o_diff = o.upper_bound - o.lower_bound;
float o_val_scaled = (o.value - o.lower_bound) / diff;

return (val_scaled + lower_bound) == (o.value + o.lower_bound);
}

float lower_bound = 0.f;
float upper_bound = 1.f;
};
Expand Down Expand Up @@ -58,4 +76,6 @@ struct QFactor : Value<T> {

struct Milliseconds : Value<int32_t> {
static constexpr std::string_view unit = "ms";
auto operator<=>(const Milliseconds& o) const { return this->value <=> o.value; }
bool operator==(const Milliseconds& o) const { return this->value == o.value; }
};

0 comments on commit 3b4f0f0

Please sign in to comment.