diff --git a/source/include/signalflow/node/processors/delays/stutter.h b/source/include/signalflow/node/processors/delays/stutter.h index df5827ee..383791b2 100644 --- a/source/include/signalflow/node/processors/delays/stutter.h +++ b/source/include/signalflow/node/processors/delays/stutter.h @@ -18,6 +18,8 @@ class Stutter : public UnaryOpNode Stutter(NodeRef input = 0.0, NodeRef stutter_time = 0.1, NodeRef stutter_count = 1, + NodeRef stutter_probability = 1.0, + NodeRef stutter_advance_time = 0.0, NodeRef clock = nullptr, float max_stutter_time = 1.0); ~Stutter() override; @@ -29,6 +31,8 @@ class Stutter : public UnaryOpNode private: NodeRef stutter_time; NodeRef stutter_count; + NodeRef stutter_probability; + NodeRef stutter_advance_time; NodeRef clock; float max_stutter_time; @@ -37,6 +41,7 @@ class Stutter : public UnaryOpNode std::vector stutters_to_do; std::vector stutter_sample_buffer_offset; std::vector stutter_samples_remaining; + std::vector stutter_sample_index; }; REGISTER(Stutter, "stutter") diff --git a/source/src/node/processors/delays/stutter.cpp b/source/src/node/processors/delays/stutter.cpp index 4963457e..8634598e 100644 --- a/source/src/node/processors/delays/stutter.cpp +++ b/source/src/node/processors/delays/stutter.cpp @@ -1,18 +1,27 @@ #include "signalflow/core/graph.h" +#include "signalflow/core/random.h" #include "signalflow/node/oscillators/constant.h" #include "signalflow/node/processors/delays/stutter.h" namespace signalflow { -Stutter::Stutter(NodeRef input, NodeRef stutter_time, NodeRef stutter_count, NodeRef clock, float max_stutter_time) - : UnaryOpNode(input), stutter_time(stutter_time), stutter_count(stutter_count), clock(clock), max_stutter_time(max_stutter_time) +Stutter::Stutter(NodeRef input, + NodeRef stutter_time, + NodeRef stutter_count, + NodeRef stutter_probability, + NodeRef stutter_advance_time, + NodeRef clock, + float max_stutter_time) + : UnaryOpNode(input), stutter_time(stutter_time), stutter_count(stutter_count), stutter_probability(stutter_probability), stutter_advance_time(stutter_advance_time), clock(clock), max_stutter_time(max_stutter_time) { SIGNALFLOW_CHECK_GRAPH(); this->name = "stutter"; this->create_input("stutter_time", this->stutter_time); this->create_input("stutter_count", this->stutter_count); + this->create_input("stutter_probability", this->stutter_probability); + this->create_input("stutter_advance_time", this->stutter_advance_time); this->create_input("clock", this->clock); this->alloc(); } @@ -23,6 +32,7 @@ void Stutter::alloc() this->stutter_sample_buffer_offset.resize(this->num_output_channels_allocated); this->stutters_to_do.resize(this->num_output_channels_allocated); this->stutter_samples_remaining.resize(this->num_output_channels_allocated); + this->stutter_sample_index.resize(this->num_output_channels_allocated); int buffers_to_allocate = this->num_output_channels_allocated - buffers.size(); for (int i = 0; i < buffers_to_allocate; i++) @@ -43,12 +53,17 @@ void Stutter::trigger(std::string name, float value) { if (name == SIGNALFLOW_DEFAULT_TRIGGER) { - for (int channel = 0; channel < this->num_input_channels; channel++) + float probability = this->stutter_probability->out[0][0]; + if (random_coin(probability)) { - this->stutter_index[channel] = 0; - this->stutters_to_do[channel] = this->stutter_count->out[channel][0]; - this->stutter_samples_remaining[channel] = this->stutter_time->out[channel][0] * graph->get_sample_rate(); - this->stutter_sample_buffer_offset[channel] = stutter_samples_remaining[channel]; + for (int channel = 0; channel < this->num_input_channels; channel++) + { + this->stutter_index[channel] = 0; + this->stutters_to_do[channel] = this->stutter_count->out[channel][0]; + this->stutter_samples_remaining[channel] = this->stutter_time->out[channel][0] * graph->get_sample_rate(); + this->stutter_sample_buffer_offset[channel] = stutter_samples_remaining[channel]; + this->stutter_sample_index[channel] = 0; + } } } } @@ -61,18 +76,26 @@ void Stutter::process(Buffer &out, int num_frames) { if (SIGNALFLOW_CHECK_CHANNEL_TRIGGER(this->clock, channel, frame)) { - this->stutter_index[channel] = 0; - this->stutters_to_do[channel] = this->stutter_count->out[channel][0]; - this->stutter_samples_remaining[channel] = this->stutter_time->out[channel][0] * graph->get_sample_rate(); - this->stutter_sample_buffer_offset[channel] = stutter_samples_remaining[channel]; + float probability = this->stutter_probability->out[channel][frame]; + if (random_coin(probability)) + { + this->stutter_index[channel] = 0; + this->stutters_to_do[channel] = this->stutter_count->out[channel][0]; + this->stutter_samples_remaining[channel] = this->stutter_time->out[channel][frame] * graph->get_sample_rate(); + this->stutter_sample_buffer_offset[channel] = stutter_samples_remaining[channel]; + this->stutter_sample_index[channel] = 0; + } } if (this->stutters_to_do[channel] > 0) { this->stutter_samples_remaining[channel]--; + this->stutter_sample_index[channel] += 1; + if (this->stutter_samples_remaining[channel] <= 0) { + // one of the stutters has finished this->stutter_index[channel] += 1; if (this->stutter_index[channel] > this->stutters_to_do[channel]) { @@ -92,8 +115,8 @@ void Stutter::process(Buffer &out, int num_frames) } else { - // TODO this won't quite work - int buffer_sample_offset = -this->stutter_samples_remaining[channel]; + float stutter_advance_samples = (this->stutter_advance_time->out[channel][frame] * this->stutter_index[channel]) * this->graph->get_sample_rate(); + int buffer_sample_offset = -this->stutter_samples_remaining[channel] + stutter_advance_samples - this->stutter_sample_index[channel]; out[channel][frame] = this->buffers[channel]->get(buffer_sample_offset); } } @@ -103,13 +126,10 @@ void Stutter::process(Buffer &out, int num_frames) out[channel][frame] = this->input->out[channel][frame]; } - if (this->stutter_index[channel] == 0) - { - // stutter_index is zero in the first stutter or when we're not stuttering - this->buffers[channel]->append(this->input->out[channel][frame]); - } - } - } + this->buffers[channel]->append(this->input->out[channel][frame]); + + } // foreach frame + } // foreach channel } }