Pulp 1.88.0
Cross-platform audio plugin framework
Loading...
Searching...
No Matches
cartesian_walk.hpp
Go to the documentation of this file.
1#pragma once
2
3#include <pulp/signal/detail/modular_sequencing_common.hpp>
4
5#include <algorithm>
6#include <cmath>
7
8namespace pulp::signal {
9
10// ── Cartesian (2-D) sequencer ─────────────────────────────────────────────
11
22
53template <typename SampleType = float>
55public:
58 static constexpr int kMaxDim = 8;
59 static constexpr int kDefaultDim = 4;
60
65 static constexpr int kMaxOffset = 1000000;
66
67 struct Frame {
68 SampleType cv = SampleType{0};
69 bool gate = false;
70 };
71
72 void prepare(double sample_rate) {
73 sample_rate_ = sample_rate > 0.0 ? sample_rate : sample_rate_;
74 }
75
76 void set_size(int w, int h) {
77 width_ = std::clamp(w, 1, kMaxDim);
78 height_ = std::clamp(h, 1, kMaxDim);
79 if (x_ >= width_) x_ = 0;
80 if (y_ >= height_) y_ = 0;
81 }
82 int width() const { return width_; }
83 int height() const { return height_; }
84
85 void set_value(int x, int y, SampleType volts) {
86 if (x >= 0 && x < kMaxDim && y >= 0 && y < kMaxDim &&
87 std::isfinite(static_cast<double>(volts)))
88 value_[y][x] = volts;
89 }
90 SampleType value(int x, int y) const {
91 return (x >= 0 && x < kMaxDim && y >= 0 && y < kMaxDim) ? value_[y][x] : SampleType{0};
92 }
93
94 void set_access(CartesianAccess mode) { access_ = mode; }
95 CartesianAccess access() const { return access_; }
96
100 void set_offsets(int x_cv, int y_cv) {
101 x_offset_ = std::clamp(x_cv, -kMaxOffset, kMaxOffset);
102 y_offset_ = std::clamp(y_cv, -kMaxOffset, kMaxOffset);
103 }
104
109 x_ = 0;
110 y_ = 0;
111 cell_x_ = 0;
112 cell_y_ = 0;
113 started_ = false;
114 gate_ = false;
115 }
116
117 void reset() {
119 held_ = SampleType{0};
120 }
121
122 static constexpr int latency_samples() { return 0; }
123
124 int x() const { return x_; }
125 int y() const { return y_; }
126 int cell_x() const { return cell_x_; }
127 int cell_y() const { return cell_y_; }
128 bool gate() const { return gate_; }
129 SampleType cv() const { return held_; }
130
131 Frame process(bool run_high, bool reset_edge, bool x_clock_edge, bool y_clock_edge) {
132 if (reset_edge) apply_reset_edge();
133
134 if (!run_high) {
135 gate_ = false;
136 return Frame{held_, false};
137 }
138
139 gate_ = false;
140 if (x_clock_edge || y_clock_edge) {
141 const int prev_x = cell_x_;
142 const int prev_y = cell_y_;
143
144 if (!started_) {
145 // The downbeat: land on the home cell without advancing.
146 started_ = true;
147 gate_ = true;
148 } else {
149 if (access_ == CartesianAccess::row_major) {
150 if (x_clock_edge) {
151 x_ = (x_ + 1) % width_;
152 if (x_ == 0) y_ = (y_ + 1) % height_;
153 }
154 } else {
155 if (x_clock_edge) x_ = (x_ + 1) % width_;
156 if (y_clock_edge) y_ = (y_ + 1) % height_;
157 }
158 }
159
160 cell_x_ = wrap(x_ + x_offset_, width_);
161 cell_y_ = wrap(y_ + y_offset_, height_);
162 held_ = value_[cell_y_][cell_x_];
163 if (cell_x_ != prev_x || cell_y_ != prev_y) gate_ = true;
164 }
165
166 return Frame{held_, gate_};
167 }
168
169private:
170 static int wrap(int v, int m) {
171 const int r = v % m;
172 return r < 0 ? r + m : r;
173 }
174
175 double sample_rate_ = 44100.0;
176 int width_ = kDefaultDim;
177 int height_ = kDefaultDim;
179 int x_offset_ = 0;
180 int y_offset_ = 0;
181 SampleType value_[kMaxDim][kMaxDim]{};
182
183 int x_ = 0;
184 int y_ = 0;
185 int cell_x_ = 0;
186 int cell_y_ = 0;
187 bool started_ = false;
188 bool gate_ = false;
189 SampleType held_ = SampleType{0};
190};
191
194
195} // namespace pulp::signal
The 2-D (Cartesian) sequencer.
Definition cartesian_walk.hpp:54
int height() const
Definition cartesian_walk.hpp:83
int y() const
Definition cartesian_walk.hpp:125
void prepare(double sample_rate)
Definition cartesian_walk.hpp:72
bool gate() const
Definition cartesian_walk.hpp:128
Frame process(bool run_high, bool reset_edge, bool x_clock_edge, bool y_clock_edge)
Definition cartesian_walk.hpp:131
static constexpr int kMaxDim
Definition cartesian_walk.hpp:58
SampleType cv() const
Definition cartesian_walk.hpp:129
void apply_reset_edge()
Verb 2: both counters home, gate low, output CV held.
Definition cartesian_walk.hpp:108
static constexpr int latency_samples()
Definition cartesian_walk.hpp:122
int x() const
Definition cartesian_walk.hpp:124
int cell_y() const
Definition cartesian_walk.hpp:127
int cell_x() const
Definition cartesian_walk.hpp:126
void set_offsets(int x_cv, int y_cv)
Integer CV offsets added to each axis counter.
Definition cartesian_walk.hpp:100
void set_value(int x, int y, SampleType volts)
Definition cartesian_walk.hpp:85
static constexpr int kMaxOffset
Definition cartesian_walk.hpp:65
static constexpr int kDefaultDim
Definition cartesian_walk.hpp:59
void set_access(CartesianAccess mode)
Definition cartesian_walk.hpp:94
int width() const
Definition cartesian_walk.hpp:82
void set_size(int w, int h)
Definition cartesian_walk.hpp:76
SampleType value(int x, int y) const
Definition cartesian_walk.hpp:90
CartesianAccess access() const
Definition cartesian_walk.hpp:95
void reset()
Definition cartesian_walk.hpp:117
Definition additive_bank.hpp:218
CartesianAccess
How a CartesianWalkT's two axes relate.
Definition cartesian_walk.hpp:13
@ row_major
X advances on its clock; an X wrap carries into Y, like reading text.
Definition cartesian_walk.hpp:17
@ independent
X and Y advance only on their own clocks and never on each other's.
Definition cartesian_walk.hpp:20
CartesianWalkT< double > CartesianWalk64
Definition cartesian_walk.hpp:193
CartesianWalkT< float > CartesianWalk
Definition cartesian_walk.hpp:192
Definition cartesian_walk.hpp:67
SampleType cv
Definition cartesian_walk.hpp:68
bool gate
Definition cartesian_walk.hpp:69