-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathpantograph.cpp
More file actions
133 lines (109 loc) · 3.24 KB
/
Copy pathpantograph.cpp
File metadata and controls
133 lines (109 loc) · 3.24 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
#include "ComputerCard.h"
#include "pantograph.h"
namespace pantograph {
class Pantograph : public ComputerCard {
public:
virtual void ProcessSample() override {
auto switchChanged = SwitchChanged();
auto switchVal = SwitchVal();
auto knobX = KnobVal(Knob::X);
auto knobY = KnobVal(Knob::Y);
auto cvIn1 = CVIn1();
auto cvIn2 = CVIn2();
switch (switchVal) {
case Up: {
return Live(knobX, knobY, cvIn1, cvIn2);
}
case Down: {
if (switchChanged) {
// Record
buffer_.Reset();
decim_.Reset();
}
if (decim_.Tick()) {
buffer_.Push({static_cast<int16_t>(KnobToCV(knobX)), static_cast<int16_t>(KnobToCV(knobY))});
}
uint32_t count = (buffer_.Length() * 6) / buffer_.Capacity();
for (uint32_t k = 0; k < 6; ++k) {
LedOn(k, k < count);
}
return MonitorOutputs(knobX, knobY, cvIn1, cvIn2);
}
case Middle: {
if (switchChanged) {
phasor_.SetLength(buffer_.Length());
}
auto length = buffer_.Length();
if (length == 0) {
return Live(knobX, knobY, cvIn1, cvIn2);
}
auto pulseIn = Connected(Pulse1);
if (pulseIn && PulseIn1RisingEdge()) {
phasor_.Trigger();
}
bool wrapped = phasor_.Advance(KnobToSpeed(KnobVal(Knob::Main)) / kFramePeriod, !pulseIn);
bool finished = phasor_.Finished();
bool isEdge = finished && !lastFinished_;
lastFinished_ = finished;
PulseOut1(trigger_.tick(wrapped || isEdge));
auto i = phasor_.Index();
auto frac = phasor_.Frac();
uint32_t dot = (i * 6) / length;
for (uint32_t k = 0; k < 6; ++k) {
LedOn(k, k == dot);
}
int xVal = ApplyDepth(
LerpI(buffer_.At(i).x, buffer_.At((i + 1) % length).x, frac), knobX);
CVOut1(SumClamp(xVal, cvIn1));
PulseOut2(schmitt_.update(xVal));
CVOut2(SumClamp(
ApplyDepth(
LerpI(buffer_.At(i).y, buffer_.At((i + 1) % length).y, frac), knobY),
cvIn2));
break;
}
default: {
return;
}
}
}
private:
void MonitorOutputs(int knobX, int knobY, int cvIn1, int cvIn2) {
auto out1 = SumClamp(KnobToCV(knobX), cvIn1);
auto out2 = SumClamp(KnobToCV(knobY), cvIn2);
CVOut1(out1);
CVOut2(out2);
// Playback gates are meaningless outside Middle; without this they
// would freeze at whatever state they had when the switch left Play.
PulseOut1(false);
PulseOut2(false);
lastOut1_ = out1;
lastOut2_ = out2;
}
void Live(int knobX, int knobY, int cvIn1, int cvIn2) {
MonitorOutputs(knobX, knobY, cvIn1, cvIn2);
uint16_t bx = std::min(std::abs(lastOut1_) * 2, 4095);
uint16_t by = std::min(std::abs(lastOut2_) * 2, 4095);
LedBrightness(0, bx);
LedBrightness(2, bx);
LedBrightness(4, bx);
LedBrightness(1, by);
LedBrightness(3, by);
LedBrightness(5, by);
}
Buffer buffer_;
Phasor phasor_;
TriggerOut trigger_;
Schmitt schmitt_;
Decimator decim_{kFramePeriod};
int lastOut1_ = 0;
int lastOut2_ = 0;
bool lastFinished_ = false;
};
} // namespace pantograph
int main() {
set_sys_clock_khz(144000, true);
static pantograph::Pantograph card;
card.EnableNormalisationProbe();
card.Run();
}