Skip to content

Commit

Permalink
Marbles: latest update with reset input mode and bug fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
Emilie Gillet committed Jan 8, 2023
1 parent 897806f commit db1566f
Show file tree
Hide file tree
Showing 22 changed files with 654 additions and 66 deletions.
17 changes: 16 additions & 1 deletion marbles/cv_reader.cc
Original file line number Diff line number Diff line change
Expand Up @@ -90,12 +90,27 @@ void CvReader::Copy(uint16_t* output) {
adc_.Convert();
}

void CvReader::Process(const uint16_t* raw_values, float* output) {
void CvReader::Process(
const bool treat_cv_as_reset_inputs,
const uint16_t* raw_values,
float* output,
stmlib::GateFlags* gate_flags) {
if (treat_cv_as_reset_inputs) {
// Set virtual attenuverter to 12 o'clock to prevent the CV value
// from being added to the pot.
attenuverter_[ADC_CHANNEL_T_JITTER] = 0.5f;
attenuverter_[ADC_CHANNEL_X_STEPS] = 0.5f;
} else {
attenuverter_[ADC_CHANNEL_T_JITTER] = 1.01f;
attenuverter_[ADC_CHANNEL_X_STEPS] = 1.01f;
}

for (int i = 0; i < ADC_CHANNEL_LAST; ++i) {
output[i] = channel_[i].Process(
static_cast<float>(raw_values[ADC_GROUP_POT + i]) / 65536.0f,
static_cast<float>(raw_values[ADC_GROUP_CV + i]) / 65536.0f,
attenuverter_[i]);
gate_flags[i] = channel_[i].gate_flags();
}
}

Expand Down
6 changes: 5 additions & 1 deletion marbles/cv_reader.h
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,11 @@ class CvReader {
}

void Copy(uint16_t* output);
void Process(const uint16_t* values, float* output);
void Process(
const bool treat_cv_as_reset_inputs,
const uint16_t* values,
float* output,
stmlib::GateFlags* gates);

inline const CvReaderChannel& channel(size_t index) {
return channel_[index];
Expand Down
14 changes: 13 additions & 1 deletion marbles/cv_reader_channel.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,9 @@
#define MARBLES_CV_READER_CHANNEL_H_

#include "stmlib/stmlib.h"

#include "stmlib/dsp/dsp.h"
#include "stmlib/utils/gate_flags.h"

namespace marbles {

Expand Down Expand Up @@ -82,7 +84,7 @@ class CvReaderChannel {
float max;
float hysteresis;
};

void Init(float* cv_scale, float* cv_offset, const Settings& settings) {
cv_scale_ = cv_scale;
cv_offset_ = cv_offset;
Expand All @@ -99,6 +101,8 @@ class CvReaderChannel {
stored_pot_value_ = 0.0f;
attenuverter_value_ = 0.0f;
previous_pot_value_ = 0.0f;

gate_flags_ = stmlib::GATE_FLAG_LOW;

pot_state_ = POT_STATE_TRACKING;

Expand All @@ -112,6 +116,10 @@ class CvReaderChannel {
inline float Process(float pot, float cv, float attenuverter) {
cv *= *cv_scale_;
cv += *cv_offset_;

gate_flags_ = stmlib::ExtractGateFlags(
gate_flags_,
cv > ((gate_flags_ & stmlib::GATE_FLAG_HIGH) ? 0.1f : 0.2f));

attenuverter -= 0.5f;
attenuverter = attenuverter * attenuverter * attenuverter * 8.0f;
Expand Down Expand Up @@ -160,6 +168,7 @@ class CvReaderChannel {
return value;
}

inline stmlib::GateFlags gate_flags() const { return gate_flags_; }
inline float cv() const { return cv_value_; }
inline float scaled_raw_cv() const { return raw_cv_value_; }
inline float unscaled_cv_lp() const {
Expand Down Expand Up @@ -193,6 +202,9 @@ class CvReaderChannel {
float attenuverter_lp_;
float min_;
float max_;

// Convert each CV input to a gate, for firmware hacking purposes!
stmlib::GateFlags gate_flags_;

PotState pot_state_;

Expand Down
2 changes: 2 additions & 0 deletions marbles/drivers/switches.h
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,8 @@ class Switches {
return !GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_11);
} else if (s == SWITCH_X_MODE) {
return !GPIO_ReadInputDataBit(GPIOC, GPIO_Pin_13);
} else if (s == SWITCH_X_DEJA_VU) {
return !GPIO_ReadInputDataBit(GPIOC, GPIO_Pin_15);
} else {
return false;
}
Expand Down
3 changes: 2 additions & 1 deletion marbles/io_buffer.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ const size_t kNumInputs = 2;
const size_t kNumCvOutputs = 4;
const size_t kNumGateOutputs = 3;
const size_t kNumParameters = 8;
const size_t kMaximumGateDelay = 4;

class IOBuffer {
public:
Expand All @@ -52,7 +53,7 @@ class IOBuffer {

stmlib::GateFlags input[kNumInputs][kBlockSize];
uint16_t cv_output[kNumCvOutputs][kBlockSize];
bool gate_output[kNumGateOutputs][kBlockSize + 2];
bool gate_output[kNumGateOutputs][kBlockSize + kMaximumGateDelay];
};

struct Slice {
Expand Down
37 changes: 27 additions & 10 deletions marbles/marbles.cc
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ CvReader cv_reader;
Dac dac;
DebugPort debug_port;
GateOutputs gate_outputs;
HysteresisQuantizer deja_vu_length_quantizer;
HysteresisQuantizer2 deja_vu_length_quantizer;
IOBuffer io_buffer;
NoteFilter note_filter;
Rng rng;
Expand Down Expand Up @@ -144,8 +144,9 @@ inline uint16_t DacCode(int index, float voltage) {

void ProcessTest(IOBuffer::Block* block, size_t size) {
float parameters[kNumParameters];
GateFlags hidden_gates[kNumParameters];
static float phase;
cv_reader.Process(&block->adc_value[0], parameters);
cv_reader.Process(false, &block->adc_value[0], parameters, hidden_gates);
for (size_t i = 0; i < size; ++i) {
phase += 100.0f / static_cast<float>(kSampleRate);
if (phase >= 1.0f) {
Expand Down Expand Up @@ -203,9 +204,10 @@ int loop_length[] = {
8, 8, 8, 8, 8, 8, 8, 8, 8,
10, 10, 10,
12, 12, 12, 12, 12, 12, 12,
14,
14, 14,
16
};
GateFlags hidden_gates[kNumParameters];
float parameters[kNumParameters];
float ramp_buffer[kBlockSize * 4];
bool gates[kBlockSize * 2];
Expand All @@ -231,7 +233,11 @@ void Process(IOBuffer::Block* block, size_t size) {
}

// Filter CV values (3.5%)
cv_reader.Process(&block->adc_value[0], parameters);
cv_reader.Process(
settings.explicit_reset(),
&block->adc_value[0],
parameters,
hidden_gates);

float deja_vu = parameters[ADC_CHANNEL_DEJA_VU_AMOUNT];

Expand Down Expand Up @@ -266,7 +272,7 @@ void Process(IOBuffer::Block* block, size_t size) {
}
}
}

// Generate gates for T-section (16%).
ramps.master = &ramp_buffer[0];
ramps.external = &ramp_buffer[kBlockSize];
Expand All @@ -276,8 +282,10 @@ void Process(IOBuffer::Block* block, size_t size) {
const State& state = settings.state();
int deja_vu_length = deja_vu_length_quantizer.Lookup(
loop_length,
parameters[ADC_CHANNEL_DEJA_VU_LENGTH],
sizeof(loop_length) / sizeof(int));
parameters[ADC_CHANNEL_DEJA_VU_LENGTH]);

bool t_section_reset = settings.explicit_reset() && \
hidden_gates[ADC_CHANNEL_T_JITTER] & GATE_FLAG_RISING;

t_generator.set_model(TGeneratorModel(state.t_model));
t_generator.set_range(TGeneratorRange(state.t_range));
Expand All @@ -293,11 +301,12 @@ void Process(IOBuffer::Block* block, size_t size) {
t_generator.set_pulse_width_std(float(state.t_pulse_width_std) / 256.0f);
t_generator.Process(
block->input_patched[0],
&t_section_reset,
t_clock,
ramps,
gates,
size);

// Generate voltages for X-section (40%).
float note_cv_1 = cv_reader.channel(ADC_CHANNEL_X_SPREAD).scaled_raw_cv();
float note_cv_2 = cv_reader.channel(ADC_CHANNEL_X_SPREAD_2).scaled_raw_cv();
Expand Down Expand Up @@ -364,11 +373,18 @@ void Process(IOBuffer::Block* block, size_t size) {
}

y.scale_index = x.scale_index = state.x_scale;

bool x_section_reset = settings.explicit_reset() && \
hidden_gates[ADC_CHANNEL_X_STEPS] & GATE_FLAG_RISING;
if (xy_clock_source != CLOCK_SOURCE_EXTERNAL) {
x_section_reset |= t_section_reset;
}

xy_generator.Process(
xy_clock_source,
x,
y,
&x_section_reset,
xy_clock,
ramps,
voltages,
Expand All @@ -377,8 +393,8 @@ void Process(IOBuffer::Block* block, size_t size) {

const float* v = voltages;
const bool* g = gates;

for (size_t i = 0; i < size; ++i) {
//block->cv_output[1][i] = DacCode(1, SineOscillator(*v++));
block->cv_output[1][i] = DacCode(1, *v++);
block->cv_output[2][i] = DacCode(2, *v++);
block->cv_output[3][i] = DacCode(3, *v++);
Expand Down Expand Up @@ -412,7 +428,8 @@ void Init() {
gate_outputs.Init();
io_buffer.Init();

deja_vu_length_quantizer.Init();
deja_vu_length_quantizer.Init(
sizeof(loop_length) / sizeof(int), 0.25f, false);
cv_reader.Init(settings.mutable_calibration_data());
scale_recorder.Init();
ui.Init(&settings, &cv_reader, &scale_recorder, &clock_inputs);
Expand Down
9 changes: 9 additions & 0 deletions marbles/ramp/ramp_divider.h
Original file line number Diff line number Diff line change
Expand Up @@ -62,12 +62,20 @@ class RampDivider {
f_ratio_ = 0.99999f;
reset_counter_ = 1;
}

void Reset() {
reset_at_next_pulse_ = true;
}

void Process(Ratio ratio, const float* in, float* out, size_t size) {
while (size--) {
float new_phase = *in++;
float frequency = new_phase - phase_;
if (frequency < 0.0f) {
if (reset_at_next_pulse_) {
reset_at_next_pulse_ = false;
reset_counter_ = 1;
}
frequency += 1.0f;
--reset_counter_;
if (!reset_counter_) {
Expand Down Expand Up @@ -98,6 +106,7 @@ class RampDivider {
float max_train_phase_;
float f_ratio_;
int reset_counter_;
bool reset_at_next_pulse_;

DISALLOW_COPY_AND_ASSIGN(RampDivider);
};
Expand Down
38 changes: 28 additions & 10 deletions marbles/ramp/ramp_extractor.cc
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ using namespace stmlib;

const float kLogOneFourth = 1.189207115f;
const float kPulseWidthTolerance = 0.05f;
const int kNumConsistentPulses = 3;

inline bool IsWithinTolerance(float x, float y, float error) {
return x >= y * (1.0f - error) && x <= y * (1.0f + error);
Expand All @@ -61,6 +62,7 @@ void RampExtractor::Init(float max_frequency) {

void RampExtractor::Reset() {
audio_rate_ = false;
num_consistent_audio_rate_pulses_ = 0;
train_phase_ = 0.0f;
target_frequency_ = frequency_ = 0.0001f;
lp_coefficient_ = 0.5f;
Expand All @@ -69,6 +71,7 @@ void RampExtractor::Reset() {
reset_counter_ = 1;
reset_frequency_ = 0.0f;
reset_interval_ = 32000 * 3;
reset_at_next_pulse_ = false;

Pulse p;
p.bucket = 1;
Expand Down Expand Up @@ -166,13 +169,16 @@ RampExtractor::Prediction RampExtractor::PredictNextPeriod() {
return p;
}

bool RampExtractor::Process(
void RampExtractor::Process(
Ratio ratio,
bool always_ramp_to_maximum,
bool* reset,
const GateFlags* gate_flags,
float* ramp,
size_t size) {
bool reset_observed = false;
if (*reset) {
reset_at_next_pulse_ = true;
}
while (size--) {
GateFlags flags = *gate_flags++;
// We are done with the previous pulse.
Expand All @@ -187,13 +193,29 @@ bool RampExtractor::Process(
train_phase_ = 0.0f;
reset_counter_ = ratio.q;
reset_interval_ = 4 * p.total_duration;
reset_observed = true;

// Flag a reset so that everything can be reset downstream.
*reset = true;
} else {
if (reset_at_next_pulse_) {
reset_counter_ = 1;
reset_at_next_pulse_ = false;
}

float period = float(p.total_duration);
if (period <= audio_rate_period_hysteresis_) {
audio_rate_ = true;
num_consistent_audio_rate_pulses_ = min(
num_consistent_audio_rate_pulses_ + 1, kNumConsistentPulses);
audio_rate_period_hysteresis_ = audio_rate_period_ * 1.1f;

} else {
num_consistent_audio_rate_pulses_ = 0;
audio_rate_period_hysteresis_ = audio_rate_period_;
}

// Only switch to audio rate after a consistent number of uninterrupted
// audio rate pulses.
audio_rate_ = num_consistent_audio_rate_pulses_ == kNumConsistentPulses;
if (audio_rate_) {
average_pulse_width_ = 0.0f;

bool no_glide = f_ratio_ != ratio.to_float();
Expand All @@ -205,11 +227,8 @@ bool RampExtractor::Process(
float down_tolerance = (0.98f - 2.0f * frequency) * frequency_;
no_glide |= target_frequency_ > up_tolerance ||
target_frequency_ < down_tolerance;
lp_coefficient_ = no_glide ? 1.0f : period * 0.00001f;
lp_coefficient_ = no_glide ? 1.0f : min(period * 0.00001f, 0.1f);
} else {
audio_rate_ = false;
audio_rate_period_hysteresis_ = audio_rate_period_;

// Compute the pulse width of the previous pulse, and check if the
// PW has been consistent over the past pulses.
p.pulse_width = static_cast<float>(p.on_duration) / period;
Expand Down Expand Up @@ -309,7 +328,6 @@ bool RampExtractor::Process(
*ramp++ = output_phase;
}
}
return reset_observed;
}

} // namespace marbles
Loading

0 comments on commit db1566f

Please sign in to comment.