├── .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 | 
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
--------------------------------------------------------------------------------