From 0973796948f6ae562120c40d7ff7d05df81b1bf8 Mon Sep 17 00:00:00 2001 From: beserge Date: Mon, 6 Jul 2020 19:47:49 -0400 Subject: [PATCH 01/10] first commit --- patch/DrumLooper/DrumLooper.cpp | 194 ++++++++++++++++++++++++++++++++ patch/DrumLooper/Makefile | 14 +++ 2 files changed, 208 insertions(+) create mode 100644 patch/DrumLooper/DrumLooper.cpp create mode 100644 patch/DrumLooper/Makefile diff --git a/patch/DrumLooper/DrumLooper.cpp b/patch/DrumLooper/DrumLooper.cpp new file mode 100644 index 000000000..c26c4d9ca --- /dev/null +++ b/patch/DrumLooper/DrumLooper.cpp @@ -0,0 +1,194 @@ +#include "daisysp.h" +#include "daisy_patch.h" +#include + +using namespace daisy; +using namespace daisysp; + +#define MAX_SIZE (1000 * 1) // 1 minute at 1000Hz + +DaisyPatch patch; + +Oscillator osc; +WhiteNoise noise; + +AdEnv kckPitchEnv; +AdEnv volEnvs[3]; + +bool first = true; //first loop (sets length) +bool rec = false; //currently recording +bool play = false; //currently playing + +int pos = 0; +bool DSY_SDRAM_BSS buf[3][MAX_SIZE]; +int mod = MAX_SIZE; +int len = 0; +float drywet = 0; +bool res = false; + +int recChan = 0; + +void UpdateControls(); + +void UpdateEnvs() +{ + for (int chn = 0; chn < 3; chn++) + { + if (buf[chn][pos]) + { + volEnvs[chn].Trigger(); + if (chn == 0) + { + kckPitchEnv.Trigger(); + } + } + } + + if(play) + { + pos++; + pos %= mod; + } + + //automatic looptime + if(len >= MAX_SIZE) + { + first = false; + mod = MAX_SIZE; + len = 0; + } +} + +static void AudioCallback(float **in, float **out, size_t size) +{ + UpdateControls(); + UpdateEnvs(); + + for (size_t i = 0; i < size; i ++){ + float outs[3]; + + osc.SetFreq(kckPitchEnv.Process()); + osc.SetAmp(volEnvs[0].Process()); + outs[0] = osc.Process(); + + float noise_out = noise.Process(); + outs[1] = noise_out * volEnvs[1].Process(); + + outs[2]= noise_out * volEnvs[2].Process(); + + float mix = 0.f; + + for (size_t chn = 0; chn < 3; chn++) + { + out[chn][i] = outs[chn]; + mix += .3f * outs[chn]; + } + out[4][i] = mix; + } +} + +void UpdateOled(); + +void InitEnvs(float samplerate) +{ + for (int i = 0; i < 3; i++) + { + volEnvs[i].Init(samplerate); + volEnvs[i].SetTime(ADENV_SEG_ATTACK, .01); + } + + //This envelope will control the kick oscillator's pitch + //Note that this envelope is much faster than the volume + kckPitchEnv.Init(samplerate); + kckPitchEnv.SetTime(ADENV_SEG_ATTACK, .01); + kckPitchEnv.SetMax(400); + kckPitchEnv.SetMin(50); +} + +int main(void) +{ + float samplerate; + int num_waves = Oscillator::WAVE_LAST - 1; + patch.Init(); // Initialize hardware (daisy seed, and patch) + samplerate = patch.AudioSampleRate(); + + //Initialize oscillator for kickdrum + osc.Init(samplerate); + osc.SetWaveform(Oscillator::WAVE_TRI); + + //Initialize noise + noise.Init(); + + InitEnvs(samplerate); + + //Init loop stuff + ResetBuffer(); + + patch.StartAdc(); + patch.StartAudio(AudioCallback); + while(1) + { + UpdateOled(); + } +} + +//Resets the buffer +void ResetBuffer() +{ + play = false; + rec = false; + first = true; + pos = 0; + len = 0; + + for (int chn = 0; chn < 3; chn++){ + for(int i = 0; i < mod; i++) + { + buf[chn][i] = 0; + } + } + + mod = MAX_SIZE; +} + +void UpdateOled() +{ + +} +void UpdateControls() +{ + patch.UpdateAnalogControls(); + patch.DebounceControls(); + + //encoder pressed + if(patch.encoder.RisingEdge()) + { + if(first && rec) + { + first = false; + mod = len; + len = 0; + } + + res = true; + play = true; + rec = !rec; + } + + //encoder held + if(patch.encoder.TimeHeldMs() >= 1000 && res) + { + ResetBuffer(); + res = false; + } + + //encoder turned + recChan = patch.encoder.Increment(); + recChan = (recChan % 3 + 3) % 3; + + //gate in + if (patch.gate_input[0].Trig() && rec) + { + buf[recChan][pos] = true; + } +} diff --git a/patch/DrumLooper/Makefile b/patch/DrumLooper/Makefile new file mode 100644 index 000000000..f882d3d88 --- /dev/null +++ b/patch/DrumLooper/Makefile @@ -0,0 +1,14 @@ +# Project Name +TARGET = DrumLooper + +# Sources +CPP_SOURCES = DrumLooper.cpp + +# Library Locations +LIBDAISY_DIR = ../../libdaisy +DAISYSP_DIR = ../../DaisySP + +# Core location, and generic Makefile. +SYSTEM_FILES_DIR = $(LIBDAISY_DIR)/core +include $(SYSTEM_FILES_DIR)/Makefile + From a5dd89496a9e93c9e14687f46a97e71bcc16fd16 Mon Sep 17 00:00:00 2001 From: beserge Date: Mon, 6 Jul 2020 20:33:33 -0400 Subject: [PATCH 02/10] bugfixes --- patch/DrumLooper/DrumLooper.cpp | 105 +++++++++++++++++++++----------- 1 file changed, 69 insertions(+), 36 deletions(-) diff --git a/patch/DrumLooper/DrumLooper.cpp b/patch/DrumLooper/DrumLooper.cpp index c26c4d9ca..7c2d0aef3 100644 --- a/patch/DrumLooper/DrumLooper.cpp +++ b/patch/DrumLooper/DrumLooper.cpp @@ -5,7 +5,7 @@ using namespace daisy; using namespace daisysp; -#define MAX_SIZE (1000 * 1) // 1 minute at 1000Hz +#define MAX_SIZE (1000 * 60) // 1 minute at 1000Hz DaisyPatch patch; @@ -50,20 +50,28 @@ void UpdateEnvs() pos %= mod; } - //automatic looptime - if(len >= MAX_SIZE) + if(first && rec) { - first = false; - mod = MAX_SIZE; - len = 0; + len++; + //automatic looptime + if(len >= MAX_SIZE) + { + first = false; + mod = MAX_SIZE; + len = 0; + } } } static void AudioCallback(float **in, float **out, size_t size) { UpdateControls(); - UpdateEnvs(); - + + if (play) + { + UpdateEnvs(); + } + for (size_t i = 0; i < size; i ++){ float outs[3]; @@ -77,13 +85,15 @@ static void AudioCallback(float **in, float **out, size_t size) outs[2]= noise_out * volEnvs[2].Process(); float mix = 0.f; + float mixLevels[] = {.5f, .1f, .05f}; for (size_t chn = 0; chn < 3; chn++) { out[chn][i] = outs[chn]; - mix += .3f * outs[chn]; + mix += mixLevels[chn] * outs[chn]; } - out[4][i] = mix; + + out[3][i] = mix; } } @@ -95,20 +105,39 @@ void InitEnvs(float samplerate) { volEnvs[i].Init(samplerate); volEnvs[i].SetTime(ADENV_SEG_ATTACK, .01); + volEnvs[i].SetCurve(-4); } //This envelope will control the kick oscillator's pitch //Note that this envelope is much faster than the volume kckPitchEnv.Init(samplerate); - kckPitchEnv.SetTime(ADENV_SEG_ATTACK, .01); - kckPitchEnv.SetMax(400); - kckPitchEnv.SetMin(50); + kckPitchEnv.SetTime(ADENV_SEG_ATTACK, .02); + kckPitchEnv.SetMax(200); + kckPitchEnv.SetCurve(-4); +} + +void ResetBuffer() +{ + play = false; + rec = false; + first = true; + pos = 0; + len = 0; + + for (int chn = 0; chn < 3; chn++){ + for(int i = 0; i < mod; i++) + { + buf[chn][i] = 0; + } + } + + mod = MAX_SIZE; } + int main(void) { float samplerate; - int num_waves = Oscillator::WAVE_LAST - 1; patch.Init(); // Initialize hardware (daisy seed, and patch) samplerate = patch.AudioSampleRate(); @@ -119,6 +148,8 @@ int main(void) //Initialize noise noise.Init(); + patch.display.Fill(false); + InitEnvs(samplerate); //Init loop stuff @@ -132,28 +163,16 @@ int main(void) } } -//Resets the buffer -void ResetBuffer() -{ - play = false; - rec = false; - first = true; - pos = 0; - len = 0; - - for (int chn = 0; chn < 3; chn++){ - for(int i = 0; i < mod; i++) - { - buf[chn][i] = 0; - } - } - - mod = MAX_SIZE; -} - void UpdateOled() { + patch.display.Fill(false); + + patch.display.SetCursor(0,0); + std::string str = rec ? "rec" : "play"; + char* cstr = &str[0]; + patch.display.WriteString(cstr, Font_7x10, true); + patch.display.Update(); } void UpdateControls() { @@ -168,6 +187,7 @@ void UpdateControls() first = false; mod = len; len = 0; + pos = 0; } res = true; @@ -183,12 +203,25 @@ void UpdateControls() } //encoder turned - recChan = patch.encoder.Increment(); + recChan += patch.encoder.Increment(); recChan = (recChan % 3 + 3) % 3; + //parameters + volEnvs[0].SetTime(ADENV_SEG_DECAY, patch.controls[0].Process() * 3); + + volEnvs[1].SetTime(ADENV_SEG_DECAY, patch.controls[2].Process()); + volEnvs[2].SetTime(ADENV_SEG_DECAY, patch.controls[3].Process()); + + kckPitchEnv.SetMin((patch.controls[1].Process() * 4 + 1) * 20); + //gate in - if (patch.gate_input[0].Trig() && rec) + if (patch.gate_input[0].Trig()) { - buf[recChan][pos] = true; + if(rec || first) + { + buf[recChan][pos] = true; + rec = true; + play = true; + } } } From e5a6bff9a4412fafe75c8b6f7fac8b8d2a57a54e Mon Sep 17 00:00:00 2001 From: beserge Date: Mon, 6 Jul 2020 20:36:56 -0400 Subject: [PATCH 03/10] more bugfixes --- patch/DrumLooper/DrumLooper.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/patch/DrumLooper/DrumLooper.cpp b/patch/DrumLooper/DrumLooper.cpp index 7c2d0aef3..b115580f9 100644 --- a/patch/DrumLooper/DrumLooper.cpp +++ b/patch/DrumLooper/DrumLooper.cpp @@ -127,7 +127,7 @@ void ResetBuffer() for (int chn = 0; chn < 3; chn++){ for(int i = 0; i < mod; i++) { - buf[chn][i] = 0; + buf[chn][i] = false; } } From 971810d049faf4095303e60bba58fa0e667ce4ce Mon Sep 17 00:00:00 2001 From: beserge Date: Mon, 6 Jul 2020 20:40:38 -0400 Subject: [PATCH 04/10] remove play, broken --- patch/DrumLooper/DrumLooper.cpp | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/patch/DrumLooper/DrumLooper.cpp b/patch/DrumLooper/DrumLooper.cpp index b115580f9..ae032c59a 100644 --- a/patch/DrumLooper/DrumLooper.cpp +++ b/patch/DrumLooper/DrumLooper.cpp @@ -17,7 +17,6 @@ AdEnv volEnvs[3]; bool first = true; //first loop (sets length) bool rec = false; //currently recording -bool play = false; //currently playing int pos = 0; bool DSY_SDRAM_BSS buf[3][MAX_SIZE]; @@ -44,11 +43,8 @@ void UpdateEnvs() } } - if(play) - { - pos++; - pos %= mod; - } + pos++; + pos %= mod; if(first && rec) { @@ -67,10 +63,7 @@ static void AudioCallback(float **in, float **out, size_t size) { UpdateControls(); - if (play) - { - UpdateEnvs(); - } + UpdateEnvs(); for (size_t i = 0; i < size; i ++){ float outs[3]; @@ -118,7 +111,6 @@ void InitEnvs(float samplerate) void ResetBuffer() { - play = false; rec = false; first = true; pos = 0; @@ -191,7 +183,6 @@ void UpdateControls() } res = true; - play = true; rec = !rec; } @@ -221,7 +212,6 @@ void UpdateControls() { buf[recChan][pos] = true; rec = true; - play = true; } } } From 01518c609292879b460cbcdfb190ebbd63f170a9 Mon Sep 17 00:00:00 2001 From: beserge Date: Wed, 22 Jul 2020 12:16:06 -0400 Subject: [PATCH 05/10] updates --- patch/DrumLooper/DrumLooper.cpp | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/patch/DrumLooper/DrumLooper.cpp b/patch/DrumLooper/DrumLooper.cpp index ae032c59a..2c7ca70c0 100644 --- a/patch/DrumLooper/DrumLooper.cpp +++ b/patch/DrumLooper/DrumLooper.cpp @@ -23,7 +23,6 @@ bool DSY_SDRAM_BSS buf[3][MAX_SIZE]; int mod = MAX_SIZE; int len = 0; float drywet = 0; -bool res = false; int recChan = 0; @@ -55,6 +54,7 @@ void UpdateEnvs() first = false; mod = MAX_SIZE; len = 0; + pos = 0; } } } @@ -164,6 +164,12 @@ void UpdateOled() char* cstr = &str[0]; patch.display.WriteString(cstr, Font_7x10, true); + patch.display.SetCursor(0,25); + str = recChan == 0 ? "Kick" : ""; + str = recChan == 1 ? "Snare": str; + str = recChan == 2 ? "Hat " : str; + patch.display.WriteString(cstr, Font_7x10, true); + patch.display.Update(); } void UpdateControls() @@ -182,15 +188,13 @@ void UpdateControls() pos = 0; } - res = true; rec = !rec; } //encoder held - if(patch.encoder.TimeHeldMs() >= 1000 && res) + if(patch.encoder.TimeHeldMs() >= 1000) { ResetBuffer(); - res = false; } //encoder turned From 310d4966ab10136ea35e1dc5a565db7a5aa0fb7a Mon Sep 17 00:00:00 2001 From: beserge Date: Wed, 22 Jul 2020 13:52:45 -0400 Subject: [PATCH 06/10] bugfix --- patch/DrumLooper/DrumLooper.cpp | 39 +++++++++++++++++++-------------- 1 file changed, 23 insertions(+), 16 deletions(-) diff --git a/patch/DrumLooper/DrumLooper.cpp b/patch/DrumLooper/DrumLooper.cpp index 2c7ca70c0..5b6b9fd61 100644 --- a/patch/DrumLooper/DrumLooper.cpp +++ b/patch/DrumLooper/DrumLooper.cpp @@ -41,22 +41,6 @@ void UpdateEnvs() } } } - - pos++; - pos %= mod; - - if(first && rec) - { - len++; - //automatic looptime - if(len >= MAX_SIZE) - { - first = false; - mod = MAX_SIZE; - len = 0; - pos = 0; - } - } } static void AudioCallback(float **in, float **out, size_t size) @@ -218,4 +202,27 @@ void UpdateControls() rec = true; } } + + //the only situation in which we don't increment is when \ + //we're waiting for the first recording + if(!(first && !rec)) + { + //array stuff + pos++; + pos %= mod; + } + + //if we're making our first loop + if(first && rec) + { + len++; + //automatic looptime + if(len >= MAX_SIZE) + { + first = false; + mod = MAX_SIZE; + len = 0; + pos = 0; + } + } } From de50e01c35fc3faca83ec3b61aeb17992ad2e98a Mon Sep 17 00:00:00 2001 From: beserge Date: Wed, 22 Jul 2020 14:03:17 -0400 Subject: [PATCH 07/10] TODO eoc --- patch/DrumLooper/DrumLooper.cpp | 31 +++++++++++++++++++------------ 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/patch/DrumLooper/DrumLooper.cpp b/patch/DrumLooper/DrumLooper.cpp index 5b6b9fd61..df43483d4 100644 --- a/patch/DrumLooper/DrumLooper.cpp +++ b/patch/DrumLooper/DrumLooper.cpp @@ -47,8 +47,6 @@ static void AudioCallback(float **in, float **out, size_t size) { UpdateControls(); - UpdateEnvs(); - for (size_t i = 0; i < size; i ++){ float outs[3]; @@ -144,7 +142,7 @@ void UpdateOled() patch.display.Fill(false); patch.display.SetCursor(0,0); - std::string str = rec ? "rec" : "play"; + std::string str = rec ? "rec" : "stop"; char* cstr = &str[0]; patch.display.WriteString(cstr, Font_7x10, true); @@ -164,6 +162,7 @@ void UpdateControls() //encoder pressed if(patch.encoder.RisingEdge()) { + //set loop len if(first && rec) { first = false; @@ -203,15 +202,6 @@ void UpdateControls() } } - //the only situation in which we don't increment is when \ - //we're waiting for the first recording - if(!(first && !rec)) - { - //array stuff - pos++; - pos %= mod; - } - //if we're making our first loop if(first && rec) { @@ -225,4 +215,21 @@ void UpdateControls() pos = 0; } } + + //we want this to happen before we update the pos + UpdateEnvs(); + + //the only situation in which we don't increment is when + //we're waiting for the first recording + if(!(first && !rec)) + { + pos++; + pos %= mod; + //EOC + if (pos == 0) + { + patch.GateOut = 1; + } + } + } From 20ffc0bc31d37efc683730b1619c05483e404a51 Mon Sep 17 00:00:00 2001 From: beserge Date: Wed, 22 Jul 2020 14:18:25 -0400 Subject: [PATCH 08/10] gpio bug --- patch/DrumLooper/DrumLooper.cpp | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/patch/DrumLooper/DrumLooper.cpp b/patch/DrumLooper/DrumLooper.cpp index df43483d4..fbc5c557c 100644 --- a/patch/DrumLooper/DrumLooper.cpp +++ b/patch/DrumLooper/DrumLooper.cpp @@ -226,10 +226,7 @@ void UpdateControls() pos++; pos %= mod; //EOC - if (pos == 0) - { - patch.GateOut = 1; - } +// dsy_gpio_write(&patch.gate_output, 1); } - + dsy_gpio_write(&patch.gate_output, 1); } From 0617cdfa0db9610cb242cb6de19c015918b845a6 Mon Sep 17 00:00:00 2001 From: beserge Date: Thu, 23 Jul 2020 13:54:53 -0400 Subject: [PATCH 09/10] final changes and makefile --- patch/DrumLooper/DrumLooper.cpp | 6 ++-- patch/DrumLooper/README.md | 53 +++++++++++++++++++++++++++++++++ 2 files changed, 56 insertions(+), 3 deletions(-) create mode 100644 patch/DrumLooper/README.md diff --git a/patch/DrumLooper/DrumLooper.cpp b/patch/DrumLooper/DrumLooper.cpp index fbc5c557c..a382d50de 100644 --- a/patch/DrumLooper/DrumLooper.cpp +++ b/patch/DrumLooper/DrumLooper.cpp @@ -142,7 +142,8 @@ void UpdateOled() patch.display.Fill(false); patch.display.SetCursor(0,0); - std::string str = rec ? "rec" : "stop"; + std::string str = rec ? "rec" : "play"; + str = (first && !rec) ? "ready" : str; char* cstr = &str[0]; patch.display.WriteString(cstr, Font_7x10, true); @@ -226,7 +227,6 @@ void UpdateControls() pos++; pos %= mod; //EOC -// dsy_gpio_write(&patch.gate_output, 1); + dsy_gpio_write(&patch.gate_output, pos == 0); } - dsy_gpio_write(&patch.gate_output, 1); } diff --git a/patch/DrumLooper/README.md b/patch/DrumLooper/README.md new file mode 100644 index 000000000..b4af65fbe --- /dev/null +++ b/patch/DrumLooper/README.md @@ -0,0 +1,53 @@ +# Description +Free looping drum machine. Record unquantized drums on three different channels. +The first recording sets the loop length. The recording can be started by either a trigger or pressing the encoder. +After the first loop, turn record back on to record more drums. There is an end of cycle output available to sync other gear. +Try different trigger inputs! Drum pads, switches, and sequencers are all good ideas! + +# Controls + +| Control | Description | Comment | +| --- | --- | --- | +| Ctrl 1 | Kick Decay Time | | +| Ctrl 2 | Kick Pitch | | +| Ctrl 3 | Snare Decay Time | | +| Ctrl 4 | Hat Decay Time | | +| Encoder Turn | Select Channel | | +| Encoder Press | Record | The first loop sets the length. | +| Encoder long press | Reset looper | | +| Gate In 1 | Trigger to record | | +| Gate Out | End of Cycle | | +| Audio Out 1 | Kick Out | | +| Audio Out 2 | Snare Out | +| Audio Out 3 | Hat Out | | +| Audio Out 4 | Mix Out | | + +# Diagram +DrumLooper.png + +# Code Snippet +```cpp +for (size_t i = 0; i < size; i ++){ + float outs[3]; + + osc.SetFreq(kckPitchEnv.Process()); + osc.SetAmp(volEnvs[0].Process()); + outs[0] = osc.Process(); + + float noise_out = noise.Process(); + outs[1] = noise_out * volEnvs[1].Process(); + + outs[2]= noise_out * volEnvs[2].Process(); + + float mix = 0.f; + float mixLevels[] = {.5f, .1f, .05f}; + + for (size_t chn = 0; chn < 3; chn++) + { + out[chn][i] = outs[chn]; + mix += mixLevels[chn] * outs[chn]; + } + + out[3][i] = mix; +} +``` \ No newline at end of file From 72921215f91c298110a1ea869071e458d09b1de4 Mon Sep 17 00:00:00 2001 From: beserge Date: Mon, 27 Jul 2020 19:02:14 -0400 Subject: [PATCH 10/10] small readme change --- patch/DrumLooper/README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/patch/DrumLooper/README.md b/patch/DrumLooper/README.md index b4af65fbe..8396fd2d6 100644 --- a/patch/DrumLooper/README.md +++ b/patch/DrumLooper/README.md @@ -1,6 +1,7 @@ # Description Free looping drum machine. Record unquantized drums on three different channels. -The first recording sets the loop length. The recording can be started by either a trigger or pressing the encoder. +The first recording sets the loop length. +The recording can be launched by a trigger or encoder press. After the first loop, turn record back on to record more drums. There is an end of cycle output available to sync other gear. Try different trigger inputs! Drum pads, switches, and sequencers are all good ideas! @@ -15,7 +16,7 @@ Try different trigger inputs! Drum pads, switches, and sequencers are all good i | Encoder Turn | Select Channel | | | Encoder Press | Record | The first loop sets the length. | | Encoder long press | Reset looper | | -| Gate In 1 | Trigger to record | | +| Gate In 1 | Trigger to record | Starts recording in ready mode | | Gate Out | End of Cycle | | | Audio Out 1 | Kick Out | | | Audio Out 2 | Snare Out |