├── .gitignore ├── DaisyPatchCheatSheet.md ├── README.md ├── firmwares ├── PatchSkeleton.bin ├── README.md └── TorusPlus.bin └── patch_algodaisy ├── PatchSkeleton ├── .vscode │ ├── STM32H750x.svd │ ├── c_cpp_properties.json │ ├── launch.json │ └── tasks.json ├── Makefile ├── README.md ├── build │ └── PatchSkeleton.bin ├── patchskeleton.cpp ├── printf.c └── printf.h └── TorusPlus ├── .vscode ├── STM32H750x.svd ├── c_cpp_properties.json ├── launch.json └── tasks.json ├── Makefile ├── README.md ├── UiHardware.h ├── cliff.hpp ├── cv_scaler.cc ├── cv_scaler.h ├── drone.hpp ├── dsp ├── dsp.h ├── fm_voice.cpp ├── fm_voice.h ├── follower.h ├── fx │ ├── chorus.h │ ├── ensemble.h │ ├── fx_engine.h │ └── reverb.h ├── limiter.h ├── note_filter.h ├── onset_detector.h ├── part.cpp ├── part.h ├── patch.h ├── performance_state.h ├── plucker.h ├── resonator.cpp ├── resonator.h ├── string.cpp ├── string.h ├── string_synth_envelope.h ├── string_synth_oscillator.h ├── string_synth_part.cpp ├── string_synth_part.h ├── string_synth_voice.h └── strummer.h ├── euclide.hpp ├── resources.cpp ├── resources.h ├── sandrack.hpp ├── snap_torusplus.png └── torus.cpp /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Compiled Object files 5 | *.slo 6 | *.lo 7 | *.o 8 | *.obj 9 | 10 | # Precompiled Headers 11 | *.gch 12 | *.pch 13 | 14 | # Compiled Dynamic libraries 15 | *.so 16 | *.dylib 17 | *.dll 18 | 19 | # Fortran module files 20 | *.mod 21 | *.smod 22 | 23 | # Compiled Static libraries 24 | *.lai 25 | *.la 26 | *.a 27 | *.lib 28 | 29 | # Executables 30 | *.exe 31 | *.out 32 | *.app 33 | 34 | /**/local 35 | /**/build/*.* 36 | !*.bin 37 | .cortex* 38 | *.backup 39 | -------------------------------------------------------------------------------- /DaisyPatchCheatSheet.md: -------------------------------------------------------------------------------- 1 | # Daisy Patch Cheat Sheet 2 | 3 | Version 1.04 4 | 5 | Some information about the Daisy Patch that can be useful while becoming familiar with the Daisy platform. **It is not meant to be a substitute for official documentation** which should be read before: [Daisy Wiki documentation](https://github.com/electro-smith/DaisyWiki/wiki). 6 | 7 | Sections: 8 | 9 | - [Hardware hints](#hw) 10 | - [Software hints](#sw) 11 | - [Build/toolchain](#build) 12 | - [Links](#links) 13 | 14 | If you find errors or have suggestions contact me: development [at] algoritmarte [dot] com 15 | 16 | Latest version can be found on [github](https://github.com/algoritmarte/algodaisy). Use a Markdown viewer to open this file for better readability. 17 | 18 | 19 | # 1. Hardware hints 20 | 21 | ## 1.1. Summary (voltages) 22 | 23 | Schematics can be downloaded here: [Daisy patch schematics](https://github.com/electro-smith/Hardware/tree/master/reference/daisy_patch) *[update: the schematics are no longer available?!?]* 24 | 25 | In the follwing table `hw` is declared as 26 | 27 | ``` 28 | DaisyPatch hw; 29 | ``` 30 | 31 | 32 | | Port | Range | C++ type | C++ | 33 | |------|---------------------|------------|---| 34 | |CTRL1-4 CV IN | [+/-5V] | - | automatically divided by 5 and added to Knob value| 35 | | KNOB 1-4 | [0,1] | float | `hw.GetKnobValue(DaisyPatch::CTRL_1)` | 36 | |AUDIO1-4 IN | [+/-5V] | float |`AudioHandle::InputBuffer in[i]` in `AudioCallback` | 37 | |AUDIO1-4 OUT | [+/-5V] | float |`AudioHandle::OutputBuffer out[i]`in `AudioCallback` 38 | |CV1-2 OUT | [0V,+5V] | uint16_t (12bit) | `hw.seed.dac.WriteValue(DacHandle::Channel::ONE,value);` | 39 | |GATE1-2 IN | - | bool | `hw.gate_input[0].State(); // or ....Trig();` | 40 | |GATE OUT | [0V,+5V] | bool | `dsy_gpio_write(&hw.gate_output, trigOut);` | 41 | | ENCODER (button + increment) | -1,0,1 | int | see below 42 | | LED | - | bool | hw.seed.SetLed( ledStatus ); | 43 | 44 | ## CV Inputs and Knobs 45 | 46 | The CV\_Input voltage is divided by 5 and added to the corresponding Knob_Position; the result is then clamped to [0,1] 47 | 48 | ``` 49 | Knob_Position (0=all way down, 1=all way up) 50 | CV_Input (-5,+5 Volt) 51 | 52 | Knob_Value = Knob_Position + CV_Input / 5.0 53 | Knob_Value = clamp( Knob_Value, 0, 1 ) // restrict it to [0,1] 54 | ``` 55 | 56 | For example if a Knob is fully turned CCW (left) and a CV of 1 volt is applied then the value of the knob is 0.200 57 | 58 | **Tip**: beware that _the hardware is not that accurate_ (a ~4% error is not so rare); for some parameters/CV you may want to make sure that a knob is 0 when it is fully CCW or 1 when it is fully CW (or 0.5 if it's at 12 o'clock), so you should consider to clamp it to 0/1 if the value read is near those positions. 59 | 60 | ``` 61 | float knob1Value = hw.GetKnobValue(DaisyPatch::CTRL_1); 62 | if ( knob1Value < 0.05f ) knob1Value = 0.f; 63 | else 64 | if ( knob1Value > 0.95f ) knob1Value = 1.f; 65 | // ... or define it as an inline function 66 | ``` 67 | Also condìsider that the values read are often "floating" a little bit so be sure to add some LP filtering if the operations linked to the change of their values are computational expensive. 68 | 69 | 70 | 71 | ## CV OUT1 / OUT2 72 | 73 | The seed generates a voltage between 0 and 3V, and the circuitry converts it to a value between 0 and 5V. 74 | 75 | The DAC has a 12 bit resolution so valid values are 0 to 4095. 76 | 77 | ``` 78 | // writes 1 volt to CV1 (819.2f is 4096/5) 79 | float volt = 1; 80 | hw.seed.dac.WriteValue(DacHandle::Channel::ONE, (uint16_t)( volt * 819.2f ) ); 81 | ``` 82 | 83 | ## AUDIO 84 | 85 | The Patch uses an AK4556 3V 192kHz 24bit codec chip. The (hot) level at AUDIO inputs is lowered (+/-5V to 3V) using 2 TL074, then raised again on output (3V to +/-5V) using 2 TL074. (*NdA: is it correct ?!?*). 86 | 87 | ### !Audio inputs normalization! 88 | 89 | Beware that audio inputs are **normalized** `1->2->3->4.` If an input is NOT connected, it will receive the same audio signal of the previous one. 90 | 91 | Example 1: if you put a jack in IN1 and leave the others unplugged, then in IN2, IN3, IN4 you'll have the same signal (and in the `AudioCallback` you'll have: `in[0][N]==in[1][N]==in[2][N]==in[3][N]` ). 92 | 93 | Example 2: if you put two jacks in IN1 and IN3 and leave the others unplugged, then IN2 will be the same signal as IN1 and IN4 will be the same signal as IN3 (in the `AudioCallback`: `in[0][N]==in[1][N]` and `in[2][N]==in[3][N]`) 94 | 95 | There is no way to know from software if a jack is connected or not, so the only way to "avoid" normalization (if it is an issue for your application) is to put grounded jacks in the unused inputs. 96 | 97 | 98 | ## OLED 99 | 100 | ### Info 101 | 102 | Model: SSD130x ( 128 x 64 Dot Matrix OLED/PLED Segment/Common Driver with Controller) 103 | 104 | ``` 105 | Resolution: 128x64 pixels 106 | 107 | Characters using Font_7x10: 18x5 (Columns x Rows) 108 | Characters using Font_11x18: 11x3 109 | Characters using Font_6x8: 21x8 110 | ``` 111 | 112 | ### Drawing/printing 113 | 114 | ``` 115 | hw.display.Fill(false); 116 | hw.display.DrawLine(0, 10, 128, 10, true); 117 | //... 118 | int line = 0; 119 | hw.display.SetCursor(0, line++ * 13 ); 120 | hw.display.WriteString("Hello world", Font_7x10, true); 121 | hw.display.SetCursor(0, line++ * 13 ); 122 | hw.display.WriteString("Inverse string", Font_7x10, false); 123 | //... 124 | hw.display.Update(); 125 | ``` 126 | 127 | Available fonts: 128 | 129 | ``` 130 | libDaisy\src\util\oled_fonts.c:FontDef 131 | /** These are the different sizes of fonts 132 | (width x height in pixels per character) */ 133 | extern FontDef Font_6x8; 134 | extern FontDef Font_7x10; /**< & */ 135 | extern FontDef Font_11x18; /**< & */ 136 | extern FontDef Font_16x26; /**< & */ 137 | ``` 138 | 139 | 140 | ## LED 141 | 142 | Lil cute small led ... 143 | 144 | ``` 145 | 146 | hw.seed.SetLed( true ); 147 | hw.seed.SetLed( false ); 148 | 149 | ``` 150 | 151 | ## Memory 152 | 153 | Memory stuff defined in `libDaisy/src/sys/system.cpp` 154 | 155 | 156 | | Region | Start | Size(hex)| Size | 157 | |----------------|-----------|----------|------| 158 | | FLASH| |0x20000 |128 KB| 159 | | DTCMRAM| | |128 KB| 160 | | SRAM| | |512 KB| 161 | | RAM_D2| | |288 KB| 162 | | RAM_D3| | |64 KB | 163 | | ITCMRAM| | |64 KB | 164 | | SDRAM|0xC0000000 |0x4000000| 64 MB | 165 | | QSPIFLASH| | |8 MB | 166 | 167 | 168 | In order to alloc some space on the SDRAM use the `DSY_SDRAM_BSS` modifier: 169 | 170 | ``` 171 | // 10 second delay line on the external SDRAM 172 | #define MAX_DELAY ((size_t)(10.0f * 48000.0f)) 173 | daisysp::DelayLine DSY_SDRAM_BSS delay; 174 | 175 | // 16K buffer 176 | #define MYBUFSIZE 16384 177 | uint8_t DSY_SDRAM_BSS mybuf[MYBUFSIZE]; 178 | ``` 179 | 180 | Note that there is no `malloc/free` (on embedded systems, memory should be used very carefully); but for most purposes (*when you are sure that objects are allocated only once dynamically*), a simple trivial wrapper could do the job: 181 | 182 | ``` 183 | int memAllocated = 0; 184 | void *malloc( size_t numbytes ) { 185 | if ( memAllocated + numbytes > MYBUFSIZE ) return 0; 186 | void *res = & mybuf[ memAllocated ]; 187 | memAllocated += numbytes; 188 | return res; 189 | } 190 | void free(void *mem) { /* donothing*/ } 191 | ``` 192 | 193 | 194 | 195 | If your program takes >99.0% of the FLASH memory: you can try to add 196 | ``` 197 | OPT = -Os 198 | ``` 199 | to your Makefile anywhere above the include `$(SYSTEM_FILES_DIR)/Makefile` line to compile with optimization targeted to reduce code size. Sometimes that may break things, but sometimes you can reduce the size up to ~15 KB. 200 | 201 | 202 | # 2. Software hints 203 | 204 | 205 | ## DaisyPatch class 206 | 207 | /* These are exposed for the user to access and manipulate directly 208 | Helper functions above provide easier access to much of what they are capable of. 209 | */ 210 | DaisySeed seed; /**< Seed object */ 211 | Encoder encoder; /**< Encoder object */ 212 | AnalogControl controls[CTRL_LAST]; /**< Array of controls*/ 213 | GateIn gate_input[GATE_IN_LAST]; /**< Gate inputs */ 214 | MidiUartHandler midi; /**< Handles midi*/ 215 | OledDisplay display; /**< & */ 216 | // TODO: Add class for Gate output 217 | dsy_gpio gate_output; /**< & */ 218 | 219 | 220 | ## Skeleton program 221 | Simple skeleton program (*NOTE not fully working code - only as a reference for a common program structure*). 222 | 223 | For a fully working example download the [PatchSkeleton example](https://github.com/algoritmarte/algodaisy/patch_algodaisy/PatchSkeleton) (main source code: [patchskeleton.cpp](https://github.com/algoritmarte/algodaisy/blob/main/patch_algodaisy/PatchSkeleton/patchskeleton.cpp)) 224 | 225 | 226 | ``` 227 | using namespace daisysp; 228 | using namespace daisy; 229 | 230 | DaisyPatch hw; 231 | float sample_rate; 232 | bool led_state = false; 233 | 234 | const std::string labels[2] = { "Hello", ""World" }; 235 | 236 | int void main() { 237 | hw.Init(); 238 | //hw.SetAudioBlockSize(32); // if needed (default 48 ) 239 | float sample_rate = hw.AudioSampleRate(); 240 | 241 | hw.StartAdc(); 242 | hw.StartAudio(AudioCallback); 243 | while(1) { 244 | hw.seed.SetLed(led_state); 245 | 246 | // update screen ... 247 | UpdateScreen(); 248 | 249 | // Wait 50ms 250 | DaisyPatch::DelayMs(50); // limit to 20Hz ?!? 251 | } 252 | } 253 | 254 | void AudioCallback(AudioHandle::InputBuffer in, 255 | AudioHandle::OutputBuffer out, 256 | size_t size) 257 | { 258 | UpdateControls(); // process digital/analog values 259 | float myin0,myin1,myin2,myin3; 260 | for(size_t i = 0; i < size; i++) { 261 | out[0][i] = in[0][i]; 262 | out[1][i] = in[1][i]; 263 | out[2][i] = in[2][i]; 264 | out[3][i] = in[3][i]; 265 | } 266 | } 267 | 268 | void UpdateControls() 269 | { 270 | hw.ProcessAllControls(); 271 | // shortcut for: hw.ProcessDigitalControls(); hw.ProcessAnalogControls(); 272 | 273 | //read knob values 274 | 275 | float ctrl[4]; 276 | for(int i = 0; i < 4; i++) 277 | { 278 | ctrl[i] = hw.GetKnobValue((DaisyPatch::Ctrl)i); 279 | } 280 | 281 | //process encoder 282 | bool encoder_helddown = hw.encoder.Pressed(); 283 | bool encoder_pressed = hw.encoder.RisingEdge(); 284 | int encoder_inc = hw.encoder.Increment(); 285 | bool encoder_released = hw.encoder.FallingEdge(); 286 | float encoder_timeheld = hw.encoder.TimeHeldMs(); 287 | 288 | bool gate1_state = hw.gate_input[0].State(); 289 | bool gate1_trig = hw.gate_input[0].Trig(); 290 | bool gate2_state = hw.gate_input[1].State(); 291 | bool gate2_trig = hw.gate_input[1].Trig(); 292 | 293 | 294 | float cv1_out = 2; // 2 volts 295 | hw.seed.dac.WriteValue(DacHandle::Channel::ONE, (uint16_t)( cv1_out * 819.2f ) ); 296 | 297 | // write gate out 298 | dsy_gpio_write(&hw.gate_output, encoder_press ); 299 | } 300 | 301 | void UpdateScreen() { 302 | hw.display.Fill(false); 303 | // hw.display.DrawLine(0, 10, 128, 10, true); 304 | int line = 0; 305 | hw.display.SetCursor(0, line++ * 13 ); 306 | hw.display.WriteString(labels[0], Font_7x10, true); 307 | hw.display.Update(); 308 | } 309 | ``` 310 | 311 | ## Timing 312 | 313 | ``` 314 | DaisyPatch::DelayMs(uint32_t msec); // delay del milliseconds 315 | DaisyPatch::DelayUs(uint32_t usec); // delay del microseconds 316 | 317 | # example: 318 | hw.DelayMs(50); 319 | 320 | uint32_t hw.seed.system.GetNow(); // current elapsed milliseconds 321 | uint32_t hw.seed.system.GetUs(); // current elapsed microseconds 322 | ``` 323 | ## Performance meter 324 | 325 | Include the file: 326 | 327 | ``` 328 | #include "util/CpuLoadMeter.h" 329 | ``` 330 | 331 | Declare in the global section 332 | 333 | ``` 334 | CpuLoadMeter meter; 335 | ``` 336 | 337 | Initialize it in the main section: 338 | 339 | ``` 340 | 341 | int main(void) { 342 | // ... 343 | meter.Init( hw.AudioSampleRate(), hw.AudioBlockSize() ); 344 | hw.StartAdc(); 345 | hw.StartAudio( AudioCallback ); 346 | // ... 347 | } 348 | ``` 349 | 350 | In the audio callback routine at the beginning / end call `OnBlockStart()` / `OnBlockEnd()` 351 | 352 | ``` 353 | void AudioCallback(AudioHandle::InputBuffer in, 354 | AudioHandle::OutputBuffer out, 355 | size_t size) 356 | { 357 | meter.OnBlockStart(); 358 | 359 | // ... 360 | 361 | meter.OnBlockEnd(); 362 | } 363 | ``` 364 | 365 | Then you can call 366 | `meter.GetMaxCpuLoad()` `meter.GetAvgCpuLoad()` `meter.GetMinCpuLoad()` 367 | to get the Max / Avg / Min average CPU load in the range 0..1; for example to print them on the screen: 368 | 369 | 370 | ``` 371 | int main(void) { 372 | // ... 373 | // main loop 374 | while (1) { 375 | char buf[18] = {}; 376 | hw.display.Fill(false); 377 | int line = 0; 378 | 379 | hw.display.SetCursor(0, line++ * 13); 380 | float2str(buf, 0, 8, meter.GetMaxCpuLoad(), 3); 381 | hw.display.WriteString(buf, Font_7x10, true); 382 | 383 | hw.display.SetCursor(0, line++ * 13); 384 | float2str(buf, 0, 8, meter.GetAvgCpuLoad(), 3); 385 | hw.display.WriteString(buf, Font_7x10, true); 386 | 387 | hw.display.Update(); 388 | hw.DelayMs(50); 389 | } 390 | } 391 | ``` 392 | 393 | ## MIDI In/Out 394 | In order to use MIDI you must call `hw.midi.StartReceive()` and regularly check if new events are received with `hw.midi.Listen()` and `hw.midi.HasEvents()`. You should also process the events received with `hw.midi.PopEvent()` in order to prevent the queue from filling up. 395 | 396 | A typical program skeleton is: 397 | 398 | ``` 399 | int main(void) 400 | { 401 | hw.Init(); 402 | hw.midi.StartReceive(); // start MIDI receieve 403 | hw.StartAdc(); 404 | hw.StartAudio(AudioCallback); 405 | while(1) { 406 | // fetch data from uart 407 | hw.midi.Listen(); 408 | // Handle MIDI Events 409 | while(hw.midi.HasEvents()) 410 | { 411 | HandleMidiMessage(hw.midi.PopEvent()); 412 | } 413 | } 414 | } 415 | // Typical Switch case for Message Type. 416 | // implements a MIIMALISTIC monophonic MIDI to CV/gate converter 417 | void HandleMidiMessage(MidiEvent m) 418 | { 419 | switch(m.type) 420 | { 421 | case NoteOn: 422 | { 423 | NoteOnEvent p = m.AsNoteOn(); 424 | // it's quite common to use a NoteOn event with velocity=0 425 | // as an alternative to NoteOff 426 | if(m.data[1] != 0) 427 | { 428 | p = m.AsNoteOn(); 429 | int channel = p.channel; 430 | int vel = p.velocity; 431 | int midinote = p.note; 432 | 433 | midinote -= 36; // C2 as lowest note 434 | if ( midinote < 0) midinote = 0; 435 | 436 | // write 1V/oct pitch on CV OUT 1 437 | float v = 1.0f * midinote / 12.0f; 438 | hw.seed.dac.WriteValue(DacHandle::Channel::ONE, 439 | (uint16_t)( v * 819.2f ) ); 440 | dsy_gpio_write(&hw.gate_output, true); 441 | } else 442 | { // m.data[1] == 0 is equivalent to a note off 443 | dsy_gpio_write(&hw.gate_output, false); 444 | } 445 | } 446 | break; 447 | case NoteOff: 448 | { 449 | NoteOffEvent p = m.AsNoteOff(); 450 | dsy_gpio_write(&hw.gate_output, false); 451 | } 452 | break; 453 | case ControlChange: 454 | { 455 | ControlChangeEvent p = m.AsControlChange(); 456 | switch(p.control_number) 457 | { 458 | case 1: 459 | // CC 1 handling ... 460 | break; 461 | case 2: 462 | // CC 1 handling ... 463 | break; 464 | default: break; 465 | } 466 | break; 467 | } 468 | default: break; 469 | } 470 | } 471 | 472 | ``` 473 | 474 | If you want to send a MIDI message use: 475 | 476 | 477 | ``` 478 | hw.midi.SendMessage (uint8_t *bytes, size_t size ) 479 | ``` 480 | 481 | For further information see: 482 | 483 | - [MidiHandler class reference](https://electro-smith.github.io/libDaisy/classdaisy_1_1_midi_handler.html) 484 | - [MidiEvent class reference](https://electro-smith.github.io/libDaisy/structdaisy_1_1_midi_event.html) 485 | 486 | 487 | ## Formatting numbers 488 | 489 | `sprintf`, `snprintf`, ... (``) don't work with floats; if you want a lightweight replacement use the great Marco Paland's routines: 490 | 491 | [https://github.com/mpaland/printf](https://github.com/mpaland/printf) 492 | 493 | 494 | 495 | 496 | 497 | 498 | # Building / toolchain 499 | 500 | The full toolchain can be downloaded here: [Daisy Toolchain](https://github.com/electro-smith/DaisyToolchain) (follow the instructions on the page) 501 | 502 | Note if you already have a MSYS2/MINGW64 installation you can simply add the bin directory of the toolchain to the PATH: 503 | 504 | ``` 505 | PATH=$PATH:/D/SDK/DaisyToolchain-0.3.3/windows/bin 506 | export PATH 507 | ``` 508 | 509 | To manually download/install the packages: 510 | 511 | ``` 512 | GNU Make: 4.3 513 | openocd: 0.11.0 514 | dfu-util: 0.10 515 | clang-format: 10.0.0 516 | arm toolchain: 10-2020-q4-major 517 | ``` 518 | 519 | Use the package manager: 520 | 521 | ``` 522 | pacman -S mingw-w64-x86_64-dfu-util 523 | pacman -S mingw-w64-x86_64-openocd 524 | .... 525 | ``` 526 | 527 | 528 | VisualCode: be sure that in the project directory there is a `.vscode` subfolder 529 | with the files: 530 | 531 | ``` 532 | c_cpp_properties.json 533 | launch.json 534 | STM32H750x.svd 535 | tasks.json 536 | ``` 537 | 538 | ## Creating a new program 539 | 540 | In order to create a new program you can use the helper.py program in the DaisyExamples root directory: 541 | 542 | ``` 543 | ./helper.py create patch/MyProgram --board patch 544 | ``` 545 | 546 | 547 | 548 | 549 | # Useful links documentation/stuff 550 | 551 | 552 | - [libDaisy official documentation](https://electro-smith.github.io/libDaisy/index.html) 553 | 554 | - [Main Daisy Patch control code daisy_patch.cpp](https://github.com/electro-smith/libDaisy/blob/master/src/daisy_patch.cpp) 555 | 556 | - [Daisy patch schematics](https://github.com/electro-smith/Hardware/tree/master/reference/daisy_patch) *[update: the schematics are no longer available?!?]* 557 | 558 | # To-do 559 | 560 | Coming soon ... 561 | 562 | * add a link to a fully working test patch 563 | * details on how to measure CPU workload 564 | * considerations about memory (where to alloc buffers) 565 | * MIDI read/write 566 | * load/save configuration parameters 567 | * how-to access SD-card / file system 568 | * menu(s) 569 | * daisy library 570 | * hardware abstraction -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Algo(ritmarte)Daisy 2 | 3 | This repository contains (will contain) some projects/experiments made on the Daisy Patch programmable Eurorack module. 4 | 5 | If you're a developer and want to start making your own firmwares, then you can find complete information and examples on the [Daisy Wiki page](https://github.com/electro-smith/DaisyWiki/wiki). 6 | 7 | ## Daisy Patch Cheat Sheet 8 | 9 | I gathered some key information, that can be used while becoming familiar with the platform, on a cheat-sheet: 10 | 11 | Open or download it here 12 | 13 | * [Daisy Patch Cheat Sheet](./DaisyPatchCheatSheet.md) 14 | 15 | *Note: if you find errors or have suggestions, feel free to contact me: development [at] algoritmarte [dot] com* 16 | 17 | ## PatchSkeleton 18 | 19 | A simple skeleton program that demonstrates how to read/write the hardware. 20 | 21 | * [PatchSkeleton source code](https://github.com/algoritmarte/algodaisy/tree/main/patch_algodaisy/PatchSkeleton) 22 | 23 | Firmware downloadable here: [PatchSkeleton.bin](firmwares/PatchSkeleton.bin) 24 | 25 | ## Torus Plus 26 | 27 | The Torus Rings clone with some new extra features: delay, embedded random rythm/note generator,.... 28 | 29 | * [TorusPlus source code](https://github.com/algoritmarte/algodaisy/tree/main/patch_algodaisy/TorusPlus) 30 | 31 | Firmware downloadable here: [TorusPlus.bin](firmwares/TorusPlus.bin) 32 | 33 | ## History 34 | 35 | ``` 36 | 2022-01-22: TorusPlus 37 | 2022-01-18: first commit, cheat sheet and patchskeleton 38 | ``` -------------------------------------------------------------------------------- /firmwares/PatchSkeleton.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/algoritmarte/algodaisy/f85012db5c5a2a63d13f843532a0bedb7cb58540/firmwares/PatchSkeleton.bin -------------------------------------------------------------------------------- /firmwares/README.md: -------------------------------------------------------------------------------- 1 | # Compiled firmwares for the Daisy Patch 2 | 3 | 4 | You can use the [online web progrmmer](https://electro-smith.github.io/Programmer/) to upload the firmware. 5 | 6 | 7 | 8 | | File | Version | Platform |Date | Doc | 9 | |---------------|----------|-------------|------------|------| 10 | | [PatchSkeleton.bin](PatchSkeleton.bin) | v1.0 | Daisy Patch | 2022-01-19 | [PatchSkeleton](../patch_algodaisy/PatchSkeleton/README.md) | 11 | | [TorusPlus.bin](TorusPlus.bin) | v1.0 | Daisy Patch | 2022-01-20 | [TorusPlus](../patch_algodaisy/TorusPlus/README.md) | -------------------------------------------------------------------------------- /firmwares/TorusPlus.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/algoritmarte/algodaisy/f85012db5c5a2a63d13f843532a0bedb7cb58540/firmwares/TorusPlus.bin -------------------------------------------------------------------------------- /patch_algodaisy/PatchSkeleton/.vscode/c_cpp_properties.json: -------------------------------------------------------------------------------- 1 | { 2 | "configurations": [ 3 | { 4 | "cStandard": "c11", 5 | "compilerPath": "arm-none-eabi-g++.exe", 6 | "cppStandard": "c++17", 7 | "defines": [ 8 | "_DEBUG", 9 | "UNICODE", 10 | "_UNICODE" 11 | ], 12 | "includePath": [ 13 | "${workspaceFolder}/**", 14 | "../../libdaisy//**", 15 | "../../DaisySP//**" 16 | ], 17 | "name": "Win32", 18 | "windowsSdkVersion": "10.0.17763.0" 19 | }, 20 | { 21 | "cStandard": "c11", 22 | "compilerPath": "/usr/local/bin/arm-none-eabi-g++", 23 | "cppStandard": "c++17", 24 | "defines": [ 25 | "_DEBUG", 26 | "UNICODE", 27 | "_UNICODE" 28 | ], 29 | "includePath": [ 30 | "${workspaceFolder}/**", 31 | "../../libdaisy//**", 32 | "../../DaisySP//**" 33 | ], 34 | "name": "macOS" 35 | } 36 | ], 37 | "version": 4 38 | } 39 | -------------------------------------------------------------------------------- /patch_algodaisy/PatchSkeleton/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "configurations": [ 3 | { 4 | "configFiles": [ 5 | "interface/stlink.cfg", 6 | "target/stm32h7x.cfg" 7 | ], 8 | "cwd": "${workspaceFolder}", 9 | "debuggerArgs": [ 10 | "-d", 11 | "${workspaceRoot}" 12 | ], 13 | "executable": "${workspaceRoot}/build/PatchSkeleton.elf", 14 | "interface": "swd", 15 | "name": "Cortex Debug", 16 | "openOCDLaunchCommands": [ 17 | "init", 18 | "reset init" 19 | ], 20 | "preLaunchTask": "build_all_debug", 21 | "preRestartCommands": [ 22 | "load", 23 | "enable breakpoint", 24 | "monitor reset" 25 | ], 26 | "request": "launch", 27 | "runToMain": true, 28 | "servertype": "openocd", 29 | "showDevDebugOutput": true, 30 | "svdFile": "${workspaceRoot}/.vscode/STM32H750x.svd", 31 | "type": "cortex-debug" 32 | } 33 | ], 34 | "version": "0.2.0" 35 | } 36 | -------------------------------------------------------------------------------- /patch_algodaisy/PatchSkeleton/.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "tasks": [ 3 | { 4 | "command": "make clean; make", 5 | "group": { 6 | "isDefault": true, 7 | "kind": "build" 8 | }, 9 | "label": "build", 10 | "options": { 11 | "cwd": "${workspaceFolder}" 12 | }, 13 | "problemMatcher": [ 14 | "$gcc" 15 | ], 16 | "type": "shell" 17 | }, 18 | { 19 | "command": "make clean; make; make program", 20 | "label": "build_and_program", 21 | "options": { 22 | "cwd": "${workspaceFolder}" 23 | }, 24 | "problemMatcher": [ 25 | "$gcc" 26 | ], 27 | "type": "shell" 28 | }, 29 | { 30 | "command": "make clean; make; make program-dfu", 31 | "label": "build_and_program_dfu", 32 | "options": { 33 | "cwd": "${workspaceFolder}" 34 | }, 35 | "problemMatcher": [ 36 | "$gcc" 37 | ], 38 | "type": "shell" 39 | }, 40 | { 41 | "command": "make clean;make", 42 | "dependsOn": [ 43 | "build_libdaisy", 44 | "build_daisysp" 45 | ], 46 | "label": "build_all", 47 | "options": { 48 | "cwd": "${workspaceFolder}" 49 | }, 50 | "problemMatcher": [ 51 | "$gcc" 52 | ], 53 | "type": "shell" 54 | }, 55 | { 56 | "command": "make clean;DEBUG=1 make", 57 | "dependsOn": [ 58 | "build_libdaisy", 59 | "build_daisysp" 60 | ], 61 | "label": "build_all_debug", 62 | "options": { 63 | "cwd": "${workspaceFolder}" 64 | }, 65 | "problemMatcher": [ 66 | "$gcc" 67 | ], 68 | "type": "shell" 69 | }, 70 | { 71 | "command": "make program-dfu", 72 | "label": "program-dfu", 73 | "problemMatcher": [], 74 | "type": "shell" 75 | }, 76 | { 77 | "command": "make program", 78 | "label": "program", 79 | "problemMatcher": [], 80 | "type": "shell" 81 | }, 82 | { 83 | "command": "make", 84 | "label": "build_libdaisy", 85 | "options": { 86 | "cwd": "../../libdaisy/" 87 | }, 88 | "problemMatcher": [ 89 | "$gcc" 90 | ], 91 | "type": "shell" 92 | }, 93 | { 94 | "command": "make", 95 | "label": "build_daisysp", 96 | "options": { 97 | "cwd": "../../DaisySP/" 98 | }, 99 | "problemMatcher": [ 100 | "$gcc" 101 | ], 102 | "type": "shell" 103 | } 104 | ], 105 | "version": "2.0.0" 106 | } 107 | -------------------------------------------------------------------------------- /patch_algodaisy/PatchSkeleton/Makefile: -------------------------------------------------------------------------------- 1 | # Project Name 2 | TARGET = PatchSkeleton 3 | 4 | # Sources 5 | CPP_SOURCES = patchskeleton.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 | -------------------------------------------------------------------------------- /patch_algodaisy/PatchSkeleton/README.md: -------------------------------------------------------------------------------- 1 | # PatchSkeleton 2 | 3 | A simple skeleton program that shows some basic methods to control the Daisy Patch. 4 | 5 | By Algoritmarte 6 | 7 | Web: [https://www.algoritmarte.com](https://www.algoritmarte.com) 8 | 9 | GitHub: [https://github.com/algoritmarte/algodaisy](https://github.com/algoritmarte/algodaisy) 10 | 11 | ## Description 12 | 13 | The patch is for demonstration purposes only: 14 | 15 | 16 | | Port | Description | 17 | |------------------|-------------| 18 | |AUDIO1 OUT | Simple scale using a sine wave | 19 | |AUDIO2 OUT | White noise | 20 | |AUDIO3 OUT | Mix AUDIO1 IN + AUDIO2 IN | 21 | |AUDIO4 OUT | Mix AUDIO3 IN + AUDIO4 IN | 22 | |GATE1 IN | a pulse on GATE1 IN inverts the outputs of GATE OUT and led status | 23 | |GATE2 IN | unused | 24 | |CV1 OUT | the 1V/OCT pitch of the current note | 25 | |CV2 OUT | unused | 26 | |GATE OUT | simple on/off gate triggered by GATE1 IN or encoder button | 27 | |LED | same state of GATE OUT | 28 | |Encoder press | same as a trigger on GATE1 IN | 29 | |Encoder increment | used to change the value `encoder_track` on the screen | 30 | |KNOBs and CVs | their current (raw) values are displayed on the screen | 31 | 32 | -------------------------------------------------------------------------------- /patch_algodaisy/PatchSkeleton/build/PatchSkeleton.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/algoritmarte/algodaisy/f85012db5c5a2a63d13f843532a0bedb7cb58540/patch_algodaisy/PatchSkeleton/build/PatchSkeleton.bin -------------------------------------------------------------------------------- /patch_algodaisy/PatchSkeleton/patchskeleton.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Daisy Patch skeleton program 3 | https://github.com/algoritmarte/algodaisy 4 | */ 5 | #include "daisy_patch.h" 6 | #include "daisysp.h" 7 | 8 | using namespace daisy; 9 | using namespace daisysp; 10 | 11 | namespace patchutil { 12 | #include "printf.h" 13 | #include "printf.c" 14 | } 15 | 16 | #define OLEDCOLS 18 17 | 18 | DaisyPatch hw; 19 | bool gateOut1 = false; 20 | float cv1_volt = 0; 21 | float cv2_volt = 0; 22 | bool encoder_held; 23 | bool encoder_press; 24 | int encoder_inc; 25 | float last_cv1_volt = -1; 26 | float last_cv2_volt = -1; 27 | 28 | float knobValues[4] = {}; 29 | 30 | float audioSampleRate; 31 | float cvSampleRate; 32 | 33 | const int notes[8] = { 0,2,4,5,7,9,11,12}; 34 | int inote = 0; 35 | float freq = 440.0; 36 | int notetick = 0; 37 | int encoder_track = 0; 38 | 39 | WhiteNoise noise = {}; 40 | Oscillator osc = {}; 41 | 42 | void UpdateControls(); 43 | void UpdateScreen(); 44 | 45 | void AudioCallback(AudioHandle::InputBuffer in, AudioHandle::OutputBuffer out, size_t size) 46 | { 47 | UpdateControls(); 48 | 49 | // prepare freq for AUDIO1 50 | float midinote = 60 + notes[ inote ]; 51 | float freq = 440.0f * pow( 2, (midinote - 69) / 12.0f ); 52 | osc.SetFreq( freq ); 53 | osc.SetAmp( 0.2 ); // keep it on LINE level 54 | 55 | for (size_t i = 0; i < size; i++) 56 | { 57 | // sine wave on AUDIO1 58 | out[0][i] = osc.Process(); 59 | 60 | // white noise on AUDIO2 61 | out[1][i] = noise.Process() * 0.2f; // keep it on LINE levels 62 | 63 | out[2][i] = (in[0][i] + in[1][i]) * 0.5f; // mix IN1 and IN2 64 | out[3][i] = (in[2][i] + in[3][i]) * 0.5f; // mix IN3 and IN4 65 | } 66 | } 67 | 68 | int main(void) 69 | { 70 | hw.Init(); 71 | //hw.SetAudioBlockSize(32); // if needed (default 48 ) 72 | 73 | audioSampleRate = hw.AudioSampleRate(); 74 | cvSampleRate = audioSampleRate / hw.AudioBlockSize(); 75 | 76 | noise.Init(); 77 | osc.Init(audioSampleRate); 78 | 79 | hw.StartAdc(); 80 | hw.StartAudio(AudioCallback); 81 | while(1) { 82 | UpdateScreen(); 83 | hw.DelayMs(50); // 20 Hz refresh 84 | } 85 | } 86 | 87 | void UpdateControls() { 88 | // needed for properly reading hardware 89 | hw.ProcessAllControls(); // shortcut for hw.ProcessAnalogControls(); hw.ProcessDigitalControls(); 90 | 91 | encoder_held = hw.encoder.Pressed(); 92 | encoder_press = hw.encoder.RisingEdge(); 93 | encoder_inc = hw.encoder.Increment(); 94 | encoder_track += encoder_inc; 95 | 96 | // on encoder button press or GATE_IN_1 trig change the state of the GATE_OUTPUT and LED 97 | if( encoder_press || hw.gate_input[DaisyPatch::GATE_IN_1].Trig()) { 98 | gateOut1 = !gateOut1; 99 | dsy_gpio_write(&hw.gate_output, gateOut1 ); 100 | hw.seed.SetLed( gateOut1 ); 101 | } 102 | 103 | knobValues[0] = hw.GetKnobValue( DaisyPatch::CTRL_1 ); 104 | knobValues[1] = hw.GetKnobValue( DaisyPatch::CTRL_2 ); 105 | knobValues[2] = hw.GetKnobValue( DaisyPatch::CTRL_3 ); 106 | knobValues[3] = hw.GetKnobValue( DaisyPatch::CTRL_4 ); 107 | 108 | // every sec change the note on OUT1 109 | float elapsed = notetick++ / cvSampleRate; // note that UpdateControls is called at cvSampleRate 110 | if (elapsed > 1) 111 | { 112 | notetick = 0; 113 | inote = (inote + 1 ) % 8; 114 | } 115 | 116 | float note = notes[ inote ]; 117 | cv1_volt = note / 12.0f; // standard 1 Volt / Octave pitch 118 | 119 | if ( last_cv1_volt != cv1_volt ) { 120 | last_cv1_volt = cv1_volt; 121 | hw.seed.dac.WriteValue(DacHandle::Channel::ONE, 122 | (uint16_t)round(cv1_volt * 819.2f)); 123 | } 124 | if ( last_cv2_volt != cv2_volt ) { 125 | last_cv2_volt = cv2_volt; 126 | hw.seed.dac.WriteValue(DacHandle::Channel::TWO, 127 | (uint16_t)round(cv1_volt * 819.2f)); 128 | } 129 | } 130 | 131 | void UpdateScreen() { 132 | char s[OLEDCOLS] = {}; 133 | hw.display.Fill(false); 134 | hw.display.DrawLine(0, 10, 128, 10, true); 135 | 136 | int cursorpos = 0; 137 | hw.display.SetCursor(0, cursorpos++ * 13 ); 138 | patchutil::snprintf( s, OLEDCOLS, "K1 %.3f K2 %.3f", knobValues[0], knobValues[1] ); 139 | hw.display.WriteString( s, Font_7x10, true); 140 | 141 | hw.display.SetCursor(0, cursorpos++ * 13 ); 142 | patchutil::snprintf( s, OLEDCOLS, "K3 %.3f K4 %.3f", knobValues[2], knobValues[3] ); 143 | hw.display.WriteString( s, Font_7x10, true); 144 | 145 | hw.display.SetCursor(0, cursorpos++ * 13); 146 | patchutil::snprintf( s, OLEDCOLS, "Enc.track: %d", encoder_track ); 147 | hw.display.WriteString(s, Font_7x10, true); 148 | 149 | hw.display.SetCursor(0, cursorpos++ * 13); 150 | hw.display.WriteString("0123456789ABCDEFGH", Font_7x10, true); 151 | hw.display.SetCursor(0, cursorpos++ * 13); 152 | hw.display.WriteString(" AlgoritmArte.com ", Font_7x10, true); 153 | 154 | hw.display.Update(); 155 | } 156 | -------------------------------------------------------------------------------- /patch_algodaisy/PatchSkeleton/printf.h: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////////// 2 | // \author (c) Marco Paland (info@paland.com) 3 | // 2014-2019, PALANDesign Hannover, Germany 4 | // 5 | // \license The MIT License (MIT) 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be included in 15 | // all copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | // THE SOFTWARE. 24 | // 25 | // \brief Tiny printf, sprintf and snprintf implementation, optimized for speed on 26 | // embedded systems with a very limited resources. 27 | // Use this instead of bloated standard/newlib printf. 28 | // These routines are thread safe and reentrant. 29 | // 30 | /////////////////////////////////////////////////////////////////////////////// 31 | 32 | #ifndef _PRINTF_H_ 33 | #define _PRINTF_H_ 34 | 35 | #include 36 | #include 37 | 38 | 39 | #ifdef __cplusplus 40 | extern "C" { 41 | #endif 42 | 43 | 44 | /** 45 | * Output a character to a custom device like UART, used by the printf() function 46 | * This function is declared here only. You have to write your custom implementation somewhere 47 | * \param character Character to output 48 | */ 49 | void _putchar(char character); 50 | 51 | 52 | /** 53 | * Tiny printf implementation 54 | * You have to implement _putchar if you use printf() 55 | * To avoid conflicts with the regular printf() API it is overridden by macro defines 56 | * and internal underscore-appended functions like printf_() are used 57 | * \param format A string that specifies the format of the output 58 | * \return The number of characters that are written into the array, not counting the terminating null character 59 | */ 60 | #define printf printf_ 61 | int printf_(const char* format, ...); 62 | 63 | 64 | /** 65 | * Tiny sprintf implementation 66 | * Due to security reasons (buffer overflow) YOU SHOULD CONSIDER USING (V)SNPRINTF INSTEAD! 67 | * \param buffer A pointer to the buffer where to store the formatted string. MUST be big enough to store the output! 68 | * \param format A string that specifies the format of the output 69 | * \return The number of characters that are WRITTEN into the buffer, not counting the terminating null character 70 | */ 71 | #define sprintf sprintf_ 72 | int sprintf_(char* buffer, const char* format, ...); 73 | 74 | 75 | /** 76 | * Tiny snprintf/vsnprintf implementation 77 | * \param buffer A pointer to the buffer where to store the formatted string 78 | * \param count The maximum number of characters to store in the buffer, including a terminating null character 79 | * \param format A string that specifies the format of the output 80 | * \param va A value identifying a variable arguments list 81 | * \return The number of characters that COULD have been written into the buffer, not counting the terminating 82 | * null character. A value equal or larger than count indicates truncation. Only when the returned value 83 | * is non-negative and less than count, the string has been completely written. 84 | */ 85 | #define snprintf snprintf_ 86 | #define vsnprintf vsnprintf_ 87 | int snprintf_(char* buffer, size_t count, const char* format, ...); 88 | int vsnprintf_(char* buffer, size_t count, const char* format, va_list va); 89 | 90 | 91 | /** 92 | * Tiny vprintf implementation 93 | * \param format A string that specifies the format of the output 94 | * \param va A value identifying a variable arguments list 95 | * \return The number of characters that are WRITTEN into the buffer, not counting the terminating null character 96 | */ 97 | #define vprintf vprintf_ 98 | int vprintf_(const char* format, va_list va); 99 | 100 | 101 | /** 102 | * printf with output function 103 | * You may use this as dynamic alternative to printf() with its fixed _putchar() output 104 | * \param out An output function which takes one character and an argument pointer 105 | * \param arg An argument pointer for user data passed to output function 106 | * \param format A string that specifies the format of the output 107 | * \return The number of characters that are sent to the output function, not counting the terminating null character 108 | */ 109 | int fctprintf(void (*out)(char character, void* arg), void* arg, const char* format, ...); 110 | 111 | 112 | #ifdef __cplusplus 113 | } 114 | #endif 115 | 116 | 117 | #endif // _PRINTF_H_ 118 | -------------------------------------------------------------------------------- /patch_algodaisy/TorusPlus/.vscode/c_cpp_properties.json: -------------------------------------------------------------------------------- 1 | { 2 | "configurations": [ 3 | { 4 | "cStandard": "c11", 5 | "compilerPath": "arm-none-eabi-g++.exe", 6 | "cppStandard": "c++17", 7 | "defines": [ 8 | "_DEBUG", 9 | "UNICODE", 10 | "_UNICODE" 11 | ], 12 | "includePath": [ 13 | "${workspaceFolder}/**", 14 | "${workspaceFolder}/../../libdaisy/**", 15 | "${workspaceFolder}/../../DaisySP/**" 16 | ], 17 | "name": "Win32", 18 | "windowsSdkVersion": "10.0.17763.0" 19 | }, 20 | { 21 | "cStandard": "c11", 22 | "compilerPath": "/usr/local/bin/arm-none-eabi-g++", 23 | "cppStandard": "c++17", 24 | "defines": [ 25 | "_DEBUG", 26 | "UNICODE", 27 | "_UNICODE" 28 | ], 29 | "includePath": [ 30 | "${workspaceFolder}/**", 31 | "${workspaceFolder}/../../libDaisy/**", 32 | "${workspaceFolder}/../../DaisySP/**" 33 | ], 34 | "name": "macOS" 35 | } 36 | ], 37 | "version": 4 38 | } 39 | -------------------------------------------------------------------------------- /patch_algodaisy/TorusPlus/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "configurations": [ 3 | { 4 | "configFiles": [ 5 | "interface/stlink.cfg", 6 | "target/stm32h7x.cfg" 7 | ], 8 | "cwd": "${workspaceFolder}", 9 | "debuggerArgs": [ 10 | "-d", 11 | "${workspaceRoot}" 12 | ], 13 | "executable": "${workspaceRoot}/build/Nimbus.elf", 14 | "interface": "swd", 15 | "name": "Cortex Debug", 16 | "openOCDLaunchCommands": [ 17 | "init", 18 | "reset init" 19 | ], 20 | "preLaunchTask": "build_all_debug", 21 | "preRestartCommands": [ 22 | "load", 23 | "enable breakpoint", 24 | "monitor reset" 25 | ], 26 | "request": "launch", 27 | "runToMain": true, 28 | "servertype": "openocd", 29 | "showDevDebugOutput": true, 30 | "svdFile": "${workspaceRoot}/.vscode/STM32H750x.svd", 31 | "type": "cortex-debug" 32 | } 33 | ], 34 | "version": "0.2.0" 35 | } 36 | -------------------------------------------------------------------------------- /patch_algodaisy/TorusPlus/.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "tasks": [ 3 | { 4 | "command": "make clean; make", 5 | "group": { 6 | "isDefault": true, 7 | "kind": "build" 8 | }, 9 | "label": "build", 10 | "options": { 11 | "cwd": "${workspaceFolder}" 12 | }, 13 | "problemMatcher": [ 14 | "$gcc" 15 | ], 16 | "type": "shell" 17 | }, 18 | { 19 | "command": "make clean; make; make program", 20 | "label": "build_and_program", 21 | "options": { 22 | "cwd": "${workspaceFolder}" 23 | }, 24 | "problemMatcher": [ 25 | "$gcc" 26 | ], 27 | "type": "shell" 28 | }, 29 | { 30 | "command": "make clean; make; make program-dfu", 31 | "label": "build_and_program_dfu", 32 | "options": { 33 | "cwd": "${workspaceFolder}" 34 | }, 35 | "problemMatcher": [ 36 | "$gcc" 37 | ], 38 | "type": "shell" 39 | }, 40 | { 41 | "command": "make clean;make", 42 | "dependsOn": [ 43 | "build_libdaisy", 44 | "build_daisysp" 45 | ], 46 | "label": "build_all", 47 | "options": { 48 | "cwd": "${workspaceFolder}" 49 | }, 50 | "problemMatcher": [ 51 | "$gcc" 52 | ], 53 | "type": "shell" 54 | }, 55 | { 56 | "command": "make clean;DEBUG=1 make", 57 | "dependsOn": [ 58 | "build_libdaisy", 59 | "build_daisysp" 60 | ], 61 | "label": "build_all_debug", 62 | "options": { 63 | "cwd": "${workspaceFolder}" 64 | }, 65 | "problemMatcher": [ 66 | "$gcc" 67 | ], 68 | "type": "shell" 69 | }, 70 | { 71 | "command": "make program-dfu", 72 | "label": "program-dfu", 73 | "problemMatcher": [], 74 | "type": "shell" 75 | }, 76 | { 77 | "command": "make program", 78 | "label": "program", 79 | "problemMatcher": [], 80 | "type": "shell" 81 | }, 82 | { 83 | "command": "make", 84 | "label": "build_libdaisy", 85 | "options": { 86 | "cwd": "${workspaceFolder}/../../libDaisy/" 87 | }, 88 | "problemMatcher": [ 89 | "$gcc" 90 | ], 91 | "type": "shell" 92 | }, 93 | { 94 | "command": "make", 95 | "label": "build_daisysp", 96 | "options": { 97 | "cwd": "${workspaceFolder}/../../DaisySP/" 98 | }, 99 | "problemMatcher": [ 100 | "$gcc" 101 | ], 102 | "type": "shell" 103 | } 104 | ], 105 | "version": "2.0.0" 106 | } 107 | -------------------------------------------------------------------------------- /patch_algodaisy/TorusPlus/Makefile: -------------------------------------------------------------------------------- 1 | # Project Name 2 | TARGET = torusplus 3 | 4 | # Library Locations 5 | LIBDAISY_DIR = ../../libdaisy 6 | DAISYSP_DIR = ../../DaisySP/ 7 | STMLIB_DIR = ../../stmlib 8 | 9 | # Sources 10 | CPP_SOURCES = torus.cpp \ 11 | dsp/fm_voice.cpp \ 12 | dsp/part.cpp \ 13 | dsp/resonator.cpp \ 14 | dsp/string.cpp \ 15 | dsp/string_synth_part.cpp \ 16 | resources.cpp \ 17 | 18 | CC_SOURCES += $(STMLIB_DIR)/dsp/units.cc \ 19 | $(STMLIB_DIR)/utils/random.cc \ 20 | cv_scaler.cc \ 21 | 22 | C_INCLUDES += \ 23 | -I. \ 24 | -Iresources \ 25 | -I../.. 26 | 27 | # Can't actually add to CFLAGS.. due to libDaisy stuff 28 | # silences warning from stmlib JOIN macros 29 | C_INCLUDES += -Wno-unused-local-typedefs 30 | OPT = -Os 31 | 32 | # Core location, and generic makefile. 33 | SYSTEM_FILES_DIR = $(LIBDAISY_DIR)/core 34 | include $(SYSTEM_FILES_DIR)/Makefile 35 | 36 | 37 | ### Need to override all to get support for .cc files 38 | all: $(BUILD_DIR)/$(TARGET).elf $(BUILD_DIR)/$(TARGET).hex $(BUILD_DIR)/$(TARGET).bin 39 | 40 | ####################################### 41 | # build the application 42 | ####################################### 43 | # list of objects 44 | OBJECTS = $(addprefix $(BUILD_DIR)/,$(notdir $(C_SOURCES:.c=.o))) 45 | vpath %.c $(sort $(dir $(C_SOURCES))) 46 | OBJECTS += $(addprefix $(BUILD_DIR)/,$(notdir $(CPP_SOURCES:.cpp=.o))) 47 | vpath %.cpp $(sort $(dir $(CPP_SOURCES))) 48 | OBJECTS += $(addprefix $(BUILD_DIR)/,$(notdir $(CC_SOURCES:.cc=.o))) 49 | vpath %.cc $(sort $(dir $(CC_SOURCES))) 50 | # list of ASM program objects 51 | OBJECTS += $(addprefix $(BUILD_DIR)/,$(notdir $(ASM_SOURCES:.s=.o))) 52 | vpath %.s $(sort $(dir $(ASM_SOURCES))) 53 | 54 | $(BUILD_DIR)/%.o: %.c Makefile | $(BUILD_DIR) 55 | $(CC) -c $(CFLAGS) $(C_STANDARD) -Wa,-a,-ad,-alms=$(BUILD_DIR)/$(notdir $(<:.c=.lst)) $< -o $@ 56 | 57 | $(BUILD_DIR)/%.o: %.cpp Makefile | $(BUILD_DIR) 58 | $(CXX) -c $(CPPFLAGS) $(CPP_STANDARD) -Wa,-a,-ad,-alms=$(BUILD_DIR)/$(notdir $(<:.cpp=.lst)) $< -o $@ 59 | 60 | $(BUILD_DIR)/%.o: %.cc Makefile | $(BUILD_DIR) 61 | $(CXX) -c $(CPPFLAGS) $(CPP_STANDARD) -Wa,-a,-ad,-alms=$(BUILD_DIR)/$(notdir $(<:.cc=.lst)) $< -o $@ 62 | 63 | $(BUILD_DIR)/%.o: %.s Makefile | $(BUILD_DIR) 64 | $(AS) -c $(CFLAGS) $< -o $@ 65 | 66 | $(BUILD_DIR)/$(TARGET).elf: $(OBJECTS) Makefile 67 | $(CXX) $(OBJECTS) $(LDFLAGS) -o $@ 68 | $(SZ) $@ 69 | 70 | $(BUILD_DIR)/%.hex: $(BUILD_DIR)/%.elf | $(BUILD_DIR) 71 | $(HEX) $< $@ 72 | 73 | $(BUILD_DIR)/%.bin: $(BUILD_DIR)/%.elf | $(BUILD_DIR) 74 | $(BIN) $< $@ 75 | 76 | $(BUILD_DIR): 77 | mkdir $@ 78 | -------------------------------------------------------------------------------- /patch_algodaisy/TorusPlus/README.md: -------------------------------------------------------------------------------- 1 | # DaisyMutations Torus Plus 2 | 3 | Mutable Instruments Rings for the Daisy Patch. 4 | 5 | **Version: 1.03** 6 | 7 | The **"Plus"** version adds some features that allow to experiment with the patch without any other external gear. 8 | 9 | Differences between Torus Plus and Torus: 10 | 11 | ``` 12 | + delay effect (with time/feedback and mix control) 13 | + menu rearranged and different default knobs assignment 14 | + an internal random duophonic note/rythm generator 15 | + white noise on AUDIO2 16 | + different (better?) 1V/oct tracking 17 | + extra LFO or random CV signal 18 | + audio input mix 19 | + test mode 20 | + mono mode (polyphony 2 and 4 split the audio signal in the original version) 21 | ``` 22 | 23 | You can download the (latest) compiled firmware here: 24 | 25 | - [TorusPlus.bin](../../firmwares/TorusPlus.bin) 26 | 27 | Enhancements by: Marzio De Biasi (AlgoritmArte) 28 | Ported by: Ben Sergentanis 29 | Originally by: Émilie Gillet 30 | 31 | Please refer to the [Rings manual](https://mutable-instruments.net/modules/rings/manual/) for more detail on everything Rings. 32 | 33 | If you find some probelms or have suggestions, contact me: development [at] algoritmarte [dot] com 34 | 35 | ## Controls and I/O 36 | 37 | Summary: 38 | 39 | |Control | Function | 40 | |-------------------|-----------| 41 | | Ctrl 1 (default) | Frequency | 42 | | Ctrl 2 (default) | Structure | 43 | | Ctrl 3 (default) | Damping | 44 | | Ctrl 4 (default) | Position | 45 | | Gate In 1 | Trigger In (Rings Strum) | 46 | | Gate In 2 | _unused_ | 47 | | Audio In 1 | External Exciter In _(still under testing)_| 48 | | Audio In 2 | _unused_ | 49 | | Audio In 3 + 4 | Stereo input that can be mixed with the output of rings 50 | | Audio Out 1 | Left Out (or Mono) | 51 | | Audio Out 2 | Right Out | 52 | | Audio Out 3 | White noise | 53 | | Audio Out 4 | Extra trigger when in "bassline" mode (the trig is generated also in other modes)| 54 | | Gate Out 1 | Triggered when the internal generator generates a new note | 55 | | CV Out 1 | 1V/oct of the note generated by the internal generator | 56 | 57 | | CV Out 2 | 1V/oct of the bass note when in "bassline" mode | 58 | | Encoder | Menu Navigation | 59 | 60 | NOTE: there is no "normalized internal route", so in order to use the internal note/rythm generator you should self patch the Daisy connecting : 61 | 62 | - CV Out1 ===> CTRL1 (frequency) 63 | - Gate Out1 ===> Gate In 1 (trigger in) 64 | 65 | ![TorusPlus self patching](snap_torusplus.png) 66 | 67 | 68 | 69 | ## Menus 70 | 71 | ### Polyphony 72 | Polyphony: How many voices can play at once. Polyphony modes 2 and 4 cause every other note to hardpan left and right. 73 | 74 | - One 75 | - Two (default) 76 | - Four 77 | 78 | ### Model 79 | Model: The resonator model to be used. 80 | 81 | - Modal (default) 82 | - Sympathetic Strings 83 | - Inharmonic Strings 84 | - FM Voice 85 | - Western Chords 86 | - String and Reverb 87 | 88 | ### Delay time / Delay feedback / Delay Mix 89 | 90 | Controls: 91 | 92 | - delay time 93 | - delay feedback 94 | - delay mix 95 | 96 | ### Plus 97 | 98 | Controls the internal note rythm random generator: 99 | 100 | - BPM : tempo in beats per minute 101 | - Zoom : the zoom on the "pseudo fractal" note generator; *usually adjusting it in combination with Note offset can lead to interesting results*; lower values means higher probability towards the "base" note of the scale; 102 | - Note offset : base note of the (diatonic) scale; *usually adjusting it in combination with Zoom can lead to interesting results*; 103 | - Scale : scales used by the note generator: Major, Minor, Pentatonic or Phrygian; 104 | - Randomize ... there is no detailed control over the parameters of the generative process, so "you should trust your eurorack system", if you don't like the results click Randomize and soon or later *something happy will happen* :-) 105 | - Test mode : the C D E F G A B C scale is repeated on CV1 out / Gate 1 106 | 107 | ### Mod 108 | 109 | Controls the function of CV2: 110 | 111 | - Mode: can be 112 | + LFO (default): in this mode CV2 is a sine wave which freqeuncy and amplitude is controlled by Mod.Freq and Mod.Gain 113 | + RAND: in this mode CV2 is set to a random voltage whenever a new "bass" note is generated; the amplitude of the value is controlled by Mod.Gain 114 | + BASS LINE: in this mode CV1 Out/Gate 1 out "plays" the higher line of the randomly generated melody, CV2 Out/Audio 4 "plays" the bass line of the melody (audio 4 is used as a trigger) 115 | - Mod. Freq: controls the frequency of the LFO in LFO mode 116 | - Mod. Gain: controls the amplitude of the LFO in LFO mode, or the range of random voltages in RAND mode 117 | 118 | Due to the hardware architecture, LFO and RANDs are unipolar (positive values). 119 | 120 | ### Audio mix 121 | 122 | Audio 3+4 can be mixed with the output signal of Rings (_actually after the delay, but an option to mix it before the delay will be added soon_). 123 | 124 | The mix value has a default value of 0.25 which should be fine for Eurorack audio levels; if you use Line levels you must increase it. 125 | 126 | 127 | ### Normalize 128 | This menu simulates the normalization detection used on Rings. 129 | For proper behavior you **must** turn on normalization for each of the controls you have patched. 130 | 131 | These are: 132 | 133 | - Exciter (default OFF) 134 | - Note (default ON) 135 | - Strum (default ON) 136 | - Easter Egg enable/disable: used to toggle easter egg mode (default OFF) 137 | 138 | ### Easter Egg FX 139 | 140 | Easter Egg FX: Change the effect used in easter egg mode: 141 | 142 | - Formant 143 | - Chorus 144 | - Reverb 145 | - Formant 2 146 | - Ensemble 147 | - Reverb 2 148 | 149 | ### Mono output 150 | 151 | Merge AUDIO1 and AUDIO2 into AUDIO1 (useful if polyphony is 2 or 3 and you cannot handle a stereo signal on you system) 152 | 153 | ### Controls 154 | Use this menu to customize Rings' five controls to the Daisy Patch's four. 155 | Your options are: 156 | 157 | - Frequency (default KNOB1) 158 | - Structure (default KNOB2) 159 | - Damping (default KNOB3) 160 | - Position (default KNOB4) 161 | - Brightness 162 | 163 | ### History 164 | 165 | 2020-01-20 v1.00 first Plus version 166 | 2020-01-26 v1.01 better handling of 1V/oct input 167 | 2020-01-29 v1.02 added CV2 modulation options, scales and audio in mix 168 | 2020-01-30 v1.03 improved audio mixing, test scale on CV1 -------------------------------------------------------------------------------- /patch_algodaisy/TorusPlus/UiHardware.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "daisy_patch.h" 4 | #include "daisy.h" 5 | 6 | extern daisy::DaisyPatch hw; 7 | 8 | /** To make it easier to refer to specific buttons in the 9 | * code, we use this enum to give each button ID a specific name. 10 | */ 11 | enum ButtonIds 12 | { 13 | bttnEncoder = 0, 14 | // We don't have any more buttons on the Patch, but if there were more, you'd add them here 15 | NUM_BUTTONS 16 | }; 17 | 18 | /** To make it easier to refer to specific encoders in the 19 | * code, we use this enum to give each encoder ID a specific name. 20 | */ 21 | enum EncoderIds 22 | { 23 | encoderMain = 0, 24 | // We don't have any more encoders on the Patch, but if there were more, you'd add them here 25 | NUM_ENCODERS 26 | }; 27 | 28 | /** To make it easier to refer to specific canvases in the 29 | * code, we use this enum to give each canvas ID a specific name. 30 | */ 31 | enum CanvasIds 32 | { 33 | // This canvas is for the OLED display 34 | canvasOledDisplay = 0, 35 | NUM_CANVASES 36 | }; 37 | 38 | /** This is the type of display we use on the patch. This is provided here for better readability. */ 39 | using OledDisplayType = decltype(daisy::DaisyPatch::display); 40 | 41 | // These will be called from the UI system. @see InitUi() in UiSystemDemo.cpp 42 | void FlushCanvas(const daisy::UiCanvasDescriptor& canvasDescriptor) 43 | { 44 | if(canvasDescriptor.id_ == canvasOledDisplay) 45 | { 46 | OledDisplayType& display 47 | = *((OledDisplayType*)(canvasDescriptor.handle_)); 48 | display.Update(); 49 | } 50 | } 51 | void ClearCanvas(const daisy::UiCanvasDescriptor& canvasDescriptor) 52 | { 53 | if(canvasDescriptor.id_ == canvasOledDisplay) 54 | { 55 | OledDisplayType& display 56 | = *((OledDisplayType*)(canvasDescriptor.handle_)); 57 | display.Fill(false); 58 | } 59 | } -------------------------------------------------------------------------------- /patch_algodaisy/TorusPlus/cliff.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "sandrack.hpp" 6 | 7 | using namespace sandrack; 8 | 9 | #define MAXCLIFFLEN 10 10 | #define NUMCOEFF 8 11 | #define NUMSCALES 4 12 | 13 | struct Cliff 14 | { 15 | const int cliffscales[NUMSCALES][MAXCLIFFLEN] { 16 | {0, 2, 4, 5, 7, 9, 11, 12, 14, 16 }, 17 | {0, 2, 3, 5, 7, 8, 10, 12, 14, 15 }, 18 | {0, 2, 4, 7, 9, 12, 14, 16, 19, 21 }, 19 | {0, 1, 3, 5, 7, 8, 10, 12, 13, 15 }, 20 | }; 21 | 22 | float x; 23 | float y; 24 | float coeff[NUMCOEFF] = {0, 0, 0, 0, 0, 0, 0, 0}; 25 | 26 | float cliffzoom = 3; 27 | int base = 0; 28 | int cliffscale = 0; 29 | 30 | void Randomize() 31 | { 32 | for(int i = 0; i < NUMCOEFF; i++) 33 | { 34 | coeff[i] = frands(0.1, 5); 35 | //println( coeff[i] ); 36 | } 37 | coeff[2] = frand(-1.3, 1.3); 38 | coeff[3] = frand(-1.3, 1.3); 39 | coeff[6] = frand(-1.3, 1.3); 40 | coeff[7] = frand(-1.3, 1.3); 41 | x = frand(-2, 2); 42 | y = frand(-2, 2); 43 | } 44 | 45 | int NextNote(int ch) 46 | { 47 | int k = (irand(0, 3) == 0) ? 4 : 0; 48 | 49 | float x1 = std::sin(coeff[k + 0] * y) 50 | + coeff[k + 2] * std::cos(coeff[k + 0] * x); 51 | float y1 = std::sin(coeff[k + 1] * x) 52 | + coeff[k + 3] * std::cos(coeff[k + 1] * y); 53 | 54 | x = x1; 55 | y = y1; 56 | int note = 0; 57 | if(ch == 0) 58 | { 59 | note = cliffscales[cliffscale][( base + fround(std::abs(x * cliffzoom))) 60 | % MAXCLIFFLEN]; 61 | } 62 | else 63 | { 64 | note = cliffscales[cliffscale][(base + fround(std::abs(y * cliffzoom))) 65 | % MAXCLIFFLEN] + 12; 66 | } 67 | return note; 68 | } 69 | }; 70 | -------------------------------------------------------------------------------- /patch_algodaisy/TorusPlus/cv_scaler.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2015 Emilie Gillet. 2 | // 3 | // Author: Emilie Gillet (emilie.o.gillet@gmail.com) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | // THE SOFTWARE. 22 | // 23 | // See http://creativecommons.org/licenses/MIT/ for more information. 24 | // 25 | // ----------------------------------------------------------------------------- 26 | // 27 | // Filtering and scaling of ADC values + input calibration. 28 | 29 | #include "cv_scaler.h" 30 | 31 | #include 32 | 33 | #include "stmlib/dsp/dsp.h" 34 | #include "stmlib/utils/random.h" 35 | 36 | #include "dsp/part.h" 37 | #include "dsp/patch.h" 38 | 39 | #include "daisy.h" 40 | #include "daisy_patch.h" 41 | 42 | extern daisy::DaisyPatch hw; 43 | 44 | namespace torus { 45 | 46 | using namespace std; 47 | using namespace stmlib; 48 | 49 | /* static */ 50 | ChannelSettings CvScaler::channel_settings_[CHAN_LAST] = { 51 | // { LAW_LINEAR, true, 1.00f }, // ADC_CHANNEL_CV_FREQUENCY 52 | // { LAW_LINEAR, true, 0.1f }, // ADC_CHANNEL_CV_STRUCTURE 53 | // { LAW_LINEAR, true, 0.1f }, // ADC_CHANNEL_CV_BRIGHTNESS 54 | // { LAW_LINEAR, true, 0.05f }, // ADC_CHANNEL_CV_DAMPING 55 | // { LAW_LINEAR, true, 0.01f }, // ADC_CHANNEL_CV_POSITION 56 | // { LAW_LINEAR, false, 1.00f }, // ADC_CHANNEL_CV_V_OCT 57 | { LAW_LINEAR, false, 1.f }, // ADC_CHANNEL_POT_FREQUENCY 58 | { LAW_LINEAR, false, 0.1f }, // ADC_CHANNEL_POT_STRUCTURE 59 | { LAW_LINEAR, false, 0.05f }, // ADC_CHANNEL_POT_DAMPING 60 | { LAW_LINEAR, false, 0.01f }, // ADC_CHANNEL_POT_POSITION 61 | { LAW_LINEAR, false, 0.1f }, // ADC_CHANNEL_POT_BRIGHTNESS 62 | }; 63 | 64 | void CvScaler::Init() { 65 | transpose_ = 0.0f; 66 | 67 | inhibit_strum_ = 0; 68 | fm_cv_ = 0.0f; 69 | 70 | for(int i = 0; i < 4; i++){ 71 | channel_map_[i] = i; 72 | } 73 | } 74 | 75 | /*#define ATTENUVERT(destination, NAME, min, max) \ 76 | // { \ 77 | // float value = adc_lp_[ADC_CHANNEL_CV_ ## NAME]; \ 78 | // value *= adc_lp_[ADC_CHANNEL_ATTENUVERTER_ ## NAME]; \ 79 | // value += adc_lp_[ADC_CHANNEL_POT_ ## NAME]; \ 80 | // CONSTRAIN(value, min, max) \ 81 | // destination = value; \ 82 | // }*/ 83 | 84 | void CvScaler::Read(Patch* patch, PerformanceState* performance_state) { 85 | 86 | adc_lp_[ POT_BRIGHTNESS ] = 0; 87 | 88 | // Process all CVs / pots. 89 | for (size_t i = 0; i < 4; i++) { 90 | const ChannelSettings& settings = channel_settings_[i]; 91 | float value = hw.controls[i].Value(); 92 | 93 | switch (settings.law) { 94 | 95 | case LAW_QUADRATIC_BIPOLAR: 96 | { 97 | value = value - 0.5f; 98 | float value2 = value * value * 4.0f * 3.3f; 99 | value = value < 0.0f ? -value2 : value2; 100 | } 101 | break; 102 | 103 | case LAW_QUARTIC_BIPOLAR: 104 | { 105 | value = value - 0.5f; 106 | float value2 = value * value * 4.0f; 107 | float value4 = value2 * value2 * 3.3f; 108 | value = value < 0.0f ? -value4 : value4; 109 | } 110 | break; 111 | 112 | default: 113 | break; 114 | } 115 | 116 | // apply low-pass filter 117 | adc_lp_[channel_map_[i]] += settings.lp_coefficient * (value - adc_lp_[channel_map_[i]]); 118 | } 119 | 120 | // ATTENUVERT(patch->structure, STRUCTURE, 0.0f, 0.9995f); 121 | // ATTENUVERT(patch->brightness, BRIGHTNESS, 0.0f, 1.0f); 122 | // ATTENUVERT(patch->damping, DAMPING, 0.0f, 1.0f); 123 | // ATTENUVERT(patch->position, POSITION, 0.0f, 1.0f); 124 | 125 | patch->structure = adc_lp_[POT_STRUCTURE]; 126 | patch->brightness = brightnessOffset + adc_lp_[POT_BRIGHTNESS]; 127 | patch->damping = adc_lp_[POT_DAMPING]; 128 | patch->position = adc_lp_[POT_POSITION]; 129 | 130 | float fm = 0; 131 | float error = fm - fm_cv_; 132 | if (fabs(error) >= 0.8f) { 133 | fm_cv_ = fm; 134 | } else { 135 | fm_cv_ += 0.02f * error; 136 | } 137 | performance_state->fm = fm_cv_ * 0; 138 | CONSTRAIN(performance_state->fm, -48.0f, 48.0f); 139 | 140 | float transpose = 60.0f * (adc_lp_[POT_FREQUENCY] - 0.5f); 141 | float hysteresis = transpose - transpose_ > 0.0f ? -0.3f : +0.3f; 142 | transpose_ = static_cast(transpose + hysteresis + 0.5f); 143 | 144 | // float note = calibration_data_->pitch_offset; 145 | // note += adc_lp_[ADC_CHANNEL_CV_V_OCT] * calibration_data_->pitch_scale; 146 | 147 | ////performance_state->note = adc_lp_[POT_FREQUENCY] * 48.0f; 148 | ////performance_state->tonic = 12.0f + transpose_; 149 | int quantized = static_cast(transpose + ((transpose >= 0)? 0.5 : -0.5f) ); 150 | performance_state->note = quantized; 151 | performance_state->tonic = 48.0f; // should use another configuration parameter and adjust this via encoder 152 | 153 | // Strumming / internal exciter triggering logic. 154 | 155 | if (performance_state->internal_note) { 156 | // Remove quantization when nothing is plugged in the V/OCT input. 157 | performance_state->note = 0.0f; 158 | performance_state->tonic = 48.0f + transpose; 159 | } 160 | 161 | // Hysteresis on chord. 162 | float chord = adc_lp_[POT_STRUCTURE]; 163 | chord *= static_cast(kNumChords - 1); 164 | hysteresis = chord - chord_ > 0.0f ? -0.1f : +0.1f; 165 | chord_ = static_cast(chord + hysteresis + 0.5f); 166 | CONSTRAIN(chord_, 0, kNumChords - 1); 167 | performance_state->chord = chord_; 168 | } 169 | 170 | } // namespace torus 171 | -------------------------------------------------------------------------------- /patch_algodaisy/TorusPlus/cv_scaler.h: -------------------------------------------------------------------------------- 1 | // Copyright 2015 Emilie Gillet. 2 | // 3 | // Author: Emilie Gillet (emilie.o.gillet@gmail.com) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | // THE SOFTWARE. 22 | // 23 | // See http://creativecommons.org/licenses/MIT/ for more information. 24 | // 25 | // ----------------------------------------------------------------------------- 26 | // 27 | // Filtering and scaling of ADC values + input calibration. 28 | 29 | #ifndef TORUS_CV_SCALER_H_ 30 | #define TORUS_CV_SCALER_H_ 31 | 32 | #include "stmlib/stmlib.h" 33 | 34 | namespace torus 35 | { 36 | enum Law 37 | { 38 | LAW_LINEAR, 39 | LAW_QUADRATIC_BIPOLAR, 40 | LAW_QUARTIC_BIPOLAR 41 | }; 42 | 43 | struct ChannelSettings 44 | { 45 | Law law; 46 | bool remove_offset; 47 | float lp_coefficient; 48 | }; 49 | 50 | enum Channels 51 | { 52 | POT_FREQUENCY, 53 | POT_STRUCTURE, 54 | POT_DAMPING, 55 | POT_POSITION, 56 | POT_BRIGHTNESS, 57 | CHAN_LAST 58 | }; 59 | 60 | struct Patch; 61 | struct PerformanceState; 62 | 63 | class CvScaler 64 | { 65 | public: 66 | CvScaler() {} 67 | ~CvScaler() {} 68 | 69 | void Init(); 70 | void Read(Patch* patch, PerformanceState* performance_state); 71 | 72 | inline bool easter_egg() const 73 | { 74 | return false; 75 | // return adc_lp_[ADC_CHANNEL_POT_FREQUENCY] < 0.1f && 76 | // adc_lp_[ADC_CHANNEL_POT_STRUCTURE] > 0.9f && 77 | // adc_lp_[ADC_CHANNEL_POT_BRIGHTNESS] < 0.1f && 78 | // adc_lp_[ADC_CHANNEL_POT_POSITION] > 0.9f && 79 | // adc_lp_[ADC_CHANNEL_POT_DAMPING] > 0.4f && 80 | // adc_lp_[ADC_CHANNEL_POT_DAMPING] < 0.6f && 81 | // adc_lp_[ADC_CHANNEL_ATTENUVERTER_BRIGHTNESS] < -1.00f && 82 | // adc_lp_[ADC_CHANNEL_ATTENUVERTER_FREQUENCY] > 1.00f && 83 | // adc_lp_[ADC_CHANNEL_ATTENUVERTER_DAMPING] < -1.00f && 84 | // adc_lp_[ADC_CHANNEL_ATTENUVERTER_STRUCTURE] > 1.00f && 85 | // adc_lp_[ADC_CHANNEL_ATTENUVERTER_POSITION] < -1.00f; 86 | } 87 | 88 | int channel_map_[4]; 89 | float brightnessOffset = 0; 90 | 91 | private: 92 | static ChannelSettings channel_settings_[CHAN_LAST]; 93 | float adc_lp_[CHAN_LAST] = {}; 94 | 95 | int32_t inhibit_strum_ = 0; 96 | 97 | float transpose_ = 0; 98 | float fm_cv_ = 0; 99 | float cv_c1_ = 0; 100 | float cv_low_ = 0; 101 | 102 | int32_t chord_ = 0; 103 | }; 104 | 105 | } // namespace torus 106 | 107 | #endif // TORUS_CV_SCALER_H_ 108 | -------------------------------------------------------------------------------- /patch_algodaisy/TorusPlus/drone.hpp: -------------------------------------------------------------------------------- 1 | #ifndef DRONE_HPP 2 | #define DRONE_HPP 3 | 4 | #include 5 | 6 | #include "daisysp.h" 7 | 8 | #include "sandrack.hpp" 9 | #include "cliff.hpp" 10 | #include "euclide.hpp" 11 | 12 | using namespace daisysp; 13 | 14 | struct Drone { 15 | 16 | WhiteNoise noise = {}; 17 | Cliff cliff = {}; 18 | EuclideSet eset = {}; 19 | 20 | const int test_notes[8] = { 0, 2, 4, 5, 7, 9, 11, 12 }; 21 | 22 | float audioRate = 44100; 23 | float cvRate = 44100; 24 | float bpm = 60; 25 | 26 | float v_noise = 0; 27 | 28 | int v_beat = 0; 29 | int v_pulse1 = 0; 30 | int v_pulse2 = 0; 31 | int v_note1 = -1; 32 | int v_note2 = -1; 33 | 34 | float tick = 0; 35 | float tickoff = 0; 36 | bool testMode = false; 37 | int itest = 0; 38 | 39 | void Init(float audioSampleRate, float cvSampleRate = 0 ) { 40 | audioRate = audioSampleRate; 41 | cvRate = (cvSampleRate > 0)? cvSampleRate : audioSampleRate; 42 | noise.Init(); 43 | Randomize(); 44 | } 45 | 46 | void Randomize() { 47 | cliff.Randomize(); 48 | eset.Randomize(); 49 | } 50 | 51 | void ProcessCV() { 52 | v_pulse1 = v_pulse2 = 0; 53 | 54 | float elapsed = tickoff + tick / cvRate; // secs elapsed 55 | tickoff = 0; 56 | float beatlen = 60.0f / bpm; // beat duration 57 | float steplen = beatlen / 2.0f; 58 | if ( elapsed >= steplen ) { 59 | tickoff = elapsed - steplen; 60 | tick = 0; 61 | v_beat = (v_beat + 1) % 2; 62 | 63 | if ( testMode ) { 64 | if (v_beat == 0) { 65 | v_pulse2 = 1; 66 | v_note2 = test_notes[ itest++ % 8]; 67 | } 68 | } else { 69 | eset.Step(); 70 | if ( eset.m_values[0] || eset.m_values[1] ) { 71 | v_pulse1 = 1; 72 | v_note1 = cliff.NextNote( 0 ); 73 | } 74 | if ( eset.m_values[2] || eset.m_values[3] ) { 75 | v_pulse2 = 1; 76 | v_note2 = cliff.NextNote( 1 ); 77 | } 78 | } 79 | } 80 | tick++; 81 | } 82 | 83 | void ProcessAudio() { 84 | v_noise = noise.Process(); 85 | } 86 | }; 87 | 88 | #endif -------------------------------------------------------------------------------- /patch_algodaisy/TorusPlus/dsp/dsp.h: -------------------------------------------------------------------------------- 1 | // Copyright 2015 Emilie Gillet. 2 | // 3 | // Author: Emilie Gillet (emilie.o.gillet@gmail.com) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | // THE SOFTWARE. 22 | // 23 | // See http://creativecommons.org/licenses/MIT/ for more information. 24 | // 25 | // ----------------------------------------------------------------------------- 26 | // 27 | // Utility DSP routines. 28 | 29 | #ifndef TORUS_DSP_DSP_H_ 30 | #define TORUS_DSP_DSP_H_ 31 | 32 | #include "stmlib/stmlib.h" 33 | 34 | // #define MIC_W 35 | #define BRYAN_CHORDS 36 | 37 | namespace torus 38 | { 39 | static const float kSampleRate = 48000.0f; 40 | const float a3 = 440.0f / kSampleRate; 41 | const size_t kMaxBlockSize = 48; 42 | 43 | } // namespace torus 44 | 45 | #endif // TORUS_DSP_DSP_H_ 46 | -------------------------------------------------------------------------------- /patch_algodaisy/TorusPlus/dsp/fm_voice.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2015 Emilie Gillet. 2 | // 3 | // Author: Emilie Gillet (emilie.o.gillet@gmail.com) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | // THE SOFTWARE. 22 | // 23 | // See http://creativecommons.org/licenses/MIT/ for more information. 24 | // 25 | // ----------------------------------------------------------------------------- 26 | // 27 | // FM Voice. 28 | 29 | #include "dsp/fm_voice.h" 30 | 31 | #include 32 | 33 | #include "stmlib/dsp/dsp.h" 34 | #include "stmlib/dsp/parameter_interpolator.h" 35 | #include "stmlib/dsp/units.h" 36 | 37 | #include "resources.h" 38 | 39 | namespace torus 40 | { 41 | using namespace stmlib; 42 | 43 | void FMVoice::Init() 44 | { 45 | set_frequency(220.0f / kSampleRate); 46 | set_ratio(0.5f); 47 | set_brightness(0.5f); 48 | set_damping(0.5f); 49 | set_position(0.5f); 50 | set_feedback_amount(0.0f); 51 | 52 | previous_carrier_frequency_ = carrier_frequency_; 53 | previous_modulator_frequency_ = carrier_frequency_; 54 | previous_brightness_ = brightness_; 55 | previous_damping_ = damping_; 56 | previous_feedback_amount_ = feedback_amount_; 57 | 58 | amplitude_envelope_ = 0.0f; 59 | brightness_envelope_ = 0.0f; 60 | 61 | carrier_phase_ = 0; 62 | modulator_phase_ = 0; 63 | gain_ = 0.0f; 64 | fm_amount_ = 0.0f; 65 | 66 | follower_.Init( 67 | 8.0f / kSampleRate, 160.0f / kSampleRate, 1600.0f / kSampleRate); 68 | } 69 | 70 | void FMVoice::Process(const float* in, float* out, float* aux, size_t size) 71 | { 72 | // Interpolate between the "oscillator" behaviour and the "FMLPGed thing" 73 | // behaviour. 74 | float envelope_amount = damping_ < 0.9f ? 1.0f : (1.0f - damping_) * 10.0f; 75 | float amplitude_rt60 76 | = 0.1f * SemitonesToRatio(damping_ * 96.0f) * kSampleRate; 77 | float amplitude_decay = 1.0f - powf(0.001f, 1.0f / amplitude_rt60); 78 | 79 | float brightness_rt60 80 | = 0.1f * SemitonesToRatio(damping_ * 84.0f) * kSampleRate; 81 | float brightness_decay = 1.0f - powf(0.001f, 1.0f / brightness_rt60); 82 | 83 | float ratio = Interpolate(lut_fm_frequency_quantizer, ratio_, 128.0f); 84 | float modulator_frequency = carrier_frequency_ * SemitonesToRatio(ratio); 85 | 86 | if(modulator_frequency > 0.5f) 87 | { 88 | modulator_frequency = 0.5f; 89 | } 90 | 91 | float feedback = (feedback_amount_ - 0.5f) * 2.0f; 92 | 93 | ParameterInterpolator carrier_increment( 94 | &previous_carrier_frequency_, carrier_frequency_, size); 95 | ParameterInterpolator modulator_increment( 96 | &previous_modulator_frequency_, modulator_frequency, size); 97 | ParameterInterpolator brightness(&previous_brightness_, brightness_, size); 98 | ParameterInterpolator feedback_amount( 99 | &previous_feedback_amount_, feedback, size); 100 | 101 | uint32_t carrier_phase = carrier_phase_; 102 | uint32_t modulator_phase = modulator_phase_; 103 | float previous_sample = previous_sample_; 104 | 105 | while(size--) 106 | { 107 | // Envelope follower and internal envelope. 108 | float amplitude_envelope, brightness_envelope; 109 | follower_.Process(*in++, &litude_envelope, &brightness_envelope); 110 | 111 | brightness_envelope 112 | *= 2.0f * amplitude_envelope * (2.0f - amplitude_envelope); 113 | 114 | SLOPE(amplitude_envelope_, amplitude_envelope, 0.05f, amplitude_decay); 115 | SLOPE( 116 | brightness_envelope_, brightness_envelope, 0.01f, brightness_decay); 117 | 118 | // Compute envelopes. 119 | float brightness_value = brightness.Next(); 120 | brightness_value *= brightness_value; 121 | float fm_amount_min 122 | = brightness_value < 0.5f ? 0.0f : brightness_value * 2.0f - 1.0f; 123 | float fm_amount_max 124 | = brightness_value < 0.5f ? 2.0f * brightness_value : 1.0f; 125 | float fm_envelope 126 | = 0.5f + envelope_amount * (brightness_envelope_ - 0.5f); 127 | float fm_amount = (fm_amount_min + fm_amount_max * fm_envelope) * 2.0f; 128 | SLEW(fm_amount_, fm_amount, 0.005f + fm_amount_max * 0.015f); 129 | 130 | // FM synthesis in itself 131 | float phase_feedback 132 | = feedback < 0.0f ? 0.5f * feedback * feedback : 0.0f; 133 | modulator_phase += static_cast( 134 | 4294967296.0f * modulator_increment.Next() 135 | * (1.0f + previous_sample * phase_feedback)); 136 | carrier_phase 137 | += static_cast(4294967296.0f * carrier_increment.Next()); 138 | 139 | float feedback = feedback_amount.Next(); 140 | float modulator_fb 141 | = feedback > 0.0f ? 0.25f * feedback * feedback : 0.0f; 142 | float modulator 143 | = SineFm(modulator_phase, modulator_fb * previous_sample); 144 | float carrier = SineFm(carrier_phase, fm_amount_ * modulator); 145 | ONE_POLE(previous_sample, carrier, 0.1f); 146 | 147 | // Compute amplitude envelope. 148 | float gain = 1.0f + envelope_amount * (amplitude_envelope_ - 1.0f); 149 | ONE_POLE(gain_, gain, 0.005f + 0.045f * fm_amount_); 150 | 151 | *out++ = (carrier + 0.5f * modulator) * gain_; 152 | *aux++ = 0.5f * modulator * gain_; 153 | } 154 | carrier_phase_ = carrier_phase; 155 | modulator_phase_ = modulator_phase; 156 | previous_sample_ = previous_sample; 157 | } 158 | 159 | } // namespace torus 160 | -------------------------------------------------------------------------------- /patch_algodaisy/TorusPlus/dsp/fm_voice.h: -------------------------------------------------------------------------------- 1 | // Copyright 2015 Emilie Gillet. 2 | // 3 | // Author: Emilie Gillet (emilie.o.gillet@gmail.com) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | // THE SOFTWARE. 22 | // 23 | // See http://creativecommons.org/licenses/MIT/ for more information. 24 | // 25 | // ----------------------------------------------------------------------------- 26 | // 27 | // FM Voice. 28 | 29 | #ifndef TORUS_DSP_FM_VOICE_H_ 30 | #define TORUS_DSP_FM_VOICE_H_ 31 | 32 | #include "stmlib/stmlib.h" 33 | 34 | #include 35 | 36 | #include "stmlib/dsp/filter.h" 37 | 38 | #include "dsp/dsp.h" 39 | #include "dsp/follower.h" 40 | 41 | #include "resources.h" 42 | 43 | namespace torus 44 | { 45 | using namespace stmlib; 46 | 47 | class FMVoice 48 | { 49 | public: 50 | FMVoice() {} 51 | ~FMVoice() {} 52 | 53 | void Init(); 54 | void Process(const float* in, float* out, float* aux, size_t size); 55 | 56 | inline void set_frequency(float frequency) 57 | { 58 | carrier_frequency_ = frequency; 59 | } 60 | 61 | inline void set_ratio(float ratio) { ratio_ = ratio; } 62 | 63 | inline void set_brightness(float brightness) { brightness_ = brightness; } 64 | 65 | inline void set_damping(float damping) { damping_ = damping; } 66 | 67 | inline void set_position(float position) { position_ = position; } 68 | 69 | inline void set_feedback_amount(float feedback_amount) 70 | { 71 | feedback_amount_ = feedback_amount; 72 | } 73 | 74 | inline void TriggerInternalEnvelope() 75 | { 76 | amplitude_envelope_ = 1.0f; 77 | brightness_envelope_ = 1.0f; 78 | } 79 | 80 | inline float SineFm(uint32_t phase, float fm) const 81 | { 82 | phase += (static_cast((fm + 4.0f) * 536870912.0f)) << 3; 83 | uint32_t integral = phase >> 20; 84 | float fractional = static_cast(phase << 12) / 4294967296.0f; 85 | float a = lut_sine[integral]; 86 | float b = lut_sine[integral + 1]; 87 | return a + (b - a) * fractional; 88 | } 89 | 90 | private: 91 | float carrier_frequency_; 92 | float ratio_; 93 | float brightness_; 94 | float damping_; 95 | float position_; 96 | float feedback_amount_; 97 | 98 | float previous_carrier_frequency_; 99 | float previous_modulator_frequency_; 100 | float previous_brightness_; 101 | float previous_damping_; 102 | float previous_feedback_amount_; 103 | 104 | float amplitude_envelope_; 105 | float brightness_envelope_; 106 | float gain_; 107 | float fm_amount_; 108 | uint32_t carrier_phase_; 109 | uint32_t modulator_phase_; 110 | float previous_sample_; 111 | 112 | Follower follower_; 113 | 114 | DISALLOW_COPY_AND_ASSIGN(FMVoice); 115 | }; 116 | 117 | } // namespace torus 118 | 119 | #endif // TORUS_DSP_FM_VOICE_H_ 120 | -------------------------------------------------------------------------------- /patch_algodaisy/TorusPlus/dsp/follower.h: -------------------------------------------------------------------------------- 1 | // Copyright 2015 Emilie Gillet. 2 | // 3 | // Author: Emilie Gillet (emilie.o.gillet@gmail.com) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | // THE SOFTWARE. 22 | // 23 | // See http://creativecommons.org/licenses/MIT/ for more information. 24 | // 25 | // ----------------------------------------------------------------------------- 26 | // 27 | // Envelope / centroid follower for FM voice. 28 | 29 | #ifndef TORUS_DSP_FOLLOWER_H_ 30 | #define TORUS_DSP_FOLLOWER_H_ 31 | 32 | #include "stmlib/stmlib.h" 33 | 34 | #include 35 | 36 | #include "stmlib/dsp/dsp.h" 37 | #include "stmlib/dsp/filter.h" 38 | 39 | namespace torus 40 | { 41 | using namespace stmlib; 42 | 43 | class Follower 44 | { 45 | public: 46 | Follower() {} 47 | ~Follower() {} 48 | 49 | void Init(float low, float low_mid, float mid_high) 50 | { 51 | low_mid_filter_.Init(); 52 | mid_high_filter_.Init(); 53 | 54 | low_mid_filter_.set_f_q(low_mid, 0.5f); 55 | mid_high_filter_.set_f_q(mid_high, 0.5f); 56 | attack_[0] = low_mid; 57 | decay_[0] = Sqrt(low_mid * low); 58 | 59 | attack_[1] = Sqrt(low_mid * mid_high); 60 | decay_[1] = low_mid; 61 | 62 | attack_[2] = Sqrt(mid_high * 0.5f); 63 | decay_[2] = Sqrt(mid_high * low_mid); 64 | 65 | std::fill(&detector_[0], &detector_[3], 0.0f); 66 | 67 | centroid_ = 0.0f; 68 | } 69 | 70 | void Process(float sample, float* envelope, float* centroid) 71 | { 72 | float bands[3] = {0.0f, 0.0f, 0.0f}; 73 | 74 | bands[2] = mid_high_filter_.Process(sample); 75 | bands[1] = low_mid_filter_.Process( 76 | mid_high_filter_.lp()); 77 | bands[0] = low_mid_filter_.lp(); 78 | 79 | float weighted = 0.0f; 80 | float total = 0.0f; 81 | float frequency = 0.0f; 82 | for(int32_t i = 0; i < 3; ++i) 83 | { 84 | SLOPE(detector_[i], fabs(bands[i]), attack_[i], decay_[i]); 85 | weighted += detector_[i] * frequency; 86 | total += detector_[i]; 87 | frequency += 0.5f; 88 | } 89 | 90 | float error = weighted / (total + 0.001f) - centroid_; 91 | float coefficient = error > 0.0f ? 0.05f : 0.001f; 92 | centroid_ += error * coefficient; 93 | 94 | *envelope = total; 95 | *centroid = centroid_; 96 | } 97 | 98 | private: 99 | NaiveSvf low_mid_filter_; 100 | NaiveSvf mid_high_filter_; 101 | 102 | float attack_[3]; 103 | float decay_[3]; 104 | float detector_[3]; 105 | 106 | float centroid_; 107 | 108 | DISALLOW_COPY_AND_ASSIGN(Follower); 109 | }; 110 | 111 | } // namespace torus 112 | 113 | #endif // TORUS_DSP_FOLLOWER_H_ 114 | -------------------------------------------------------------------------------- /patch_algodaisy/TorusPlus/dsp/fx/chorus.h: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Emilie Gillet. 2 | // 3 | // Author: Emilie Gillet (emilie.o.gillet@gmail.com) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | // THE SOFTWARE. 22 | // 23 | // See http://creativecommons.org/licenses/MIT/ for more information. 24 | // 25 | // ----------------------------------------------------------------------------- 26 | // 27 | // Chorus. 28 | 29 | #ifndef TORUS_DSP_FX_CHORUS_H_ 30 | #define TORUS_DSP_FX_CHORUS_H_ 31 | 32 | #include "stmlib/stmlib.h" 33 | 34 | #include "stmlib/dsp/dsp.h" 35 | 36 | #include "dsp/fx/fx_engine.h" 37 | #include "resources.h" 38 | 39 | namespace torus 40 | { 41 | class Chorus 42 | { 43 | public: 44 | Chorus() {} 45 | ~Chorus() {} 46 | 47 | void Init(uint16_t* buffer) 48 | { 49 | engine_.Init(buffer); 50 | phase_1_ = 0; 51 | phase_2_ = 0; 52 | } 53 | 54 | void Process(float* left, float* right, size_t size) 55 | { 56 | typedef E::Reserve<2047> Memory; 57 | E::DelayLine line; 58 | E::Context c; 59 | 60 | while(size--) 61 | { 62 | engine_.Start(&c); 63 | float dry_amount = 1.0f - amount_ * 0.5f; 64 | 65 | // Update LFO. 66 | phase_1_ += 4.17e-06f; 67 | if(phase_1_ >= 1.0f) 68 | { 69 | phase_1_ -= 1.0f; 70 | } 71 | phase_2_ += 5.417e-06f; 72 | if(phase_2_ >= 1.0f) 73 | { 74 | phase_2_ -= 1.0f; 75 | } 76 | float sin_1 = stmlib::Interpolate(lut_sine, phase_1_, 4096.0f); 77 | float cos_1 78 | = stmlib::Interpolate(lut_sine, phase_1_ + 0.25f, 4096.0f); 79 | float sin_2 = stmlib::Interpolate(lut_sine, phase_2_, 4096.0f); 80 | float cos_2 81 | = stmlib::Interpolate(lut_sine, phase_2_ + 0.25f, 4096.0f); 82 | 83 | float wet; 84 | 85 | // Sum L & R channel to send to chorus line. 86 | c.Read(*left, 0.5f); 87 | c.Read(*right, 0.5f); 88 | c.Write(line, 0.0f); 89 | 90 | c.Interpolate(line, sin_1 * depth_ + 1200, 0.5f); 91 | c.Interpolate(line, sin_2 * depth_ + 800, 0.5f); 92 | c.Write(wet, 0.0f); 93 | *left = wet * amount_ + *left * dry_amount; 94 | 95 | c.Interpolate(line, cos_1 * depth_ + 800 + cos_2 * 0, 0.5f); 96 | c.Interpolate(line, cos_2 * depth_ + 1200, 0.5f); 97 | c.Write(wet, 0.0f); 98 | *right = wet * amount_ + *right * dry_amount; 99 | left++; 100 | right++; 101 | } 102 | } 103 | 104 | inline void set_amount(float amount) { amount_ = amount; } 105 | 106 | inline void set_depth(float depth) { depth_ = depth * 384.0f; } 107 | 108 | private: 109 | typedef FxEngine<2048, FORMAT_16_BIT> E; 110 | E engine_; 111 | 112 | float amount_; 113 | float depth_; 114 | 115 | float phase_1_; 116 | float phase_2_; 117 | 118 | DISALLOW_COPY_AND_ASSIGN(Chorus); 119 | }; 120 | 121 | } // namespace torus 122 | 123 | #endif // TORUS_DSP_FX_CHORUS_H_ -------------------------------------------------------------------------------- /patch_algodaisy/TorusPlus/dsp/fx/ensemble.h: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Emilie Gillet. 2 | // 3 | // Author: Emilie Gillet (emilie.o.gillet@gmail.com) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | // THE SOFTWARE. 22 | // 23 | // See http://creativecommons.org/licenses/MIT/ for more information. 24 | // 25 | // ----------------------------------------------------------------------------- 26 | // 27 | // Ensemble FX. 28 | 29 | #ifndef TORUS_DSP_FX_ENSEMBLE_H_ 30 | #define TORUS_DSP_FX_ENSEMBLE_H_ 31 | 32 | #include "stmlib/stmlib.h" 33 | 34 | #include "stmlib/dsp/dsp.h" 35 | 36 | #include "dsp/fx/fx_engine.h" 37 | #include "resources.h" 38 | 39 | namespace torus 40 | { 41 | class Ensemble 42 | { 43 | public: 44 | Ensemble() {} 45 | ~Ensemble() {} 46 | 47 | void Init(uint16_t* buffer) 48 | { 49 | engine_.Init(buffer); 50 | phase_1_ = 0; 51 | phase_2_ = 0; 52 | } 53 | 54 | void Process(float* left, float* right, size_t size) 55 | { 56 | typedef E::Reserve<2047, E::Reserve<2047>> Memory; 57 | E::DelayLine line_l; 58 | E::DelayLine line_r; 59 | E::Context c; 60 | 61 | while(size--) 62 | { 63 | engine_.Start(&c); 64 | float dry_amount = 1.0f - amount_ * 0.5f; 65 | 66 | // Update LFO. 67 | phase_1_ += 1.57e-05f; 68 | if(phase_1_ >= 1.0f) 69 | { 70 | phase_1_ -= 1.0f; 71 | } 72 | phase_2_ += 1.37e-04f; 73 | if(phase_2_ >= 1.0f) 74 | { 75 | phase_2_ -= 1.0f; 76 | } 77 | int32_t phi_1 = (phase_1_ * 4096.0f); 78 | float slow_0 = lut_sine[phi_1 & 4095]; 79 | float slow_120 = lut_sine[(phi_1 + 1365) & 4095]; 80 | float slow_240 = lut_sine[(phi_1 + 2730) & 4095]; 81 | int32_t phi_2 = (phase_2_ * 4096.0f); 82 | float fast_0 = lut_sine[phi_2 & 4095]; 83 | float fast_120 = lut_sine[(phi_2 + 1365) & 4095]; 84 | float fast_240 = lut_sine[(phi_2 + 2730) & 4095]; 85 | 86 | float a = depth_ * 1.0f; 87 | float b = depth_ * 0.1f; 88 | 89 | float mod_1 = slow_0 * a + fast_0 * b; 90 | float mod_2 = slow_120 * a + fast_120 * b; 91 | float mod_3 = slow_240 * a + fast_240 * b; 92 | 93 | float wet = 0.0f; 94 | 95 | // Sum L & R channel to send to chorus line. 96 | c.Read(*left, 1.0f); 97 | c.Write(line_l, 0.0f); 98 | c.Read(*right, 1.0f); 99 | c.Write(line_r, 0.0f); 100 | 101 | c.Interpolate(line_l, mod_1 + 1024, 0.33f); 102 | c.Interpolate(line_l, mod_2 + 1024, 0.33f); 103 | c.Interpolate(line_r, mod_3 + 1024, 0.33f); 104 | c.Write(wet, 0.0f); 105 | *left = wet * amount_ + *left * dry_amount; 106 | 107 | c.Interpolate(line_r, mod_1 + 1024, 0.33f); 108 | c.Interpolate(line_r, mod_2 + 1024, 0.33f); 109 | c.Interpolate(line_l, mod_3 + 1024, 0.33f); 110 | c.Write(wet, 0.0f); 111 | *right = wet * amount_ + *right * dry_amount; 112 | left++; 113 | right++; 114 | } 115 | } 116 | 117 | inline void set_amount(float amount) { amount_ = amount; } 118 | 119 | inline void set_depth(float depth) { depth_ = depth * 128.0f; } 120 | 121 | private: 122 | typedef FxEngine<4096, FORMAT_16_BIT> E; 123 | E engine_; 124 | 125 | float amount_; 126 | float depth_; 127 | 128 | float phase_1_; 129 | float phase_2_; 130 | 131 | DISALLOW_COPY_AND_ASSIGN(Ensemble); 132 | }; 133 | 134 | } // namespace torus 135 | 136 | #endif // TORUS_DSP_FX_ENSEMBLE_H_ -------------------------------------------------------------------------------- /patch_algodaisy/TorusPlus/dsp/fx/fx_engine.h: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Emilie Gillet. 2 | // 3 | // Author: Emilie Gillet (emilie.o.gillet@gmail.com) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | // THE SOFTWARE. 22 | // 23 | // See http://creativecommons.org/licenses/MIT/ for more information. 24 | // 25 | // ----------------------------------------------------------------------------- 26 | // 27 | // Base class for building reverb. 28 | 29 | #ifndef TORUS_DSP_FX_FX_ENGINE_H_ 30 | #define TORUS_DSP_FX_FX_ENGINE_H_ 31 | 32 | #include 33 | 34 | #include "stmlib/stmlib.h" 35 | 36 | #include "stmlib/dsp/dsp.h" 37 | #include "stmlib/dsp/cosine_oscillator.h" 38 | 39 | namespace torus 40 | { 41 | #define TAIL , -1 42 | 43 | enum Format 44 | { 45 | FORMAT_12_BIT, 46 | FORMAT_16_BIT, 47 | FORMAT_32_BIT 48 | }; 49 | 50 | enum LFOIndex 51 | { 52 | LFO_1, 53 | LFO_2 54 | }; 55 | 56 | template 57 | struct DataType 58 | { 59 | }; 60 | 61 | template <> 62 | struct DataType 63 | { 64 | typedef uint16_t T; 65 | 66 | static inline float Decompress(T value) 67 | { 68 | return static_cast(static_cast(value)) / 4096.0f; 69 | } 70 | 71 | static inline T Compress(float value) 72 | { 73 | return static_cast( 74 | stmlib::Clip16(static_cast(value * 4096.0f))); 75 | } 76 | }; 77 | 78 | template <> 79 | struct DataType 80 | { 81 | typedef uint16_t T; 82 | 83 | static inline float Decompress(T value) 84 | { 85 | return static_cast(static_cast(value)) / 32768.0f; 86 | } 87 | 88 | static inline T Compress(float value) 89 | { 90 | return static_cast( 91 | stmlib::Clip16(static_cast(value * 32768.0f))); 92 | } 93 | }; 94 | 95 | template <> 96 | struct DataType 97 | { 98 | typedef float T; 99 | 100 | static inline float Decompress(T value) 101 | { 102 | return value; 103 | ; 104 | } 105 | 106 | static inline T Compress(float value) { return value; } 107 | }; 108 | 109 | template 110 | class FxEngine 111 | { 112 | public: 113 | typedef typename DataType::T T; 114 | FxEngine() {} 115 | ~FxEngine() {} 116 | 117 | void Init(T* buffer) 118 | { 119 | buffer_ = buffer; 120 | Clear(); 121 | } 122 | 123 | void Clear() 124 | { 125 | std::fill(&buffer_[0], &buffer_[size], 0); 126 | write_ptr_ = 0; 127 | } 128 | 129 | struct Empty 130 | { 131 | }; 132 | 133 | template 134 | struct Reserve 135 | { 136 | typedef T Tail; 137 | enum 138 | { 139 | length = l 140 | }; 141 | }; 142 | 143 | template 144 | struct DelayLine 145 | { 146 | enum 147 | { 148 | length = DelayLine::length, 149 | base = DelayLine::base 150 | + DelayLine::length + 1 151 | }; 152 | }; 153 | 154 | template 155 | struct DelayLine 156 | { 157 | enum 158 | { 159 | length = Memory::length, 160 | base = 0 161 | }; 162 | }; 163 | 164 | class Context 165 | { 166 | friend class FxEngine; 167 | 168 | public: 169 | Context() {} 170 | ~Context() {} 171 | 172 | inline void Load(float value) { accumulator_ = value; } 173 | 174 | inline void Read(float value, float scale) 175 | { 176 | accumulator_ += value * scale; 177 | } 178 | 179 | inline void Read(float value) { accumulator_ += value; } 180 | 181 | inline void Write(float& value) { value = accumulator_; } 182 | 183 | inline void Write(float& value, float scale) 184 | { 185 | value = accumulator_; 186 | accumulator_ *= scale; 187 | } 188 | 189 | template 190 | inline void Write(D& d, int32_t offset, float scale) 191 | { 192 | STATIC_ASSERT(D::base + D::length <= size, delay_memory_full); 193 | T w = DataType::Compress(accumulator_); 194 | if(offset == -1) 195 | { 196 | buffer_[(write_ptr_ + D::base + D::length - 1) & MASK] = w; 197 | } 198 | else 199 | { 200 | buffer_[(write_ptr_ + D::base + offset) & MASK] = w; 201 | } 202 | accumulator_ *= scale; 203 | } 204 | 205 | template 206 | inline void Write(D& d, float scale) 207 | { 208 | Write(d, 0, scale); 209 | } 210 | 211 | template 212 | inline void WriteAllPass(D& d, int32_t offset, float scale) 213 | { 214 | Write(d, offset, scale); 215 | accumulator_ += previous_read_; 216 | } 217 | 218 | template 219 | inline void WriteAllPass(D& d, float scale) 220 | { 221 | WriteAllPass(d, 0, scale); 222 | } 223 | 224 | template 225 | inline void Read(D& d, int32_t offset, float scale) 226 | { 227 | STATIC_ASSERT(D::base + D::length <= size, delay_memory_full); 228 | T r; 229 | if(offset == -1) 230 | { 231 | r = buffer_[(write_ptr_ + D::base + D::length - 1) & MASK]; 232 | } 233 | else 234 | { 235 | r = buffer_[(write_ptr_ + D::base + offset) & MASK]; 236 | } 237 | float r_f = DataType::Decompress(r); 238 | previous_read_ = r_f; 239 | accumulator_ += r_f * scale; 240 | } 241 | 242 | template 243 | inline void Read(D& d, float scale) 244 | { 245 | Read(d, 0, scale); 246 | } 247 | 248 | inline void Lp(float& state, float coefficient) 249 | { 250 | state += coefficient * (accumulator_ - state); 251 | accumulator_ = state; 252 | } 253 | 254 | inline void Hp(float& state, float coefficient) 255 | { 256 | state += coefficient * (accumulator_ - state); 257 | accumulator_ -= state; 258 | } 259 | 260 | template 261 | inline void Interpolate(D& d, float offset, float scale) 262 | { 263 | STATIC_ASSERT(D::base + D::length <= size, delay_memory_full); 264 | MAKE_INTEGRAL_FRACTIONAL(offset); 265 | float a = DataType::Decompress( 266 | buffer_[(write_ptr_ + offset_integral + D::base) & MASK]); 267 | float b = DataType::Decompress( 268 | buffer_[(write_ptr_ + offset_integral + D::base + 1) & MASK]); 269 | float x = a + (b - a) * offset_fractional; 270 | previous_read_ = x; 271 | accumulator_ += x * scale; 272 | } 273 | 274 | template 275 | inline void Interpolate(D& d, 276 | float offset, 277 | LFOIndex index, 278 | float amplitude, 279 | float scale) 280 | { 281 | STATIC_ASSERT(D::base + D::length <= size, delay_memory_full); 282 | offset += amplitude * lfo_value_[index]; 283 | MAKE_INTEGRAL_FRACTIONAL(offset); 284 | float a = DataType::Decompress( 285 | buffer_[(write_ptr_ + offset_integral + D::base) & MASK]); 286 | float b = DataType::Decompress( 287 | buffer_[(write_ptr_ + offset_integral + D::base + 1) & MASK]); 288 | float x = a + (b - a) * offset_fractional; 289 | previous_read_ = x; 290 | accumulator_ += x * scale; 291 | } 292 | 293 | private: 294 | float accumulator_; 295 | float previous_read_; 296 | float lfo_value_[2]; 297 | T* buffer_; 298 | int32_t write_ptr_; 299 | 300 | DISALLOW_COPY_AND_ASSIGN(Context); 301 | }; 302 | 303 | inline void SetLFOFrequency(LFOIndex index, float frequency) 304 | { 305 | lfo_[index].template Init( 306 | frequency * 32.0f); 307 | } 308 | 309 | inline void Start(Context* c) 310 | { 311 | --write_ptr_; 312 | if(write_ptr_ < 0) 313 | { 314 | write_ptr_ += size; 315 | } 316 | c->accumulator_ = 0.0f; 317 | c->previous_read_ = 0.0f; 318 | c->buffer_ = buffer_; 319 | c->write_ptr_ = write_ptr_; 320 | if((write_ptr_ & 31) == 0) 321 | { 322 | c->lfo_value_[0] = lfo_[0].Next(); 323 | c->lfo_value_[1] = lfo_[1].Next(); 324 | } 325 | else 326 | { 327 | c->lfo_value_[0] = lfo_[0].value(); 328 | c->lfo_value_[1] = lfo_[1].value(); 329 | } 330 | } 331 | 332 | private: 333 | enum 334 | { 335 | MASK = size - 1 336 | }; 337 | 338 | int32_t write_ptr_; 339 | T* buffer_; 340 | stmlib::CosineOscillator lfo_[2]; 341 | 342 | DISALLOW_COPY_AND_ASSIGN(FxEngine); 343 | }; 344 | 345 | } // namespace torus 346 | 347 | #endif // TORUS_DSP_FX_FX_ENGINE_H_ 348 | -------------------------------------------------------------------------------- /patch_algodaisy/TorusPlus/dsp/fx/reverb.h: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Emilie Gillet. 2 | // 3 | // Author: Emilie Gillet (emilie.o.gillet@gmail.com) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | // THE SOFTWARE. 22 | // 23 | // See http://creativecommons.org/licenses/MIT/ for more information. 24 | // 25 | // ----------------------------------------------------------------------------- 26 | // 27 | // Reverb. 28 | 29 | #ifndef TORUS_DSP_FX_REVERB_H_ 30 | #define TORUS_DSP_FX_REVERB_H_ 31 | 32 | #include "stmlib/stmlib.h" 33 | 34 | #include "dsp/fx/fx_engine.h" 35 | 36 | namespace torus 37 | { 38 | class Reverb 39 | { 40 | public: 41 | Reverb() {} 42 | ~Reverb() {} 43 | 44 | void Init(uint16_t* buffer) 45 | { 46 | engine_.Init(buffer); 47 | engine_.SetLFOFrequency(LFO_1, 0.5f / 48000.0f); 48 | engine_.SetLFOFrequency(LFO_2, 0.3f / 48000.0f); 49 | lp_ = 0.7f; 50 | diffusion_ = 0.625f; 51 | } 52 | 53 | void Process(float* left, float* right, size_t size) 54 | { 55 | // This is the Griesinger topology described in the Dattorro paper 56 | // (4 AP diffusers on the input, then a loop of 2x 2AP+1Delay). 57 | // Modulation is applied in the loop of the first diffuser AP for additional 58 | // smearing; and to the two long delays for a slow shimmer/chorus effect. 59 | typedef E::Reserve< 60 | 150, 61 | E::Reserve< 62 | 214, 63 | E::Reserve< 64 | 319, 65 | E::Reserve< 66 | 527, 67 | E::Reserve< 68 | 2182, 69 | E::Reserve< 70 | 2690, 71 | E::Reserve< 72 | 4501, 73 | E::Reserve< 74 | 2525, 75 | E::Reserve<2197, 76 | E::Reserve<6312>>>>>>>>>> 77 | Memory; 78 | E::DelayLine ap1; 79 | E::DelayLine ap2; 80 | E::DelayLine ap3; 81 | E::DelayLine ap4; 82 | E::DelayLine dap1a; 83 | E::DelayLine dap1b; 84 | E::DelayLine del1; 85 | E::DelayLine dap2a; 86 | E::DelayLine dap2b; 87 | E::DelayLine del2; 88 | E::Context c; 89 | 90 | const float kap = diffusion_; 91 | const float klp = lp_; 92 | const float krt = reverb_time_; 93 | const float amount = amount_; 94 | const float gain = input_gain_; 95 | 96 | float lp_1 = lp_decay_1_; 97 | float lp_2 = lp_decay_2_; 98 | 99 | while(size--) 100 | { 101 | float wet; 102 | float apout = 0.0f; 103 | engine_.Start(&c); 104 | 105 | // Smear AP1 inside the loop. 106 | //c.Interpolate(ap1, 10.0f, LFO_1, 80.0f, 1.0f); 107 | //c.Write(ap1, 100, 0.0f); 108 | 109 | c.Read(*left + *right, gain); 110 | 111 | // Diffuse through 4 allpasses. 112 | c.Read(ap1 TAIL, kap); 113 | c.WriteAllPass(ap1, -kap); 114 | c.Read(ap2 TAIL, kap); 115 | c.WriteAllPass(ap2, -kap); 116 | c.Read(ap3 TAIL, kap); 117 | c.WriteAllPass(ap3, -kap); 118 | c.Read(ap4 TAIL, kap); 119 | c.WriteAllPass(ap4, -kap); 120 | c.Write(apout); 121 | 122 | // Main reverb loop. 123 | c.Load(apout); 124 | c.Interpolate(del2, 6261.0f, LFO_2, 50.0f, krt); 125 | c.Lp(lp_1, klp); 126 | c.Read(dap1a TAIL, -kap); 127 | c.WriteAllPass(dap1a, kap); 128 | c.Read(dap1b TAIL, kap); 129 | c.WriteAllPass(dap1b, -kap); 130 | c.Write(del1, 2.0f); 131 | c.Write(wet, 0.0f); 132 | 133 | *left += (wet - *left) * amount; 134 | 135 | c.Load(apout); 136 | c.Interpolate(del1, 4460.0f, LFO_1, 40.0f, krt); 137 | c.Lp(lp_2, klp); 138 | c.Read(dap2a TAIL, kap); 139 | c.WriteAllPass(dap2a, -kap); 140 | c.Read(dap2b TAIL, -kap); 141 | c.WriteAllPass(dap2b, kap); 142 | c.Write(del2, 2.0f); 143 | c.Write(wet, 0.0f); 144 | 145 | *right += (wet - *right) * amount; 146 | 147 | ++left; 148 | ++right; 149 | } 150 | 151 | lp_decay_1_ = lp_1; 152 | lp_decay_2_ = lp_2; 153 | } 154 | 155 | inline void set_amount(float amount) { amount_ = amount; } 156 | 157 | inline void set_input_gain(float input_gain) { input_gain_ = input_gain; } 158 | 159 | inline void set_time(float reverb_time) { reverb_time_ = reverb_time; } 160 | 161 | inline void set_diffusion(float diffusion) { diffusion_ = diffusion; } 162 | 163 | inline void set_lp(float lp) { lp_ = lp; } 164 | 165 | inline void Clear() { engine_.Clear(); } 166 | 167 | private: 168 | typedef FxEngine<32768, FORMAT_16_BIT> E; 169 | E engine_; 170 | 171 | float amount_; 172 | float input_gain_; 173 | float reverb_time_; 174 | float diffusion_; 175 | float lp_; 176 | 177 | float lp_decay_1_; 178 | float lp_decay_2_; 179 | 180 | DISALLOW_COPY_AND_ASSIGN(Reverb); 181 | }; 182 | 183 | } // namespace torus 184 | 185 | #endif // TORUS_DSP_FX_REVERB_H_ 186 | -------------------------------------------------------------------------------- /patch_algodaisy/TorusPlus/dsp/limiter.h: -------------------------------------------------------------------------------- 1 | // Copyright 2015 Emilie Gillet. 2 | // 3 | // Author: Emilie Gillet (emilie.o.gillet@gmail.com) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | // THE SOFTWARE. 22 | // 23 | // See http://creativecommons.org/licenses/MIT/ for more information. 24 | // 25 | // ----------------------------------------------------------------------------- 26 | // 27 | // Limiter. 28 | 29 | #ifndef TORUS_DSP_LIMITER_H_ 30 | #define TORUS_DSP_LIMITER_H_ 31 | 32 | #include "stmlib/stmlib.h" 33 | 34 | #include 35 | 36 | #include "stmlib/dsp/dsp.h" 37 | #include "stmlib/dsp/filter.h" 38 | 39 | namespace torus 40 | { 41 | class Limiter 42 | { 43 | public: 44 | Limiter() {} 45 | ~Limiter() {} 46 | 47 | void Init() { peak_ = 0.5f; } 48 | 49 | void Process(float* l, float* r, size_t size, float pre_gain) 50 | { 51 | while(size--) 52 | { 53 | float l_pre = *l * pre_gain; 54 | float r_pre = *r * pre_gain; 55 | 56 | float l_peak = fabs(l_pre); 57 | float r_peak = fabs(r_pre); 58 | float s_peak = fabs(r_pre - l_pre); 59 | 60 | float peak = std::max(std::max(l_peak, r_peak), s_peak); 61 | SLOPE(peak_, peak, 0.05f, 0.00002f); 62 | 63 | // Clamp to 8Vpp, clipping softly towards 10Vpp 64 | float gain = (peak_ <= 1.0f ? 1.0f : 1.0f / peak_); 65 | *l++ = stmlib::SoftLimit(l_pre * gain * 0.8f); 66 | *r++ = stmlib::SoftLimit(r_pre * gain * 0.8f); 67 | } 68 | } 69 | 70 | private: 71 | float peak_; 72 | 73 | DISALLOW_COPY_AND_ASSIGN(Limiter); 74 | }; 75 | 76 | } // namespace torus 77 | 78 | #endif // TORUS_DSP_LIMITER_H_ 79 | -------------------------------------------------------------------------------- /patch_algodaisy/TorusPlus/dsp/note_filter.h: -------------------------------------------------------------------------------- 1 | // Copyright 2015 Emilie Gillet. 2 | // 3 | // Author: Emilie Gillet (emilie.o.gillet@gmail.com) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | // THE SOFTWARE. 22 | // 23 | // See http://creativecommons.org/licenses/MIT/ for more information. 24 | // 25 | // ----------------------------------------------------------------------------- 26 | // 27 | // Low pass filter for getting stable pitch data. 28 | 29 | #ifndef TORUS_DSP_NOTE_FILTER_H_ 30 | #define TORUS_DSP_NOTE_FILTER_H_ 31 | 32 | #include "stmlib/dsp/dsp.h" 33 | #include "stmlib/dsp/delay_line.h" 34 | 35 | namespace torus 36 | { 37 | class NoteFilter 38 | { 39 | public: 40 | enum 41 | { 42 | N = 4 // Median filter order 43 | }; 44 | NoteFilter() {} 45 | ~NoteFilter() {} 46 | 47 | void Init(float sample_rate, 48 | float time_constant_fast_edge, 49 | float time_constant_steady_part, 50 | float edge_recovery_time, 51 | float edge_avoidance_delay) 52 | { 53 | fast_coefficient_ = 1.0f / (time_constant_fast_edge * sample_rate); 54 | slow_coefficient_ = 1.0f / (time_constant_steady_part * sample_rate); 55 | lag_coefficient_ = 1.0f / (edge_recovery_time * sample_rate); 56 | 57 | delayed_stable_note_.Init(); 58 | delayed_stable_note_.set_delay( 59 | std::min(size_t(15), size_t(edge_avoidance_delay * sample_rate))); 60 | 61 | stable_note_ = note_ = 69.0f; 62 | coefficient_ = fast_coefficient_; 63 | stable_coefficient_ = slow_coefficient_; 64 | std::fill(&previous_values_[0], &previous_values_[N], note_); 65 | } 66 | 67 | inline float Process(float note, bool strum) 68 | { 69 | // If there is a sharp change, follow it instantly. 70 | if(fabs(note - note_) > 0.4f || strum) 71 | { 72 | stable_note_ = note_ = note; 73 | coefficient_ = fast_coefficient_; 74 | stable_coefficient_ = slow_coefficient_; 75 | std::fill(&previous_values_[0], &previous_values_[N], note); 76 | } 77 | else 78 | { 79 | // Median filtering of the raw ADC value. 80 | float sorted_values[N]; 81 | std::rotate(&previous_values_[0], 82 | &previous_values_[1], 83 | &previous_values_[N]); 84 | previous_values_[N - 1] = note; 85 | std::copy( 86 | &previous_values_[0], &previous_values_[N], &sorted_values[0]); 87 | std::sort(&sorted_values[0], &sorted_values[N]); 88 | float median 89 | = 0.5f * (sorted_values[(N - 1) / 2] + sorted_values[N / 2]); 90 | 91 | // Adaptive lag processor. 92 | note_ += coefficient_ * (median - note_); 93 | stable_note_ += stable_coefficient_ * (note_ - stable_note_); 94 | 95 | coefficient_ 96 | += lag_coefficient_ * (slow_coefficient_ - coefficient_); 97 | stable_coefficient_ 98 | += lag_coefficient_ * (lag_coefficient_ - stable_coefficient_); 99 | 100 | delayed_stable_note_.Write(stable_note_); 101 | } 102 | return note_; 103 | } 104 | 105 | inline float note() const { return note_; } 106 | 107 | inline float stable_note() const { return delayed_stable_note_.Read(); } 108 | 109 | private: 110 | float previous_values_[N]; 111 | float note_; 112 | float stable_note_; 113 | stmlib::DelayLine delayed_stable_note_; 114 | 115 | float coefficient_; 116 | float stable_coefficient_; 117 | 118 | float fast_coefficient_; 119 | float slow_coefficient_; 120 | float lag_coefficient_; 121 | }; 122 | 123 | } // namespace torus 124 | 125 | #endif // TORUS_DSP_NOTE_FILTER_H_ 126 | -------------------------------------------------------------------------------- /patch_algodaisy/TorusPlus/dsp/onset_detector.h: -------------------------------------------------------------------------------- 1 | // Copyright 2015 Emilie Gillet. 2 | // 3 | // Author: Emilie Gillet (emilie.o.gillet@gmail.com) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | // THE SOFTWARE. 22 | // 23 | // See http://creativecommons.org/licenses/MIT/ for more information. 24 | // 25 | // ----------------------------------------------------------------------------- 26 | // 27 | // Onset detector. 28 | 29 | #ifndef TORUS_DSP_ONSET_DETECTOR_H_ 30 | #define TORUS_DSP_ONSET_DETECTOR_H_ 31 | 32 | #include "stmlib/stmlib.h" 33 | 34 | #include 35 | 36 | #include "stmlib/dsp/dsp.h" 37 | #include "stmlib/dsp/filter.h" 38 | 39 | namespace torus 40 | { 41 | using namespace std; 42 | using namespace stmlib; 43 | 44 | class ZScorer 45 | { 46 | public: 47 | ZScorer() {} 48 | ~ZScorer() {} 49 | 50 | void Init(float cutoff) 51 | { 52 | coefficient_ = cutoff; 53 | mean_ = 0.0f; 54 | variance_ = 0.00f; 55 | } 56 | 57 | inline float Normalize(float sample) 58 | { 59 | return Update(sample) / Sqrt(variance_); 60 | } 61 | 62 | inline bool Test(float sample, float threshold) 63 | { 64 | float value = Update(sample); 65 | return value > Sqrt(variance_) * threshold; 66 | } 67 | 68 | inline bool Test(float sample, float threshold, float absolute_threshold) 69 | { 70 | float value = Update(sample); 71 | return value > Sqrt(variance_) * threshold 72 | && value > absolute_threshold; 73 | } 74 | 75 | private: 76 | inline float Update(float sample) 77 | { 78 | float centered = sample - mean_; 79 | mean_ += coefficient_ * centered; 80 | variance_ += coefficient_ * (centered * centered - variance_); 81 | return centered; 82 | } 83 | 84 | float coefficient_; 85 | float mean_; 86 | float variance_; 87 | 88 | DISALLOW_COPY_AND_ASSIGN(ZScorer); 89 | }; 90 | 91 | class Compressor 92 | { 93 | public: 94 | Compressor() {} 95 | ~Compressor() {} 96 | 97 | void Init(float attack, float decay, float max_gain) 98 | { 99 | attack_ = attack; 100 | decay_ = decay; 101 | level_ = 0.0f; 102 | skew_ = 1.0f / max_gain; 103 | } 104 | 105 | void Process(const float* in, float* out, size_t size) 106 | { 107 | float level = level_; 108 | while(size--) 109 | { 110 | SLOPE(level, fabs(*in), attack_, decay_); 111 | *out++ = *in++ / (skew_ + level); 112 | } 113 | level_ = level; 114 | } 115 | 116 | private: 117 | float attack_; 118 | float decay_; 119 | float level_; 120 | float skew_; 121 | 122 | DISALLOW_COPY_AND_ASSIGN(Compressor); 123 | }; 124 | 125 | class OnsetDetector 126 | { 127 | public: 128 | OnsetDetector() {} 129 | ~OnsetDetector() {} 130 | 131 | void Init(float low, 132 | float low_mid, 133 | float mid_high, 134 | float decimated_sr, 135 | float ioi_time) 136 | { 137 | float ioi_f = 1.0f / (ioi_time * decimated_sr); 138 | compressor_.Init(ioi_f * 10.0f, ioi_f * 0.05f, 40.0f); 139 | 140 | low_mid_filter_.Init(); 141 | mid_high_filter_.Init(); 142 | low_mid_filter_.set_f_q(low_mid, 0.5f); 143 | mid_high_filter_.set_f_q(mid_high, 0.5f); 144 | 145 | attack_[0] = low_mid; 146 | decay_[0] = low * 0.25f; 147 | 148 | attack_[1] = low_mid; 149 | decay_[1] = low * 0.25f; 150 | 151 | attack_[2] = low_mid; 152 | decay_[2] = low * 0.25f; 153 | 154 | fill(&envelope_[0], &envelope_[3], 0.0f); 155 | fill(&energy_[0], &energy_[3], 0.0f); 156 | 157 | z_df_.Init(ioi_f * 0.05f); 158 | 159 | inhibit_time_ = static_cast(ioi_time * decimated_sr); 160 | inhibit_decay_ = 1.0f / (ioi_time * decimated_sr); 161 | 162 | inhibit_threshold_ = 0.0f; 163 | inhibit_counter_ = 0; 164 | onset_df_ = 0.0f; 165 | } 166 | 167 | bool Process(const float* samples, size_t size) 168 | { 169 | // Automatic gain control. 170 | compressor_.Process(samples, bands_[0], size); 171 | 172 | // Quick and dirty filter bank - split the signal in three bands. 173 | mid_high_filter_.Split(bands_[0], bands_[1], bands_[2], size); 174 | low_mid_filter_.Split(bands_[1], bands_[0], bands_[1], size); 175 | 176 | // Compute low-pass energy and onset detection function 177 | // (derivative of energy) in each band. 178 | float onset_df = 0.0f; 179 | float total_energy = 0.0f; 180 | for(int32_t i = 0; i < 3; ++i) 181 | { 182 | float* s = bands_[i]; 183 | float energy = 0.0f; 184 | float envelope = envelope_[i]; 185 | size_t increment = 4 >> i; 186 | for(size_t j = 0; j < size; j += increment) 187 | { 188 | SLOPE(envelope, s[j] * s[j], attack_[i], decay_[i]); 189 | energy += envelope; 190 | } 191 | energy = Sqrt(energy) * float(increment); 192 | envelope_[i] = envelope; 193 | 194 | float derivative = energy - energy_[i]; 195 | onset_df += derivative + fabs(derivative); 196 | energy_[i] = energy; 197 | total_energy += energy; 198 | } 199 | 200 | onset_df_ += 0.05f * (onset_df - onset_df_); 201 | bool outlier_in_df = z_df_.Test(onset_df_, 1.0f, 0.01f); 202 | bool exceeds_energy_threshold = total_energy >= inhibit_threshold_; 203 | bool not_inhibited = !inhibit_counter_; 204 | bool has_onset 205 | = outlier_in_df && exceeds_energy_threshold && not_inhibited; 206 | 207 | if(has_onset) 208 | { 209 | inhibit_threshold_ = total_energy * 1.5f; 210 | inhibit_counter_ = inhibit_time_; 211 | } 212 | else 213 | { 214 | inhibit_threshold_ -= inhibit_decay_ * inhibit_threshold_; 215 | if(inhibit_counter_) 216 | { 217 | --inhibit_counter_; 218 | } 219 | } 220 | return has_onset; 221 | } 222 | 223 | private: 224 | Compressor compressor_; 225 | NaiveSvf low_mid_filter_; 226 | NaiveSvf mid_high_filter_; 227 | 228 | float attack_[3]; 229 | float decay_[3]; 230 | float energy_[3]; 231 | float envelope_[3]; 232 | float onset_df_; 233 | 234 | float bands_[3][48]; 235 | 236 | ZScorer z_df_; 237 | 238 | float inhibit_threshold_; 239 | float inhibit_decay_; 240 | int32_t inhibit_time_; 241 | int32_t inhibit_counter_; 242 | }; 243 | 244 | } // namespace torus 245 | 246 | #endif // TORUS_DSP_ONSET_DETECTOR_H_ 247 | -------------------------------------------------------------------------------- /patch_algodaisy/TorusPlus/dsp/part.h: -------------------------------------------------------------------------------- 1 | // Copyright 2015 Emilie Gillet. 2 | // 3 | // Author: Emilie Gillet (emilie.o.gillet@gmail.com) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | // THE SOFTWARE. 22 | // 23 | // See http://creativecommons.org/licenses/MIT/ for more information. 24 | // 25 | // ----------------------------------------------------------------------------- 26 | // 27 | // Group of voices. 28 | 29 | #ifndef TORUS_DSP_PART_H_ 30 | #define TORUS_DSP_PART_H_ 31 | 32 | #include 33 | 34 | #include "stmlib/stmlib.h" 35 | #include "stmlib/dsp/cosine_oscillator.h" 36 | #include "stmlib/dsp/delay_line.h" 37 | 38 | #include "dsp/dsp.h" 39 | #include "dsp/fm_voice.h" 40 | #include "dsp/fx/reverb.h" 41 | #include "dsp/limiter.h" 42 | #include "dsp/note_filter.h" 43 | #include "dsp/patch.h" 44 | #include "dsp/performance_state.h" 45 | #include "dsp/plucker.h" 46 | #include "dsp/resonator.h" 47 | #include "dsp/string.h" 48 | 49 | namespace torus 50 | { 51 | enum ResonatorModel 52 | { 53 | RESONATOR_MODEL_MODAL, 54 | RESONATOR_MODEL_SYMPATHETIC_STRING, 55 | RESONATOR_MODEL_STRING, 56 | 57 | // Bonus! 58 | RESONATOR_MODEL_FM_VOICE, 59 | RESONATOR_MODEL_SYMPATHETIC_STRING_QUANTIZED, 60 | RESONATOR_MODEL_STRING_AND_REVERB, 61 | RESONATOR_MODEL_LAST 62 | }; 63 | 64 | const int32_t kMaxPolyphony = 4; 65 | const int32_t kNumStrings = kMaxPolyphony * 2; 66 | 67 | class Part 68 | { 69 | public: 70 | Part() {} 71 | ~Part() {} 72 | 73 | void Init(uint16_t* reverb_buffer); 74 | 75 | void Process(const PerformanceState& performance_state, 76 | const Patch& patch, 77 | const float* in, 78 | float* out, 79 | float* aux, 80 | size_t size); 81 | 82 | inline bool bypass() const { return bypass_; } 83 | inline void set_bypass(bool bypass) { bypass_ = bypass; } 84 | 85 | inline int32_t polyphony() const { return polyphony_; } 86 | inline void set_polyphony(int32_t polyphony) 87 | { 88 | int32_t old_polyphony = polyphony_; 89 | polyphony_ = std::min(polyphony, kMaxPolyphony); 90 | for(int32_t i = old_polyphony; i < polyphony_; ++i) 91 | { 92 | note_[i] = note_[0] + i * 0.05f; 93 | } 94 | dirty_ = true; 95 | } 96 | 97 | inline ResonatorModel model() const { return model_; } 98 | inline void set_model(ResonatorModel model) 99 | { 100 | if(model != model_) 101 | { 102 | model_ = model; 103 | dirty_ = true; 104 | } 105 | } 106 | 107 | private: 108 | void ConfigureResonators(); 109 | void RenderModalVoice(int32_t voice, 110 | const PerformanceState& performance_state, 111 | const Patch& patch, 112 | float frequency, 113 | float filter_cutoff, 114 | size_t size); 115 | void RenderStringVoice(int32_t voice, 116 | const PerformanceState& performance_state, 117 | const Patch& patch, 118 | float frequency, 119 | float filter_cutoff, 120 | size_t size); 121 | void RenderFMVoice(int32_t voice, 122 | const PerformanceState& performance_state, 123 | const Patch& patch, 124 | float frequency, 125 | float filter_cutoff, 126 | size_t size); 127 | 128 | 129 | inline float Squash(float x) const 130 | { 131 | if(x < 0.5f) 132 | { 133 | x *= 2.0f; 134 | x *= x; 135 | x *= x; 136 | x *= x; 137 | x *= x; 138 | x *= 0.5f; 139 | } 140 | else 141 | { 142 | x = 2.0f - 2.0f * x; 143 | x *= x; 144 | x *= x; 145 | x *= x; 146 | x *= x; 147 | x = 1.0f - 0.5f * x; 148 | } 149 | return x; 150 | } 151 | 152 | void ComputeSympatheticStringsNotes(float tonic, 153 | float note, 154 | float parameter, 155 | float* destination, 156 | size_t num_strings); 157 | 158 | bool bypass_; 159 | bool dirty_; 160 | 161 | ResonatorModel model_; 162 | 163 | int32_t num_voices_; 164 | int32_t active_voice_; 165 | uint32_t step_counter_; 166 | int32_t polyphony_; 167 | 168 | Resonator resonator_[kMaxPolyphony]; 169 | String string_[kNumStrings]; 170 | stmlib::CosineOscillator lfo_[kNumStrings]; 171 | FMVoice fm_voice_[kMaxPolyphony]; 172 | 173 | stmlib::Svf excitation_filter_[kMaxPolyphony]; 174 | stmlib::DCBlocker dc_blocker_[kMaxPolyphony]; 175 | Plucker plucker_[kMaxPolyphony]; 176 | 177 | float note_[kMaxPolyphony]; 178 | NoteFilter note_filter_; 179 | 180 | float resonator_input_[kMaxBlockSize]; 181 | float sympathetic_resonator_input_[kMaxBlockSize]; 182 | float noise_burst_buffer_[kMaxBlockSize]; 183 | 184 | float out_buffer_[kMaxBlockSize]; 185 | float aux_buffer_[kMaxBlockSize]; 186 | 187 | Reverb reverb_; 188 | Limiter limiter_; 189 | 190 | static float model_gains_[RESONATOR_MODEL_LAST]; 191 | 192 | DISALLOW_COPY_AND_ASSIGN(Part); 193 | }; 194 | 195 | } // namespace torus 196 | 197 | #endif // TORUS_DSP_PART_H_ 198 | -------------------------------------------------------------------------------- /patch_algodaisy/TorusPlus/dsp/patch.h: -------------------------------------------------------------------------------- 1 | // Copyright 2015 Emilie Gillet. 2 | // 3 | // Author: Emilie Gillet (emilie.o.gillet@gmail.com) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | // THE SOFTWARE. 22 | // 23 | // See http://creativecommons.org/licenses/MIT/ for more information. 24 | // 25 | // ----------------------------------------------------------------------------- 26 | // 27 | // Patch parameters. 28 | 29 | #ifndef TORUS_DSP_PATCH_H_ 30 | #define TORUS_DSP_PATCH_H_ 31 | 32 | namespace torus 33 | { 34 | struct Patch 35 | { 36 | float structure; 37 | float brightness; 38 | float damping; 39 | float position; 40 | }; 41 | 42 | } // namespace torus 43 | 44 | #endif // TORUS_DSP_PATCH_H_ 45 | -------------------------------------------------------------------------------- /patch_algodaisy/TorusPlus/dsp/performance_state.h: -------------------------------------------------------------------------------- 1 | // Copyright 2015 Emilie Gillet. 2 | // 3 | // Author: Emilie Gillet (emilie.o.gillet@gmail.com) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | // THE SOFTWARE. 22 | // 23 | // See http://creativecommons.org/licenses/MIT/ for more information. 24 | // 25 | // ----------------------------------------------------------------------------- 26 | // 27 | // Note triggering state. 28 | 29 | #ifndef TORUS_DSP_PERFORMANCE_STATE_H_ 30 | #define TORUS_DSP_PERFORMANCE_STATE_H_ 31 | 32 | namespace torus 33 | { 34 | const int32_t kNumChords = 11; 35 | 36 | struct PerformanceState 37 | { 38 | bool strum; 39 | bool internal_exciter; 40 | bool internal_strum; 41 | bool internal_note; 42 | 43 | float tonic; 44 | float note; 45 | float fm; 46 | int32_t chord; 47 | }; 48 | 49 | } // namespace torus 50 | 51 | #endif // TORUS_DSP_PERFORMANCE_STATE_H_ 52 | -------------------------------------------------------------------------------- /patch_algodaisy/TorusPlus/dsp/plucker.h: -------------------------------------------------------------------------------- 1 | // Copyright 2015 Emilie Gillet. 2 | // 3 | // Author: Emilie Gillet (emilie.o.gillet@gmail.com) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | // THE SOFTWARE. 22 | // 23 | // See http://creativecommons.org/licenses/MIT/ for more information. 24 | // 25 | // ----------------------------------------------------------------------------- 26 | // 27 | // Noise burst generator for Karplus-Strong synthesis. 28 | 29 | #ifndef TORUS_DSP_PLUCKER_H_ 30 | #define TORUS_DSP_PLUCKER_H_ 31 | 32 | #include "stmlib/stmlib.h" 33 | 34 | #include 35 | 36 | #include "stmlib/dsp/filter.h" 37 | #include "stmlib/dsp/delay_line.h" 38 | #include "stmlib/utils/random.h" 39 | 40 | namespace torus 41 | { 42 | class Plucker 43 | { 44 | public: 45 | Plucker() {} 46 | ~Plucker() {} 47 | 48 | void Init() 49 | { 50 | svf_.Init(); 51 | comb_filter_.Init(); 52 | remaining_samples_ = 0; 53 | comb_filter_period_ = 0.0f; 54 | } 55 | 56 | void Trigger(float frequency, float cutoff, float position) 57 | { 58 | float ratio = position * 0.9f + 0.05f; 59 | float comb_period = 1.0f / frequency * ratio; 60 | remaining_samples_ = static_cast(comb_period); 61 | while(comb_period >= 255.0f) 62 | { 63 | comb_period *= 0.5f; 64 | } 65 | comb_filter_period_ = comb_period; 66 | comb_filter_gain_ = (1.0f - position) * 0.8f; 67 | svf_.set_f_q(std::min(cutoff, 0.499f), 1.0f); 68 | } 69 | 70 | void Process(float* out, size_t size) 71 | { 72 | const float comb_gain = comb_filter_gain_; 73 | const float comb_delay = comb_filter_period_; 74 | for(size_t i = 0; i < size; ++i) 75 | { 76 | float in = 0.0f; 77 | if(remaining_samples_) 78 | { 79 | in = 2.0f * Random::GetFloat() - 1.0f; 80 | --remaining_samples_; 81 | } 82 | out[i] = in + comb_gain * comb_filter_.Read(comb_delay); 83 | comb_filter_.Write(out[i]); 84 | } 85 | svf_.Process(out, out, size); 86 | } 87 | 88 | private: 89 | stmlib::Svf svf_; 90 | stmlib::DelayLine comb_filter_; 91 | size_t remaining_samples_; 92 | float comb_filter_period_; 93 | float comb_filter_gain_; 94 | 95 | DISALLOW_COPY_AND_ASSIGN(Plucker); 96 | }; 97 | 98 | } // namespace torus 99 | 100 | #endif // TORUS_DSP_PLUCKER_H_ 101 | -------------------------------------------------------------------------------- /patch_algodaisy/TorusPlus/dsp/resonator.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2015 Emilie Gillet. 2 | // 3 | // Author: Emilie Gillet (emilie.o.gillet@gmail.com) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | // THE SOFTWARE. 22 | // 23 | // See http://creativecommons.org/licenses/MIT/ for more information. 24 | // 25 | // ----------------------------------------------------------------------------- 26 | // 27 | // Resonator. 28 | 29 | #include "dsp/resonator.h" 30 | 31 | #include "stmlib/dsp/dsp.h" 32 | #include "stmlib/dsp/cosine_oscillator.h" 33 | #include "stmlib/dsp/parameter_interpolator.h" 34 | 35 | #include "resources.h" 36 | 37 | namespace torus 38 | { 39 | using namespace std; 40 | using namespace stmlib; 41 | 42 | void Resonator::Init() 43 | { 44 | for(int32_t i = 0; i < kMaxModes; ++i) 45 | { 46 | f_[i].Init(); 47 | } 48 | 49 | set_frequency(220.0f / kSampleRate); 50 | set_structure(0.25f); 51 | set_brightness(0.5f); 52 | set_damping(0.3f); 53 | set_position(0.999f); 54 | previous_position_ = 0.0f; 55 | set_resolution(kMaxModes); 56 | } 57 | 58 | int32_t Resonator::ComputeFilters() 59 | { 60 | float stiffness = Interpolate(lut_stiffness, structure_, 256.0f); 61 | float harmonic = frequency_; 62 | float stretch_factor = 1.0f; 63 | float q = 500.0f * Interpolate(lut_4_decades, damping_, 256.0f); 64 | float brightness_attenuation = 1.0f - structure_; 65 | // Reduces the range of brightness when structure is very low, to prevent 66 | // clipping. 67 | brightness_attenuation *= brightness_attenuation; 68 | brightness_attenuation *= brightness_attenuation; 69 | brightness_attenuation *= brightness_attenuation; 70 | float brightness = brightness_ * (1.0f - 0.2f * brightness_attenuation); 71 | float q_loss = brightness * (2.0f - brightness) * 0.85f + 0.15f; 72 | float q_loss_damping_rate = structure_ * (2.0f - structure_) * 0.1f; 73 | int32_t num_modes = 0; 74 | for(int32_t i = 0; i < min(kMaxModes, resolution_); ++i) 75 | { 76 | float partial_frequency = harmonic * stretch_factor; 77 | if(partial_frequency >= 0.49f) 78 | { 79 | partial_frequency = 0.49f; 80 | } 81 | else 82 | { 83 | num_modes = i + 1; 84 | } 85 | f_[i].set_f_q(partial_frequency, 86 | 1.0f + partial_frequency * q); 87 | stretch_factor += stiffness; 88 | if(stiffness < 0.0f) 89 | { 90 | // Make sure that the partials do not fold back into negative frequencies. 91 | stiffness *= 0.93f; 92 | } 93 | else 94 | { 95 | // This helps adding a few extra partials in the highest frequencies. 96 | stiffness *= 0.98f; 97 | } 98 | // This prevents the highest partials from decaying too fast. 99 | q_loss += q_loss_damping_rate * (1.0f - q_loss); 100 | harmonic += frequency_; 101 | q *= q_loss; 102 | } 103 | 104 | return num_modes; 105 | } 106 | 107 | void Resonator::Process(const float* in, float* out, float* aux, size_t size) 108 | { 109 | int32_t num_modes = ComputeFilters(); 110 | 111 | ParameterInterpolator position(&previous_position_, position_, size); 112 | while(size--) 113 | { 114 | CosineOscillator amplitudes; 115 | amplitudes.Init(position.Next()); 116 | 117 | float input = *in++ * 0.125f; 118 | float odd = 0.0f; 119 | float even = 0.0f; 120 | amplitudes.Start(); 121 | for(int32_t i = 0; i < num_modes;) 122 | { 123 | odd += amplitudes.Next() 124 | * f_[i++].Process(input); 125 | even += amplitudes.Next() 126 | * f_[i++].Process(input); 127 | } 128 | *out++ = odd; 129 | *aux++ = even; 130 | } 131 | } 132 | 133 | } // namespace torus 134 | -------------------------------------------------------------------------------- /patch_algodaisy/TorusPlus/dsp/resonator.h: -------------------------------------------------------------------------------- 1 | // Copyright 2015 Emilie Gillet. 2 | // 3 | // Author: Emilie Gillet (emilie.o.gillet@gmail.com) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | // THE SOFTWARE. 22 | // 23 | // See http://creativecommons.org/licenses/MIT/ for more information. 24 | // 25 | // ----------------------------------------------------------------------------- 26 | // 27 | // Resonator. 28 | 29 | #ifndef TORUS_DSP_RESONATOR_H_ 30 | #define TORUS_DSP_RESONATOR_H_ 31 | 32 | #include "stmlib/stmlib.h" 33 | 34 | #include 35 | 36 | #include "dsp/dsp.h" 37 | #include "stmlib/dsp/filter.h" 38 | #include "stmlib/dsp/delay_line.h" 39 | 40 | namespace torus 41 | { 42 | const int32_t kMaxModes = 64; 43 | 44 | class Resonator 45 | { 46 | public: 47 | Resonator() {} 48 | ~Resonator() {} 49 | 50 | void Init(); 51 | void Process(const float* in, float* out, float* aux, size_t size); 52 | 53 | inline void set_frequency(float frequency) { frequency_ = frequency; } 54 | 55 | inline void set_structure(float structure) { structure_ = structure; } 56 | 57 | inline void set_brightness(float brightness) { brightness_ = brightness; } 58 | 59 | inline void set_damping(float damping) { damping_ = damping; } 60 | 61 | inline void set_position(float position) { position_ = position; } 62 | 63 | inline void set_resolution(int32_t resolution) 64 | { 65 | resolution -= resolution & 1; // Must be even! 66 | resolution_ = std::min(resolution, kMaxModes); 67 | } 68 | 69 | private: 70 | int32_t ComputeFilters(); 71 | float frequency_; 72 | float structure_; 73 | float brightness_; 74 | float position_; 75 | float previous_position_; 76 | float damping_; 77 | 78 | int32_t resolution_; 79 | 80 | stmlib::Svf f_[kMaxModes]; 81 | 82 | DISALLOW_COPY_AND_ASSIGN(Resonator); 83 | }; 84 | 85 | } // namespace torus 86 | 87 | #endif // TORUS_DSP_RESONATOR_H_ 88 | -------------------------------------------------------------------------------- /patch_algodaisy/TorusPlus/dsp/string.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2015 Emilie Gillet. 2 | // 3 | // Author: Emilie Gillet (emilie.o.gillet@gmail.com) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | // THE SOFTWARE. 22 | // 23 | // See http://creativecommons.org/licenses/MIT/ for more information. 24 | // 25 | // ----------------------------------------------------------------------------- 26 | // 27 | // Comb filter / KS string. 28 | 29 | #include "dsp/string.h" 30 | 31 | #include 32 | 33 | #include "stmlib/dsp/dsp.h" 34 | #include "stmlib/dsp/parameter_interpolator.h" 35 | #include "stmlib/dsp/units.h" 36 | #include "stmlib/utils/random.h" 37 | 38 | #include "resources.h" 39 | 40 | namespace torus 41 | { 42 | using namespace std; 43 | using namespace stmlib; 44 | 45 | void String::Init(bool enable_dispersion) 46 | { 47 | enable_dispersion_ = enable_dispersion; 48 | 49 | string_.Init(); 50 | stretch_.Init(); 51 | fir_damping_filter_.Init(); 52 | iir_damping_filter_.Init(); 53 | 54 | set_frequency(220.0f / kSampleRate); 55 | set_dispersion(0.25f); 56 | set_brightness(0.5f); 57 | set_damping(0.3f); 58 | set_position(0.8f); 59 | 60 | delay_ = 1.0f / frequency_; 61 | clamped_position_ = 0.0f; 62 | previous_dispersion_ = 0.0f; 63 | dispersion_noise_ = 0.0f; 64 | curved_bridge_ = 0.0f; 65 | previous_damping_compensation_ = 0.0f; 66 | 67 | out_sample_[0] = out_sample_[1] = 0.0f; 68 | aux_sample_[0] = aux_sample_[1] = 0.0f; 69 | 70 | dc_blocker_.Init(1.0f - 20.0f / kSampleRate); 71 | } 72 | 73 | template 74 | void String::ProcessInternal(const float* in, 75 | float* out, 76 | float* aux, 77 | size_t size) 78 | { 79 | float delay = 1.0f / frequency_; 80 | CONSTRAIN(delay, 4.0f, kDelayLineSize - 4.0f); 81 | 82 | // If there is not enough delay time in the delay line, we play at the 83 | // lowest possible note and we upsample on the fly with a shitty linear 84 | // interpolator. We don't care because it's a corner case (f0 < 11.7Hz) 85 | float src_ratio = delay * frequency_; 86 | if(src_ratio >= 0.9999f) 87 | { 88 | // When we are above 11.7 Hz, we make sure that the linear interpolator 89 | // does not get in the way. 90 | src_phase_ = 1.0f; 91 | src_ratio = 1.0f; 92 | } 93 | 94 | float clamped_position = 0.5f - 0.98f * fabs(position_ - 0.5f); 95 | 96 | // Linearly interpolate all comb-related CV parameters for each sample. 97 | ParameterInterpolator delay_modulation(&delay_, delay, size); 98 | ParameterInterpolator position_modulation( 99 | &clamped_position_, clamped_position, size); 100 | ParameterInterpolator dispersion_modulation( 101 | &previous_dispersion_, dispersion_, size); 102 | 103 | // For damping/absorption, the interpolation is done in the filter code. 104 | float lf_damping = damping_ * (2.0f - damping_); 105 | float rt60 = 0.07f * SemitonesToRatio(lf_damping * 96.0f) * kSampleRate; 106 | float rt60_base_2_12 = max(-120.0f * delay / src_ratio / rt60, -127.0f); 107 | float damping_coefficient = SemitonesToRatio(rt60_base_2_12); 108 | float brightness = brightness_ * brightness_; 109 | float noise_filter = SemitonesToRatio((brightness_ - 1.0f) * 48.0f); 110 | float damping_cutoff = min(24.0f + damping_ * damping_ * 48.0f 111 | + brightness_ * brightness_ * 24.0f, 112 | 84.0f); 113 | float damping_f 114 | = min(frequency_ * SemitonesToRatio(damping_cutoff), 0.499f); 115 | 116 | // Crossfade to infinite decay. 117 | if(damping_ >= 0.95f) 118 | { 119 | float to_infinite = 20.0f * (damping_ - 0.95f); 120 | damping_coefficient += to_infinite * (1.0f - damping_coefficient); 121 | brightness += to_infinite * (1.0f - brightness); 122 | damping_f += to_infinite * (0.4999f - damping_f); 123 | damping_cutoff += to_infinite * (128.0f - damping_cutoff); 124 | } 125 | 126 | fir_damping_filter_.Configure(damping_coefficient, brightness, size); 127 | iir_damping_filter_.set_f_q(damping_f, 0.5f); 128 | ParameterInterpolator damping_compensation_modulation( 129 | &previous_damping_compensation_, 130 | 1.0f - Interpolate(lut_svf_shift, damping_cutoff, 1.0f), 131 | size); 132 | 133 | while(size--) 134 | { 135 | src_phase_ += src_ratio; 136 | if(src_phase_ > 1.0f) 137 | { 138 | src_phase_ -= 1.0f; 139 | 140 | float delay = delay_modulation.Next(); 141 | float comb_delay = delay * position_modulation.Next(); 142 | 143 | #ifndef MIC_W 144 | delay *= damping_compensation_modulation.Next(); // IIR delay. 145 | #endif // MIC_W 146 | delay -= 1.0f; // FIR delay. 147 | 148 | float s = 0.0f; 149 | 150 | if(enable_dispersion) 151 | { 152 | float noise = 2.0f * Random::GetFloat() - 1.0f; 153 | noise *= 1.0f / (0.2f + noise_filter); 154 | dispersion_noise_ += noise_filter * (noise - dispersion_noise_); 155 | 156 | float dispersion = dispersion_modulation.Next(); 157 | float stretch_point 158 | = dispersion <= 0.0f 159 | ? 0.0f 160 | : dispersion * (2.0f - dispersion) * 0.475f; 161 | float noise_amount 162 | = dispersion > 0.75f ? 4.0f * (dispersion - 0.75f) : 0.0f; 163 | float bridge_curving = dispersion < 0.0f ? -dispersion : 0.0f; 164 | 165 | noise_amount = noise_amount * noise_amount * 0.025f; 166 | float ac_blocking_amount = bridge_curving; 167 | 168 | bridge_curving = bridge_curving * bridge_curving * 0.01f; 169 | float ap_gain 170 | = -0.618f * dispersion / (0.15f + fabs(dispersion)); 171 | 172 | float delay_fm = 1.0f; 173 | delay_fm += dispersion_noise_ * noise_amount; 174 | delay_fm -= curved_bridge_ * bridge_curving; 175 | delay *= delay_fm; 176 | 177 | float ap_delay = delay * stretch_point; 178 | float main_delay = delay - ap_delay; 179 | if(ap_delay >= 4.0f && main_delay >= 4.0f) 180 | { 181 | s = string_.ReadHermite(main_delay); 182 | s = stretch_.Allpass(s, ap_delay, ap_gain); 183 | } 184 | else 185 | { 186 | s = string_.ReadHermite(delay); 187 | } 188 | float s_ac = s; 189 | dc_blocker_.Process(&s_ac, 1); 190 | s += ac_blocking_amount * (s_ac - s); 191 | 192 | float value = fabs(s) - 0.025f; 193 | float sign = s > 0.0f ? 1.0f : -1.5f; 194 | curved_bridge_ = (fabs(value) + value) * sign; 195 | } 196 | else 197 | { 198 | s = string_.ReadHermite(delay); 199 | } 200 | 201 | s += *in; // When f0 < 11.7 Hz, causes ugly bitcrushing on the input! 202 | s = fir_damping_filter_.Process(s); 203 | #ifndef MIC_W 204 | s = iir_damping_filter_.Process(s); 205 | #endif // MIC_W 206 | string_.Write(s); 207 | 208 | out_sample_[1] = out_sample_[0]; 209 | aux_sample_[1] = aux_sample_[0]; 210 | 211 | out_sample_[0] = s; 212 | aux_sample_[0] = string_.Read(comb_delay); 213 | } 214 | *out++ += Crossfade(out_sample_[1], out_sample_[0], src_phase_); 215 | *aux++ += Crossfade(aux_sample_[1], aux_sample_[0], src_phase_); 216 | in++; 217 | } 218 | } 219 | 220 | void String::Process(const float* in, float* out, float* aux, size_t size) 221 | { 222 | if(enable_dispersion_) 223 | { 224 | ProcessInternal(in, out, aux, size); 225 | } 226 | else 227 | { 228 | ProcessInternal(in, out, aux, size); 229 | } 230 | } 231 | 232 | } // namespace torus 233 | -------------------------------------------------------------------------------- /patch_algodaisy/TorusPlus/dsp/string.h: -------------------------------------------------------------------------------- 1 | // Copyright 2015 Emilie Gillet. 2 | // 3 | // Author: Emilie Gillet (emilie.o.gillet@gmail.com) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | // THE SOFTWARE. 22 | // 23 | // See http://creativecommons.org/licenses/MIT/ for more information. 24 | // 25 | // ----------------------------------------------------------------------------- 26 | // 27 | // Comb filter / KS string. 28 | 29 | #ifndef TORUS_DSP_STRING_H_ 30 | #define TORUS_DSP_STRING_H_ 31 | 32 | #include "stmlib/stmlib.h" 33 | 34 | #include 35 | 36 | #include "stmlib/dsp/delay_line.h" 37 | #include "stmlib/dsp/filter.h" 38 | 39 | #include "dsp/dsp.h" 40 | 41 | namespace torus 42 | { 43 | const size_t kDelayLineSize = 2048; 44 | 45 | class DampingFilter 46 | { 47 | public: 48 | DampingFilter() {} 49 | ~DampingFilter() {} 50 | 51 | void Init() 52 | { 53 | x_ = 0.0f; 54 | x__ = 0.0f; 55 | brightness_ = 0.0f; 56 | brightness_increment_ = 0.0f; 57 | damping_ = 0.0f; 58 | damping_increment_ = 0.0f; 59 | } 60 | 61 | inline void Configure(float damping, float brightness, size_t size) 62 | { 63 | if(!size) 64 | { 65 | damping_ = damping; 66 | brightness_ = brightness; 67 | damping_increment_ = 0.0f; 68 | brightness_increment_ = 0.0f; 69 | } 70 | else 71 | { 72 | float step = 1.0f / static_cast(size); 73 | damping_increment_ = (damping - damping_) * step; 74 | brightness_increment_ = (brightness - brightness_) * step; 75 | } 76 | } 77 | 78 | inline float Process(float x) 79 | { 80 | float h0 = (1.0f + brightness_) * 0.5f; 81 | float h1 = (1.0f - brightness_) * 0.25f; 82 | float y = damping_ * (h0 * x_ + h1 * (x + x__)); 83 | x__ = x_; 84 | x_ = x; 85 | brightness_ += brightness_increment_; 86 | damping_ += damping_increment_; 87 | return y; 88 | } 89 | 90 | private: 91 | float x_; 92 | float x__; 93 | float brightness_; 94 | float brightness_increment_; 95 | float damping_; 96 | float damping_increment_; 97 | 98 | DISALLOW_COPY_AND_ASSIGN(DampingFilter); 99 | }; 100 | 101 | typedef stmlib::DelayLine StringDelayLine; 102 | typedef stmlib::DelayLine StiffnessDelayLine; 103 | 104 | class String 105 | { 106 | public: 107 | String() {} 108 | ~String() {} 109 | 110 | void Init(bool enable_dispersion); 111 | void Process(const float* in, float* out, float* aux, size_t size); 112 | 113 | inline void set_frequency(float frequency) { frequency_ = frequency; } 114 | 115 | inline void set_frequency(float frequency, float coefficient) 116 | { 117 | frequency_ += coefficient * (frequency - frequency_); 118 | } 119 | 120 | inline void set_dispersion(float dispersion) { dispersion_ = dispersion; } 121 | 122 | inline void set_brightness(float brightness) { brightness_ = brightness; } 123 | 124 | inline void set_damping(float damping) { damping_ = damping; } 125 | 126 | inline void set_position(float position) { position_ = position; } 127 | 128 | inline StringDelayLine* mutable_string() { return &string_; } 129 | 130 | private: 131 | template 132 | void ProcessInternal(const float* in, float* out, float* aux, size_t size); 133 | 134 | float frequency_; 135 | float dispersion_; 136 | float brightness_; 137 | float damping_; 138 | float position_; 139 | 140 | float delay_; 141 | float clamped_position_; 142 | float previous_dispersion_; 143 | float previous_damping_compensation_; 144 | 145 | bool enable_dispersion_; 146 | bool enable_iir_damping_; 147 | float dispersion_noise_; 148 | 149 | // Very crappy linear interpolation upsampler used for low pitches that 150 | // do not fit the delay line. Rarely used. 151 | float src_phase_; 152 | float out_sample_[2]; 153 | float aux_sample_[2]; 154 | 155 | float curved_bridge_; 156 | 157 | StringDelayLine string_; 158 | StiffnessDelayLine stretch_; 159 | 160 | DampingFilter fir_damping_filter_; 161 | stmlib::Svf iir_damping_filter_; 162 | stmlib::DCBlocker dc_blocker_; 163 | 164 | DISALLOW_COPY_AND_ASSIGN(String); 165 | }; 166 | 167 | } // namespace torus 168 | 169 | #endif // TORUS_DSP_STRING_H_ 170 | -------------------------------------------------------------------------------- /patch_algodaisy/TorusPlus/dsp/string_synth_envelope.h: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Emilie Gillet. 2 | // 3 | // Author: Emilie Gillet (emilie.o.gillet@gmail.com) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | // THE SOFTWARE. 22 | // 23 | // See http://creativecommons.org/licenses/MIT/ for more information. 24 | // 25 | // ----------------------------------------------------------------------------- 26 | // 27 | // AD envelope for the string synth. 28 | 29 | #ifndef ELEMENTS_DSP_STRING_SYNTH_ENVELOPE_H_ 30 | #define ELEMENTS_DSP_STRING_SYNTH_ENVELOPE_H_ 31 | 32 | #include "stmlib/stmlib.h" 33 | 34 | namespace torus 35 | { 36 | enum EnvelopeShape 37 | { 38 | ENVELOPE_SHAPE_LINEAR, 39 | ENVELOPE_SHAPE_QUARTIC 40 | }; 41 | 42 | enum EnvelopeFlags 43 | { 44 | ENVELOPE_FLAG_RISING_EDGE = 1, 45 | ENVELOPE_FLAG_FALLING_EDGE = 2, 46 | ENVELOPE_FLAG_GATE = 4 47 | }; 48 | 49 | class StringSynthEnvelope 50 | { 51 | public: 52 | StringSynthEnvelope() {} 53 | ~StringSynthEnvelope() {} 54 | 55 | void Init() 56 | { 57 | set_ad(0.1f, 0.001f); 58 | segment_ = num_segments_; 59 | phase_ = 0.0f; 60 | start_value_ = 0.0f; 61 | value_ = 0.0f; 62 | } 63 | 64 | inline float Process(uint8_t flags) 65 | { 66 | if(flags & ENVELOPE_FLAG_RISING_EDGE) 67 | { 68 | start_value_ = segment_ == num_segments_ ? level_[0] : value_; 69 | segment_ = 0; 70 | phase_ = 0.0f; 71 | } 72 | else if(flags & ENVELOPE_FLAG_FALLING_EDGE && sustain_point_) 73 | { 74 | start_value_ = value_; 75 | segment_ = sustain_point_; 76 | phase_ = 0.0f; 77 | } 78 | else if(phase_ >= 1.0f) 79 | { 80 | start_value_ = level_[segment_ + 1]; 81 | ++segment_; 82 | phase_ = 0.0f; 83 | } 84 | 85 | bool done = segment_ == num_segments_; 86 | bool sustained = sustain_point_ && segment_ == sustain_point_ 87 | && flags & ENVELOPE_FLAG_GATE; 88 | 89 | float phase_increment = 0.0f; 90 | if(!sustained && !done) 91 | { 92 | phase_increment = rate_[segment_]; 93 | } 94 | float t = phase_; 95 | if(shape_[segment_] == ENVELOPE_SHAPE_QUARTIC) 96 | { 97 | t = 1.0f - t; 98 | t *= t; 99 | t *= t; 100 | t = 1.0f - t; 101 | } 102 | 103 | phase_ += phase_increment; 104 | value_ = start_value_ + (level_[segment_ + 1] - start_value_) * t; 105 | return value_; 106 | } 107 | 108 | inline void set_ad(float attack, float decay) 109 | { 110 | num_segments_ = 2; 111 | sustain_point_ = 0; 112 | 113 | level_[0] = 0.0f; 114 | level_[1] = 1.0f; 115 | level_[2] = 0.0f; 116 | 117 | rate_[0] = attack; 118 | rate_[1] = decay; 119 | 120 | shape_[0] = ENVELOPE_SHAPE_LINEAR; 121 | shape_[1] = ENVELOPE_SHAPE_QUARTIC; 122 | } 123 | 124 | inline void set_ar(float attack, float decay) 125 | { 126 | num_segments_ = 2; 127 | sustain_point_ = 1; 128 | 129 | level_[0] = 0.0f; 130 | level_[1] = 1.0f; 131 | level_[2] = 0.0f; 132 | 133 | rate_[0] = attack; 134 | rate_[1] = decay; 135 | 136 | shape_[0] = ENVELOPE_SHAPE_LINEAR; 137 | shape_[1] = ENVELOPE_SHAPE_LINEAR; 138 | } 139 | 140 | private: 141 | float level_[4]; 142 | float rate_[4]; 143 | EnvelopeShape shape_[4]; 144 | 145 | int16_t segment_; 146 | float start_value_; 147 | float value_; 148 | float phase_; 149 | 150 | uint16_t num_segments_; 151 | uint16_t sustain_point_; 152 | 153 | DISALLOW_COPY_AND_ASSIGN(StringSynthEnvelope); 154 | }; 155 | 156 | } // namespace torus 157 | 158 | #endif // ELEMENTS_DSP_STRING_SYNTH_ENVELOPE_H_ 159 | -------------------------------------------------------------------------------- /patch_algodaisy/TorusPlus/dsp/string_synth_oscillator.h: -------------------------------------------------------------------------------- 1 | // Copyright 2015 Emilie Gillet. 2 | // 3 | // Author: Emilie Gillet (emilie.o.gillet@gmail.com) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | // THE SOFTWARE. 22 | // 23 | // See http://creativecommons.org/licenses/MIT/ for more information. 24 | // 25 | // ----------------------------------------------------------------------------- 26 | // 27 | // Polyblep oscillator used for string synth synthesis. 28 | 29 | #ifndef TORUS_DSP_STRING_SYNTH_OSCILLATOR_H_ 30 | #define TORUS_DSP_STRING_SYNTH_OSCILLATOR_H_ 31 | 32 | #include "stmlib/stmlib.h" 33 | 34 | #include "stmlib/dsp/dsp.h" 35 | #include "stmlib/dsp/parameter_interpolator.h" 36 | #include "stmlib/dsp/units.h" 37 | 38 | namespace torus 39 | { 40 | using namespace stmlib; 41 | 42 | enum OscillatorShape 43 | { 44 | OSCILLATOR_SHAPE_BRIGHT_SQUARE, 45 | OSCILLATOR_SHAPE_SQUARE, 46 | OSCILLATOR_SHAPE_DARK_SQUARE, 47 | OSCILLATOR_SHAPE_TRIANGLE, 48 | }; 49 | 50 | class StringSynthOscillator 51 | { 52 | public: 53 | StringSynthOscillator() {} 54 | ~StringSynthOscillator() {} 55 | 56 | inline void Init() 57 | { 58 | phase_ = 0.0f; 59 | phase_increment_ = 0.01f; 60 | filter_state_ = 0.0f; 61 | high_ = false; 62 | 63 | next_sample_ = 0.0f; 64 | next_sample_saw_ = 0.0f; 65 | 66 | gain_ = 0.0f; 67 | gain_saw_ = 0.0f; 68 | } 69 | 70 | template 71 | inline void Render(float target_increment, 72 | float target_gain, 73 | float target_gain_saw, 74 | float* out, 75 | size_t size) 76 | { 77 | // Cut harmonics above 12kHz, and low-pass harmonics above 8kHz to clear 78 | // highs 79 | if(target_increment >= 0.17f) 80 | { 81 | target_gain *= 1.0f - (target_increment - 0.17f) * 12.5f; 82 | if(target_increment >= 0.25f) 83 | { 84 | return; 85 | } 86 | } 87 | float phase = phase_; 88 | ParameterInterpolator phase_increment( 89 | &phase_increment_, target_increment, size); 90 | ParameterInterpolator gain(&gain_, target_gain, size); 91 | ParameterInterpolator gain_saw(&gain_saw_, target_gain_saw, size); 92 | 93 | float next_sample = next_sample_; 94 | float next_sample_saw = next_sample_saw_; 95 | float filter_state = filter_state_; 96 | bool high = high_; 97 | 98 | while(size--) 99 | { 100 | float this_sample = next_sample; 101 | float this_sample_saw = next_sample_saw; 102 | next_sample = 0.0f; 103 | next_sample_saw = 0.0f; 104 | 105 | float increment 106 | = interpolate_pitch ? phase_increment.Next() : target_increment; 107 | phase += increment; 108 | 109 | float sample = 0.0f; 110 | const float pw = 0.5f; 111 | 112 | if(!high && phase >= pw) 113 | { 114 | float t = (phase - pw) / increment; 115 | this_sample += ThisBlepSample(t); 116 | next_sample += NextBlepSample(t); 117 | high = true; 118 | } 119 | if(phase >= 1.0f) 120 | { 121 | phase -= 1.0f; 122 | float t = phase / increment; 123 | float a = ThisBlepSample(t); 124 | float b = NextBlepSample(t); 125 | this_sample -= a; 126 | next_sample -= b; 127 | this_sample_saw -= a; 128 | next_sample_saw -= b; 129 | high = false; 130 | } 131 | 132 | next_sample += phase < pw ? 0.0f : 1.0f; 133 | next_sample_saw += phase; 134 | 135 | if(shape == OSCILLATOR_SHAPE_TRIANGLE) 136 | { 137 | const float integrator_coefficient = increment * 0.125f; 138 | this_sample = 64.0f * (this_sample - 0.5f); 139 | filter_state 140 | += integrator_coefficient * (this_sample - filter_state); 141 | sample = filter_state; 142 | } 143 | else if(shape == OSCILLATOR_SHAPE_DARK_SQUARE) 144 | { 145 | const float integrator_coefficient = increment * 2.0f; 146 | this_sample = 4.0f * (this_sample - 0.5f); 147 | filter_state 148 | += integrator_coefficient * (this_sample - filter_state); 149 | sample = filter_state; 150 | } 151 | else if(shape == OSCILLATOR_SHAPE_BRIGHT_SQUARE) 152 | { 153 | const float integrator_coefficient = increment * 2.0f; 154 | this_sample = 2.0f * this_sample - 1.0f; 155 | filter_state 156 | += integrator_coefficient * (this_sample - filter_state); 157 | sample = (this_sample - filter_state) * 0.5f; 158 | } 159 | else 160 | { 161 | this_sample = 2.0f * this_sample - 1.0f; 162 | sample = this_sample; 163 | } 164 | this_sample_saw = 2.0f * this_sample_saw - 1.0f; 165 | 166 | *out++ += sample * gain.Next() + this_sample_saw * gain_saw.Next(); 167 | } 168 | high_ = high; 169 | phase_ = phase; 170 | next_sample_ = next_sample; 171 | next_sample_saw_ = next_sample_saw; 172 | filter_state_ = filter_state; 173 | } 174 | 175 | private: 176 | static inline float ThisBlepSample(float t) { return 0.5f * t * t; } 177 | static inline float NextBlepSample(float t) 178 | { 179 | t = 1.0f - t; 180 | return -0.5f * t * t; 181 | } 182 | 183 | bool high_; 184 | float phase_; 185 | float phase_increment_; 186 | float next_sample_; 187 | float next_sample_saw_; 188 | float filter_state_; 189 | float gain_; 190 | float gain_saw_; 191 | 192 | DISALLOW_COPY_AND_ASSIGN(StringSynthOscillator); 193 | }; 194 | 195 | } // namespace torus 196 | 197 | #endif // TORUS_DSP_STRING_SYNTH_OSCILLATOR_H_ 198 | -------------------------------------------------------------------------------- /patch_algodaisy/TorusPlus/dsp/string_synth_part.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2015 Emilie Gillet. 2 | // 3 | // Author: Emilie Gillet (emilie.o.gillet@gmail.com) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | // THE SOFTWARE. 22 | // 23 | // See http://creativecommons.org/licenses/MIT/ for more information. 24 | // 25 | // ----------------------------------------------------------------------------- 26 | // 27 | // String synth part. 28 | 29 | #include "dsp/string_synth_part.h" 30 | 31 | #include "dsp/dsp.h" 32 | 33 | namespace torus 34 | { 35 | using namespace std; 36 | using namespace stmlib; 37 | 38 | void StringSynthPart::Init(uint16_t* reverb_buffer) 39 | { 40 | active_group_ = 0; 41 | acquisition_delay_ = 0; 42 | 43 | polyphony_ = 1; 44 | fx_type_ = FX_ENSEMBLE; 45 | 46 | for(int32_t i = 0; i < kStringSynthVoices; ++i) 47 | { 48 | voice_[i].Init(); 49 | } 50 | 51 | for(int32_t i = 0; i < kMaxStringSynthPolyphony; ++i) 52 | { 53 | group_[i].tonic = 0.0f; 54 | group_[i].envelope.Init(); 55 | } 56 | 57 | for(int32_t i = 0; i < kNumFormants; ++i) 58 | { 59 | formant_filter_[i].Init(); 60 | } 61 | 62 | limiter_.Init(); 63 | 64 | reverb_.Init(reverb_buffer); 65 | chorus_.Init(reverb_buffer); 66 | ensemble_.Init(reverb_buffer); 67 | 68 | note_filter_.Init( 69 | kSampleRate / kMaxBlockSize, 70 | 0.001f, // Lag time with a sharp edge on the V/Oct input or trigger. 71 | 0.005f, // Lag time after the trigger has been received. 72 | 0.050f, // Time to transition from reactive to filtered. 73 | 0.004f); // Prevent a sharp edge to partly leak on the previous voice. 74 | } 75 | 76 | const int32_t kRegistrationTableSize = 11; 77 | const float registrations[kRegistrationTableSize][kNumHarmonics * 2] = { 78 | {1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, 79 | {1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f}, 80 | {1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f}, 81 | {1.0f, 0.1f, 0.0f, 0.0f, 1.0f, 0.0f}, 82 | {1.0f, 0.5f, 1.0f, 0.0f, 1.0f, 0.0f}, 83 | {1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f}, 84 | {0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f}, 85 | {0.0f, 0.5f, 1.0f, 0.0f, 1.0f, 0.0f}, 86 | {0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f}, 87 | {0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f}, 88 | {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f}, 89 | }; 90 | 91 | void StringSynthPart::ComputeRegistration(float gain, 92 | float registration, 93 | float* amplitudes) 94 | { 95 | registration *= (kRegistrationTableSize - 1.001f); 96 | MAKE_INTEGRAL_FRACTIONAL(registration); 97 | float total = 0.0f; 98 | for(int32_t i = 0; i < kNumHarmonics * 2; ++i) 99 | { 100 | float a = registrations[registration_integral][i]; 101 | float b = registrations[registration_integral + 1][i]; 102 | amplitudes[i] = a + (b - a) * registration_fractional; 103 | total += amplitudes[i]; 104 | } 105 | for(int32_t i = 0; i < kNumHarmonics * 2; ++i) 106 | { 107 | amplitudes[i] = gain * amplitudes[i] / total; 108 | } 109 | } 110 | 111 | #ifdef BRYAN_CHORDS 112 | 113 | // Chord table by Bryan Noll: 114 | // - more compact, leaving room for a bass 115 | // - more frequent note changes between adjacent chords. 116 | // - dropped fifth. 117 | const float chords[kMaxStringSynthPolyphony][kNumChords][kMaxChordSize] 118 | = {{ 119 | {-12.0f, -0.01f, 0.0f, 0.01f, 0.02f, 11.99f, 12.0f, 24.0f}, // OCT 120 | {-12.0f, -5.01f, -5.0f, 0.0f, 7.0f, 12.0f, 19.0f, 24.0f}, // 5 121 | {-12.0f, -5.0f, 0.0f, 5.0f, 7.0f, 12.0f, 17.0f, 24.0f}, // sus4 122 | {-12.0f, -5.0f, 0.0f, 0.01f, 3.0f, 12.0f, 19.0f, 24.0f}, // m 123 | {-12.0f, -5.01f, -5.0f, 0.0f, 3.0f, 10.0f, 19.0f, 24.0f}, // m7 124 | {-12.0f, -5.0f, 0.0f, 3.0f, 10.0f, 14.0f, 19.0f, 24.0f}, // m9 125 | {-12.0f, -5.01f, -5.0f, 0.0f, 3.0f, 10.0f, 17.0f, 24.0f}, // m11 126 | {-12.0f, -5.0f, 0.0f, 2.0f, 9.0f, 16.0f, 19.0f, 24.0f}, // 69 127 | {-12.0f, -5.0f, 0.0f, 4.0f, 11.0f, 14.0f, 19.0f, 24.0f}, // M9 128 | {-12.0f, -5.0f, 0.0f, 4.0f, 7.0f, 11.0f, 19.0f, 24.0f}, // M7 129 | {-12.0f, -5.0f, 0.0f, 4.0f, 7.0f, 12.0f, 19.0f, 24.0f}, // M 130 | }, 131 | { 132 | {-12.0f, -0.01f, 0.0f, 0.01f, 12.0f, 12.01f}, // OCT 133 | {-12.0f, -5.01f, -5.0f, 0.0f, 7.0f, 12.0f}, // 5 134 | {-12.0f, -5.0f, 0.0f, 5.0f, 7.0f, 12.0f}, // sus4 135 | {-12.0f, -5.0f, 0.0f, 0.01f, 3.0f, 12.0f}, // m 136 | {-12.0f, -5.01f, -5.0f, 0.0f, 3.0f, 10.0f}, // m7 137 | {-12.0f, -5.0f, 0.0f, 3.0f, 10.0f, 14.0f}, // m9 138 | {-12.0f, -5.0f, 0.0f, 3.0f, 10.0f, 17.0f}, // m11 139 | {-12.0f, -5.0f, 0.0f, 2.0f, 9.0f, 16.0f}, // 69 140 | {-12.0f, -5.0f, 0.0f, 4.0f, 11.0f, 14.0f}, // M9 141 | {-12.0f, -5.0f, 0.0f, 4.0f, 7.0f, 11.0f}, // M7 142 | {-12.0f, -5.0f, 0.0f, 4.0f, 7.0f, 12.0f}, // M 143 | }, 144 | { 145 | {-12.0f, 0.0f, 0.01f, 12.0f}, // OCT 146 | {-12.0f, 6.99f, 7.0f, 12.0f}, // 5 147 | {-12.0f, 5.0f, 7.0f, 12.0f}, // sus4 148 | {-12.0f, 3.0f, 11.99f, 12.0f}, // m 149 | {-12.0f, 3.0f, 9.99f, 10.0f}, // m7 150 | {-12.0f, 3.0f, 10.0f, 14.0f}, // m9 151 | {-12.0f, 3.0f, 10.0f, 17.0f}, // m11 152 | {-12.0f, 2.0f, 9.0f, 16.0f}, // 69 153 | {-12.0f, 4.0f, 11.0f, 14.0f}, // M9 154 | {-12.0f, 4.0f, 7.0f, 11.0f}, // M7 155 | {-12.0f, 4.0f, 7.0f, 12.0f}, // M 156 | }, 157 | { 158 | {0.0f, 0.01f, 12.0f}, // OCT 159 | {0.0f, 7.0f, 12.0f}, // 5 160 | {5.0f, 7.0f, 12.0f}, // sus4 161 | {0.0f, 3.0f, 12.0f}, // m 162 | {0.0f, 3.0f, 10.0f}, // m7 163 | {3.0f, 10.0f, 14.0f}, // m9 164 | {3.0f, 10.0f, 17.0f}, // m11 165 | {2.0f, 9.0f, 16.0f}, // 69 166 | {4.0f, 11.0f, 14.0f}, // M9 167 | {4.0f, 7.0f, 11.0f}, // M7 168 | {4.0f, 7.0f, 12.0f}, // M 169 | }}; 170 | 171 | #else 172 | 173 | // Original chord table: 174 | // - wider, occupies more room in the spectrum. 175 | // - minimum number of note changes between adjacent chords. 176 | // - consistant with the chord table used for the sympathetic strings model. 177 | const float chords[kMaxStringSynthPolyphony][kNumChords][kMaxChordSize] 178 | = {{ 179 | {-24.0f, -12.0f, 0.0f, 0.01f, 0.02f, 11.99f, 12.0f, 24.0f}, 180 | {-24.0f, -12.0f, 0.0f, 3.0f, 7.0f, 10.0f, 19.0f, 24.0f}, 181 | {-24.0f, -12.0f, 0.0f, 3.0f, 7.0f, 12.0f, 19.0f, 24.0f}, 182 | {-24.0f, -12.0f, 0.0f, 3.0f, 7.0f, 14.0f, 19.0f, 24.0f}, 183 | {-24.0f, -12.0f, 0.0f, 3.0f, 7.0f, 17.0f, 19.0f, 24.0f}, 184 | {-24.0f, -12.0f, 0.0f, 6.99f, 7.0f, 18.99f, 19.0f, 24.0f}, 185 | {-24.0f, -12.0f, 0.0f, 4.0f, 7.0f, 17.0f, 19.0f, 24.0f}, 186 | {-24.0f, -12.0f, 0.0f, 4.0f, 7.0f, 14.0f, 19.0f, 24.0f}, 187 | {-24.0f, -12.0f, 0.0f, 4.0f, 7.0f, 12.0f, 19.0f, 24.0f}, 188 | {-24.0f, -12.0f, 0.0f, 4.0f, 7.0f, 11.0f, 19.0f, 24.0f}, 189 | {-24.0f, -12.0f, 0.0f, 5.0f, 7.0f, 12.0f, 17.0f, 24.0f}, 190 | }, 191 | { 192 | {-24.0f, -12.0f, 0.0f, 0.01f, 12.0f, 12.01f}, 193 | {-24.0f, -12.0f, 0.0f, 3.00f, 7.0f, 10.0f}, 194 | {-24.0f, -12.0f, 0.0f, 3.00f, 7.0f, 12.0f}, 195 | {-24.0f, -12.0f, 0.0f, 3.00f, 7.0f, 14.0f}, 196 | {-24.0f, -12.0f, 0.0f, 3.00f, 7.0f, 17.0f}, 197 | {-24.0f, -12.0f, 0.0f, 6.99f, 12.0f, 19.0f}, 198 | {-24.0f, -12.0f, 0.0f, 4.00f, 7.0f, 17.0f}, 199 | {-24.0f, -12.0f, 0.0f, 4.00f, 7.0f, 14.0f}, 200 | {-24.0f, -12.0f, 0.0f, 4.00f, 7.0f, 12.0f}, 201 | {-24.0f, -12.0f, 0.0f, 4.00f, 7.0f, 11.0f}, 202 | {-24.0f, -12.0f, 0.0f, 5.00f, 7.0f, 12.0f}, 203 | }, 204 | { 205 | {-12.0f, 0.0f, 0.01f, 12.0f}, 206 | {-12.0f, 3.0f, 7.0f, 10.0f}, 207 | {-12.0f, 3.0f, 7.0f, 12.0f}, 208 | {-12.0f, 3.0f, 7.0f, 14.0f}, 209 | {-12.0f, 3.0f, 7.0f, 17.0f}, 210 | {-12.0f, 7.0f, 12.0f, 19.0f}, 211 | {-12.0f, 4.0f, 7.0f, 17.0f}, 212 | {-12.0f, 4.0f, 7.0f, 14.0f}, 213 | {-12.0f, 4.0f, 7.0f, 12.0f}, 214 | {-12.0f, 4.0f, 7.0f, 11.0f}, 215 | {-12.0f, 5.0f, 7.0f, 12.0f}, 216 | }, 217 | { 218 | {0.0f, 0.01f, 12.0f}, 219 | {0.0f, 3.0f, 10.0f}, 220 | {0.0f, 3.0f, 7.0f}, 221 | {0.0f, 3.0f, 14.0f}, 222 | {0.0f, 3.0f, 17.0f}, 223 | {0.0f, 7.0f, 19.0f}, 224 | {0.0f, 4.0f, 17.0f}, 225 | {0.0f, 4.0f, 14.0f}, 226 | {0.0f, 4.0f, 7.0f}, 227 | {0.0f, 4.0f, 11.0f}, 228 | {0.0f, 5.0f, 7.0f}, 229 | }}; 230 | 231 | #endif // BRYAN_CHORDS 232 | 233 | void StringSynthPart::ProcessEnvelopes(float shape, 234 | uint8_t* flags, 235 | float* values) 236 | { 237 | float decay = shape; 238 | float attack = 0.0f; 239 | if(shape < 0.5f) 240 | { 241 | attack = 0.0f; 242 | } 243 | else 244 | { 245 | attack = (shape - 0.5f) * 2.0f; 246 | } 247 | 248 | // Convert the arbitrary values to actual units. 249 | float period = kSampleRate / kMaxBlockSize; 250 | float attack_time = SemitonesToRatio(attack * 96.0f) * 0.005f * period; 251 | // float decay_time = SemitonesToRatio(decay * 96.0f) * 0.125f * period; 252 | float decay_time = SemitonesToRatio(decay * 84.0f) * 0.180f * period; 253 | float attack_rate = 1.0f / attack_time; 254 | float decay_rate = 1.0f / decay_time; 255 | 256 | for(int32_t i = 0; i < polyphony_; ++i) 257 | { 258 | float drone = shape < 0.98f ? 0.0f : (shape - 0.98f) * 55.0f; 259 | if(drone >= 1.0f) 260 | drone = 1.0f; 261 | 262 | group_[i].envelope.set_ad(attack_rate, decay_rate); 263 | float value = group_[i].envelope.Process(flags[i]); 264 | values[i] = value + (1.0f - value) * drone; 265 | } 266 | } 267 | 268 | const int32_t kFormantTableSize = 5; 269 | const float formants[kFormantTableSize][kNumFormants] = { 270 | {700, 1100, 2400}, 271 | {500, 1300, 1700}, 272 | {400, 2000, 2500}, 273 | {600, 800, 2400}, 274 | {300, 900, 2200}, 275 | }; 276 | 277 | void StringSynthPart::ProcessFormantFilter(float vowel, 278 | float shift, 279 | float resonance, 280 | float* out, 281 | float* aux, 282 | size_t size) 283 | { 284 | for(size_t i = 0; i < size; ++i) 285 | { 286 | filter_in_buffer_[i] = out[i] + aux[i]; 287 | } 288 | fill(&out[0], &out[size], 0.0f); 289 | fill(&aux[0], &aux[size], 0.0f); 290 | 291 | vowel *= (kFormantTableSize - 1.001f); 292 | MAKE_INTEGRAL_FRACTIONAL(vowel); 293 | 294 | for(int32_t i = 0; i < kNumFormants; ++i) 295 | { 296 | float a = formants[vowel_integral][i]; 297 | float b = formants[vowel_integral + 1][i]; 298 | float f = a + (b - a) * vowel_fractional; 299 | f *= shift; 300 | formant_filter_[i].set_f_q(f / kSampleRate, resonance); 301 | formant_filter_[i].Process( 302 | filter_in_buffer_, filter_out_buffer_, size); 303 | const float pan = i * 0.3f + 0.2f; 304 | for(size_t j = 0; j < size; ++j) 305 | { 306 | out[j] += filter_out_buffer_[j] * pan * 0.5f; 307 | aux[j] += filter_out_buffer_[j] * (1.0f - pan) * 0.5f; 308 | } 309 | } 310 | } 311 | 312 | struct ChordNote 313 | { 314 | float note; 315 | float amplitude; 316 | }; 317 | 318 | void StringSynthPart::Process(const PerformanceState& performance_state, 319 | const Patch& patch, 320 | const float* in, 321 | float* out, 322 | float* aux, 323 | size_t size) 324 | { 325 | // Assign note to a voice. 326 | uint8_t envelope_flags[kMaxStringSynthPolyphony]; 327 | 328 | fill(&envelope_flags[0], &envelope_flags[polyphony_], 0); 329 | note_filter_.Process(performance_state.note, performance_state.strum); 330 | if(performance_state.strum) 331 | { 332 | group_[active_group_].tonic = note_filter_.stable_note(); 333 | envelope_flags[active_group_] = ENVELOPE_FLAG_FALLING_EDGE; 334 | active_group_ = (active_group_ + 1) % polyphony_; 335 | envelope_flags[active_group_] = ENVELOPE_FLAG_RISING_EDGE; 336 | acquisition_delay_ = 3; 337 | } 338 | if(acquisition_delay_) 339 | { 340 | --acquisition_delay_; 341 | } 342 | else 343 | { 344 | group_[active_group_].tonic = note_filter_.note(); 345 | group_[active_group_].chord = performance_state.chord; 346 | group_[active_group_].structure = patch.structure; 347 | envelope_flags[active_group_] |= ENVELOPE_FLAG_GATE; 348 | } 349 | 350 | // Process envelopes. 351 | float envelope_values[kMaxStringSynthPolyphony]; 352 | ProcessEnvelopes(patch.damping, envelope_flags, envelope_values); 353 | 354 | copy(&in[0], &in[size], &aux[0]); 355 | copy(&in[0], &in[size], &out[0]); 356 | int32_t chord_size = min(kStringSynthVoices / polyphony_, kMaxChordSize); 357 | for(int32_t group = 0; group < polyphony_; ++group) 358 | { 359 | ChordNote notes[kMaxChordSize]; 360 | float harmonics[kNumHarmonics * 2]; 361 | 362 | ComputeRegistration( 363 | envelope_values[group] * 0.25f, patch.brightness, harmonics); 364 | 365 | // Note enough polyphony for smooth transition between chords. 366 | for(int32_t i = 0; i < chord_size; ++i) 367 | { 368 | float n = chords[polyphony_ - 1][group_[group].chord][i]; 369 | notes[i].note = n; 370 | notes[i].amplitude = n >= 0.0f && n <= 17.0f ? 1.0f : 0.7f; 371 | } 372 | 373 | for(int32_t chord_note = 0; chord_note < chord_size; ++chord_note) 374 | { 375 | float note = 0.0f; 376 | note += group_[group].tonic; 377 | note += performance_state.tonic; 378 | note += performance_state.fm; 379 | note += notes[chord_note].note; 380 | 381 | float amplitudes[kNumHarmonics * 2]; 382 | for(int32_t i = 0; i < kNumHarmonics * 2; ++i) 383 | { 384 | amplitudes[i] = notes[chord_note].amplitude * harmonics[i]; 385 | } 386 | 387 | // Fold truncated harmonics. 388 | size_t num_harmonics = polyphony_ >= 2 && chord_note < 2 389 | ? kNumHarmonics - 1 390 | : kNumHarmonics; 391 | for(int32_t i = num_harmonics; i < kNumHarmonics; ++i) 392 | { 393 | amplitudes[2 * (num_harmonics - 1)] += amplitudes[2 * i]; 394 | amplitudes[2 * (num_harmonics - 1) + 1] 395 | += amplitudes[2 * i + 1]; 396 | } 397 | 398 | float frequency = SemitonesToRatio(note - 69.0f) * a3; 399 | voice_[group * chord_size + chord_note].Render( 400 | frequency, 401 | amplitudes, 402 | num_harmonics, 403 | (group + chord_note) & 1 ? out : aux, 404 | size); 405 | } 406 | } 407 | 408 | if(clear_fx_) 409 | { 410 | reverb_.Clear(); 411 | clear_fx_ = false; 412 | } 413 | 414 | switch(fx_type_) 415 | { 416 | case FX_FORMANT: 417 | case FX_FORMANT_2: 418 | ProcessFormantFilter(patch.position, 419 | fx_type_ == FX_FORMANT ? 1.0f : 1.1f, 420 | fx_type_ == FX_FORMANT ? 25.0f : 10.0f, 421 | out, 422 | aux, 423 | size); 424 | break; 425 | 426 | case FX_CHORUS: 427 | chorus_.set_amount(patch.position); 428 | chorus_.set_depth(0.15f + 0.5f * patch.position); 429 | chorus_.Process(out, aux, size); 430 | break; 431 | 432 | case FX_ENSEMBLE: 433 | ensemble_.set_amount(patch.position * (2.0f - patch.position)); 434 | ensemble_.set_depth(0.2f + 0.8f * patch.position * patch.position); 435 | ensemble_.Process(out, aux, size); 436 | break; 437 | 438 | case FX_REVERB: 439 | case FX_REVERB_2: 440 | reverb_.set_amount(patch.position * 0.5f); 441 | reverb_.set_diffusion(0.625f); 442 | reverb_.set_time(fx_type_ == FX_REVERB 443 | ? (0.5f + 0.49f * patch.position) 444 | : (0.3f + 0.6f * patch.position)); 445 | reverb_.set_input_gain(0.2f); 446 | reverb_.set_lp(fx_type_ == FX_REVERB ? 0.3f : 0.6f); 447 | reverb_.Process(out, aux, size); 448 | break; 449 | 450 | default: break; 451 | } 452 | 453 | // Prevent main signal cancellation when EVEN gets summed with ODD through 454 | // normalization. 455 | for(size_t i = 0; i < size; ++i) 456 | { 457 | aux[i] = -aux[i]; 458 | } 459 | limiter_.Process(out, aux, size, 1.0f); 460 | } 461 | 462 | } // namespace torus -------------------------------------------------------------------------------- /patch_algodaisy/TorusPlus/dsp/string_synth_part.h: -------------------------------------------------------------------------------- 1 | // Copyright 2015 Emilie Gillet. 2 | // 3 | // Author: Emilie Gillet (emilie.o.gillet@gmail.com) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | // THE SOFTWARE. 22 | // 23 | // See http://creativecommons.org/licenses/MIT/ for more information. 24 | // 25 | // ----------------------------------------------------------------------------- 26 | // 27 | // Part for the string synth easter egg. 28 | 29 | #ifndef TORUS_DSP_STRING_SYNTH_PART_H_ 30 | #define TORUS_DSP_STRING_SYNTH_PART_H_ 31 | 32 | #include "stmlib/stmlib.h" 33 | 34 | #include "stmlib/dsp/filter.h" 35 | 36 | #include "dsp/dsp.h" 37 | #include "dsp/fx/chorus.h" 38 | #include "dsp/fx/ensemble.h" 39 | #include "dsp/fx/reverb.h" 40 | #include "dsp/limiter.h" 41 | #include "dsp/note_filter.h" 42 | #include "dsp/patch.h" 43 | #include "dsp/performance_state.h" 44 | #include "dsp/string_synth_envelope.h" 45 | #include "dsp/string_synth_voice.h" 46 | 47 | namespace torus 48 | { 49 | const int32_t kMaxStringSynthPolyphony = 4; 50 | const int32_t kStringSynthVoices = 12; 51 | const int32_t kMaxChordSize = 8; 52 | const int32_t kNumHarmonics = 3; 53 | const int32_t kNumFormants = 3; 54 | 55 | enum FxType 56 | { 57 | FX_FORMANT, 58 | FX_CHORUS, 59 | FX_REVERB, 60 | FX_FORMANT_2, 61 | FX_ENSEMBLE, 62 | FX_REVERB_2, 63 | FX_LAST 64 | }; 65 | 66 | struct VoiceGroup 67 | { 68 | float tonic; 69 | StringSynthEnvelope envelope; 70 | int32_t chord; 71 | float structure; 72 | }; 73 | 74 | class StringSynthPart 75 | { 76 | public: 77 | StringSynthPart() {} 78 | ~StringSynthPart() {} 79 | 80 | void Init(uint16_t* reverb_buffer); 81 | 82 | void Process(const PerformanceState& performance_state, 83 | const Patch& patch, 84 | const float* in, 85 | float* out, 86 | float* aux, 87 | size_t size); 88 | 89 | inline void set_polyphony(int32_t polyphony) 90 | { 91 | int32_t old_polyphony = polyphony_; 92 | polyphony_ = std::min(polyphony, kMaxStringSynthPolyphony); 93 | for(int32_t i = old_polyphony; i < polyphony_; ++i) 94 | { 95 | group_[i].tonic = group_[0].tonic + i * 0.01f; 96 | } 97 | if(active_group_ >= polyphony_) 98 | { 99 | active_group_ = 0; 100 | } 101 | } 102 | 103 | inline void set_fx(FxType fx_type) 104 | { 105 | if((fx_type % 3) != (fx_type_ % 3)) 106 | { 107 | clear_fx_ = true; 108 | } 109 | fx_type_ = fx_type; 110 | } 111 | 112 | private: 113 | void ProcessEnvelopes(float shape, uint8_t* flags, float* values); 114 | void ComputeRegistration(float gain, float registration, float* amplitudes); 115 | 116 | void ProcessFormantFilter(float vowel, 117 | float shift, 118 | float resonance, 119 | float* out, 120 | float* aux, 121 | size_t size); 122 | 123 | StringSynthVoice voice_[kStringSynthVoices]; 124 | VoiceGroup group_[kMaxStringSynthPolyphony]; 125 | 126 | stmlib::Svf formant_filter_[kNumFormants]; 127 | Ensemble ensemble_; 128 | Reverb reverb_; 129 | Chorus chorus_; 130 | Limiter limiter_; 131 | 132 | int32_t num_voices_; 133 | int32_t active_group_; 134 | uint32_t step_counter_; 135 | int32_t polyphony_; 136 | int32_t acquisition_delay_; 137 | 138 | FxType fx_type_; 139 | 140 | NoteFilter note_filter_; 141 | 142 | float filter_in_buffer_[kMaxBlockSize]; 143 | float filter_out_buffer_[kMaxBlockSize]; 144 | 145 | bool clear_fx_; 146 | 147 | DISALLOW_COPY_AND_ASSIGN(StringSynthPart); 148 | }; 149 | 150 | } // namespace torus 151 | 152 | #endif // TORUS_DSP_STRING_SYNTH_VOICE_H_ 153 | -------------------------------------------------------------------------------- /patch_algodaisy/TorusPlus/dsp/string_synth_voice.h: -------------------------------------------------------------------------------- 1 | // Copyright 2015 Emilie Gillet. 2 | // 3 | // Author: Emilie Gillet (emilie.o.gillet@gmail.com) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | // THE SOFTWARE. 22 | // 23 | // See http://creativecommons.org/licenses/MIT/ for more information. 24 | // 25 | // ----------------------------------------------------------------------------- 26 | // 27 | // Voice for the string synth easter egg. 28 | 29 | #ifndef TORUS_DSP_STRING_SYNTH_VOICE_H_ 30 | #define TORUS_DSP_STRING_SYNTH_VOICE_H_ 31 | 32 | #include "stmlib/stmlib.h" 33 | 34 | #include "dsp/string_synth_oscillator.h" 35 | 36 | namespace torus 37 | { 38 | template 39 | class StringSynthVoice 40 | { 41 | public: 42 | StringSynthVoice() {} 43 | ~StringSynthVoice() {} 44 | 45 | void Init() 46 | { 47 | for(size_t i = 0; i < num_harmonics; ++i) 48 | { 49 | oscillator_[i].Init(); 50 | } 51 | } 52 | 53 | void Render(float frequency, 54 | const float* amplitudes, 55 | size_t summed_harmonics, 56 | float* out, 57 | size_t size) 58 | { 59 | oscillator_[0].template Render( 60 | frequency, amplitudes[0], amplitudes[1], out, size); 61 | amplitudes += 2; 62 | 63 | for(size_t i = 1; i < summed_harmonics; ++i) 64 | { 65 | frequency *= 2.0f; 66 | oscillator_[i] 67 | .template Render( 68 | frequency, amplitudes[0], amplitudes[1], out, size); 69 | amplitudes += 2; 70 | } 71 | } 72 | 73 | private: 74 | StringSynthOscillator oscillator_[num_harmonics]; 75 | DISALLOW_COPY_AND_ASSIGN(StringSynthVoice); 76 | }; 77 | 78 | } // namespace torus 79 | 80 | #endif // TORUS_DSP_STRING_SYNTH_VOICE_H_ 81 | -------------------------------------------------------------------------------- /patch_algodaisy/TorusPlus/dsp/strummer.h: -------------------------------------------------------------------------------- 1 | // Copyright 2015 Emilie Gillet. 2 | // 3 | // Author: Emilie Gillet (emilie.o.gillet@gmail.com) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | // THE SOFTWARE. 22 | // 23 | // See http://creativecommons.org/licenses/MIT/ for more information. 24 | // 25 | // ----------------------------------------------------------------------------- 26 | // 27 | // Strumming logic. 28 | 29 | #ifndef TORUS_DSP_STRUMMER_H_ 30 | #define TORUS_DSP_STRUMMER_H_ 31 | 32 | #include "stmlib/stmlib.h" 33 | 34 | #include "dsp/onset_detector.h" 35 | #include "dsp/part.h" 36 | 37 | namespace torus 38 | { 39 | class Strummer 40 | { 41 | public: 42 | Strummer() {} 43 | ~Strummer() {} 44 | 45 | void Init(float ioi, float sr) 46 | { 47 | onset_detector_.Init(8.0f / kSampleRate, 48 | 160.0f / kSampleRate, 49 | 1600.0f / kSampleRate, 50 | sr, 51 | ioi); 52 | inhibit_timer_ = static_cast(ioi * sr); 53 | inhibit_counter_ = 0; 54 | previous_note_ = 69.0f; 55 | } 56 | 57 | void 58 | Process(const float* in, size_t size, PerformanceState* performance_state) 59 | { 60 | bool has_onset = in && onset_detector_.Process(in, size); 61 | bool note_changed 62 | = fabs(performance_state->note - previous_note_) > 0.4f; 63 | 64 | int32_t inhibit_timer = inhibit_timer_; 65 | if(performance_state->internal_strum) 66 | { 67 | bool has_external_note_cv = !performance_state->internal_note; 68 | bool has_external_exciter = !performance_state->internal_exciter; 69 | if(has_external_note_cv) 70 | { 71 | performance_state->strum = note_changed; 72 | } 73 | else if(has_external_exciter) 74 | { 75 | performance_state->strum = has_onset; 76 | // Use longer inhibit time for onset detector. 77 | inhibit_timer *= 4; 78 | } 79 | else 80 | { 81 | // Nothing is connected. Should the module play itself in this case? 82 | performance_state->strum = false; 83 | } 84 | } 85 | 86 | if(inhibit_counter_) 87 | { 88 | --inhibit_counter_; 89 | performance_state->strum = false; 90 | } 91 | else 92 | { 93 | if(performance_state->strum) 94 | { 95 | inhibit_counter_ = inhibit_timer; 96 | } 97 | } 98 | previous_note_ = performance_state->note; 99 | } 100 | 101 | private: 102 | float previous_note_; 103 | int32_t inhibit_counter_; 104 | int32_t inhibit_timer_; 105 | 106 | OnsetDetector onset_detector_; 107 | }; 108 | 109 | } // namespace torus 110 | 111 | #endif // TORUS_DSP_STRUMMER_H_ 112 | -------------------------------------------------------------------------------- /patch_algodaisy/TorusPlus/euclide.hpp: -------------------------------------------------------------------------------- 1 | #ifndef EUCLIDE_HPP 2 | #define EUCLIDE_HPP 3 | 4 | #include "sandrack.hpp" 5 | 6 | #define EUC_NUMSEQ 4 7 | #define EUC_MAXSEQLEN 32 8 | 9 | struct EuclideSeq { 10 | int m_seqlen = 16; 11 | int m_seqsteps = 4; 12 | int m_seqshift = 0; 13 | int m_seq[EUC_MAXSEQLEN] = {}; 14 | int currstep = 0; 15 | float m_prob = 1; 16 | 17 | int Next() { 18 | int res = m_seq[ (currstep + m_seqshift ) % m_seqlen ]; 19 | currstep = (currstep + 1 ) % m_seqlen; 20 | if (m_prob <= 0) { 21 | res = 0; 22 | } else { 23 | if (m_prob < 1) { 24 | if ( sandrack::frand() > m_prob ) res = 0; 25 | } 26 | } 27 | return res; 28 | } 29 | 30 | void Randomize() { 31 | int len = 2 * irand(4,12); 32 | //int steps = irand( 3, 12 ); 33 | int steps = irand( 2, 2*len/3 ); 34 | int shift = irand( 0, len/2); 35 | float prob = frand( 0.66, 1 ); 36 | Configure( len, steps, shift, prob ); 37 | } 38 | 39 | void Configure( int seqlen, int seqsteps, int seqshift, float prob ) { 40 | if (seqlen > 0) m_seqlen = seqlen; 41 | if ( m_seqsteps > m_seqlen) m_seqsteps = m_seqlen; 42 | m_seqsteps = seqsteps; 43 | m_seqshift = seqshift; 44 | m_prob = prob; 45 | RebuildSeq(); 46 | } 47 | 48 | void RebuildSeq() { 49 | float v = 0; 50 | for (int i = 0; i < EUC_MAXSEQLEN; i++) m_seq[i] = 0; 51 | if ( m_seqsteps <= 0) return; 52 | float delta = 1.0f * m_seqlen / m_seqsteps; 53 | float s = sandrack::fround(v); 54 | while ( s < m_seqlen ) { 55 | m_seq[ (int)( s ) ] = 1; 56 | v += delta; 57 | s = sandrack::fround(v); 58 | } 59 | } 60 | 61 | }; 62 | 63 | struct EuclideSet { 64 | 65 | EuclideSeq seqs[EUC_NUMSEQ] = {}; 66 | int m_values[EUC_NUMSEQ] = {}; 67 | 68 | void Init() { 69 | } 70 | 71 | void Randomize() { 72 | for (int i = 0; i < EUC_NUMSEQ; i++ ) { 73 | seqs[i].Randomize(); 74 | } 75 | seqs[0].m_seqshift = 0; // force shift = 0 on the first sequence 76 | } 77 | 78 | void Step() { 79 | for (int i = 0; i < EUC_NUMSEQ; i++) { 80 | m_values[i] = seqs[i].Next(); 81 | } 82 | } 83 | 84 | void Reset() { 85 | for (int i = 0; i < EUC_NUMSEQ; i++) { 86 | m_values[i] = seqs[i].currstep = 0; 87 | } 88 | } 89 | 90 | }; 91 | 92 | #endif -------------------------------------------------------------------------------- /patch_algodaisy/TorusPlus/resources.h: -------------------------------------------------------------------------------- 1 | // Copyright 2015 Emilie Gillet. 2 | // 3 | // Author: Emilie Gillet (emilie.o.gillet@gmail.com) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | // THE SOFTWARE. 22 | // 23 | // See http://creativecommons.org/licenses/MIT/ for more information. 24 | // 25 | // ----------------------------------------------------------------------------- 26 | // 27 | // Resources definitions. 28 | // 29 | // Automatically generated with: 30 | // make resources 31 | 32 | 33 | #ifndef TORUS_RESOURCES_H_ 34 | #define TORUS_RESOURCES_H_ 35 | 36 | 37 | #include "stmlib/stmlib.h" 38 | 39 | namespace torus 40 | { 41 | #define LUT_SINE 0 42 | #define LUT_SINE_SIZE 5121 43 | #define LUT_4_DECADES 1 44 | #define LUT_4_DECADES_SIZE 257 45 | #define LUT_SVF_SHIFT 2 46 | #define LUT_SVF_SHIFT_SIZE 257 47 | #define LUT_STIFFNESS 3 48 | #define LUT_STIFFNESS_SIZE 257 49 | #define LUT_FM_FREQUENCY_QUANTIZER 4 50 | #define LUT_FM_FREQUENCY_QUANTIZER_SIZE 129 51 | 52 | typedef uint8_t ResourceId; 53 | 54 | extern const int16_t* lookup_table_int16_table[]; 55 | 56 | extern const uint32_t* lookup_table_uint32_table[]; 57 | 58 | extern const float* lookup_table_table[]; 59 | 60 | extern float lut_sine[LUT_SINE_SIZE]; 61 | extern const float lut_4_decades[]; 62 | extern const float lut_svf_shift[]; 63 | extern const float lut_stiffness[]; 64 | extern const float lut_fm_frequency_quantizer[]; 65 | 66 | void InitResources(); 67 | 68 | } // namespace torus 69 | 70 | #endif // TORUS_RESOURCES_H_ 71 | -------------------------------------------------------------------------------- /patch_algodaisy/TorusPlus/sandrack.hpp: -------------------------------------------------------------------------------- 1 | #ifndef SANDRACK_HPP 2 | #define SANDRACK_HPP 3 | 4 | #include 5 | #include 6 | 7 | namespace sandrack { 8 | 9 | const float F_PI = 2 * acos(0.0f); 10 | 11 | inline int irand(int min, int max) { 12 | return rand()%(max-min + 1) + min; 13 | } 14 | 15 | inline float frand() { 16 | return static_cast (rand()) / static_cast (RAND_MAX); 17 | } 18 | 19 | inline float frand( float min, float max ) { 20 | return min + frand() * (max-min); 21 | } 22 | 23 | inline float frands() { 24 | return 2.0f*( frand() - 0.5f); 25 | } 26 | 27 | inline float frands(float min, float max) { 28 | return frand(min, max) * (irand(0,1)==0? 1 : -1); 29 | } 30 | 31 | inline float clamp( float v, float vmin, float vmax ) { 32 | return std::max(vmin, std::min(v, vmax)); 33 | } 34 | 35 | inline float midi2freq( int midinote ) { 36 | return powf(2, (midinote - 69.0f) / 12.0f) * 440.0f;; 37 | } 38 | 39 | inline int fround( float v ) { 40 | return floor( v + 0.5 ); 41 | } 42 | 43 | } 44 | 45 | 46 | #endif -------------------------------------------------------------------------------- /patch_algodaisy/TorusPlus/snap_torusplus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/algoritmarte/algodaisy/f85012db5c5a2a63d13f843532a0bedb7cb58540/patch_algodaisy/TorusPlus/snap_torusplus.png --------------------------------------------------------------------------------