File looper.h¶
File List > DaisySP > Source > Utility > looper.h
Go to the documentation of this file
Source Code¶
/*
Copyright (c) 2020 Electrosmith, Corp
Use of this source code is governed by an MIT-style
license that can be found in the LICENSE file or at
https://opensource.org/licenses/MIT.
*/
#pragma once
#include <algorithm>
#include "dsp.h"
namespace daisysp
{
class Looper
{
public:
Looper() {}
~Looper() {}
enum class Mode
{
NORMAL,
ONETIME_DUB,
REPLACE,
FRIPPERTRONICS,
};
void Init(float *mem, size_t size)
{
buffer_size_ = size;
buff_ = mem;
InitBuff();
state_ = State::EMPTY;
mode_ = Mode::NORMAL;
half_speed_ = false;
reverse_ = false;
rec_queue_ = false;
win_idx_ = 0;
increment_size = 1.0;
}
float Process(const float input)
{
float sig = 0.f;
float inc;
bool hitloop = false;
// Record forward at normal speed during the first loop no matter what.
inc = state_ == State::EMPTY || state_ == State::REC_FIRST
? 1.f
: GetIncrementSize();
win_ = WindowVal(win_idx_ * kWindowFactor);
switch(state_)
{
case State::EMPTY:
sig = 0.0f;
pos_ = 0;
recsize_ = 0;
break;
case State::REC_FIRST:
sig = 0.f;
Write(pos_, input * win_);
if(win_idx_ < kWindowSamps - 1)
win_idx_ += 1;
recsize_ = pos_;
pos_ += inc;
if(pos_ > buffer_size_ - 1)
{
state_ = State::PLAYING;
recsize_ = pos_ - 1;
pos_ = 0;
}
break;
case State::PLAYING:
sig = Read(pos_);
if(win_idx_ < kWindowSamps - 1)
{
Write(pos_, sig + input * (1.f - win_));
win_idx_ += 1;
}
pos_ += inc;
if(pos_ > recsize_ - 1)
{
pos_ = 0;
hitloop = true;
}
else if(pos_ < 0)
{
pos_ = recsize_ - 1;
hitloop = true;
}
if(hitloop)
{
if(rec_queue_ && mode_ == Mode::ONETIME_DUB)
{
rec_queue_ = false;
state_ = State::REC_DUB;
win_idx_ = 0;
}
}
break;
case State::REC_DUB:
sig = Read(pos_);
switch(mode_)
{
case Mode::REPLACE: Write(pos_, input * win_); break;
case Mode::FRIPPERTRONICS:
Write(pos_, (input * win_) + (sig * kFripDecayVal));
break;
case Mode::NORMAL:
case Mode::ONETIME_DUB:
default: Write(pos_, (input * win_) + sig); break;
}
if(win_idx_ < kWindowSamps - 1)
win_idx_ += 1;
pos_ += inc;
if(pos_ > recsize_ - 1)
{
pos_ = 0;
hitloop = true;
}
else if(pos_ < 0)
{
pos_ = recsize_ - 1;
hitloop = true;
}
if(hitloop && mode_ == Mode::ONETIME_DUB)
{
state_ = State::PLAYING;
win_idx_ = 0;
}
break;
default: break;
}
near_beginning_ = state_ != State::EMPTY && !Recording() && pos_ < 4800
? true
: false;
return sig;
}
inline void Clear() { state_ = State::EMPTY; }
inline void TrigRecord()
{
switch(state_)
{
case State::EMPTY:
pos_ = 0;
recsize_ = 0;
state_ = State::REC_FIRST;
half_speed_ = false;
reverse_ = false;
break;
case State::REC_FIRST:
case State::REC_DUB: state_ = State::PLAYING; break;
case State::PLAYING:
if(mode_ == Mode::ONETIME_DUB)
rec_queue_ = true;
else
state_ = State::REC_DUB;
break;
default: state_ = State::EMPTY; break;
}
if(!rec_queue_)
win_idx_ = 0;
}
inline const bool Recording() const
{
return state_ == State::REC_DUB || state_ == State::REC_FIRST;
}
inline const bool RecordingQueued() const { return rec_queue_; }
inline void IncrementMode()
{
int m = static_cast<int>(mode_);
m = m + 1;
if(m > kNumModes - 1)
m = 0;
mode_ = static_cast<Mode>(m);
}
inline void SetMode(Mode mode) { mode_ = mode; }
inline const Mode GetMode() const { return mode_; }
inline void ToggleReverse() { reverse_ = !reverse_; }
inline void SetReverse(bool state) { reverse_ = state; }
inline bool GetReverse() const { return reverse_; }
inline void ToggleHalfSpeed() { half_speed_ = !half_speed_; }
inline void SetHalfSpeed(bool state) { half_speed_ = state; }
inline bool GetHalfSpeed() const { return half_speed_; }
inline bool IsNearBeginning() const { return near_beginning_; }
inline float GetIncrementSize() const
{
float inc = increment_size;
if(half_speed_)
inc *= 0.5f;
return reverse_ ? -inc : inc;
}
void SetIncrementSize(float increment) { increment_size = increment; }
inline float GetPos() const { return pos_; }
inline size_t GetRecSize() const { return recsize_; }
private:
static constexpr float kFripDecayVal = 0.7071067811865476f;
static constexpr int kNumModes = 4;
static constexpr int kNumPlaybackSpeeds = 3;
static constexpr int kWindowSamps = 1200;
static constexpr float kWindowFactor = (1.f / kWindowSamps);
void InitBuff() { std::fill(&buff_[0], &buff_[buffer_size_ - 1], 0); }
inline const float Read(size_t pos) const { return buff_[pos]; }
float ReadF(float pos)
{
float a, b, frac;
uint32_t i_idx = static_cast<uint32_t>(pos);
frac = pos - i_idx;
a = buff_[i_idx];
b = buff_[(i_idx + 1) % buffer_size_];
return a + (b - a) * frac;
}
inline void Write(size_t pos, float val) { buff_[pos] = val; }
float WindowVal(float in) { return sin(HALFPI_F * in); }
// Private Enums
enum class State
{
EMPTY,
REC_FIRST,
PLAYING,
REC_DUB,
};
Mode mode_;
State state_;
float *buff_;
size_t buffer_size_;
float pos_, win_;
size_t win_idx_;
bool half_speed_;
bool reverse_;
size_t recsize_;
bool rec_queue_;
bool near_beginning_;
float increment_size;
};
} // namespace daisysp