DaisySP
All Classes Namespaces Files Functions Enumerations Enumerator Pages
looper.h
1/*
2Copyright (c) 2020 Electrosmith, Corp
3
4Use of this source code is governed by an MIT-style
5license that can be found in the LICENSE file or at
6https://opensource.org/licenses/MIT.
7*/
8
9#pragma once
10#include <algorithm>
11#include "dsp.h"
12
13namespace daisysp
14{
25class Looper
26{
27 public:
28 Looper() {}
29 ~Looper() {}
30
41 enum class Mode
42 {
43 NORMAL,
44 ONETIME_DUB,
45 REPLACE,
46 FRIPPERTRONICS,
47 };
48
49 void Init(float *mem, size_t size)
50 {
51 buffer_size_ = size;
52 buff_ = mem;
53
54 InitBuff();
55 state_ = State::EMPTY;
56 mode_ = Mode::NORMAL;
57 half_speed_ = false;
58 reverse_ = false;
59 rec_queue_ = false;
60 win_idx_ = 0;
61
62 increment_size = 1.0;
63 }
64
66 float Process(const float input)
67 {
68 float sig = 0.f;
69 float inc;
70 bool hitloop = false;
71 // Record forward at normal speed during the first loop no matter what.
72 inc = state_ == State::EMPTY || state_ == State::REC_FIRST
73 ? 1.f
74 : GetIncrementSize();
75 win_ = WindowVal(win_idx_ * kWindowFactor);
76 switch(state_)
77 {
78 case State::EMPTY:
79 sig = 0.0f;
80 pos_ = 0;
81 recsize_ = 0;
82 break;
83 case State::REC_FIRST:
84 sig = 0.f;
85 Write(pos_, input * win_);
86 if(win_idx_ < kWindowSamps - 1)
87 win_idx_ += 1;
88 recsize_ = pos_;
89 pos_ += inc;
90 if(pos_ > buffer_size_ - 1)
91 {
92 state_ = State::PLAYING;
93 recsize_ = pos_ - 1;
94 pos_ = 0;
95 }
96 break;
97 case State::PLAYING:
98 sig = Read(pos_);
102 if(win_idx_ < kWindowSamps - 1)
103 {
104 Write(pos_, sig + input * (1.f - win_));
105 win_idx_ += 1;
106 }
107
108 pos_ += inc;
109 if(pos_ > recsize_ - 1)
110 {
111 pos_ = 0;
112 hitloop = true;
113 }
114 else if(pos_ < 0)
115 {
116 pos_ = recsize_ - 1;
117 hitloop = true;
118 }
119 if(hitloop)
120
121 {
122 if(rec_queue_ && mode_ == Mode::ONETIME_DUB)
123 {
124 rec_queue_ = false;
125 state_ = State::REC_DUB;
126 win_idx_ = 0;
127 }
128 }
129 break;
130 case State::REC_DUB:
131 sig = Read(pos_);
132 switch(mode_)
133 {
134 case Mode::REPLACE: Write(pos_, input * win_); break;
135 case Mode::FRIPPERTRONICS:
136 Write(pos_, (input * win_) + (sig * kFripDecayVal));
137 break;
138 case Mode::NORMAL:
139 case Mode::ONETIME_DUB:
140 default: Write(pos_, (input * win_) + sig); break;
141 }
142 if(win_idx_ < kWindowSamps - 1)
143 win_idx_ += 1;
144 pos_ += inc;
145 if(pos_ > recsize_ - 1)
146 {
147 pos_ = 0;
148 hitloop = true;
149 }
150 else if(pos_ < 0)
151 {
152 pos_ = recsize_ - 1;
153 hitloop = true;
154 }
155 if(hitloop && mode_ == Mode::ONETIME_DUB)
156 {
157 state_ = State::PLAYING;
158 win_idx_ = 0;
159 }
160
161 break;
162 default: break;
163 }
164 near_beginning_ = state_ != State::EMPTY && !Recording() && pos_ < 4800
165 ? true
166 : false;
167
168 return sig;
169 }
170
173 inline void Clear() { state_ = State::EMPTY; }
174
179 inline void TrigRecord()
180 {
181 switch(state_)
182 {
183 case State::EMPTY:
184 pos_ = 0;
185 recsize_ = 0;
186 state_ = State::REC_FIRST;
187 half_speed_ = false;
188 reverse_ = false;
189 break;
190 case State::REC_FIRST:
191 case State::REC_DUB: state_ = State::PLAYING; break;
192 case State::PLAYING:
193 if(mode_ == Mode::ONETIME_DUB)
194 rec_queue_ = true;
195 else
196 state_ = State::REC_DUB;
197 break;
198 default: state_ = State::EMPTY; break;
199 }
200 if(!rec_queue_)
201 win_idx_ = 0;
202 }
203
205 inline const bool Recording() const
206 {
207 return state_ == State::REC_DUB || state_ == State::REC_FIRST;
208 }
209
210 inline const bool RecordingQueued() const { return rec_queue_; }
211
213 inline void IncrementMode()
214 {
215 int m = static_cast<int>(mode_);
216 m = m + 1;
217 if(m > kNumModes - 1)
218 m = 0;
219 mode_ = static_cast<Mode>(m);
220 }
221
223 inline void SetMode(Mode mode) { mode_ = mode; }
224
226 inline const Mode GetMode() const { return mode_; }
227
228 inline void ToggleReverse() { reverse_ = !reverse_; }
229 inline void SetReverse(bool state) { reverse_ = state; }
230 inline bool GetReverse() const { return reverse_; }
231
232 inline void ToggleHalfSpeed() { half_speed_ = !half_speed_; }
233 inline void SetHalfSpeed(bool state) { half_speed_ = state; }
234 inline bool GetHalfSpeed() const { return half_speed_; }
235
236 inline bool IsNearBeginning() const { return near_beginning_; }
237
238 inline float GetIncrementSize() const
239 {
240 float inc = increment_size;
241
242 if(half_speed_)
243 inc *= 0.5f;
244
245 return reverse_ ? -inc : inc;
246 }
247
248 void SetIncrementSize(float increment) { increment_size = increment; }
249
250 inline float GetPos() const { return pos_; }
251 inline size_t GetRecSize() const { return recsize_; }
252
253 private:
255
257 static constexpr float kFripDecayVal = 0.7071067811865476f;
258 static constexpr int kNumModes = 4;
259 static constexpr int kNumPlaybackSpeeds = 3;
260 static constexpr int kWindowSamps = 1200;
261 static constexpr float kWindowFactor = (1.f / kWindowSamps);
262
264
266 void InitBuff() { std::fill(&buff_[0], &buff_[buffer_size_ - 1], 0); }
267
269 inline const float Read(size_t pos) const { return buff_[pos]; }
270
272 float ReadF(float pos)
273 {
274 float a, b, frac;
275 uint32_t i_idx = static_cast<uint32_t>(pos);
276 frac = pos - i_idx;
277 a = buff_[i_idx];
278 b = buff_[(i_idx + 1) % buffer_size_];
279 return a + (b - a) * frac;
280 }
281
283 inline void Write(size_t pos, float val) { buff_[pos] = val; }
284
286 float WindowVal(float in) { return sin(HALFPI_F * in); }
287
288 // Private Enums
289
291 enum class State
292 {
293 EMPTY,
294 REC_FIRST,
295 PLAYING,
296 REC_DUB,
297 };
298
300 Mode mode_;
301 State state_;
302 float *buff_;
303 size_t buffer_size_;
304 float pos_, win_;
305 size_t win_idx_;
306 bool half_speed_;
307 bool reverse_;
308 size_t recsize_;
309 bool rec_queue_;
310 bool near_beginning_;
311 float increment_size;
312};
313
314} // namespace daisysp
Mode
Definition looper.h:42
void SetMode(Mode mode)
Definition looper.h:223
void IncrementMode()
Definition looper.h:213
void Clear()
Definition looper.h:173
float Process(const float input)
Definition looper.h:66
const Mode GetMode() const
Definition looper.h:226
const bool Recording() const
Definition looper.h:205
void TrigRecord()
Definition looper.h:179
FIR Filter implementation, generic and ARM CMSIS DSP based.
Definition adenv.h:16