DaisySP
Loading...
Searching...
No Matches
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
64 float Process(const float input)
65 {
66 float sig = 0.f;
67 float inc;
68 bool hitloop = false;
69 // Record forward at normal speed during the first loop no matter what.
70 inc = state_ == State::EMPTY || state_ == State::REC_FIRST
71 ? 1.f
72 : GetIncrementSize();
73 win_ = WindowVal(win_idx_ * kWindowFactor);
74 switch(state_)
75 {
76 case State::EMPTY: sig = 0.0f; break;
77 case State::REC_FIRST:
78 sig = 0.f;
79 Write(pos_, input * win_);
80 if(win_idx_ < kWindowSamps - 1)
81 win_idx_ += 1;
82 recsize_ = pos_;
83 pos_ += inc;
84 if(pos_ > buffer_size_ - 1)
85 {
86 state_ = State::PLAYING;
87 recsize_ = pos_ - 1;
88 pos_ = 0;
89 }
90 break;
91 case State::PLAYING:
92 sig = Read(pos_);
96 if(win_idx_ < kWindowSamps - 1)
97 {
98 Write(pos_, sig + input * (1.f - win_));
99 win_idx_ += 1;
100 }
101
102 pos_ += inc;
103 if(pos_ > recsize_ - 1)
104 {
105 pos_ = 0;
106 hitloop = true;
107 }
108 else if(pos_ < 0)
109 {
110 pos_ = recsize_ - 1;
111 hitloop = true;
112 }
113 if(hitloop)
114
115 {
116 if(rec_queue_ && mode_ == Mode::ONETIME_DUB)
117 {
118 rec_queue_ = false;
119 state_ = State::REC_DUB;
120 win_idx_ = 0;
121 }
122 }
123 break;
124 case State::REC_DUB:
125 sig = Read(pos_);
126 switch(mode_)
127 {
128 case Mode::REPLACE: Write(pos_, input * win_); break;
129 case Mode::FRIPPERTRONICS:
130 Write(pos_, (input * win_) + (sig * kFripDecayVal));
131 break;
132 case Mode::NORMAL:
133 case Mode::ONETIME_DUB:
134 default: Write(pos_, (input * win_) + sig); break;
135 }
136 if(win_idx_ < kWindowSamps - 1)
137 win_idx_ += 1;
138 pos_ += inc;
139 if(pos_ > recsize_ - 1)
140 {
141 pos_ = 0;
142 hitloop = true;
143 }
144 else if(pos_ < 0)
145 {
146 pos_ = recsize_ - 1;
147 hitloop = true;
148 }
149 if(hitloop && mode_ == Mode::ONETIME_DUB)
150 {
151 state_ = State::PLAYING;
152 win_idx_ = 0;
153 }
154
155 break;
156 default: break;
157 }
158 near_beginning_ = state_ != State::EMPTY && !Recording() && pos_ < 4800
159 ? true
160 : false;
161
162 return sig;
163 }
164
167 inline void Clear() { state_ = State::EMPTY; }
168
173 inline void TrigRecord()
174 {
175 switch(state_)
176 {
177 case State::EMPTY:
178 pos_ = 0;
179 recsize_ = 0;
180 state_ = State::REC_FIRST;
181 half_speed_ = false;
182 reverse_ = false;
183 break;
184 case State::REC_FIRST:
185 case State::REC_DUB: state_ = State::PLAYING; break;
186 case State::PLAYING:
187 if(mode_ == Mode::ONETIME_DUB)
188 rec_queue_ = true;
189 else
190 state_ = State::REC_DUB;
191 break;
192 default: state_ = State::EMPTY; break;
193 }
194 if(!rec_queue_)
195 win_idx_ = 0;
196 }
197
199 inline const bool Recording() const
200 {
201 return state_ == State::REC_DUB || state_ == State::REC_FIRST;
202 }
203
204 inline const bool RecordingQueued() const { return rec_queue_; }
205
207 inline void IncrementMode()
208 {
209 int m = static_cast<int>(mode_);
210 m = m + 1;
211 if(m > kNumModes - 1)
212 m = 0;
213 mode_ = static_cast<Mode>(m);
214 }
215
217 inline void SetMode(Mode mode) { mode_ = mode; }
218
220 inline const Mode GetMode() const { return mode_; }
221
222 inline void ToggleReverse() { reverse_ = !reverse_; }
223 inline void SetReverse(bool state) { reverse_ = state; }
224 inline bool GetReverse() const { return reverse_; }
225
226 inline void ToggleHalfSpeed() { half_speed_ = !half_speed_; }
227 inline void SetHalfSpeed(bool state) { half_speed_ = state; }
228 inline bool GetHalfSpeed() const { return half_speed_; }
229
230 inline bool IsNearBeginning() { return near_beginning_; }
231
232 private:
236 static constexpr float kFripDecayVal = 0.7071067811865476f;
237 static constexpr int kNumModes = 4;
238 static constexpr int kNumPlaybackSpeeds = 3;
239 static constexpr int kWindowSamps = 1200;
240 static constexpr float kWindowFactor = (1.f / kWindowSamps);
241
243 float GetIncrementSize()
244 {
245 float inc = 1.f;
246 if(half_speed_)
247 inc = 0.5f;
248 return reverse_ ? -inc : inc;
249 }
250
252 void InitBuff() { std::fill(&buff_[0], &buff_[buffer_size_ - 1], 0); }
253
255 inline const float Read(size_t pos) const { return buff_[pos]; }
256
258 float ReadF(float pos)
259 {
260 float a, b, frac;
261 uint32_t i_idx = static_cast<uint32_t>(pos);
262 frac = pos - i_idx;
263 a = buff_[i_idx];
264 b = buff_[(i_idx + 1) % buffer_size_];
265 return a + (b - a) * frac;
266 }
267
269 inline void Write(size_t pos, float val) { buff_[pos] = val; }
270
272 float WindowVal(float in) { return sin(HALFPI_F * in); }
273
274 // Private Enums
275
277 enum class State
278 {
279 EMPTY,
280 REC_FIRST,
281 PLAYING,
282 REC_DUB,
283 };
284
286 Mode mode_;
287 State state_;
288 float *buff_;
289 size_t buffer_size_;
290 float pos_, win_;
291 size_t win_idx_;
292 bool half_speed_;
293 bool reverse_;
294 size_t recsize_;
295 bool rec_queue_;
296 bool near_beginning_;
297};
298
299} // namespace daisysp
Definition delayline.h:29
Definition looper.h:26
Mode
Definition looper.h:42
void SetMode(Mode mode)
Definition looper.h:217
void IncrementMode()
Definition looper.h:207
void Clear()
Definition looper.h:167
float Process(const float input)
Definition looper.h:64
const Mode GetMode() const
Definition looper.h:220
const bool Recording() const
Definition looper.h:199
void TrigRecord()
Definition looper.h:173
FIR Filter implementation, generic and ARM CMSIS DSP based.
Definition adenv.h:16