Skip to content

Commit

Permalink
Stages: extended sequencer features
Browse files Browse the repository at this point in the history
  • Loading branch information
Emilie Gillet committed Oct 14, 2020
1 parent fae351e commit 22c5613
Show file tree
Hide file tree
Showing 2 changed files with 180 additions and 5 deletions.
168 changes: 164 additions & 4 deletions stages/segment_generator.cc
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
#include "stmlib/dsp/dsp.h"
#include "stmlib/dsp/parameter_interpolator.h"
#include "stmlib/dsp/units.h"
#include "stmlib/utils/random.h"

#include <cassert>
#include <cmath>
Expand All @@ -50,7 +51,10 @@ const int kRetrigDelaySamples = 32;

// S&H delay (for all those sequencers whose CV and GATE outputs are out of
// sync).
const size_t kSampleAndHoldDelay = kSampleRate * 2 / 1000; // 2 milliseconds
const size_t kSampleAndHoldDelay = kSampleRate * 2 / 1000;

// Clock inhibition following a rising edge on the RESET input
const size_t kClockInhibitDelay = kSampleRate * 5 / 1000;

void SegmentGenerator::Init() {
process_fn_ = &SegmentGenerator::ProcessMultiSegment;
Expand Down Expand Up @@ -90,11 +94,19 @@ void SegmentGenerator::Init() {
ramp_extractor_.Init(
kSampleRate,
1000.0f / kSampleRate);
ramp_division_quantizer_.Init();
function_quantizer_.Init();
delay_line_.Init();
gate_delay_.Init();

address_quantizer_.Init();

num_segments_ = 0;
first_step_ = 1;
last_step_ = 1;

for (int i = 0; i < kMaxNumSegments; ++i) {
step_quantizer_[i].Init();
}
}

inline float SegmentGenerator::WarpPhase(float t, float curve) const {
Expand Down Expand Up @@ -304,7 +316,7 @@ Ratio divider_ratios[] = {
void SegmentGenerator::ProcessTapLFO(
const GateFlags* gate_flags, SegmentGenerator::Output* out, size_t size) {
float ramp[12];
Ratio r = ramp_division_quantizer_.Lookup(
Ratio r = function_quantizer_.Lookup(
divider_ratios, parameters_[0].primary * 1.03f, 7);
ramp_extractor_.Process(r, gate_flags, ramp, size);
for (size_t i = 0; i < size; ++i) {
Expand Down Expand Up @@ -392,7 +404,6 @@ void SegmentGenerator::ProcessPortamento(

void SegmentGenerator::ProcessZero(
const GateFlags* gate_flags, SegmentGenerator::Output* out, size_t size) {

value_ = 0.0f;
active_segment_ = 1;
while (size--) {
Expand Down Expand Up @@ -449,6 +460,143 @@ void SegmentGenerator::ShapeLFO(
}
}

enum Direction {
DIRECTION_UP,
DIRECTION_DOWN,
DIRECTION_UP_DOWN,
DIRECTION_RANDOM,
DIRECTION_ADDRESSABLE,
DIRECTION_LAST
};

void SegmentGenerator::ProcessSequencer(
const GateFlags* gate_flags, SegmentGenerator::Output* out, size_t size) {
bool change_step = false;

// Read the value of the small pot to determine the direction.
Direction direction = Direction(function_quantizer_.Process(
parameters_[0].secondary, DIRECTION_LAST));

if (direction == DIRECTION_ADDRESSABLE) {
reset_ = false;
active_segment_ = address_quantizer_.Process(
parameters_[0].primary, last_step_ - first_step_ + 1) + first_step_;
change_step = true;
} else {
// Detect a rising edge on the slider/CV to reset to the first step.
if (parameters_[0].primary > 0.125f && !reset_) {
reset_ = true;
active_segment_ = direction == DIRECTION_DOWN ? last_step_ : first_step_;
up_down_counter_ = 0;
change_step = true;
inhibit_clock_ = kClockInhibitDelay;
}
if (reset_ && parameters_[0].primary < 0.0625f) {
reset_ = false;
}
}
while (size--) {
ONE_POLE(
lp_,
value_,
PortamentoRateToLPCoefficient(parameters_[active_segment_].secondary));
if (inhibit_clock_) {
--inhibit_clock_;
}

bool clockable = !inhibit_clock_ && !reset_ && \
direction != DIRECTION_ADDRESSABLE;

// If a rising edge is detected on the gate input, advance to the next step.
if ((*gate_flags & GATE_FLAG_RISING) && clockable) {
switch (direction) {
case DIRECTION_UP:
++active_segment_;
if (active_segment_ > last_step_) {
active_segment_ = first_step_;
}
break;

case DIRECTION_DOWN:
--active_segment_;
if (active_segment_ < first_step_) {
active_segment_ = last_step_;
}
break;

case DIRECTION_UP_DOWN:
{
if (first_step_ == last_step_) {
active_segment_ = first_step_;
} else {
int n = last_step_ - first_step_ + 1;
up_down_counter_ = (up_down_counter_ + 1) % (2 * (n - 1));
active_segment_ = first_step_ + (up_down_counter_ < n
? up_down_counter_
: 2 * (n - 1) - up_down_counter_);
}
}
break;

case DIRECTION_RANDOM:
active_segment_ = first_step_ + static_cast<int>(
Random::GetFloat() * static_cast<float>(
last_step_ - first_step_ + 1));
break;

case DIRECTION_ADDRESSABLE:
case DIRECTION_LAST:
break;
}
change_step = true;
}

if (change_step) {
value_ = parameters_[active_segment_].primary;
if (quantized_output_) {
int note = step_quantizer_[active_segment_].Process(value_, 13);
value_ = static_cast<float>(note) / 96.0f;
}
change_step = false;
}

out->value = lp_;
out->phase = 0.0f;
out->segment = active_segment_;
++gate_flags;
++out;
}
}

void SegmentGenerator::ConfigureSequencer(
const Configuration* segment_configuration,
int num_segments) {
num_segments_ = num_segments;

first_step_ = 0;
for (int i = 1; i < num_segments; ++i) {
if (segment_configuration[i].loop) {
if (!first_step_) {
first_step_ = last_step_ = i;
} else {
last_step_ = i;
}
}
}
if (!first_step_) {
// No loop has been found, use the whole group.
first_step_ = 1;
last_step_ = num_segments - 1;
}

inhibit_clock_ = up_down_counter_ = 0;
quantized_output_ = segment_configuration[0].type == TYPE_RAMP;
reset_ = false;
lp_ = value_ = 0.0f;
active_segment_ = first_step_;
process_fn_ = &SegmentGenerator::ProcessSequencer;
}

void SegmentGenerator::Configure(
bool has_trigger,
const Configuration* segment_configuration,
Expand All @@ -457,6 +605,18 @@ void SegmentGenerator::Configure(
ConfigureSingleSegment(has_trigger, segment_configuration[0]);
return;
}

bool sequencer_mode = segment_configuration[0].type != TYPE_STEP && \
!segment_configuration[0].loop && num_segments >= 3;
for (int i = 1; i < num_segments; ++i) {
sequencer_mode = sequencer_mode && \
segment_configuration[i].type == TYPE_STEP;
}
if (sequencer_mode) {
ConfigureSequencer(segment_configuration, num_segments);
return;
}

num_segments_ = num_segments;

// assert(has_trigger);
Expand Down
17 changes: 16 additions & 1 deletion stages/segment_generator.h
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,10 @@ class SegmentGenerator {
const segment::Configuration* segment_configuration,
int num_segments);

void ConfigureSequencer(
const segment::Configuration* segment_configuration,
int num_segments);

inline void ConfigureSingleSegment(
bool has_trigger,
segment::Configuration segment_configuration) {
Expand Down Expand Up @@ -150,6 +154,7 @@ class SegmentGenerator {
private:
// Process function for the general case.
DECLARE_PROCESS_FN(MultiSegment);
DECLARE_PROCESS_FN(Sequencer)
DECLARE_PROCESS_FN(DecayEnvelope);
DECLARE_PROCESS_FN(TimedPulseGenerator);
DECLARE_PROCESS_FN(GateGenerator);
Expand Down Expand Up @@ -189,14 +194,24 @@ class SegmentGenerator {
ProcessFn process_fn_;

RampExtractor ramp_extractor_;
stmlib::HysteresisQuantizer ramp_division_quantizer_;
stmlib::HysteresisQuantizer function_quantizer_;

Segment segments_[kMaxNumSegments + 1]; // There's a sentinel!
segment::Parameters parameters_[kMaxNumSegments];

DelayLine16Bits<kMaxDelay> delay_line_;
stmlib::DelayLine<stmlib::GateFlags, 128> gate_delay_;

int first_step_;
int last_step_;
bool quantized_output_;

int up_down_counter_;
bool reset_;
int inhibit_clock_;
stmlib::HysteresisQuantizer address_quantizer_;
stmlib::HysteresisQuantizer step_quantizer_[kMaxNumSegments];

static ProcessFn process_fn_table_[12];

DISALLOW_COPY_AND_ASSIGN(SegmentGenerator);
Expand Down

0 comments on commit 22c5613

Please sign in to comment.