├── .gitignore ├── Looper.cpp ├── Makefile └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | *.obj 2 | *.exe 3 | *.dll 4 | *.db 5 | *.ilk 6 | *.pdb 7 | *.tlog 8 | *.cache 9 | *.idb 10 | *.ipch 11 | Log/ 12 | Debug/ 13 | Release/ 14 | x64/ 15 | .static/ 16 | *.prefs 17 | __MACOSX/ 18 | _MACOSX/ 19 | .build/ 20 | .metadata/ 21 | .build_Debug/ 22 | *.d 23 | .build_Release/ 24 | Intermediate/ 25 | *.plist 26 | *.o 27 | *.sdf 28 | *.opendb 29 | *.l## 30 | *.l#9 31 | *.l#8 32 | *.l#7 33 | *.l#6 34 | *.l#5 35 | *.l#4 36 | *.l#3 37 | *.l#2 38 | *.l#1 39 | *.l#0 40 | *.s## 41 | *.s#9 42 | *.s#8 43 | *.s#7 44 | *.s#6 45 | *.s#5 46 | *.s#4 47 | *.s#3 48 | *.s#2 49 | *.s#1 50 | *.s#0 51 | *.b## 52 | *.b#9 53 | *.b#8 54 | *.b#7 55 | *.b#6 56 | *.b#5 57 | *.b#4 58 | *.b#3 59 | *.b#2 60 | *.b#1 61 | *.b#0 62 | *.l$$ 63 | *.l$9 64 | *.l$8 65 | *.l$7 66 | *.l$6 67 | *.l$5 68 | *.l$4 69 | *.l$3 70 | *.l$2 71 | *.l$1 72 | *.l$0 73 | *.s$$ 74 | *.s$9 75 | *.s$8 76 | *.s$7 77 | *.s$6 78 | *.s$5 79 | *.s$4 80 | *.s$3 81 | *.s$2 82 | *.s$1 83 | *.s$0 84 | *.b$$ 85 | *.b$9 86 | *.b$8 87 | *.b$7 88 | *.b$6 89 | *.b$5 90 | *.b$4 91 | *.b$3 92 | *.b$2 93 | *.b$1 94 | *.b$0 95 | Backup_of_* 96 | *.gpk 97 | *~ 98 | Intermediate-llvm/ 99 | *.exp 100 | NONE 101 | temp.txt 102 | .vs/ 103 | .vscode 104 | *.pyc 105 | windows_amd64/ 106 | *.mov 107 | *.mp4 108 | hw/wmd/CI - Cursus Iteritas/CI_manual_jp.pdf 109 | sw/tables/ 110 | Universal45/ 111 | LLVM/ 112 | ._.DS_Store 113 | .DS_Store 114 | *.lib 115 | *.bak 116 | *.suo 117 | *.user 118 | .ipynb_checkpoints/ 119 | transcode report.txt 120 | daisysp/doc/*.md 121 | libdaisy/doc/*.md 122 | core/build 123 | cube/Daisy_Seed_Rev2/ 124 | cube/Daisy_Seed_Rev3/ 125 | /**/VisualGDB 126 | /**/build 127 | bin/ 128 | 129 | -------------------------------------------------------------------------------- /Looper.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "daisysp.h" 3 | #include "daisy_patch.h" 4 | 5 | #define MAX_SIZE (48000 * 60 * 5) // 5 minutes of floats at 48 khz 6 | 7 | using namespace daisysp; 8 | using namespace daisy; 9 | 10 | static DaisyPatch patch; 11 | 12 | bool first = true; //first loop (sets length) 13 | bool rec = false; //currently recording 14 | bool play = false; //currently playing 15 | std::string modeStrs[] = {"stopped", "playing", "recording"}; 16 | 17 | int pos = 0; 18 | float DSY_SDRAM_BSS buf[MAX_SIZE]; 19 | int mod = MAX_SIZE; 20 | int len = 0; 21 | float drywet = 0; 22 | float inLevel = 0; 23 | bool res = false; 24 | 25 | // taken from the private vars in daisy_patch.h 26 | uint32_t screen_update_last_, screen_update_period_; 27 | 28 | void ResetBuffer(); 29 | void Controls(); 30 | 31 | void NextSamples(float &output, float* in, size_t i); 32 | void DisplayTwoThirdsBars(bool invert); 33 | 34 | static void AudioCallback(float **in, float **out, size_t size) 35 | { 36 | float output = 0; 37 | 38 | Controls(); 39 | 40 | for(size_t i = 0; i < size; i += 2) 41 | { 42 | NextSamples(output, in[0], i); 43 | 44 | // left and right outs ? 45 | out[0][i] = out[0][i+1] = output; 46 | } 47 | 48 | } 49 | 50 | int main(void) 51 | { 52 | // Set Screen update vars - see declaration, above 53 | screen_update_period_ = 17; // roughly 60Hz 54 | screen_update_last_ = dsy_system_getnow(); 55 | 56 | patch.Init(); 57 | ResetBuffer(); 58 | 59 | // start callback 60 | patch.StartAdc(); 61 | patch.StartAudio(AudioCallback); 62 | 63 | while(1) 64 | { 65 | //display the four control levels 66 | DisplayTwoThirdsBars(false); 67 | } 68 | } 69 | 70 | //Resets the buffer 71 | void ResetBuffer() 72 | { 73 | play = false; 74 | rec = false; 75 | first = true; 76 | pos = 0; 77 | len = 0; 78 | for(int i = 0; i < mod; i++) 79 | { 80 | buf[i] = 0; 81 | } 82 | mod = MAX_SIZE; 83 | } 84 | 85 | void UpdateButtons() 86 | { 87 | // encoder pressed - plays recording 88 | if(patch.encoder.RisingEdge()) 89 | { 90 | if(first && rec) 91 | { 92 | first = false; 93 | mod = len; 94 | len = 0; 95 | } 96 | 97 | res = true; 98 | play = true; 99 | rec = !rec; 100 | } 101 | 102 | // encoder held 103 | if(patch.encoder.TimeHeldMs() >= 1000 && res) 104 | { 105 | ResetBuffer(); 106 | res = false; 107 | } 108 | 109 | // //button2 pressed and not empty buffer 110 | // if(patch.button2.RisingEdge() && !(!rec && first)) 111 | // { 112 | // play = !play; 113 | // rec = false; 114 | // } 115 | } 116 | 117 | //Deals with analog controls 118 | void Controls() 119 | { 120 | patch.UpdateAnalogControls(); 121 | patch.DebounceControls(); 122 | 123 | drywet = patch.controls[patch.CTRL_1].Process(); 124 | inLevel = patch.controls[patch.CTRL_2].Process(); 125 | 126 | UpdateButtons(); 127 | 128 | //leds 129 | // patch.led1.Set(0, play == true, 0); 130 | // patch.led2.Set(rec == true, 0, 0); 131 | 132 | // patch.UpdateLeds(); 133 | } 134 | 135 | void WriteBuffer(float* in, size_t i) 136 | { 137 | buf[pos] = (buf[pos] + in[i]) * 0.5; 138 | if(first) 139 | { 140 | len++; 141 | } 142 | } 143 | 144 | void NextSamples(float &output, float* in, size_t i) 145 | { 146 | if (rec) 147 | { 148 | WriteBuffer(in, i); 149 | } 150 | 151 | output = buf[pos]; 152 | 153 | //automatic looptime 154 | if(len >= MAX_SIZE) 155 | { 156 | first = false; 157 | mod = MAX_SIZE; 158 | len = 0; 159 | } 160 | 161 | if(play) 162 | { 163 | pos++; 164 | pos %= mod; 165 | } 166 | 167 | if (!rec) 168 | { 169 | output = ((output * drywet) + (in[i] * (1 -drywet))) * 0.5; 170 | } 171 | } 172 | 173 | // top left is y:0 x:0 174 | // This will render the display with the controls as vertical bars 175 | void DisplayTwoThirdsBars(bool invert) 176 | { 177 | size_t maxHeight = 36; 178 | bool on, off; 179 | on = invert ? false : true; 180 | off = invert ? true : false; 181 | if(dsy_system_getnow() - screen_update_last_ > screen_update_period_) 182 | { 183 | // Graph Knobs 184 | size_t barwidth, barspacing; 185 | size_t curx, cury; 186 | barwidth = 15; 187 | barspacing = 20; 188 | patch.display.Fill(off); 189 | // Bars for all four knobs. 190 | for(size_t i = 0; i < DaisyPatch::CTRL_LAST; i++) 191 | { 192 | float v; 193 | size_t dest; 194 | curx = (barspacing * i + 1) + (barwidth * i); 195 | cury = SSD1309_HEIGHT; // start at the top! (bottom) 196 | v = patch.GetCtrlValue(static_cast(i)); 197 | dest = (v * maxHeight); 198 | for(size_t j = dest; j > 0; j--) 199 | { 200 | for(size_t k = 0; k < barwidth; k++) 201 | { 202 | patch.display.DrawPixel(curx + k, cury - j, on); 203 | } 204 | } 205 | } 206 | char* currModeStr = rec ? &modeStrs[2][0] : (play ? &modeStrs[1][0] : &modeStrs[0][0]); 207 | patch.display.SetCursor(0,0); 208 | patch.display.WriteString(currModeStr, Font_6x8, true); 209 | patch.display.Update(); 210 | screen_update_last_ = dsy_system_getnow(); 211 | } 212 | } 213 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # Project Name 2 | TARGET = ex_Looper 3 | 4 | # Sources 5 | CPP_SOURCES = Looper.cpp 6 | 7 | # Library Locations 8 | LIBDAISY_DIR = ../../libdaisy 9 | DAISYSP_DIR = ../../DaisySP 10 | 11 | # Core location, and generic makefile. 12 | SYSTEM_FILES_DIR = $(LIBDAISY_DIR)/core 13 | include $(SYSTEM_FILES_DIR)/Makefile 14 | 15 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Description 2 | Loops incoming audio at a user defined interval. 3 | Use the simple controls to record loops, play and pause them, and punch-in record over them. 4 | Loops can be very long, or very short. 5 | 6 | # Controls 7 | Press button two to record. First recording sets loop length. Automatically starts looping if 5 minute limit is hit. 8 | After first loop sound on sound recording enabled. Press button two to toggle SOS recording. Hold button two to clear loop. 9 | The red light indicates record enable. The green light indicates play enable. 10 | Press button one to pause/play loop buffer. 11 | Knob one mixes live input and loop output. Left is only live thru, right is only loop output. 12 | 13 | | Control | Description | Comment | 14 | | --- | --- | --- | 15 | | Encoder Press | Play/Record | pressing the encoder once will begin recording into the buffer | 16 | | Encoder Long Press | Clear/Stop | pressing the encoder for a second or more will clear the buffer and stop the playback | 17 | | Ctrl1 | Dry/Wet | Control the balance of the wet and dry to the output | 18 | | Audio Ins | Audio to be captured by the instrument | In 1 mono signal can be recorded or played through dry | 19 | | Audio Outs | Audio mix | Output 1 plays the mono mix of the looper and the input | 20 | 21 | # Code Snippet 22 | 23 | ```cpp 24 | void NextSamples(float &output, float* in, size_t i) 25 | { 26 | if (rec) 27 | { 28 | WriteBuffer(in, i); 29 | } 30 | 31 | output = buf[pos]; 32 | 33 | ...... 34 | 35 | if(play) 36 | { 37 | pos++; 38 | pos %= mod; 39 | } 40 | 41 | if (!rec) 42 | { 43 | output = output * drywet + in[i] * (1 -drywet); 44 | } 45 | } 46 | 47 | ``` 48 | --------------------------------------------------------------------------------