Browse Source

update ImpromptuModular modules to v0.6.11

pull/1639/head
bsp2 6 years ago
parent
commit
488fb82f7f
50 changed files with 3786 additions and 3608 deletions
  1. +1
    -1
      plugins/community/repos/ImpromptuModular/Makefile
  2. +52
    -13
      plugins/community/repos/ImpromptuModular/README.md
  3. +1
    -0
      plugins/community/repos/ImpromptuModular/make.objects
  4. +813
    -0
      plugins/community/repos/ImpromptuModular/res/AdvancedGateDetails.svg
  5. +1
    -1
      plugins/community/repos/ImpromptuModular/res/dark/Clocked_dark.svg
  6. +1
    -1
      plugins/community/repos/ImpromptuModular/res/dark/GateSeq64_dark.svg
  7. +1
    -1
      plugins/community/repos/ImpromptuModular/res/dark/PhraseSeq16_dark.svg
  8. +1
    -1
      plugins/community/repos/ImpromptuModular/res/dark/PhraseSeq32_dark.svg
  9. BIN
      plugins/community/repos/ImpromptuModular/res/img/AdvancedGateDetails.jpg
  10. BIN
      plugins/community/repos/ImpromptuModular/res/img/Clocked.jpg
  11. BIN
      plugins/community/repos/ImpromptuModular/res/img/GateSeq64.jpg
  12. BIN
      plugins/community/repos/ImpromptuModular/res/img/PhraseSeq16.jpg
  13. BIN
      plugins/community/repos/ImpromptuModular/res/img/PhraseSeq32.jpg
  14. +1
    -1
      plugins/community/repos/ImpromptuModular/res/light/Clocked.svg
  15. +0
    -931
      plugins/community/repos/ImpromptuModular/res/light/EngTest1.svg
  16. +1
    -1
      plugins/community/repos/ImpromptuModular/res/light/GateSeq64.svg
  17. +1
    -1
      plugins/community/repos/ImpromptuModular/res/light/PhraseSeq16.svg
  18. +1
    -1
      plugins/community/repos/ImpromptuModular/res/light/PhraseSeq32.svg
  19. +33
    -20
      plugins/community/repos/ImpromptuModular/src/BigButtonSeq.cpp
  20. +116
    -107
      plugins/community/repos/ImpromptuModular/src/Clocked.cpp
  21. +2
    -2
      plugins/community/repos/ImpromptuModular/src/FundamentalUtil.hpp
  22. +512
    -369
      plugins/community/repos/ImpromptuModular/src/GateSeq64.cpp
  23. +10
    -123
      plugins/community/repos/ImpromptuModular/src/ImpromptuModular.cpp
  24. +35
    -10
      plugins/community/repos/ImpromptuModular/src/ImpromptuModular.hpp
  25. +0
    -1
      plugins/community/repos/ImpromptuModular/src/MidiFileModule.cpp
  26. +379
    -361
      plugins/community/repos/ImpromptuModular/src/PhraseSeq16.cpp
  27. +410
    -437
      plugins/community/repos/ImpromptuModular/src/PhraseSeq32.cpp
  28. +133
    -0
      plugins/community/repos/ImpromptuModular/src/PhraseSeqUtil.cpp
  29. +113
    -0
      plugins/community/repos/ImpromptuModular/src/PhraseSeqUtil.hpp
  30. +173
    -175
      plugins/community/repos/ImpromptuModular/src/SemiModularSynth.cpp
  31. +30
    -26
      plugins/community/repos/ImpromptuModular/src/Tact.cpp
  32. +14
    -8
      plugins/community/repos/ImpromptuModular/src/TwelveKey.cpp
  33. +45
    -38
      plugins/community/repos/ImpromptuModular/src/WriteSeq32.cpp
  34. +33
    -26
      plugins/community/repos/ImpromptuModular/src/WriteSeq64.cpp
  35. +52
    -13
      vst2_bin/plugins/ImpromptuModular/README.md
  36. +813
    -0
      vst2_bin/plugins/ImpromptuModular/res/AdvancedGateDetails.svg
  37. +1
    -1
      vst2_bin/plugins/ImpromptuModular/res/dark/Clocked_dark.svg
  38. +1
    -1
      vst2_bin/plugins/ImpromptuModular/res/dark/GateSeq64_dark.svg
  39. +1
    -1
      vst2_bin/plugins/ImpromptuModular/res/dark/PhraseSeq16_dark.svg
  40. +1
    -1
      vst2_bin/plugins/ImpromptuModular/res/dark/PhraseSeq32_dark.svg
  41. BIN
      vst2_bin/plugins/ImpromptuModular/res/img/AdvancedGateDetails.jpg
  42. BIN
      vst2_bin/plugins/ImpromptuModular/res/img/Clocked.jpg
  43. BIN
      vst2_bin/plugins/ImpromptuModular/res/img/GateSeq64.jpg
  44. BIN
      vst2_bin/plugins/ImpromptuModular/res/img/PhraseSeq16.jpg
  45. BIN
      vst2_bin/plugins/ImpromptuModular/res/img/PhraseSeq32.jpg
  46. +1
    -1
      vst2_bin/plugins/ImpromptuModular/res/light/Clocked.svg
  47. +0
    -931
      vst2_bin/plugins/ImpromptuModular/res/light/EngTest1.svg
  48. +1
    -1
      vst2_bin/plugins/ImpromptuModular/res/light/GateSeq64.svg
  49. +1
    -1
      vst2_bin/plugins/ImpromptuModular/res/light/PhraseSeq16.svg
  50. +1
    -1
      vst2_bin/plugins/ImpromptuModular/res/light/PhraseSeq32.svg

+ 1
- 1
plugins/community/repos/ImpromptuModular/Makefile View File

@@ -2,7 +2,7 @@
SLUG = ImpromptuModular SLUG = ImpromptuModular


# Must follow the format in the Versioning section of https://vcvrack.com/manual/PluginDevelopmentTutorial.html # Must follow the format in the Versioning section of https://vcvrack.com/manual/PluginDevelopmentTutorial.html
VERSION = 0.6.9
VERSION = 0.6.10


# FLAGS will be passed to both the C and C++ compiler # FLAGS will be passed to both the C and C++ compiler
FLAGS += FLAGS +=


+ 52
- 13
plugins/community/repos/ImpromptuModular/README.md View File

@@ -2,7 +2,7 @@


Modules for [VCV Rack](https://vcvrack.com), available in the [plugin manager](https://vcvrack.com/plugins.html). Modules for [VCV Rack](https://vcvrack.com), available in the [plugin manager](https://vcvrack.com/plugins.html).


Version 0.6.9
Version 0.6.10


[//]: # (!!!!!UPDATE VERSION NUMBER IN MAKEFILE ALSO!!!!! 120% Zoom for jpgs) [//]: # (!!!!!UPDATE VERSION NUMBER IN MAKEFILE ALSO!!!!! 120% Zoom for jpgs)


@@ -19,7 +19,8 @@ Impromptu Modular is not a single-person endeavor:
* Thanks to **Xavier Belmont** for suggesting improvements to the modules, for testing/bug-reports, for the concept design of the SMS16 module and the blank panel, and for graciously providing the dark panels of all modules. * Thanks to **Xavier Belmont** for suggesting improvements to the modules, for testing/bug-reports, for the concept design of the SMS16 module and the blank panel, and for graciously providing the dark panels of all modules.
* Thanks to **Steve Baker** for many fruitful discussions regarding the BPM Detection method in Clocked, testing and improvements that were suggested for that module. * Thanks to **Steve Baker** for many fruitful discussions regarding the BPM Detection method in Clocked, testing and improvements that were suggested for that module.
* Thanks to **Omri Cohen** for testing and suggesting improvements to the modules, and for the [PhraseSeq16/32 tutorial](https://www.youtube.com/watch?v=N8_rMNzsS7w). * Thanks to **Omri Cohen** for testing and suggesting improvements to the modules, and for the [PhraseSeq16/32 tutorial](https://www.youtube.com/watch?v=N8_rMNzsS7w).
* Thanks also to **Latif Fital**, **Alfredo Santamaria**, **Nay Seven**, **Alberto Zamora**, **Clément Foulc** for suggesting improvements to the modules, bug reports and testing.
* Thanks to **Latif Karoumi** for [testing](https://www.youtube.com/watch?v=5PZCXvWlFZM) and suggesting improvement to the modules, particularly the advanced gate modes in the GateSeq64 and PhraseSeq sequencers.
* Thanks also to **Pyer (Pierre Collard)**, **Alfredo Santamaria**, **Nay Seven**, **Alberto Zamora**, **Clément Foulc**, **Espen Storo**, **Wouter Spekkink**, **John Melcher** for suggesting improvements to the modules, bug reports and testing.






@@ -65,7 +66,7 @@ Such sequencers have two main inputs that allow the capturing of (pitch) CVs, as


When **AUTOSTEP** is activated, the sequencer automatically advances one step right on each write. For example, to automatically capture the notes played on a keyboard, send the midi keyboard's CV into the sequencer's CV IN, and send the keyboard's gate signal into the sequencer's Write input. With Autostep activated, each key-press will be automatically entered in sequence. An alternative way of automatically stepping the sequencer each time a note is entered is to send the gate signal of the keyboard to both the write and ">" inputs. When **AUTOSTEP** is activated, the sequencer automatically advances one step right on each write. For example, to automatically capture the notes played on a keyboard, send the midi keyboard's CV into the sequencer's CV IN, and send the keyboard's gate signal into the sequencer's Write input. With Autostep activated, each key-press will be automatically entered in sequence. An alternative way of automatically stepping the sequencer each time a note is entered is to send the gate signal of the keyboard to both the write and ">" inputs.


When Run is activated, the sequencer automatically starts playing in the current step position, provided **RESET on RUN** is not checked in the right-click menu; sequencers will start at the first step when this option is checked. All edge sensitive inputs have a threshold of 1V. In all sequencers, the duration of the gates corresponds to the pulse width (high time) of the clock signal.
When Run is activated, the sequencer automatically starts playing in the current step position, provided **RESET on RUN** is not checked in the right-click menu; sequencers will start at the first step when this option is checked. All edge sensitive inputs have a threshold of 1V. In all sequencers, the duration of the gates normally corresponds to the pulse width (high time) of the clock signal. When sequencers offer an **Advanced gate mode** and this mode is activated, the pulse width of the clock signal has no effect on the sequencer.


Many modules feature an **Expansion panel** to provide additional CV inputs for the module (available in the right-click menu of the module). An extra 4 HP is added on the right side of the module, thus it is advisable to first make room in your Rack for this. Many modules feature an **Expansion panel** to provide additional CV inputs for the module (available in the right-click menu of the module). An extra 4 HP is added on the right side of the module, thus it is advisable to first make room in your Rack for this.


@@ -141,14 +142,14 @@ The PW and Swing **CV inputs** (some are available in the expansion panel) are 0


### External synchronization <a id="clocked-sync"></a> ### External synchronization <a id="clocked-sync"></a>


By default, the clock's BPM input is level sensitive and follows [Rack standards for BPM CVs](https://vcvrack.com/manual/VoltageStandards.html#pitch-and-frequencies). For synchronizing Clocked an external clock signal, an optional setting is available in the right-click menu called "Use **BPM Detection** (as opposed to **BPM CV**)". When using this option in a chain of Clocked modules, all modules must have the option checked. The green LED to the right of the main BPM display will light up when this mode is enabled and a cable is connected to the BPM input.
By default, the clock's BPM input is level sensitive and follows [Rack standards for BPM CVs](https://vcvrack.com/manual/VoltageStandards.html#pitch-and-frequencies). For synchronizing Clocked to an external clock signal, an optional setting is available using the MODE button located below the BPM input jack, and selecting a mode other than "CV". When using a chain of Clocked modules, all modules must have the same mode setting. The LED to the right of the MODE button will light up when the sync mode is enabled and a cable is connected to the BPM input. When no cable is connected to the BPM input jack, the MODE button has no effect and the BPM CV mode is implicit. When a cable is connected, the possible settings are: P4, P8, P12, P24, where the number indicates the number of pulses per step of the external clock source.


When using BPM detection, Clocked syncs itself to the incoming clock pulse, and will stay synchronized, as opposed to just calculating the BPM from the external source. This means that it will not drift (or that it will drift in time with the incoming pulses if they drift), and it should stay perfectly synchronized over time; it also allows for latency compensation. Here are a few points to keep in mind when using BPM Detection.
When using external clock synchronization, Clocked syncs itself to the incoming clock pulse, and will stay synchronized, as opposed to just calculating the BPM from the external source. This means that it will not drift (or that it will drift in time with the incoming pulses if they drift), and it should stay perfectly synchronized over time; it also allows for latency compensation. Here are a few points to keep in mind when using clock synchronization.


1. When using the BPM detection mode, Clocked can not be manually turned on, it will autostart on the first pulse it receives.
1. Clocked can not be manually turned on in clock sync mode, it will autostart on the first pulse it receives.
1. Clocked will automatically stop when the pulses stop, but in order to detect this, it take a small amount of time. To stop the clock quickly, you can simply send a pulse to the RUN CV input, and if the clock is running, it will turn off. 1. Clocked will automatically stop when the pulses stop, but in order to detect this, it take a small amount of time. To stop the clock quickly, you can simply send a pulse to the RUN CV input, and if the clock is running, it will turn off.
1. The external clock must be capable of sending clocks at a minumum of 4 pulses per quarter note (PPQN) and should not have any swing. 1. The external clock must be capable of sending clocks at a minumum of 4 pulses per quarter note (PPQN) and should not have any swing.
1. For low clock BPMs, synchronization may take some time if the external clock changes markedly from the last BPM it was synchronized to. Making gradual tempo changes is always recommended, and increasing the PPQN setting may also help. An other method is to first prime Clocked with is correct BPM to let it learn the new BPM, so that all further runs at that BPM will sync perfectly.
1. For low clock BPMs, synchronization may take some time if the external clock changes markedly from the last BPM it was synchronized to. Making gradual tempo changes is always recommended, and increasing the PPQN setting may also help. An other method consists in priming Clocked with is correct BPM first, to let it learn the new BPM, so that all further runs at that BPM will sync perfectly.


([Back to module list](#modules)) ([Back to module list](#modules))


@@ -158,7 +159,7 @@ When using BPM detection, Clocked syncs itself to the incoming clock pulse, and


![IM](res/img/PhraseSeq16.jpg) ![IM](res/img/PhraseSeq16.jpg)


A 16 phrase sequencer module, where each phrase is an index into a set of 16 sequences of 16 steps (maximum). CVs can be entered via a CV input when using an external keyboard controller or via the built-in keyboard on the module itself. If you need a 256-step sequence in a single module, this is the sequencer for you! With two separate gates per step, gate 2 is perfect for using as an accent if desired. When notes are entered with the *right mouse button* instead of the left button, the sequencer automatically moves to the next step.
A 16 phrase sequencer module, where each phrase is an index into a set of 16 sequences of 16 steps (maximum). CVs can be entered via a CV input when using an external keyboard controller or via the built-in keyboard on the module itself. If you need a 256-step sequence in a single module, this is the sequencer for you! With two separate gates per step, gate 2 is perfect for using as an accent if desired. When notes are entered with the **right mouse button** instead of the left button, the sequencer automatically moves to the next step.


The following block diagram shows how sequences and phrases relate to each other to create a song. In the diagram, a 12-bar blues pattern is created by setting the song length to 12, the step lengths to 8 (not visible in the figure), and then creating 4 sequences. The 12 phrases are indexes into the 4 sequences that were created. (Not sure anyone plays blues in a modular synth, but it shows the idea at least!) The following block diagram shows how sequences and phrases relate to each other to create a song. In the diagram, a 12-bar blues pattern is created by setting the song length to 12, the step lengths to 8 (not visible in the figure), and then creating 4 sequences. The 12 phrases are indexes into the 4 sequences that were created. (Not sure anyone plays blues in a modular synth, but it shows the idea at least!)


@@ -176,7 +177,7 @@ Familiarity with the Fundamental SEQ-3 sequencer is recommended, as some operati


* **ATTACH**: Allows the edit head to follow the run head (Attach on). The position of the edit head is shown with a red LED, and the position of the run head is shown with a green LED. When in Seq mode, the actual content of the step corresponding to the edit head position (i.e. note, oct, gates, slide) can be modified in real time whether the sequencer is running or not. The edit head automatically follows the run head when Attach is on, or can manually positioned by using the < and > buttons when Attach is off. * **ATTACH**: Allows the edit head to follow the run head (Attach on). The position of the edit head is shown with a red LED, and the position of the run head is shown with a green LED. When in Seq mode, the actual content of the step corresponding to the edit head position (i.e. note, oct, gates, slide) can be modified in real time whether the sequencer is running or not. The edit head automatically follows the run head when Attach is on, or can manually positioned by using the < and > buttons when Attach is off.


* **MODE**: This controls the run mode of the sequences and the song (one setting for each sequence and one for the song). The modes are: FWD (forward), REV (reverse), PPG (ping-pong, also called forward-reverse), BRN (Brownian random), RND (random), FW2 (forward, play twice), FW3 (play three times) and FW4 (four times). For example, setting the run mode to FWD for sequences and to RND for the song will play the phrases that are part of a song randomly, and the probability of a given phrase playing is proportional to the number of times it appears in the song. For sequences, the FW2, FW3 and FW4 modes can be used to repeat sequences more easily without consuming additional phrases in the song. These last three modes are not available for the song's run mode however.
* **MODE**: This controls the run mode of the sequences and the song (one setting for each sequence and one for the song). The modes are: FWD (forward), REV (reverse), PPG (ping-pong, also called forward-reverse), BRN (Brownian random), RND (random), FW2 (forward, play twice), FW3 (play three times) and FW4 (four times). For example, setting the run mode to FWD for sequences and to RND for the song will play the phrases that are part of a song randomly, and the probability of a given phrase playing is proportional to the number of times it appears in the song. For sequences, the FW2, FW3 and FW4 modes can be used to repeat sequences more easily without consuming additional phrases in the song. These last three modes are not available for the song's run mode however. Holding the MODE button for **two seconds** allows the selection of the clock resolution, and is the mechanism used to enable the [advanced gate mode](#advanced-gate-mode-ps).


* **TRAN/ROT**: Transpose/Rotate the currently selected sequence up-down/left-right by a given number of semi-tones/steps. The main knob is used to set the transposition/rotation amount. Only available in Seq mode. * **TRAN/ROT**: Transpose/Rotate the currently selected sequence up-down/left-right by a given number of semi-tones/steps. The main knob is used to set the transposition/rotation amount. Only available in Seq mode.


@@ -188,7 +189,22 @@ Familiarity with the Fundamental SEQ-3 sequencer is recommended, as some operati


* **SLIDE**: Portamento between CVs of successive steps. Slide can be activated for a given step using the slide button. The slide duration can be set using the slide knob. The slide duration can range from 0 to T seconds, where T is the duration of a clock period (the default is 10% of T). This knob's setting is not memorized for each step and applies to the sequencer as a whole. * **SLIDE**: Portamento between CVs of successive steps. Slide can be activated for a given step using the slide button. The slide duration can be set using the slide knob. The slide duration can range from 0 to T seconds, where T is the duration of a clock period (the default is 10% of T). This knob's setting is not memorized for each step and applies to the sequencer as a whole.


* **TIED STEP**: When CVs are intended to be held across subsequent steps, this button can be used to tie the CV of the previous step to the current step; when tied, the gates of the current step are automatically turned off. If the CV of the head note changes, all consecutive tied notes are updated automatically.
* **TIED STEP**: When CVs are intended to be held across subsequent steps, this button can be used to tie the CV of the previous step to the current step. When tied is turned on for a step, the gates of that step are automatically turned off, but can be manually turned back on if desired. When tied, if the CV of the head note changes, all consecutive tied notes are updated automatically.


### Advanced gate mode<a id="advanced-gate-mode-ps"></a>

Holding the MODE button for **two seconds** allows the selection of the clock resolution, in number of pulses per step (PPS). When set to a value greater than 1, which unlocks the advanced gate mode, the sequencer will skip this many clock pulses before advancing to the next step. In such cases, a mutliplied clock must be supplied in order to keep the same tempo in the sequencer. In advanced gate mode, the pulse width of the clock is not used and has no effect on the gates.

In the advanced gate mode, the Gate1 and Gate2 lights will be a different color, and the onboard keyboard can be used not only to enter note values, but also to select one of the 12 types of gates for a given step. Advanced gates can only be set while in Seq mode and when the sequencer is stopped. Here are the different gate types and their minimum PPS requirements.

![IM](res/img/AdvancedGateDetails.jpg)

All PPS settings will work for the half and full gates (the D and F keys) as well as triggers (the B key). A full gate remains high during the entire step, and if the next step's gate is active, then the gate continues without interruption into that next step. When PPS requirements are not met, the sequencer will not allow invalid gate types to be entered on the keyboard. For example, if PPS is set to 6, then the 75% gate (the E key) can not be selected. Selecting a PPS value of 12 or 24 will ensure that all gate types can be used (i.e. that all PPS requirements are met irrespective of the gate type chosen).

The gate type for a given step can be selected during a short time interval after a given gate has just been turned on using the Gate1 or Gate2 buttons. If a gate is already turned on and its gate type is to be edited, clicking the gate button twice will allow it to be edited while keeping it in the same state. The onboard keyboard will temporarily show a yellow/orange light corresponding to the current gate type for that step; during this time the gate type can be changed.

Since the editing time for the advanced gate mode is kept rather short (4s), holding the Gate2 button for 2s will set that default time interval to 400s. Holding Gate1 for 2s will revert to the default time of 4s. The extended time feature is useful when the gate modes for multiple steps of a sequence are to be editied or reviewed in a single pass, for example.


([Back to module list](#modules)) ([Back to module list](#modules))


@@ -216,15 +232,22 @@ When the 1x32 configuration is selected, only the top channel outputs are used (


A 64 step gate sequencer with the ability to define **probabilities** for each step. A configuration switch allows the sequencer to output quad 16 step sequences, dual 32 step sequences or single 64 step sequences. To see the sequencer in action and for a tutorial on how it works, please see [this segment](https://www.youtube.com/watch?v=bjqWwTKqERQ&t=6111s) of Nigel Sixsmith's Talking Rackheads episode 10. A 64 step gate sequencer with the ability to define **probabilities** for each step. A configuration switch allows the sequencer to output quad 16 step sequences, dual 32 step sequences or single 64 step sequences. To see the sequencer in action and for a tutorial on how it works, please see [this segment](https://www.youtube.com/watch?v=bjqWwTKqERQ&t=6111s) of Nigel Sixsmith's Talking Rackheads episode 10.


When activating a given step by clicking it once, it will turn green showing that the step is on. Clicking the step again turns it yellow, and the main display shows the probability associated with this step. While the probability remains shown, the probability can be adjusted with the main knob, in 0.02 increments, between 0 and 1. When a yellow step is selected, clicking it again will turn it off.
When activating a given step by clicking it once, it will turn green showing that the step is on. Clicking the _"p"_ button turns it yellow, and the main display shows the probability associated with this step. While the probability remains shown, the probability can be adjusted with the main knob, in 0.02 increments, between 0 and 1. When a yellow step is selected, clicking the _"p"_ button again will turn it off.


This sequencer also features the song mode found in [PhraseSeq16](#phrase-seq-16); 16 phrases can be defined, where a phrase is an index into a set of 16 sequences. In GateSeq64, the song steps are shown using the fourth row of steps, overlapped with the actual sequence progression in lighter shades in the lights. This sequencer also features the song mode found in [PhraseSeq16](#phrase-seq-16); 16 phrases can be defined, where a phrase is an index into a set of 16 sequences. In GateSeq64, the song steps are shown using the fourth row of steps, overlapped with the actual sequence progression in lighter shades in the lights.


The **SEQ** CV input and run **MODES** are identical to those found in PhraseSeq16, and selecting sequence lengths is done in the same manner as described in [PhraseSeq32](#phrase-seq-32). Copy-pasting **ALL** also copies the run mode and length of a given sequence, along with gate states and probabilities, whereas only gates and probabilities are copied when **ROW** is selected.
The **SEQ** CV input and run **MODES** are identical to those found in PhraseSeq16, and selecting sequence lengths is done in the same manner as described in [PhraseSeq32](#phrase-seq-32). Copy-pasting ALL also copies the run mode and length of a given sequence, along with gate states and probabilities, whereas only gates and probabilities are copied when 4 or ROW are selected.


When running in the 4x16 configuration, each of the four rows is sent to the four **GATE** output jacks (jacks 1 to 4, with jack 1 being the top-most jack). In the 2x32 configuration, jacks 1 and 3 are used, and in the 1x64 configuration, only jack 1 is used (top-most jack). The pulse width of the gates emitted corresponds to the pulse width of the clock. When running in the 4x16 configuration, each of the four rows is sent to the four **GATE** output jacks (jacks 1 to 4, with jack 1 being the top-most jack). In the 2x32 configuration, jacks 1 and 3 are used, and in the 1x64 configuration, only jack 1 is used (top-most jack). The pulse width of the gates emitted corresponds to the pulse width of the clock.


Although no **write** capabilities appear in the main part of the module, automatically storing patterns into the sequencer can be performed using the CV inputs in the **expansion panel**. A write cursor is implicitly stepped forward on each write, and can be repositioned at the first step by pressing the reset button, or at an arbitrary step by simply clicking that given step.
Although no **write** capabilities appear in the main part of the module, automatically storing patterns into the sequencer can be performed using the CV inputs in the **expansion panel**. The cursor is stepped forward on each write, and can be repositioned at the first step by pressing the reset button, or at an arbitrary step by simply clicking that given step. When the cursor is not flashing, clicking any step will make it appear.


### Advanced gate mode<a id="advanced-gate-mode-gs"></a>

Holding the MODE button for **two seconds** allows the selection of the clock resolution, in number of pulses per step (PPS). When set to a value greater than 1, which unlocks the advanced gate mode, the sequencer will skip this many clock pulses before advancing to the next step. In such cases, a mutliplied clock must be supplied in order to keep the same tempo in the sequencer. In advanced gate mode, the pulse width of the clock is not used and has no effect on the gates.

The PPS be a multiple of 4 for the first three gate types, while the PPS be a multiple of 6 for the last five gate types. A chosen gate type not meeting its required pulse rate will have a red LED beside it to indicate this (normally it is green). When a gate type is red, the sequencer will still emit a (possibly empty) gate pattern for that step, but with no guarantee that it will match the type that was selected. All gate types can be used when selecting a PPS value of 12 or 24.


([Back to module list](#modules)) ([Back to module list](#modules))


@@ -260,6 +283,8 @@ Here are a few more details on some of the uses of the buttons. The sequencer us


**FILL**: plays continuous triggers for the given channel as long as the button is kept pressed. By default the fills are not written to memory and are only for playback; however, an option to allow the writing of fill steps to memory is available in the right-click menu. **FILL**: plays continuous triggers for the given channel as long as the button is kept pressed. By default the fills are not written to memory and are only for playback; however, an option to allow the writing of fill steps to memory is available in the right-click menu.


The BIG and DEL buttons are **quantized to the nearest beat**. Without quantization, button presses typically affect the current beat (step) of the sequencer. With quantized buttons, they affect the nearest beat. For example, pressing the big button 1 microsecond before a beat would normally record the beat in the current step and not the next one that is about to occur, which is actually the closest. For the quantization to work properly, the sequencer must recieve a stable clock of at least 30 BPM. When this is not the case, the option is automatically disabled internally. When manually advancing the clock to program a sequence in non real-time, for example, the option has no effect and the current step is always the target of a button press.

([Back to module list](#modules)) ([Back to module list](#modules))




@@ -315,3 +340,17 @@ WriteSeq64 has dual clock inputs, where each controls a pair of channels. When n
Ideas: The first part of the famous [Piano Phase](https://en.wikipedia.org/wiki/Piano_Phase) piece by Steve Reich can be easily programmed into the sequencer by entering the twelve notes into channel 1 with a keyboard, setting STEPS to 12, copy-pasting channel 1 into channel 3, and then driving each clock input with two LFOs that have ever so slightly different frequencies. Exercise left to the reader! Ideas: The first part of the famous [Piano Phase](https://en.wikipedia.org/wiki/Piano_Phase) piece by Steve Reich can be easily programmed into the sequencer by entering the twelve notes into channel 1 with a keyboard, setting STEPS to 12, copy-pasting channel 1 into channel 3, and then driving each clock input with two LFOs that have ever so slightly different frequencies. Exercise left to the reader!


([Back to module list](#modules)) ([Back to module list](#modules))


# Hall of Fame <a id="hall-of-fame"></a>

Here are a few videos featuring Impromptu Modular, which I find particularly very inspiring and interesting (listed in no particular order). Many talented Rackheads have made tracks using Impromptu modules, and this list could in fact be quite long. I have no formal criteria for why a track ends up in the list or doesn't.

* **Nigel Sixsmith**, [Talking Rackheads episode 8, PS16 MegaPatch Play Out](https://www.youtube.com/watch?v=KOpo2oUPTjg&t=5504s)

* **Omri Cohen**, [Arvo Pärt - Spiegel im Spiegel VCV Rack Cover](https://www.youtube.com/watch?v=6bm4LjRYDmI)

* **Isaac Hu**, [In the Hall of the Mountain King](https://www.youtube.com/watch?v=fxYc0H5i6HA)

* **John Melcher**, [Steppes](https://www.youtube.com/watch?v=ruo4s_Hyhrw)


+ 1
- 0
plugins/community/repos/ImpromptuModular/make.objects View File

@@ -14,6 +14,7 @@ ALL_OBJ= \
src/MidiFileModule.o \ src/MidiFileModule.o \
src/PhraseSeq16.o \ src/PhraseSeq16.o \
src/PhraseSeq32.o \ src/PhraseSeq32.o \
src/PhraseSeqUtil.o \
src/SemiModularSynth.o \ src/SemiModularSynth.o \
src/Tact.o \ src/Tact.o \
src/TwelveKey.o \ src/TwelveKey.o \


+ 813
- 0
plugins/community/repos/ImpromptuModular/res/AdvancedGateDetails.svg View File

@@ -0,0 +1,813 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="220"
height="183"
viewBox="0 0 58.208549 48.418911"
version="1.1"
id="svg321"
sodipodi:docname="AdvancedGateDetails.svg"
inkscape:version="0.92.3 (2405546, 2018-03-11)">
<metadata
id="metadata325">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title />
</cc:Work>
</rdf:RDF>
</metadata>
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1920"
inkscape:window-height="1017"
id="namedview323"
showgrid="false"
inkscape:zoom="3.1174557"
inkscape:cx="144.03182"
inkscape:cy="84.993533"
inkscape:window-x="-8"
inkscape:window-y="-8"
inkscape:window-maximized="1"
inkscape:current-layer="svg321"
showguides="true"
inkscape:guide-bbox="true" />
<defs
id="defs89">
<filter
height="1"
width="1"
y="0"
x="0"
filterUnits="objectBoundingBox"
id="a">
<feColorMatrix
values="0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 1 0"
in="SourceGraphic"
id="feColorMatrix2" />
</filter>
<filter
height="1"
width="1"
y="0"
x="0"
filterUnits="objectBoundingBox"
id="b">
<feColorMatrix
values="0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 1 0"
in="SourceGraphic"
id="feColorMatrix5" />
</filter>
<filter
height="1"
width="1"
y="0"
x="0"
filterUnits="objectBoundingBox"
id="c">
<feColorMatrix
values="0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 1 0"
in="SourceGraphic"
id="feColorMatrix8" />
</filter>
<filter
height="1"
width="1"
y="0"
x="0"
filterUnits="objectBoundingBox"
id="d">
<feColorMatrix
values="0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 1 0"
in="SourceGraphic"
id="feColorMatrix11" />
</filter>
<filter
height="1"
width="1"
y="0"
x="0"
filterUnits="objectBoundingBox"
id="e">
<feColorMatrix
values="0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 1 0"
in="SourceGraphic"
id="feColorMatrix14" />
</filter>
<filter
height="1"
width="1"
y="0"
x="0"
filterUnits="objectBoundingBox"
id="f">
<feColorMatrix
values="0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 1 0"
in="SourceGraphic"
id="feColorMatrix17" />
</filter>
<filter
height="1"
width="1"
y="0"
x="0"
filterUnits="objectBoundingBox"
id="g">
<feColorMatrix
values="0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 1 0"
in="SourceGraphic"
id="feColorMatrix20" />
</filter>
<filter
height="1"
width="1"
y="0"
x="0"
filterUnits="objectBoundingBox"
id="h">
<feColorMatrix
values="0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 1 0"
in="SourceGraphic"
id="feColorMatrix23" />
</filter>
<filter
height="1"
width="1"
y="0"
x="0"
filterUnits="objectBoundingBox"
id="i">
<feColorMatrix
values="0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 1 0"
in="SourceGraphic"
id="feColorMatrix26" />
</filter>
<filter
height="1"
width="1"
y="0"
x="0"
filterUnits="objectBoundingBox"
id="j">
<feColorMatrix
values="0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 1 0"
in="SourceGraphic"
id="feColorMatrix29" />
</filter>
<filter
height="1"
width="1"
y="0"
x="0"
filterUnits="objectBoundingBox"
id="k">
<feColorMatrix
values="0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 1 0"
in="SourceGraphic"
id="feColorMatrix32" />
</filter>
<filter
height="1"
width="1"
y="0"
x="0"
filterUnits="objectBoundingBox"
id="l">
<feColorMatrix
values="0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 1 0"
in="SourceGraphic"
id="feColorMatrix35" />
</filter>
<filter
height="1"
width="1"
y="0"
x="0"
filterUnits="objectBoundingBox"
id="m">
<feColorMatrix
values="0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 1 0"
in="SourceGraphic"
id="feColorMatrix38" />
</filter>
<filter
height="1"
width="1"
y="0"
x="0"
filterUnits="objectBoundingBox"
id="n">
<feColorMatrix
values="0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 1 0"
in="SourceGraphic"
id="feColorMatrix41" />
</filter>
<filter
height="1"
width="1"
y="0"
x="0"
filterUnits="objectBoundingBox"
id="o">
<feColorMatrix
values="0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 1 0"
in="SourceGraphic"
id="feColorMatrix44" />
</filter>
<filter
height="1"
width="1"
y="0"
x="0"
filterUnits="objectBoundingBox"
id="p">
<feColorMatrix
values="0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 1 0"
in="SourceGraphic"
id="feColorMatrix47" />
</filter>
<filter
height="1"
width="1"
y="0"
x="0"
filterUnits="objectBoundingBox"
id="q">
<feColorMatrix
values="0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 1 0"
in="SourceGraphic"
id="feColorMatrix50" />
</filter>
<filter
height="1"
width="1"
y="0"
x="0"
filterUnits="objectBoundingBox"
id="r">
<feColorMatrix
values="0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 1 0"
in="SourceGraphic"
id="feColorMatrix53" />
</filter>
<filter
height="1"
width="1"
y="0"
x="0"
filterUnits="objectBoundingBox"
id="s">
<feColorMatrix
values="0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 1 0"
in="SourceGraphic"
id="feColorMatrix56" />
</filter>
<filter
height="1"
width="1"
y="0"
x="0"
filterUnits="objectBoundingBox"
id="t">
<feColorMatrix
values="0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 1 0"
in="SourceGraphic"
id="feColorMatrix59" />
</filter>
<filter
height="1"
width="1"
y="0"
x="0"
filterUnits="objectBoundingBox"
id="u">
<feColorMatrix
values="0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 1 0"
in="SourceGraphic"
id="feColorMatrix62" />
</filter>
<filter
height="1"
width="1"
y="0"
x="0"
filterUnits="objectBoundingBox"
id="v">
<feColorMatrix
values="0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 1 0"
in="SourceGraphic"
id="feColorMatrix65" />
</filter>
<filter
height="1"
width="1"
y="0"
x="0"
filterUnits="objectBoundingBox"
id="w">
<feColorMatrix
values="0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 1 0"
in="SourceGraphic"
id="feColorMatrix68" />
</filter>
<filter
height="1"
width="1"
y="0"
x="0"
filterUnits="objectBoundingBox"
id="x">
<feColorMatrix
values="0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 1 0"
in="SourceGraphic"
id="feColorMatrix71" />
</filter>
<filter
height="1"
width="1"
y="0"
x="0"
filterUnits="objectBoundingBox"
id="y">
<feColorMatrix
values="0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 1 0"
in="SourceGraphic"
id="feColorMatrix74" />
</filter>
<filter
height="1"
width="1"
y="0"
x="0"
filterUnits="objectBoundingBox"
id="z">
<feColorMatrix
values="0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 1 0"
in="SourceGraphic"
id="feColorMatrix77" />
</filter>
<filter
height="1"
width="1"
y="0"
x="0"
filterUnits="objectBoundingBox"
id="A">
<feColorMatrix
values="0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 1 0"
in="SourceGraphic"
id="feColorMatrix80" />
</filter>
<filter
height="1"
width="1"
y="0"
x="0"
filterUnits="objectBoundingBox"
id="B">
<feColorMatrix
values="0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 1 0"
in="SourceGraphic"
id="feColorMatrix83" />
</filter>
<filter
height="1"
width="1"
y="0"
x="0"
filterUnits="objectBoundingBox"
id="C">
<feColorMatrix
values="0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 1 0"
in="SourceGraphic"
id="feColorMatrix86" />
</filter>
</defs>
<text
id="text95"
word-spacing="0"
letter-spacing="0"
font-size="10.583"
font-weight="400"
y="34.662903"
x="64.782875"
style="font-weight:400;font-size:10.58300018px;line-height:1.25;font-family:sans-serif;letter-spacing:0;word-spacing:0;stroke-width:0.26499999" />
<rect
y="12.95517"
x="2.9069221"
height="22.225079"
width="52.916325"
rx="0.60890275"
ry="0.60897696"
id="rect117"
style="fill:#333333;stroke-width:1.000314;paint-order:markers stroke fill" />
<path
d="m 15.230183,13.882545 0.03219,9.537557 2.327392,1.289025 v 9.560551 h -6.358203 v -9.560551 l 2.370542,-1.289025 v -9.537557 z m 22.398581,0 0.03288,9.537557 2.326708,1.289025 v 9.560551 h -6.35821 v -9.560551 l 2.370543,-1.289025 v -9.537557 z m 5.806151,0 -0.03288,9.537557 -2.326707,1.289025 v 9.560551 h 6.35752 v -9.560551 l -2.369854,-1.289025 v -9.537557 z m -35.6499211,0 0.03288,9.537557 2.3267081,1.289025 v 9.560551 H 3.7863718 v -9.560551 l -0.0055,-10.826582 h 2.3753371 z m 13.2465461,0 -0.03288,9.537557 -2.326707,1.289025 v 9.560551 h 6.35752 v -9.560551 l 0.0048,-10.826582 h -2.375337 z m 9.152035,0 0.03288,9.537557 2.326708,1.289025 v 9.560551 h -6.357525 v -9.560551 l -0.0055,-10.826582 h 2.376022 z m 20.69653,0 -0.03288,9.537557 -2.326707,1.289025 v 9.560551 h 6.35752 v -9.560551 l 0.0048,-10.826582 h -2.375337 z"
id="path119"
inkscape:connector-curvature="0"
style="fill:#999999;stroke:#000000;stroke-width:0.26458582"
sodipodi:nodetypes="ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc" />
<g
id="g169"
transform="matrix(0.26458426,0,0,0.26458426,-6.0187653,3.9901329)">
<g
id="g58">
<line
id="line52"
style="fill:none;stroke:#a8a8a8;stroke-linecap:round;stroke-linejoin:round"
y2="128.75461"
x2="58.888561"
y1="122.37562"
x1="58.888561" />
<line
id="line54"
style="fill:none;stroke:#a8a8a8;stroke-linecap:round;stroke-linejoin:round"
y2="128.75461"
x2="53.964432"
y1="122.37562"
x1="53.964432" />
<line
id="line56"
style="fill:none;stroke:#a8a8a8;stroke-linecap:round;stroke-linejoin:round"
y2="128.75461"
x2="49.040298"
y1="122.37562"
x1="49.040298" />
</g>
<path
id="path96"
style="fill:none;stroke:#272727;stroke-linecap:round;stroke-linejoin:round"
transform="translate(-14.81094,-16.5)"
d="m 54.003,145.25461 v -5.2617 a 1.11729,1.11729 0 0 1 1.1173,-1.11729 h 2.68954 a 1.11729,1.11729 0 0 1 1.11729,1.11729 v 4.1444 a 1.1173,1.1173 0 0 0 1.1173,1.1173 H 73.6995"
inkscape:connector-curvature="0" />
</g>
<path
inkscape:connector-curvature="0"
d="M 11.854922,38.056573 V 36.66441 a 0.29561735,0.29561735 0 0 1 0.29562,-0.295618 h 2.014447 a 0.29561735,0.29561735 0 0 1 0.295617,0.295618 v 1.096543 a 0.29561999,0.29561999 0 0 0 0.29562,0.29562 h 2.310085"
style="fill:none;stroke:#333333;stroke-width:0.26458427;stroke-linecap:round;stroke-linejoin:round"
id="path98" />
<g
id="g183"
transform="matrix(0.26458426,0,0,0.26458426,-10.607803,3.9901329)">
<g
id="g74">
<line
id="line68"
style="fill:none;stroke:#a8a8a8;stroke-linecap:round;stroke-linejoin:round"
y2="128.75461"
x2="132.77985"
y1="122.37562"
x1="132.77985" />
<line
id="line70"
style="fill:none;stroke:#a8a8a8;stroke-linecap:round;stroke-linejoin:round"
y2="128.75461"
x2="122.93159"
y1="122.37562"
x1="122.93159" />
<line
id="line72"
style="fill:none;stroke:#a8a8a8;stroke-linecap:round;stroke-linejoin:round"
y2="128.75461"
x2="118.00746"
y1="122.37562"
x1="118.00746" />
</g>
<path
id="path100"
style="fill:none;stroke:#333333;stroke-linecap:round;stroke-linejoin:round"
transform="translate(-14.81094,-16.5)"
d="m 127.89428,145.25461 v -5.2617 a 1.11729,1.11729 0 0 1 1.11729,-1.11729 h 12.5378 a 1.11729,1.11729 0 0 1 1.1173,1.11729 v 4.1444 a 1.11729,1.11729 0 0 0 1.11729,1.1173 h 3.80684"
inkscape:connector-curvature="0" />
</g>
<path
inkscape:connector-curvature="0"
d="m 26.749066,38.056572 v -1.392163 a 0.29561735,0.29561735 0 0 1 0.295618,-0.295617 h 4.620152 a 0.29561735,0.29561735 0 0 1 0.29562,0.295617 v 1.392163"
style="fill:none;stroke:#333333;stroke-width:0.26458427;stroke-linecap:round;stroke-linejoin:round"
id="path102" />
<g
id="g194"
transform="matrix(0.26458426,0,0,0.26458426,-13.778451,3.9901329)">
<line
id="line4"
style="fill:none;stroke:#a8a8a8;stroke-linecap:round;stroke-linejoin:round"
y2="128.75461"
x2="201.02985"
y1="122.37562"
x1="201.02985" />
<path
id="path104"
style="fill:none;stroke:#333333;stroke-linecap:round;stroke-linejoin:round"
transform="translate(-14.81094,-16.5)"
d="m 196.14428,145.25461 v -5.2617 a 1.11729,1.11729 0 0 1 1.11729,-1.11729 h 2.68954 a 1.11729,1.11729 0 0 1 1.1173,1.11729 v 4.1444 a 1.11729,1.11729 0 0 0 1.11729,1.1173 h 2.68954 a 1.1173,1.1173 0 0 0 1.1173,-1.1173 v -4.1444 a 1.11729,1.11729 0 0 1 1.11729,-1.11729 h 2.68954 a 1.11729,1.11729 0 0 1 1.1173,1.11729 v 4.1444 a 1.11729,1.11729 0 0 0 1.11729,1.1173 h 3.80684"
inkscape:connector-curvature="0" />
</g>
<g
id="g140"
transform="matrix(0.26458426,0,0,0.26458426,-3.8684196,9.9159005)">
<g
id="g14">
<line
id="line8"
style="fill:none;stroke:#a8a8a8;stroke-linecap:round;stroke-linejoin:round"
y2="6.8789902"
x2="136.35571"
y1="0.5"
x1="136.35571" />
<line
id="line10"
style="fill:none;stroke:#a8a8a8;stroke-linecap:round;stroke-linejoin:round"
y2="6.8789902"
x2="149.48431"
y1="0.5"
x1="149.48431" />
<line
id="line12"
style="fill:none;stroke:#a8a8a8;stroke-linecap:round;stroke-linejoin:round"
y2="6.8789902"
x2="142.92365"
y1="0.5"
x1="142.92365" />
</g>
<path
id="path32"
style="fill:none;stroke:#333333;stroke-linecap:round;stroke-linejoin:round"
transform="translate(-14.81094,-16.5)"
d="m 144.59874,23.379 v 0 -5.2617 A 1.1173,1.1173 0 0 1 145.716,17 h 1.04816 a 1.1173,1.1173 0 0 1 1.1173,1.1173 v 4.14439 a 1.11729,1.11729 0 0 0 1.11729,1.1173 h 15.29647"
inkscape:connector-curvature="0" />
</g>
<g
id="g126"
transform="matrix(0.26458426,0,0,0.26458426,36.52507,36.236497)">
<g
id="g40">
<line
id="line34"
style="fill:none;stroke:#a8a8a8;stroke-linecap:round;stroke-linejoin:round"
y2="6.8789902"
x2="39.228661"
y1="0.5"
x1="39.228661" />
<line
id="line36"
style="fill:none;stroke:#a8a8a8;stroke-linecap:round;stroke-linejoin:round"
y2="6.8789902"
x2="24.45627"
y1="0.5"
x1="24.45627" />
<line
id="line38"
style="fill:none;stroke:#a8a8a8;stroke-linecap:round;stroke-linejoin:round"
y2="6.8789902"
x2="19.532141"
y1="0.5"
x1="19.532141" />
</g>
<path
transform="translate(-14.81094,-16.5)"
id="path84"
style="fill:none;stroke:#333333;stroke-linecap:round;stroke-linejoin:round"
d="m 34.34308,23.379 h 8.731 a 1.11729,1.11729 0 0 0 1.11729,-1.1173 V 18.1173 A 1.1173,1.1173 0 0 1 45.30864,17 h 2.68954 a 1.11729,1.11729 0 0 1 1.11729,1.1173 v 4.14439 a 1.1173,1.1173 0 0 0 1.1173,1.1173 h 3.80683"
inkscape:connector-curvature="0" />
</g>
<g
id="g358"
transform="translate(-6.7638893,-11.032398)">
<g
id="g22-4"
transform="matrix(0.26458426,0,0,0.26458426,-2.729345,16.582661)">
<line
id="line16-2"
style="fill:none;stroke:#a8a8a8;stroke-linecap:round;stroke-linejoin:round"
y2="23.37899"
x2="179.22273"
y1="17"
x1="179.22273" />
<line
id="line18-9"
style="fill:none;stroke:#a8a8a8;stroke-linecap:round;stroke-linejoin:round"
y2="23.37899"
x2="198.91927"
y1="17"
x1="198.91927" />
<line
id="line20-1"
style="fill:none;stroke:#a8a8a8;stroke-linecap:round;stroke-linejoin:round"
y2="23.37899"
x2="192.3586"
y1="17"
x1="192.3586" />
</g>
<path
id="path108-6"
style="fill:none;stroke:#333333;stroke-width:0.26458427;stroke-linecap:round;stroke-linejoin:round"
d="m 44.690171,22.768376 h 1.432131 a 0.30500215,0.30500215 0 0 0 0.305,-0.304999 v -1.077784 a 0.30499951,0.30499951 0 0 1 0.304999,-0.305 h 0.258565 a 0.30499951,0.30499951 0 0 1 0.305,0.305 v 1.077781 a 0.30499951,0.30499951 0 0 0 0.304999,0.305 h 2.300695"
inkscape:connector-curvature="0" />
</g>
<g
id="g351"
transform="translate(-8.8805593,-11.032398)">
<g
id="g30-4"
transform="matrix(0.26458426,0,0,0.26458426,-2.729345,16.582661)">
<line
id="line24-4"
style="fill:none;stroke:#a8a8a8;stroke-linecap:round;stroke-linejoin:round"
y2="23.37899"
x2="215.2529"
y1="17"
x1="215.2529" />
<line
id="line26-9"
style="fill:none;stroke:#a8a8a8;stroke-linecap:round;stroke-linejoin:round"
y2="23.37899"
x2="221.82082"
y1="17"
x1="221.82082" />
<line
id="line28-5"
style="fill:none;stroke:#a8a8a8;stroke-linecap:round;stroke-linejoin:round"
y2="23.37899"
x2="234.94942"
y1="17"
x1="234.94942" />
</g>
<path
id="path110-3"
style="fill:none;stroke:#333333;stroke-width:0.26458427;stroke-linecap:round;stroke-linejoin:round"
d="m 54.223185,22.768376 h 3.169259 a 0.30499951,0.30499951 0 0 0 0.304999,-0.304999 v -1.077784 a 0.30499951,0.30499951 0 0 1 0.305,-0.305 h 0.258567 a 0.30499951,0.30499951 0 0 1 0.305,0.305 v 1.077781 a 0.30499951,0.30499951 0 0 0 0.304999,0.305 h 0.563565"
inkscape:connector-curvature="0" />
</g>
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:10.58337021px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458427"
x="7.3508749"
y="42.490799"
id="text245"><tspan
sodipodi:role="line"
id="tspan243"
x="7.3508749"
y="52.146332"
style="stroke-width:0.26458427" /></text>
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:2.82223225px;line-height:1.25;font-family:Sniglet;-inkscape-font-specification:'Sniglet, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458427"
x="3.1753566"
y="3.9635246"
id="text249"><tspan
sodipodi:role="line"
id="tspan247"
x="3.1753566"
y="3.9635246"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:2.82223225px;font-family:Sniglet;-inkscape-font-specification:'Sniglet, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;stroke-width:0.26458427">PULSES PER STEPS (PPS) - REQUIREMENTS</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:2.82223225px;line-height:1.25;font-family:Sniglet;-inkscape-font-specification:'Sniglet, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458427"
x="12.148573"
y="41.321423"
id="text249-5-3"><tspan
sodipodi:role="line"
id="tspan247-9-6"
x="12.148573"
y="41.321423"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:2.82223225px;font-family:Sniglet;-inkscape-font-specification:'Sniglet, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;stroke-width:0.26458427">ALL</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:2.82223225px;line-height:1.25;font-family:Sniglet;-inkscape-font-specification:'Sniglet, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458427"
x="19.675114"
y="8.6498356"
id="text249-5-8"><tspan
sodipodi:role="line"
id="tspan247-9-4"
x="19.675114"
y="8.6498356"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:2.82223225px;font-family:Sniglet;-inkscape-font-specification:'Sniglet, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;stroke-width:0.26458427">MULTIPLES OF 6</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:2.82223225px;line-height:1.25;font-family:Sniglet;-inkscape-font-specification:'Sniglet, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458427"
x="49.741993"
y="41.321423"
id="text249-5-3-8"><tspan
sodipodi:role="line"
id="tspan247-9-6-2"
x="49.741993"
y="41.321423"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:2.82223225px;font-family:Sniglet;-inkscape-font-specification:'Sniglet, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;stroke-width:0.26458427">ALL</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:2.82223225px;line-height:1.25;font-family:Sniglet;-inkscape-font-specification:'Sniglet, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458427"
x="27.327574"
y="41.332336"
id="text249-5-3-3"><tspan
sodipodi:role="line"
id="tspan247-9-6-7"
x="27.327574"
y="41.332336"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:2.82223225px;font-family:Sniglet;-inkscape-font-specification:'Sniglet, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;stroke-width:0.26458427">ALL</tspan></text>
<g
id="g198-7"
transform="matrix(0.26458426,0,0,0.26458426,-49.235941,-22.33047)">
<line
id="line6-2"
style="fill:none;stroke:#a8a8a8;stroke-linecap:round;stroke-linejoin:round"
y2="128.75461"
x2="236.47182"
y1="122.37562"
x1="236.47182" />
<path
id="path106-8"
style="fill:none;stroke:#333333;stroke-linecap:round;stroke-linejoin:round"
transform="translate(-14.81094,-16.5)"
d="m 231.58623,145.25461 v -5.2617 a 1.11729,1.11729 0 0 1 1.1173,-1.11729 h 1.04816 a 1.11729,1.11729 0 0 1 1.1173,1.11729 v 4.1444 a 1.11729,1.11729 0 0 0 1.11729,1.1173 h 1.04816 a 1.1173,1.1173 0 0 0 1.1173,-1.1173 v -4.1444 a 1.11729,1.11729 0 0 1 1.1173,-1.11729 h 1.04816 a 1.11729,1.11729 0 0 1 1.11729,1.11729 v 4.1444 a 1.1173,1.1173 0 0 0 1.1173,1.1173 H 243.6 a 1.1173,1.1173 0 0 0 1.1173,-1.1173 v -4.1444 a 1.11729,1.11729 0 0 1 1.11729,-1.11729 h 1.04816 A 1.11729,1.11729 0 0 1 248,139.99291 v 4.1444 a 1.1173,1.1173 0 0 0 1.1173,1.1173 h 2.16545"
inkscape:connector-curvature="0" />
</g>
<g
id="g1007"
transform="translate(-45.905405,-6.2726063)">
<line
id="line6-2-3"
style="fill:none;stroke:#a8a8a8;stroke-width:0.26458427;stroke-linecap:round;stroke-linejoin:round"
y2="18.029966"
x2="66.630135"
y1="16.342186"
x1="66.630135" />
<line
id="line6-2-3-3"
style="fill:none;stroke:#a8a8a8;stroke-width:0.26458427;stroke-linecap:round;stroke-linejoin:round"
y2="18.029966"
x2="61.418743"
y1="16.342186"
x1="61.418743" />
<path
sodipodi:nodetypes="ccccccccssccccccsc"
id="path106-8-9"
style="fill:none;stroke:#333333;stroke-width:0.26458427;stroke-linecap:round;stroke-linejoin:round"
d="m 61.418742,18.029966 c 2.141299,0 -0.951586,0 1.441511,0 0.163266,0 0.295619,-0.132353 0.29562,-0.295619 v -1.096544 c 0,-0.163266 0.132354,-0.295618 0.29562,-0.295617 h 0.277326 c 0.163265,0 0.295618,0.132352 0.295618,0.295617 v 1.096544 c 10e-7,0.163266 0.132354,0.295619 0.29562,0.295619 h 0.27734 c 0.163266,0 0.295619,-0.132353 0.29562,-0.295619 v -1.096544 c 0,-0.163265 0.132352,-0.295617 0.295617,-0.295617 h 0.277327 c 0.16326,6e-6 0.295606,0.132357 0.295606,0.295617 v 1.096544 c 10e-7,0.163266 0.132354,0.295619 0.29562,0.295619 h 0.572944"
inkscape:connector-curvature="0" />
</g>
<path
inkscape:connector-curvature="0"
d="m 49.106667,36.368793 v 1.392161 a 0.29562,0.29562 0 0 0 0.29562,0.295619 h 4.915769"
style="fill:none;stroke:#333333;stroke-width:0.26458427;stroke-linecap:round;stroke-linejoin:round"
id="path94-9" />
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:10.58337021px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458427"
x="5.1611581"
y="41.337326"
id="text1040"><tspan
sodipodi:role="line"
id="tspan1038"
x="5.1611581"
y="41.337326"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:2.82223225px;font-family:Sniglet;-inkscape-font-specification:'Sniglet, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;stroke-width:0.26458427">M4</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:10.58337021px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458427"
x="20.233774"
y="41.303471"
id="text1040-4"><tspan
sodipodi:role="line"
id="tspan1038-5"
x="20.233774"
y="41.303471"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:2.82223225px;font-family:Sniglet;-inkscape-font-specification:'Sniglet, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;stroke-width:0.26458427">M4</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:10.58337021px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458427"
x="38.53788"
y="41.303474"
id="text1040-8"><tspan
sodipodi:role="line"
id="tspan1038-4"
x="38.53788"
y="41.303474"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:2.82223225px;font-family:Sniglet;-inkscape-font-specification:'Sniglet, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;stroke-width:0.26458427">M4</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:10.58337021px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458427"
x="17.403894"
y="46.498486"
id="text1072"><tspan
sodipodi:role="line"
id="tspan1070"
x="17.403894"
y="46.498486"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:2.82223225px;font-family:Sniglet;-inkscape-font-specification:'Sniglet, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;stroke-width:0.26458427">M4: MULTIPLES OF 4</tspan></text>
</svg>

+ 1
- 1
plugins/community/repos/ImpromptuModular/res/dark/Clocked_dark.svg
File diff suppressed because it is too large
View File


+ 1
- 1
plugins/community/repos/ImpromptuModular/res/dark/GateSeq64_dark.svg
File diff suppressed because it is too large
View File


+ 1
- 1
plugins/community/repos/ImpromptuModular/res/dark/PhraseSeq16_dark.svg
File diff suppressed because it is too large
View File


+ 1
- 1
plugins/community/repos/ImpromptuModular/res/dark/PhraseSeq32_dark.svg
File diff suppressed because it is too large
View File


BIN
plugins/community/repos/ImpromptuModular/res/img/AdvancedGateDetails.jpg View File

Before After
Width: 340  |  Height: 282  |  Size: 28KB

BIN
plugins/community/repos/ImpromptuModular/res/img/Clocked.jpg View File

Before After
Width: 594  |  Height: 456  |  Size: 84KB Width: 594  |  Height: 456  |  Size: 83KB

BIN
plugins/community/repos/ImpromptuModular/res/img/GateSeq64.jpg View File

Before After
Width: 432  |  Height: 456  |  Size: 48KB Width: 432  |  Height: 456  |  Size: 53KB

BIN
plugins/community/repos/ImpromptuModular/res/img/PhraseSeq16.jpg View File

Before After
Width: 540  |  Height: 456  |  Size: 71KB Width: 539  |  Height: 456  |  Size: 73KB

BIN
plugins/community/repos/ImpromptuModular/res/img/PhraseSeq32.jpg View File

Before After
Width: 576  |  Height: 456  |  Size: 84KB Width: 576  |  Height: 456  |  Size: 87KB

+ 1
- 1
plugins/community/repos/ImpromptuModular/res/light/Clocked.svg
File diff suppressed because it is too large
View File


+ 0
- 931
plugins/community/repos/ImpromptuModular/res/light/EngTest1.svg
File diff suppressed because it is too large
View File


+ 1
- 1
plugins/community/repos/ImpromptuModular/res/light/GateSeq64.svg
File diff suppressed because it is too large
View File


+ 1
- 1
plugins/community/repos/ImpromptuModular/res/light/PhraseSeq16.svg
File diff suppressed because it is too large
View File


+ 1
- 1
plugins/community/repos/ImpromptuModular/res/light/PhraseSeq32.svg
File diff suppressed because it is too large
View File


+ 33
- 20
plugins/community/repos/ImpromptuModular/src/BigButtonSeq.cpp View File

@@ -14,7 +14,7 @@




#include "ImpromptuModular.hpp" #include "ImpromptuModular.hpp"
#include "dsp/digital.hpp"
#include "PhraseSeqUtil.hpp"


namespace rack_plugin_ImpromptuModular { namespace rack_plugin_ImpromptuModular {


@@ -91,6 +91,7 @@ struct BigButtonSeq : Module {
PulseGenerator outLightPulse; PulseGenerator outLightPulse;
PulseGenerator bigPulse; PulseGenerator bigPulse;
PulseGenerator bigLightPulse; PulseGenerator bigLightPulse;
int lightRefreshCounter;


inline void toggleGate(int chan) {gates[chan][bank[chan]] ^= (((uint64_t)1) << (uint64_t)indexStep);} inline void toggleGate(int chan) {gates[chan][bank[chan]] ^= (((uint64_t)1) << (uint64_t)indexStep);}
@@ -122,6 +123,7 @@ struct BigButtonSeq : Module {
outLightPulse.reset(); outLightPulse.reset();
bigPulse.reset(); bigPulse.reset();
bigLightPulse.reset(); bigLightPulse.reset();
lightRefreshCounter = 0;
onReset(); onReset();
} }
@@ -378,38 +380,49 @@ struct BigButtonSeq : Module {
//********** Outputs and lights ********** //********** Outputs and lights **********
// Gate outputs
bool bigPulseState = bigPulse.process((float)sampleTime); bool bigPulseState = bigPulse.process((float)sampleTime);
bool bigLightPulseState = bigLightPulse.process((float)sampleTime);
bool outPulseState = outPulse.process((float)sampleTime); bool outPulseState = outPulse.process((float)sampleTime);
bool outLightPulseState = outLightPulse.process((float)sampleTime);
// Gate and light outputs
for (int i = 0; i < 6; i++) { for (int i = 0; i < 6; i++) {
bool gate = getGate(i); bool gate = getGate(i);
bool outSignal = (((gate || (i == chan && fillPressed)) && outPulseState) || (gate && bigPulseState && i == chan)); bool outSignal = (((gate || (i == chan && fillPressed)) && outPulseState) || (gate && bigPulseState && i == chan));
bool outLight = (((gate || (i == chan && fillPressed)) && outLightPulseState) || (gate && bigLightPulseState && i == chan));
outputs[CHAN_OUTPUTS + i].value = outSignal ? 10.0f : 0.0f; outputs[CHAN_OUTPUTS + i].value = outSignal ? 10.0f : 0.0f;
lights[(CHAN_LIGHTS + i) * 2 + 1].setBrightnessSmooth(outLight ? 1.0f : 0.0f);
lights[(CHAN_LIGHTS + i) * 2 + 0].setBrightnessSmooth(i == chan ? (1.0f - lights[(CHAN_LIGHTS + i) * 2 + 1].value) / 2.0f : 0.0f);
} }

// Big button lights
lights[BIG_LIGHT].value = bank[chan] == 1 ? 1.0f : 0.0f;
lights[BIGC_LIGHT].value = bigLight;
lightRefreshCounter++;
if (lightRefreshCounter > displayRefreshStepSkips) {
lightRefreshCounter = 0;

// Gate light outputs
bool bigLightPulseState = bigLightPulse.process((float)sampleTime * displayRefreshStepSkips);
bool outLightPulseState = outLightPulse.process((float)sampleTime * displayRefreshStepSkips);
for (int i = 0; i < 6; i++) {
bool gate = getGate(i);
bool outLight = (((gate || (i == chan && fillPressed)) && outLightPulseState) || (gate && bigLightPulseState && i == chan));
lights[(CHAN_LIGHTS + i) * 2 + 1].setBrightnessSmooth(outLight ? 1.0f : 0.0f, displayRefreshStepSkips);
lights[(CHAN_LIGHTS + i) * 2 + 0].value = (i == chan ? (1.0f - lights[(CHAN_LIGHTS + i) * 2 + 1].value) / 2.0f : 0.0f);
}

// Big button lights
lights[BIG_LIGHT].value = bank[chan] == 1 ? 1.0f : 0.0f;
lights[BIGC_LIGHT].value = bigLight;
// Metronome light
lights[METRONOME_LIGHT + 1].value = metronomeLightStart;
lights[METRONOME_LIGHT + 0].value = metronomeLightDiv;
// Metronome light
lights[METRONOME_LIGHT + 1].value = metronomeLightStart;
lights[METRONOME_LIGHT + 0].value = metronomeLightDiv;
if (clockIgnoreOnReset > 0l)
clockIgnoreOnReset--;
bigLight -= (bigLight / lightLambda) * (float)sampleTime * displayRefreshStepSkips;
metronomeLightStart -= (metronomeLightStart / lightLambda) * (float)sampleTime * displayRefreshStepSkips;
metronomeLightDiv -= (metronomeLightDiv / lightLambda) * (float)sampleTime * displayRefreshStepSkips;
}
bigLight -= (bigLight / lightLambda) * (float)sampleTime;
metronomeLightStart -= (metronomeLightStart / lightLambda) * (float)sampleTime;
metronomeLightDiv -= (metronomeLightDiv / lightLambda) * (float)sampleTime;

clockTime += sampleTime; clockTime += sampleTime;
if (clockIgnoreOnReset > 0l)
clockIgnoreOnReset--;
scheduledReset = false; scheduledReset = false;
}// step() }// step()


+ 116
- 107
plugins/community/repos/ImpromptuModular/src/Clocked.cpp View File

@@ -12,7 +12,6 @@




#include "ImpromptuModular.hpp" #include "ImpromptuModular.hpp"
#include "dsp/digital.hpp"


namespace rack_plugin_ImpromptuModular { namespace rack_plugin_ImpromptuModular {


@@ -186,6 +185,8 @@ struct Clocked : Module {
RESET_PARAM, RESET_PARAM,
RUN_PARAM, RUN_PARAM,
ENUMS(DELAY_PARAMS, 4),// index 0 is unused ENUMS(DELAY_PARAMS, 4),// index 0 is unused
// -- 0.6.9 ^^
BPMMODE_PARAM,
NUM_PARAMS NUM_PARAMS
}; };
enum InputIds { enum InputIds {
@@ -206,7 +207,8 @@ struct Clocked : Module {
enum LightIds { enum LightIds {
RESET_LIGHT, RESET_LIGHT,
RUN_LIGHT, RUN_LIGHT,
ENUMS(CLK_LIGHTS, 4),// master is index 0
ENUMS(CLK_LIGHTS, 4),// master is index 0 (not used)
ENUMS(BPMSYNC_LIGHT, 2),// room for GreenRed
NUM_LIGHTS NUM_LIGHTS
}; };
@@ -256,6 +258,10 @@ struct Clocked : Module {
double extIntervalTime; double extIntervalTime;
double timeoutTime; double timeoutTime;
long cantRunWarning;// 0 when no warning, positive downward step counter timer when warning long cantRunWarning;// 0 when no warning, positive downward step counter timer when warning
long editingBpmMode;// 0 when no edit bpmMode, downward step counter timer when edit, negative upward when show can't edit ("--")
int lightRefreshCounter;
SchmittTrigger bpmModeTrigger;


// called from the main thread (step() can not be called until all modules created) // called from the main thread (step() can not be called until all modules created)
@@ -291,6 +297,8 @@ struct Clocked : Module {
extIntervalTime = 0.0; extIntervalTime = 0.0;
timeoutTime = 2.0 / ppqn + 0.1; timeoutTime = 2.0 / ppqn + 0.1;
cantRunWarning = 0ul; cantRunWarning = 0ul;
bpmModeTrigger.reset();
lightRefreshCounter = 0;
onReset(); onReset();
} }
@@ -441,7 +449,6 @@ struct Clocked : Module {
void step() override { void step() override {
double sampleRate = (double)engineGetSampleRate(); double sampleRate = (double)engineGetSampleRate();
double sampleTime = 1.0 / sampleRate;// do this here since engineGetSampleRate() returns float double sampleTime = 1.0 / sampleRate;// do this here since engineGetSampleRate() returns float
long cantRunWarningInit = (long) (0.7 * engineGetSampleRate());


// Scheduled reset (just the parts that do not have a place below in rest of function) // Scheduled reset (just the parts that do not have a place below in rest of function)
if (scheduledReset) { if (scheduledReset) {
@@ -453,6 +460,7 @@ struct Clocked : Module {
runPulse.reset(); runPulse.reset();
bpmDetectTrigger.reset(); bpmDetectTrigger.reset();
cantRunWarning = 0l; cantRunWarning = 0l;
editingBpmMode = 0l;
} }
// Run button // Run button
@@ -467,7 +475,7 @@ struct Clocked : Module {
} }
} }
else else
cantRunWarning = cantRunWarningInit;
cantRunWarning = (long) (0.7 * sampleRate / displayRefreshStepSkips);
} }


// Reset (has to be near top because it sets steps to 0, and 0 not a real step (clock section will move to 1 before reaching outputs) // Reset (has to be near top because it sets steps to 0, and 0 not a real step (clock section will move to 1 before reaching outputs)
@@ -475,10 +483,33 @@ struct Clocked : Module {
resetLight = 1.0f; resetLight = 1.0f;
resetPulse.trigger(0.001f); resetPulse.trigger(0.001f);
resetClocked(); resetClocked();
}

// BPM mode
if (bpmModeTrigger.process(params[BPMMODE_PARAM].value)) {
if (inputs[BPM_INPUT].active) {
if (editingBpmMode != 0ul) {// force active before allow change
if (bpmDetectionMode == false) {
bpmDetectionMode = true;
ppqn = 4;
}
else {
if (ppqn == 4)
ppqn = 8;
else if (ppqn == 8)
ppqn = 12;
else if (ppqn == 12)
ppqn = 24;
else
bpmDetectionMode = false;
}
}
editingBpmMode = (long) (3.0 * sampleRate / displayRefreshStepSkips);
}
else
editingBpmMode = (long) (-1.5 * sampleRate / displayRefreshStepSkips);
} }
else
resetLight -= (resetLight / lightLambda) * (float)sampleTime;

// BPM input and knob // BPM input and knob
float newMasterLength = masterLength; float newMasterLength = masterLength;
if (inputs[BPM_INPUT].active) { if (inputs[BPM_INPUT].active) {
@@ -566,7 +597,7 @@ struct Clocked : Module {
} }
if (newSwingVal != swingVal[i]) { if (newSwingVal != swingVal[i]) {
swingVal[i] = newSwingVal; swingVal[i] = newSwingVal;
swingInfo[i] = (long) (swingInfoTime * (float)sampleRate);// trigger swing info on channel i
swingInfo[i] = (long) (swingInfoTime * (float)sampleRate / displayRefreshStepSkips);// trigger swing info on channel i
delayInfo[i] = 0l;// cancel delayed being displayed (if so) delayInfo[i] = 0l;// cancel delayed being displayed (if so)
} }
if (i > 0) { if (i > 0) {
@@ -577,7 +608,7 @@ struct Clocked : Module {
} }
if (newDelayKnobIndex != delayKnobIndexes[i]) { if (newDelayKnobIndex != delayKnobIndexes[i]) {
delayKnobIndexes[i] = newDelayKnobIndex; delayKnobIndexes[i] = newDelayKnobIndex;
delayInfo[i] = (long) (delayInfoTime * (float)sampleRate);// trigger delay info on channel i
delayInfo[i] = (long) (delayInfoTime * (float)sampleRate / displayRefreshStepSkips);// trigger delay info on channel i
swingInfo[i] = 0l;// cancel swing being displayed (if so) swingInfo[i] = 0l;// cancel swing being displayed (if so)
} }
} }
@@ -664,44 +695,60 @@ struct Clocked : Module {
for (int i = 0; i < 4; i++) for (int i = 0; i < 4; i++)
outputs[CLK_OUTPUTS + i].value = 0.0f; outputs[CLK_OUTPUTS + i].value = 0.0f;
} }
for (int i = 0; i < 4; i++)
clk[i].stepClock();
// Chaining outputs // Chaining outputs
outputs[RESET_OUTPUT].value = (resetPulse.process((float)sampleTime) ? 10.0f : 0.0f); outputs[RESET_OUTPUT].value = (resetPulse.process((float)sampleTime) ? 10.0f : 0.0f);
outputs[RUN_OUTPUT].value = (runPulse.process((float)sampleTime) ? 10.0f : 0.0f); outputs[RUN_OUTPUT].value = (runPulse.process((float)sampleTime) ? 10.0f : 0.0f);
outputs[BPM_OUTPUT].value = inputs[BPM_INPUT].active ? inputs[BPM_INPUT].value : log2f(1.0f / masterLength); outputs[BPM_OUTPUT].value = inputs[BPM_INPUT].active ? inputs[BPM_INPUT].value : log2f(1.0f / masterLength);
// Reset light
lights[RESET_LIGHT].value = resetLight;
// Run light
lights[RUN_LIGHT].value = running;
// BPM light
if (cantRunWarning > 0l) {
bool warningFlashState = calcWarningFlash(cantRunWarning, cantRunWarningInit);
lights[CLK_LIGHTS + 0].value = (warningFlashState) ? 1.0f : 0.0f;
}
else
lights[CLK_LIGHTS + 0].value = (bpmDetectionMode && inputs[BPM_INPUT].active) ? 1.0f : 0.0f;
// ratios synched lights
for (int i = 1; i < 4; i++) {
lights[CLK_LIGHTS + i].value = (syncRatios[i] && running) ? 1.0f: 0.0f;
}
lightRefreshCounter++;
if (lightRefreshCounter > displayRefreshStepSkips) {
lightRefreshCounter = 0;


// Incr/decr all counters related to step()
for (int i = 0; i < 4; i++) {
clk[i].stepClock();
if (swingInfo[i] > 0)
swingInfo[i]--;
if (delayInfo[i] > 0)
delayInfo[i]--;
}
if (cantRunWarning > 0l)
cantRunWarning--;
// Reset light
lights[RESET_LIGHT].value = resetLight;
resetLight -= (resetLight / lightLambda) * (float)sampleTime * displayRefreshStepSkips;
// Run light
lights[RUN_LIGHT].value = running ? 1.0f : 0.0f;
// BPM light
bool warningFlashState = true;
if (cantRunWarning > 0l)
warningFlashState = calcWarningFlash(cantRunWarning, (long) (0.7 * sampleRate / displayRefreshStepSkips));
lights[BPMSYNC_LIGHT + 0].value = (bpmDetectionMode && warningFlashState && inputs[BPM_INPUT].active) ? 1.0f : 0.0f;
if (editingBpmMode < 0l)
lights[BPMSYNC_LIGHT + 1].value = 1.0f;
else
lights[BPMSYNC_LIGHT + 1].value = (bpmDetectionMode && warningFlashState && inputs[BPM_INPUT].active) ? (float)((ppqn - 4)*(ppqn - 4))/400.0f : 0.0f;
// ratios synched lights
for (int i = 1; i < 4; i++) {
lights[CLK_LIGHTS + i].value = (syncRatios[i] && running) ? 1.0f: 0.0f;
}

// Incr/decr all counters related to step()
for (int i = 0; i < 4; i++) {
if (swingInfo[i] > 0)
swingInfo[i]--;
if (delayInfo[i] > 0)
delayInfo[i]--;
}
if (cantRunWarning > 0l)
cantRunWarning--;
if (editingBpmMode != 0l) {
if (editingBpmMode > 0l)
editingBpmMode--;
else
editingBpmMode++;
}
}// lightRefreshCounter
scheduledReset = false; scheduledReset = false;
}
}// step()
}; };




@@ -710,7 +757,7 @@ struct ClockedWidget : ModuleWidget {
DynamicSVGPanel *panel; DynamicSVGPanel *panel;
int oldExpansion; int oldExpansion;
int expWidth = 60; int expWidth = 60;
IMPort* expPorts[5];
IMPort* expPorts[6];




struct RatioDisplayWidget : TransparentWidget { struct RatioDisplayWidget : TransparentWidget {
@@ -770,7 +817,18 @@ struct ClockedWidget : ModuleWidget {
} }
} }
else {// BPM to display else {// BPM to display
snprintf(displayStr, 4, "%3u", (unsigned) round(120.0f / module->masterLength));
if (module->editingBpmMode != 0l) {
if (module->editingBpmMode > 0l) {
if (!module->bpmDetectionMode)
snprintf(displayStr, 4, " CV");
else
snprintf(displayStr, 4, "P%2u", (unsigned) module->ppqn);
}
else
snprintf(displayStr, 4, " --");
}
else
snprintf(displayStr, 4, "%3u", (unsigned)((120.0f / module->masterLength) + 0.5f));
} }
} }
displayStr[3] = 0;// more safety displayStr[3] = 0;// more safety
@@ -800,56 +858,12 @@ struct ClockedWidget : ModuleWidget {
module->displayDelayNoteMode = !module->displayDelayNoteMode; module->displayDelayNoteMode = !module->displayDelayNoteMode;
} }
}; };
struct BpmDetectionItem : MenuItem {
Clocked *module;
void onAction(EventAction &e) override {
module->bpmDetectionMode = !module->bpmDetectionMode;
if (module->bpmDetectionMode && module->running) {
module->running = false;
module->resetClocked();
}
}
};
struct EmitResetItem : MenuItem { struct EmitResetItem : MenuItem {
Clocked *module; Clocked *module;
void onAction(EventAction &e) override { void onAction(EventAction &e) override {
module->emitResetOnStopRun = !module->emitResetOnStopRun; module->emitResetOnStopRun = !module->emitResetOnStopRun;
} }
}; };
struct BpmPpqnItem : MenuItem {
Clocked *module;
int oldPpqn = -1;
void onAction(EventAction &e) override {
if (module->ppqn == 4) {
module->ppqn = 8;
}
else if (module->ppqn == 8) {
module->ppqn = 24;
}
else {
module->ppqn = 4;
}
}
void step() override {
if (oldPpqn != module->ppqn) {
oldPpqn = module->ppqn;
if (oldPpqn == 4) {
text = "- BPM detection PPQN: <4>, 8, 24";
}
else if (module->ppqn == 8) {
text = "- BPM detection PPQN: 4, <8>, 24";
}
else if (module->ppqn == 24) {
text = "- BPM detection PPQN: 4, 8, <24>";
}
else {
text = "- BPM detection PPQN: *error*";
}
}
MenuItem::step();
}
};
Menu *createContextMenu() override { Menu *createContextMenu() override {
Menu *menu = ModuleWidget::createContextMenu(); Menu *menu = ModuleWidget::createContextMenu();


@@ -889,14 +903,6 @@ struct ClockedWidget : ModuleWidget {
erItem->module = module; erItem->module = module;
menu->addChild(erItem); menu->addChild(erItem);


BpmDetectionItem *detectItem = MenuItem::create<BpmDetectionItem>("Use BPM Detection (as opposed to BPM CV)", CHECKMARK(module->bpmDetectionMode));
detectItem->module = module;
menu->addChild(detectItem);

BpmPpqnItem *detect4Item = MenuItem::create<BpmPpqnItem>("PPQN", CHECKMARK(false));
detect4Item->module = module;
menu->addChild(detect4Item);

menu->addChild(new MenuLabel());// empty line menu->addChild(new MenuLabel());// empty line
MenuLabel *expansionLabel = new MenuLabel(); MenuLabel *expansionLabel = new MenuLabel();
@@ -913,7 +919,7 @@ struct ClockedWidget : ModuleWidget {
void step() override { void step() override {
if(module->expansion != oldExpansion) { if(module->expansion != oldExpansion) {
if (oldExpansion!= -1 && module->expansion == 0) {// if just removed expansion panel, disconnect wires to those jacks if (oldExpansion!= -1 && module->expansion == 0) {// if just removed expansion panel, disconnect wires to those jacks
for (int i = 0; i < 5; i++)
for (int i = 0; i < 6; i++)
rack::global_ui->app.gRackWidget->wireContainer->removeAllWires(expPorts[i]); rack::global_ui->app.gRackWidget->wireContainer->removeAllWires(expPorts[i]);
} }
oldExpansion = module->expansion; oldExpansion = module->expansion;
@@ -929,11 +935,11 @@ struct ClockedWidget : ModuleWidget {
// Main panel from Inkscape // Main panel from Inkscape
panel = new DynamicSVGPanel(); panel = new DynamicSVGPanel();
panel->mode = &module->panelTheme; panel->mode = &module->panelTheme;
panel->expWidth = &expWidth;
panel->expWidth = &expWidth;
panel->addPanel(SVG::load(assetPlugin(plugin, "res/light/Clocked.svg"))); panel->addPanel(SVG::load(assetPlugin(plugin, "res/light/Clocked.svg")));
panel->addPanel(SVG::load(assetPlugin(plugin, "res/dark/Clocked_dark.svg"))); panel->addPanel(SVG::load(assetPlugin(plugin, "res/dark/Clocked_dark.svg")));
box.size = panel->box.size; box.size = panel->box.size;
box.size.x = box.size.x - (1 - module->expansion) * expWidth;
box.size.x = box.size.x - (1 - module->expansion) * expWidth;
addChild(panel); addChild(panel);
// Screws // Screws
@@ -986,8 +992,6 @@ struct ClockedWidget : ModuleWidget {
displayRatios[0]->module = module; displayRatios[0]->module = module;
displayRatios[0]->knobIndex = 0; displayRatios[0]->knobIndex = 0;
addChild(displayRatios[0]); addChild(displayRatios[0]);
// BPM external pulses lock light
addChild(ModuleLightWidget::create<SmallLight<GreenLight>>(Vec(colRulerT4 + 11 + 62, rowRuler0 + 10), module, Clocked::CLK_LIGHTS + 0));
// Row 1 // Row 1
// Reset LED bezel and light // Reset LED bezel and light
@@ -996,8 +1000,9 @@ struct ClockedWidget : ModuleWidget {
// Run LED bezel and light // Run LED bezel and light
addParam(ParamWidget::create<LEDBezel>(Vec(colRulerT1 + offsetLEDbezel, rowRuler1 + offsetLEDbezel), module, Clocked::RUN_PARAM, 0.0f, 1.0f, 0.0f)); addParam(ParamWidget::create<LEDBezel>(Vec(colRulerT1 + offsetLEDbezel, rowRuler1 + offsetLEDbezel), module, Clocked::RUN_PARAM, 0.0f, 1.0f, 0.0f));
addChild(ModuleLightWidget::create<MuteLight<GreenLight>>(Vec(colRulerT1 + offsetLEDbezel + offsetLEDbezelLight, rowRuler1 + offsetLEDbezel + offsetLEDbezelLight), module, Clocked::RUN_LIGHT)); addChild(ModuleLightWidget::create<MuteLight<GreenLight>>(Vec(colRulerT1 + offsetLEDbezel + offsetLEDbezelLight, rowRuler1 + offsetLEDbezel + offsetLEDbezelLight), module, Clocked::RUN_LIGHT));
// PW master input
addInput(createDynamicPort<IMPort>(Vec(colRulerT2, rowRuler1), Port::INPUT, module, Clocked::PW_INPUTS + 0, &module->panelTheme));
// BPM mode and light
addParam(ParamWidget::create<TL1105>(Vec(colRulerT2 + offsetTL1105, rowRuler1 + offsetTL1105), module, Clocked::BPMMODE_PARAM, 0.0f, 1.0f, 0.0f));
addChild(ModuleLightWidget::create<SmallLight<GreenRedLight>>(Vec(colRulerM1 + 62, rowRuler1 + offsetMediumLight), module, Clocked::BPMSYNC_LIGHT));
// Swing master knob // Swing master knob
addParam(createDynamicParam<IMSmallKnob>(Vec(colRulerT3 + offsetIMSmallKnob, rowRuler1 + offsetIMSmallKnob), module, Clocked::SWING_PARAMS + 0, -1.0f, 1.0f, 0.0f, &module->panelTheme)); addParam(createDynamicParam<IMSmallKnob>(Vec(colRulerT3 + offsetIMSmallKnob, rowRuler1 + offsetIMSmallKnob), module, Clocked::SWING_PARAMS + 0, -1.0f, 1.0f, 0.0f, &module->panelTheme));
// PW master knob // PW master knob
@@ -1040,15 +1045,15 @@ struct ClockedWidget : ModuleWidget {
addOutput(createDynamicPort<IMPort>(Vec(colRulerT5, rowRuler5), Port::OUTPUT, module, Clocked::CLK_OUTPUTS + 3, &module->panelTheme)); addOutput(createDynamicPort<IMPort>(Vec(colRulerT5, rowRuler5), Port::OUTPUT, module, Clocked::CLK_OUTPUTS + 3, &module->panelTheme));


// Expansion module // Expansion module
static const int rowRulerExpTop = 65;
static const int rowSpacingExp = 60;
static const int rowRulerExpTop = 60;
static const int rowSpacingExp = 50;
static const int colRulerExp = 497 - 30 -150;// Clocked is (2+10)HP less than PS32 static const int colRulerExp = 497 - 30 -150;// Clocked is (2+10)HP less than PS32
addInput(expPorts[0] = createDynamicPort<IMPort>(Vec(colRulerExp, rowRulerExpTop + rowSpacingExp * 0), Port::INPUT, module, Clocked::PW_INPUTS + 1, &module->panelTheme));
addInput(expPorts[1] = createDynamicPort<IMPort>(Vec(colRulerExp, rowRulerExpTop + rowSpacingExp * 1), Port::INPUT, module, Clocked::PW_INPUTS + 2, &module->panelTheme));
addInput(expPorts[2] = createDynamicPort<IMPort>(Vec(colRulerExp, rowRulerExpTop + rowSpacingExp * 2), Port::INPUT, module, Clocked::SWING_INPUTS + 0, &module->panelTheme));
addInput(expPorts[3] = createDynamicPort<IMPort>(Vec(colRulerExp, rowRulerExpTop + rowSpacingExp * 3), Port::INPUT, module, Clocked::SWING_INPUTS + 1, &module->panelTheme));
addInput(expPorts[4] = createDynamicPort<IMPort>(Vec(colRulerExp, rowRulerExpTop + rowSpacingExp * 4), Port::INPUT, module, Clocked::SWING_INPUTS + 2, &module->panelTheme));
addInput(expPorts[0] = createDynamicPort<IMPort>(Vec(colRulerExp, rowRulerExpTop + rowSpacingExp * 0), Port::INPUT, module, Clocked::PW_INPUTS + 0, &module->panelTheme));
addInput(expPorts[1] = createDynamicPort<IMPort>(Vec(colRulerExp, rowRulerExpTop + rowSpacingExp * 1), Port::INPUT, module, Clocked::PW_INPUTS + 1, &module->panelTheme));
addInput(expPorts[2] = createDynamicPort<IMPort>(Vec(colRulerExp, rowRulerExpTop + rowSpacingExp * 2), Port::INPUT, module, Clocked::PW_INPUTS + 2, &module->panelTheme));
addInput(expPorts[3] = createDynamicPort<IMPort>(Vec(colRulerExp, rowRulerExpTop + rowSpacingExp * 3), Port::INPUT, module, Clocked::SWING_INPUTS + 0, &module->panelTheme));
addInput(expPorts[4] = createDynamicPort<IMPort>(Vec(colRulerExp, rowRulerExpTop + rowSpacingExp * 4), Port::INPUT, module, Clocked::SWING_INPUTS + 1, &module->panelTheme));
addInput(expPorts[5] = createDynamicPort<IMPort>(Vec(colRulerExp, rowRulerExpTop + rowSpacingExp * 5), Port::INPUT, module, Clocked::SWING_INPUTS + 2, &module->panelTheme));
} }
}; };


@@ -1063,6 +1068,10 @@ RACK_PLUGIN_MODEL_INIT(ImpromptuModular, Clocked) {


/*CHANGE LOG /*CHANGE LOG


0.6.10:
add ppqn setting of 12
move master PW to expansion panel and move BPM mode from right-click menu to main pannel button

0.6.9: 0.6.9:
new approach to BPM Detection (all slaves must enable Use BPM Detect if master does, and same ppqn) new approach to BPM Detection (all slaves must enable Use BPM Detect if master does, and same ppqn)
choice of 4, 8, 24 PPQN when using BPM detection choice of 4, 8, 24 PPQN when using BPM detection


+ 2
- 2
plugins/community/repos/ImpromptuModular/src/FundamentalUtil.hpp View File

@@ -48,8 +48,8 @@ struct LadderFilter {


// From Fundamental VCO.cpp // From Fundamental VCO.cpp
//template <int OVERSAMPLE, int QUALITY> //template <int OVERSAMPLE, int QUALITY>
static const int OVERSAMPLE = 16;
static const int QUALITY = 16;
static const int OVERSAMPLE = 8;
static const int QUALITY = 8;
struct VoltageControlledOscillator { struct VoltageControlledOscillator {
bool analog = false; bool analog = false;
bool soft = false; bool soft = false;


+ 512
- 369
plugins/community/repos/ImpromptuModular/src/GateSeq64.cpp
File diff suppressed because it is too large
View File


+ 10
- 123
plugins/community/repos/ImpromptuModular/src/ImpromptuModular.cpp View File

@@ -7,6 +7,7 @@
//See ./res/fonts/ for font licenses //See ./res/fonts/ for font licenses
//*********************************************************************************************** //***********************************************************************************************



#include "ImpromptuModular.hpp" #include "ImpromptuModular.hpp"


RACK_PLUGIN_MODEL_DECLARE(ImpromptuModular, Tact); RACK_PLUGIN_MODEL_DECLARE(ImpromptuModular, Tact);
@@ -27,8 +28,8 @@ RACK_PLUGIN_INIT(ImpromptuModular) {


RACK_PLUGIN_INIT_WEBSITE("https://github.com/MarcBoule/ImpromptuModular"); RACK_PLUGIN_INIT_WEBSITE("https://github.com/MarcBoule/ImpromptuModular");
RACK_PLUGIN_INIT_MANUAL("https://github.com/MarcBoule/ImpromptuModular"); RACK_PLUGIN_INIT_MANUAL("https://github.com/MarcBoule/ImpromptuModular");
RACK_PLUGIN_INIT_VERSION("0.6.11");


//p->addModel(modelEngTest1);
RACK_PLUGIN_MODEL_ADD(ImpromptuModular, Tact); RACK_PLUGIN_MODEL_ADD(ImpromptuModular, Tact);
RACK_PLUGIN_MODEL_ADD(ImpromptuModular, TwelveKey); RACK_PLUGIN_MODEL_ADD(ImpromptuModular, TwelveKey);
RACK_PLUGIN_MODEL_ADD(ImpromptuModular, Clocked); RACK_PLUGIN_MODEL_ADD(ImpromptuModular, Clocked);
@@ -142,126 +143,12 @@ NVGcolor prepareDisplay(NVGcontext *vg, Rect *box) {
return textColor; return textColor;
} }



int moveIndex(int index, int indexNext, int numSteps) {
if (indexNext < 0)
index = numSteps - 1;
else
{
if (indexNext - index >= 0) { // if moving right or same place
if (indexNext >= numSteps)
index = 0;
else
index = indexNext;
}
else { // moving left
if (indexNext >= numSteps)
index = numSteps - 1;
else
index = indexNext;
}
}
return index;
}


bool moveIndexRunMode(int* index, int numSteps, int runMode, int* history) {
bool crossBoundary = false;
int numRuns;// for FWx
switch (runMode) {
case MODE_REV :// reverse; history base is 1000 (not needed)
(*history) = 1000;
(*index)--;
if ((*index) < 0) {
(*index) = numSteps - 1;
crossBoundary = true;
}
break;
case MODE_PPG :// forward-reverse; history base is 2000
if ((*history) != 2000 && (*history) != 2001) // 2000 means going forward, 2001 means going reverse
(*history) = 2000;
if ((*history) == 2000) {// forward phase
(*index)++;
if ((*index) >= numSteps) {
(*index) = numSteps - 1;
(*history) = 2001;
}
}
else {// it is 2001; reverse phase
(*index)--;
if ((*index) < 0) {
(*index) = 0;
(*history) = 2000;
crossBoundary = true;
}
}
break;
case MODE_BRN :// brownian random; history base is 3000
if ( (*history) < 3000 || ((*history) > (3000 + numSteps)) )
(*history) = 3000 + numSteps;
(*index) += (randomu32() % 3) - 1;
if ((*index) >= numSteps) {
(*index) = 0;
}
if ((*index) < 0) {
(*index) = numSteps - 1;
}
(*history)--;
if ((*history) <= 3000) {
(*history) = 3000 + numSteps;
crossBoundary = true;
}
break;
case MODE_RND :// random; history base is 4000
if ( (*history) < 4000 || ((*history) > (4000 + numSteps)) )
(*history) = 4000 + numSteps;
(*index) = (randomu32() % numSteps) ;
(*history)--;
if ((*history) <= 4000) {
(*history) = 4000 + numSteps;
crossBoundary = true;
}
break;
case MODE_FW2 :// forward twice
case MODE_FW3 :// forward three times
case MODE_FW4 :// forward four times
numRuns = 5002 + runMode - MODE_FW2;
if ( (*history) < 5000 || (*history) >= numRuns ) // 5000 means first pass, 5001 means 2nd pass, etc...
(*history) = 5000;
(*index)++;
if ((*index) >= numSteps) {
(*index) = 0;
(*history)++;
if ((*history) >= numRuns) {
(*history) = 5000;
crossBoundary = true;
}
}
break;

default :// MODE_FWD forward; history base is 0 (not needed)
(*history) = 0;
(*index)++;
if ((*index) >= numSteps) {
(*index) = 0;
crossBoundary = true;
}
}

return crossBoundary;
}

bool calcWarningFlash(long count, long countInit) { bool calcWarningFlash(long count, long countInit) {
bool warningFlashState = true;
if (count > (countInit * 2l / 4l) && count < (countInit * 3l / 4l))
warningFlashState = false;
else if (count < (countInit * 1l / 4l))
warningFlashState = false;
return warningFlashState;
}
bool warningFlashState = true;
if (count > (countInit * 2l / 4l) && count < (countInit * 3l / 4l))
warningFlashState = false;
else if (count < (countInit * 1l / 4l))
warningFlashState = false;
return warningFlashState;
}


+ 35
- 10
plugins/community/repos/ImpromptuModular/src/ImpromptuModular.hpp View File

@@ -13,6 +13,7 @@


#include "rack.hpp" #include "rack.hpp"
#include "IMWidgets.hpp" #include "IMWidgets.hpp"
#include "dsp/digital.hpp"


using namespace rack; using namespace rack;


@@ -23,12 +24,8 @@ static const float lightLambda = 0.075f;
static const std::string lightPanelID = "Classic"; static const std::string lightPanelID = "Classic";
static const std::string darkPanelID = "Dark-valor"; static const std::string darkPanelID = "Dark-valor";
static const std::string expansionMenuLabel = "Extra CVs (requires +4HP to the right!)"; static const std::string expansionMenuLabel = "Extra CVs (requires +4HP to the right!)";
enum RunModeIds {MODE_FWD, MODE_REV, MODE_PPG, MODE_BRN, MODE_RND, MODE_FW2, MODE_FW3, MODE_FW4, NUM_MODES};
static const std::string modeLabels[NUM_MODES]={"FWD","REV","PPG","BRN","RND","FW2","FW3","FW4"};
enum GateModeIds {GATE_24, GATE_34, GATE_44, GATE_14, GATE_TRIG, GATE_DUO, GATE_DU1, GATE_DU2,
GATE_TRIPLET, GATE_TRIP1, GATE_TRIP2, GATE_TRIP3, GATE_TRIP4, GATE_TRIP5, GATE_TRIP6, NUM_GATES};
static const std::string gateLabels[NUM_GATES]={"2/4","3/4","4/4","1/4","TRG","DUO","DU1","DU2",
"TRP","TR1","TR2","TR3","TR4","TR5","TR6"};
static const int displayRefreshStepSkips = 200;



// Constants for displaying notes // Constants for displaying notes


@@ -220,7 +217,7 @@ struct GiantLight2 : BASE {
} }
}; };


// Other
// Other widgets


struct InvisibleKey : MomentarySwitch { struct InvisibleKey : MomentarySwitch {
InvisibleKey() { InvisibleKey() {
@@ -230,7 +227,7 @@ struct InvisibleKey : MomentarySwitch {


struct InvisibleKeySmall : MomentarySwitch { struct InvisibleKeySmall : MomentarySwitch {
InvisibleKeySmall() { InvisibleKeySmall() {
box.size = Vec(23, 50);
box.size = Vec(23, 38);
} }
void onMouseDown(EventMouseDown &e) override; void onMouseDown(EventMouseDown &e) override;
void onMouseUp(EventMouseUp &e) override; void onMouseUp(EventMouseUp &e) override;
@@ -249,9 +246,37 @@ struct ScrewHole : TransparentWidget {
}; };





// Other

struct HoldDetect {
long modeHoldDetect;// 0 when not detecting, downward counter when detecting
void reset() {
modeHoldDetect = 0l;
}
void start(long startValue) {
modeHoldDetect = startValue;
}

bool process(float paramValue) {
bool ret = false;
if (modeHoldDetect > 0l) {
if (paramValue < 0.5f)
modeHoldDetect = 0l;
else {
if (modeHoldDetect == 1l) {
ret = true;
}
modeHoldDetect--;
}
}
return ret;
}
};

NVGcolor prepareDisplay(NVGcontext *vg, Rect *box); NVGcolor prepareDisplay(NVGcontext *vg, Rect *box);
int moveIndex(int index, int indexNext, int numSteps);
bool moveIndexRunMode(int* index, int numSteps, int runMode, int* history);
bool calcWarningFlash(long count, long countInit); bool calcWarningFlash(long count, long countInit);


#endif #endif

+ 0
- 1
plugins/community/repos/ImpromptuModular/src/MidiFileModule.cpp View File

@@ -26,7 +26,6 @@ https://github.com/IohannRabeson/VCVRack-Simple/commit/2d33e97d2e344d2926548a0b9




#include "ImpromptuModular.hpp" #include "ImpromptuModular.hpp"
#include "dsp/digital.hpp"
#include "midifile/MidiFile.h" #include "midifile/MidiFile.h"
#include "osdialog.h" #include "osdialog.h"
#include <iostream> #include <iostream>


+ 379
- 361
plugins/community/repos/ImpromptuModular/src/PhraseSeq16.cpp
File diff suppressed because it is too large
View File


+ 410
- 437
plugins/community/repos/ImpromptuModular/src/PhraseSeq32.cpp
File diff suppressed because it is too large
View File


+ 133
- 0
plugins/community/repos/ImpromptuModular/src/PhraseSeqUtil.cpp View File

@@ -0,0 +1,133 @@
//***********************************************************************************************
//Impromptu Modular: Modules for VCV Rack by Marc Boulé
//***********************************************************************************************


#include "PhraseSeqUtil.hpp"

namespace rack_plugin_ImpromptuModular {

int moveIndex(int index, int indexNext, int numSteps) {
if (indexNext < 0)
index = numSteps - 1;
else
{
if (indexNext - index >= 0) { // if moving right or same place
if (indexNext >= numSteps)
index = 0;
else
index = indexNext;
}
else { // moving left
if (indexNext >= numSteps)
index = numSteps - 1;
else
index = indexNext;
}
}
return index;
}


bool moveIndexRunMode(int* index, int numSteps, int runMode, int* history) {
bool crossBoundary = false;
int numRuns;// for FWx
switch (runMode) {
case MODE_REV :// reverse; history base is 1000 (not needed)
(*history) = 1000;
(*index)--;
if ((*index) < 0) {
(*index) = numSteps - 1;
crossBoundary = true;
}
break;
case MODE_PPG :// forward-reverse; history base is 2000
if ((*history) != 2000 && (*history) != 2001) // 2000 means going forward, 2001 means going reverse
(*history) = 2000;
if ((*history) == 2000) {// forward phase
(*index)++;
if ((*index) >= numSteps) {
(*index) = numSteps - 1;
(*history) = 2001;
}
}
else {// it is 2001; reverse phase
(*index)--;
if ((*index) < 0) {
(*index) = 0;
(*history) = 2000;
crossBoundary = true;
}
}
break;
case MODE_BRN :// brownian random; history base is 3000
if ( (*history) < 3000 || ((*history) > (3000 + numSteps)) )
(*history) = 3000 + numSteps;
(*index) += (randomu32() % 3) - 1;
if ((*index) >= numSteps) {
(*index) = 0;
}
if ((*index) < 0) {
(*index) = numSteps - 1;
}
(*history)--;
if ((*history) <= 3000) {
(*history) = 3000 + numSteps;
crossBoundary = true;
}
break;
case MODE_RND :// random; history base is 4000
if ( (*history) < 4000 || ((*history) > (4000 + numSteps)) )
(*history) = 4000 + numSteps;
(*index) = (randomu32() % numSteps) ;
(*history)--;
if ((*history) <= 4000) {
(*history) = 4000 + numSteps;
crossBoundary = true;
}
break;
case MODE_FW2 :// forward twice
case MODE_FW3 :// forward three times
case MODE_FW4 :// forward four times
numRuns = 5002 + runMode - MODE_FW2;
if ( (*history) < 5000 || (*history) >= numRuns ) // 5000 means first pass, 5001 means 2nd pass, etc...
(*history) = 5000;
(*index)++;
if ((*index) >= numSteps) {
(*index) = 0;
(*history)++;
if ((*history) >= numRuns) {
(*history) = 5000;
crossBoundary = true;
}
}
break;

default :// MODE_FWD forward; history base is 0 (not needed)
(*history) = 0;
(*index)++;
if ((*index) >= numSteps) {
(*index) = 0;
crossBoundary = true;
}
}

return crossBoundary;
}


int keyIndexToGateMode(int keyIndex, int pulsesPerStep) {
if (pulsesPerStep == 4 && (keyIndex == 1 || keyIndex == 3 || keyIndex == 6 || keyIndex == 8 || keyIndex == 10))
return -1;
if (pulsesPerStep == 6 && (keyIndex == 0 || keyIndex == 4 || keyIndex == 7 || keyIndex == 9))
return -1;
return keyIndex;// keyLight index now matches gate modes, so no mapping table needed anymore
}

} // namespace rack_plugin_ImpromptuModular

+ 113
- 0
plugins/community/repos/ImpromptuModular/src/PhraseSeqUtil.hpp View File

@@ -0,0 +1,113 @@
//***********************************************************************************************
//Impromptu Modular: Modules for VCV Rack by Marc Boulé
//***********************************************************************************************

#include "dsp/digital.hpp"

using namespace rack;

namespace rack_plugin_ImpromptuModular {

// General constants

enum RunModeIds {MODE_FWD, MODE_REV, MODE_PPG, MODE_BRN, MODE_RND, MODE_FW2, MODE_FW3, MODE_FW4, NUM_MODES};
static const std::string modeLabels[NUM_MODES]={"FWD","REV","PPG","BRN","RND","FW2","FW3","FW4"};

static const int NUM_GATES = 12;
static const uint32_t advGateHitMask[NUM_GATES] =
{0x00003F, 0x0F0F0F, 0x000FFF, 0x0F0F00, 0x03FFFF, 0xFFFFFF, 0x00000F, 0x03F03F, 0x000F00, 0x03F000, 0x0F0000, 0};
// 25% TRI 50% T23 75% FUL TR1 DUO TR2 D2 TR3 TRIG

enum AttributeBitMasks {ATT_MSK_GATE1 = 0x01, ATT_MSK_GATE1P = 0x02, ATT_MSK_GATE2 = 0x04, ATT_MSK_SLIDE = 0x08, ATT_MSK_TIED = 0x10};// 5 bits
static const int ATT_MSK_GATE1MODE = 0x01E0;// 4 bits
static const int gate1ModeShift = 5;
static const int ATT_MSK_GATE2MODE = 0x1E00;// 4 bits
static const int gate2ModeShift = 9;

// Inline methods
inline bool getGate1a(int attribute) {return (attribute & ATT_MSK_GATE1) != 0;}
inline bool getGate1Pa(int attribute) {return (attribute & ATT_MSK_GATE1P) != 0;}
inline bool getGate2a(int attribute) {return (attribute & ATT_MSK_GATE2) != 0;}
inline bool getSlideA(int attribute) {return (attribute & ATT_MSK_SLIDE) != 0;}
inline bool getTiedA(int attribute) {return (attribute & ATT_MSK_TIED) != 0;}
inline int getGate1aMode(int attribute) {return (attribute & ATT_MSK_GATE1MODE) >> gate1ModeShift;}
inline int getGate2aMode(int attribute) {return (attribute & ATT_MSK_GATE2MODE) >> gate2ModeShift;}

inline void setGate1a(int *attribute, bool gate1State) {(*attribute) &= ~ATT_MSK_GATE1; if (gate1State) (*attribute) |= ATT_MSK_GATE1;}
inline void setGate1Pa(int *attribute, bool gate1PState) {(*attribute) &= ~ATT_MSK_GATE1P; if (gate1PState) (*attribute) |= ATT_MSK_GATE1P;}
inline void setGate2a(int *attribute, bool gate2State) {(*attribute) &= ~ATT_MSK_GATE2; if (gate2State) (*attribute) |= ATT_MSK_GATE2;}
inline void setSlideA(int *attribute, bool slideState) {(*attribute) &= ~ATT_MSK_SLIDE; if (slideState) (*attribute) |= ATT_MSK_SLIDE;}
inline void setTiedA(int *attribute, bool tiedState) {(*attribute) &= ~ATT_MSK_TIED; if (tiedState) (*attribute) |= ATT_MSK_TIED;}

inline void toggleGate1a(int *attribute) {(*attribute) ^= ATT_MSK_GATE1;}
inline void toggleGate1Pa(int *attribute) {(*attribute) ^= ATT_MSK_GATE1P;}
inline void toggleGate2a(int *attribute) {(*attribute) ^= ATT_MSK_GATE2;}
inline void toggleSlideA(int *attribute) {(*attribute) ^= ATT_MSK_SLIDE;}
inline void toggleTiedA(int *attribute) {(*attribute) ^= ATT_MSK_TIED;}


inline int ppsToIndex(int pulsesPerStep) {// map 1,4,6,12,24, to 0,1,2,3,4
if (pulsesPerStep == 1) return 0;
if (pulsesPerStep == 4) return 1;
if (pulsesPerStep == 6) return 2;
if (pulsesPerStep == 12) return 3;
return 4;
}
inline int indexToPps(int index) {// inverse map of ppsToIndex()
index = clamp(index, 0, 4);
if (index == 0) return 1;
if (index == 1) return 4;
if (index == 2) return 6;
if (index == 3) return 12;
return 24;
}

inline bool calcGate(int gateCode, SchmittTrigger clockTrigger, unsigned long clockStep, float sampleRate) {
if (gateCode < 2)
return gateCode == 1;
if (gateCode == 2)
return clockTrigger.isHigh();
return clockStep < (unsigned long) (sampleRate * 0.001f);
}

inline int getAdvGate(int ppqnCount, int pulsesPerStep, int gateMode) {
if (gateMode == 11)
return ppqnCount == 0 ? 3 : 0;
uint32_t shiftAmt = ppqnCount * (24 / pulsesPerStep);
return (int)((advGateHitMask[gateMode] >> shiftAmt) & (uint32_t)0x1);
}

inline int calcGate1Code(int attribute, int ppqnCount, int pulsesPerStep, float randKnob) {
// -1 = gate off for whole step, 0 = gate off for current ppqn, 1 = gate on, 2 = clock high, 3 = trigger
if (ppqnCount == 0 && getGate1Pa(attribute) && !(randomUniform() < randKnob))// randomUniform is [0.0, 1.0), see include/util/common.hpp
return -1;// must do this first in this method since it will kill rest of step if prob turns off the step
if (!getGate1a(attribute))
return 0;
if (pulsesPerStep == 1)
return 2;// clock high
return getAdvGate(ppqnCount, pulsesPerStep, getGate1aMode(attribute));
}
inline int calcGate2Code(int attribute, int ppqnCount, int pulsesPerStep) {
// 0 = gate off, 1 = clock high, 2 = trigger, 3 = gate on
if (!getGate2a(attribute))
return 0;
if (pulsesPerStep == 1)
return 2;// clock high
return getAdvGate(ppqnCount, pulsesPerStep, getGate2aMode(attribute));
}

inline int gateModeToKeyLightIndex(int attribute, bool isGate1) {// keyLight index now matches gate modes, so no mapping table needed anymore
return isGate1 ? getGate1aMode(attribute) : getGate2aMode(attribute);
}



// Other methods (code in PhraseSeqUtil.cpp)
int moveIndex(int index, int indexNext, int numSteps);
bool moveIndexRunMode(int* index, int numSteps, int runMode, int* history);
int keyIndexToGateMode(int keyIndex, int pulsesPerStep);

} // namespace rack_plugin_ImpromptuModular

+ 173
- 175
plugins/community/repos/ImpromptuModular/src/SemiModularSynth.cpp View File

@@ -15,7 +15,7 @@


#include "ImpromptuModular.hpp" #include "ImpromptuModular.hpp"
#include "FundamentalUtil.hpp" #include "FundamentalUtil.hpp"
#include "PhraseSeqUtil.hpp"
namespace rack_plugin_ImpromptuModular { namespace rack_plugin_ImpromptuModular {


struct SemiModularSynth : Module { struct SemiModularSynth : Module {
@@ -223,6 +223,7 @@ struct SemiModularSynth : Module {
long tiedWarning;// 0 when no warning, positive downward step counter timer when warning long tiedWarning;// 0 when no warning, positive downward step counter timer when warning
int sequenceKnob = 0; int sequenceKnob = 0;
bool gate1RandomEnable; bool gate1RandomEnable;
int lightRefreshCounter;
static constexpr float EDIT_PARAM_INIT_VALUE = 1.0f;// so that module constructor is coherent with widget initialization, since module created before widget static constexpr float EDIT_PARAM_INIT_VALUE = 1.0f;// so that module constructor is coherent with widget initialization, since module created before widget
bool editingSequence; bool editingSequence;
@@ -320,6 +321,7 @@ struct SemiModularSynth : Module {
editingSequence = EDIT_PARAM_INIT_VALUE > 0.5f; editingSequence = EDIT_PARAM_INIT_VALUE > 0.5f;
editingSequenceLast = editingSequence; editingSequenceLast = editingSequence;
resetOnRun = false; resetOnRun = false;
lightRefreshCounter = 0;
// VCO // VCO
// none // none
@@ -577,14 +579,14 @@ struct SemiModularSynth : Module {


// Advances the module by 1 audio frame with duration 1.0 / engineGetSampleRate() // Advances the module by 1 audio frame with duration 1.0 / engineGetSampleRate()
void step() override { void step() override {

float sampleRate = engineGetSampleRate();
// SEQUENCER // SEQUENCER


static const float gateTime = 0.4f;// seconds static const float gateTime = 0.4f;// seconds
static const float copyPasteInfoTime = 0.5f;// seconds static const float copyPasteInfoTime = 0.5f;// seconds
static const float editLengthTime = 2.0f;// seconds static const float editLengthTime = 2.0f;// seconds
static const float tiedWarningTime = 0.7f;// seconds static const float tiedWarningTime = 0.7f;// seconds
long tiedWarningInit = (long) (tiedWarningTime * engineGetSampleRate());
//********** Buttons, knobs, switches and inputs ********** //********** Buttons, knobs, switches and inputs **********
@@ -635,7 +637,7 @@ struct SemiModularSynth : Module {
// Copy button // Copy button
if (copyTrigger.process(params[COPY_PARAM].value)) { if (copyTrigger.process(params[COPY_PARAM].value)) {
if (editingSequence) { if (editingSequence) {
infoCopyPaste = (long) (copyPasteInfoTime * engineGetSampleRate());
infoCopyPaste = (long) (copyPasteInfoTime * sampleRate / displayRefreshStepSkips);
//CPinfo must be set to 0 for copy/paste all, and 0x1ii for copy/paste 4 at pos ii, 0x2ii for copy/paste 8 at 0xii //CPinfo must be set to 0 for copy/paste all, and 0x1ii for copy/paste 4 at pos ii, 0x2ii for copy/paste 8 at 0xii
int sStart = stepIndexEdit; int sStart = stepIndexEdit;
int sCount = 16; int sCount = 16;
@@ -661,7 +663,7 @@ struct SemiModularSynth : Module {
// Paste button // Paste button
if (pasteTrigger.process(params[PASTE_PARAM].value)) { if (pasteTrigger.process(params[PASTE_PARAM].value)) {
if (editingSequence) { if (editingSequence) {
infoCopyPaste = (long) (-1 * copyPasteInfoTime * engineGetSampleRate());
infoCopyPaste = (long) (-1 * copyPasteInfoTime * sampleRate / displayRefreshStepSkips);
int sStart = ((countCP == 16) ? 0 : stepIndexEdit); int sStart = ((countCP == 16) ? 0 : stepIndexEdit);
int sCount = countCP; int sCount = countCP;
for (int i = 0, s = sStart; i < countCP; i++, s++) { for (int i = 0, s = sStart; i < countCP; i++, s++) {
@@ -685,7 +687,7 @@ struct SemiModularSynth : Module {
if (editingLength > 0ul) if (editingLength > 0ul)
editingLength = 0ul;// allow user to quickly leave editing mode when re-press editingLength = 0ul;// allow user to quickly leave editing mode when re-press
else else
editingLength = (unsigned long) (editLengthTime * engineGetSampleRate());
editingLength = (unsigned long) (editLengthTime * sampleRate / displayRefreshStepSkips);
displayState = DISP_NORMAL; displayState = DISP_NORMAL;
} }
@@ -695,11 +697,11 @@ struct SemiModularSynth : Module {
if (writeTrig) { if (writeTrig) {
if (editingSequence) { if (editingSequence) {
if (getTied(sequence,stepIndexEdit)) if (getTied(sequence,stepIndexEdit))
tiedWarning = tiedWarningInit;
tiedWarning = (long) (tiedWarningTime * sampleRate / displayRefreshStepSkips);
else { else {
cv[sequence][stepIndexEdit] = inputs[CV_INPUT].value; cv[sequence][stepIndexEdit] = inputs[CV_INPUT].value;
applyTiedStep(sequence, stepIndexEdit, lengths[sequence]); applyTiedStep(sequence, stepIndexEdit, lengths[sequence]);
editingGate = (unsigned long) (gateTime * engineGetSampleRate());
editingGate = (unsigned long) (gateTime * sampleRate / displayRefreshStepSkips);
editingGateCV = cv[sequence][stepIndexEdit]; editingGateCV = cv[sequence][stepIndexEdit];
editingGateKeyLight = -1; editingGateKeyLight = -1;
// Autostep (after grab all active inputs) // Autostep (after grab all active inputs)
@@ -721,7 +723,7 @@ struct SemiModularSynth : Module {
} }
if (delta != 0) { if (delta != 0) {
if (editingLength > 0ul) { if (editingLength > 0ul) {
editingLength = (unsigned long) (editLengthTime * engineGetSampleRate());// restart editing length timer
editingLength = (unsigned long) (editLengthTime * sampleRate / displayRefreshStepSkips);// restart editing length timer
if (editingSequence) { if (editingSequence) {
lengths[sequence] += delta; lengths[sequence] += delta;
if (lengths[sequence] > 16) lengths[sequence] = 16; if (lengths[sequence] > 16) lengths[sequence] = 16;
@@ -742,7 +744,7 @@ struct SemiModularSynth : Module {
stepIndexEdit = moveIndex(stepIndexEdit, stepIndexEdit + delta, 16);//lengths[sequence]);// Commented for full edit capabilities stepIndexEdit = moveIndex(stepIndexEdit, stepIndexEdit + delta, 16);//lengths[sequence]);// Commented for full edit capabilities
if (!getTied(sequence,stepIndexEdit)) {// play if non-tied step if (!getTied(sequence,stepIndexEdit)) {// play if non-tied step
if (!writeTrig) {// in case autostep when simultaneous writeCV and stepCV (keep what was done in Write Input block above) if (!writeTrig) {// in case autostep when simultaneous writeCV and stepCV (keep what was done in Write Input block above)
editingGate = (unsigned long) (gateTime * engineGetSampleRate());
editingGate = (unsigned long) (gateTime * sampleRate / displayRefreshStepSkips);
editingGateCV = cv[sequence][stepIndexEdit]; editingGateCV = cv[sequence][stepIndexEdit];
editingGateKeyLight = -1; editingGateKeyLight = -1;
} }
@@ -785,7 +787,7 @@ struct SemiModularSynth : Module {
if (deltaKnob != 0) { if (deltaKnob != 0) {
if (abs(deltaKnob) <= 3) {// avoid discontinuous step (initialize for example) if (abs(deltaKnob) <= 3) {// avoid discontinuous step (initialize for example)
if (editingLength > 0ul) { if (editingLength > 0ul) {
editingLength = (unsigned long) (editLengthTime * engineGetSampleRate());// restart editing length timer
editingLength = (unsigned long) (editLengthTime * sampleRate / displayRefreshStepSkips);// restart editing length timer
if (editingSequence) { if (editingSequence) {
lengths[sequence] += deltaKnob; lengths[sequence] += deltaKnob;
if (lengths[sequence] > 16) lengths[sequence] = 16 ; if (lengths[sequence] > 16) lengths[sequence] = 16 ;
@@ -842,8 +844,6 @@ struct SemiModularSynth : Module {
sequence += deltaKnob; sequence += deltaKnob;
if (sequence < 0) sequence = 0; if (sequence < 0) sequence = 0;
if (sequence >= 16) sequence = (16 - 1); if (sequence >= 16) sequence = (16 - 1);
//if (stepIndexEdit >= lengths[sequence])// Commented for full edit capabilities
//stepIndexEdit = lengths[sequence] - 1;// Commented for full edit capabilities
} }
} }
else { else {
@@ -867,7 +867,7 @@ struct SemiModularSynth : Module {
if (newOct >= 0 && newOct <= 6) { if (newOct >= 0 && newOct <= 6) {
if (editingSequence) { if (editingSequence) {
if (getTied(sequence,stepIndexEdit)) if (getTied(sequence,stepIndexEdit))
tiedWarning = tiedWarningInit;
tiedWarning = (long) (tiedWarningTime * sampleRate / displayRefreshStepSkips);
else { else {
float newCV = cv[sequence][stepIndexEdit] + 10.0f;//to properly handle negative note voltages float newCV = cv[sequence][stepIndexEdit] + 10.0f;//to properly handle negative note voltages
newCV = newCV - floor(newCV) + (float) (newOct - 3); newCV = newCV - floor(newCV) + (float) (newOct - 3);
@@ -875,7 +875,7 @@ struct SemiModularSynth : Module {
cv[sequence][stepIndexEdit] = newCV; cv[sequence][stepIndexEdit] = newCV;
applyTiedStep(sequence, stepIndexEdit, lengths[sequence]); applyTiedStep(sequence, stepIndexEdit, lengths[sequence]);
} }
editingGate = (unsigned long) (gateTime * engineGetSampleRate());
editingGate = (unsigned long) (gateTime * sampleRate / displayRefreshStepSkips);
editingGateCV = cv[sequence][stepIndexEdit]; editingGateCV = cv[sequence][stepIndexEdit];
editingGateKeyLight = -1; editingGateKeyLight = -1;
} }
@@ -890,12 +890,12 @@ struct SemiModularSynth : Module {
if (params[KEY_PARAMS + i].value > 1.5f) if (params[KEY_PARAMS + i].value > 1.5f)
stepIndexEdit = moveIndex(stepIndexEdit, stepIndexEdit + 1, 16); stepIndexEdit = moveIndex(stepIndexEdit, stepIndexEdit + 1, 16);
else else
tiedWarning = tiedWarningInit;
tiedWarning = (long) (tiedWarningTime * sampleRate / displayRefreshStepSkips);
} }
else { else {
cv[sequence][stepIndexEdit] = floor(cv[sequence][stepIndexEdit]) + ((float) i) / 12.0f; cv[sequence][stepIndexEdit] = floor(cv[sequence][stepIndexEdit]) + ((float) i) / 12.0f;
applyTiedStep(sequence, stepIndexEdit, lengths[sequence]); applyTiedStep(sequence, stepIndexEdit, lengths[sequence]);
editingGate = (unsigned long) (gateTime * engineGetSampleRate());
editingGate = (unsigned long) (gateTime * sampleRate / displayRefreshStepSkips);
editingGateCV = cv[sequence][stepIndexEdit]; editingGateCV = cv[sequence][stepIndexEdit];
editingGateKeyLight = -1; editingGateKeyLight = -1;
if (params[KEY_PARAMS + i].value > 1.5f) { if (params[KEY_PARAMS + i].value > 1.5f) {
@@ -911,17 +911,14 @@ struct SemiModularSynth : Module {
// Gate1, Gate1Prob, Gate2, Slide and Tied buttons // Gate1, Gate1Prob, Gate2, Slide and Tied buttons
if (gate1Trigger.process(params[GATE1_PARAM].value)) { if (gate1Trigger.process(params[GATE1_PARAM].value)) {
if (editingSequence) { if (editingSequence) {
if (getTied(sequence,stepIndexEdit))
tiedWarning = tiedWarningInit;
else
attributes[sequence][stepIndexEdit] ^= ATT_MSK_GATE1;
attributes[sequence][stepIndexEdit] ^= ATT_MSK_GATE1;
} }
displayState = DISP_NORMAL; displayState = DISP_NORMAL;
} }
if (gate1ProbTrigger.process(params[GATE1_PROB_PARAM].value)) { if (gate1ProbTrigger.process(params[GATE1_PROB_PARAM].value)) {
if (editingSequence) { if (editingSequence) {
if (getTied(sequence,stepIndexEdit)) if (getTied(sequence,stepIndexEdit))
tiedWarning = tiedWarningInit;
tiedWarning = (long) (tiedWarningTime * sampleRate / displayRefreshStepSkips);
else else
attributes[sequence][stepIndexEdit] ^= ATT_MSK_GATE1P; attributes[sequence][stepIndexEdit] ^= ATT_MSK_GATE1P;
} }
@@ -929,17 +926,14 @@ struct SemiModularSynth : Module {
} }
if (gate2Trigger.process(params[GATE2_PARAM].value)) { if (gate2Trigger.process(params[GATE2_PARAM].value)) {
if (editingSequence) { if (editingSequence) {
if (getTied(sequence,stepIndexEdit))
tiedWarning = tiedWarningInit;
else
attributes[sequence][stepIndexEdit] ^= ATT_MSK_GATE2;
attributes[sequence][stepIndexEdit] ^= ATT_MSK_GATE2;
} }
displayState = DISP_NORMAL; displayState = DISP_NORMAL;
} }
if (slideTrigger.process(params[SLIDE_BTN_PARAM].value)) { if (slideTrigger.process(params[SLIDE_BTN_PARAM].value)) {
if (editingSequence) { if (editingSequence) {
if (getTied(sequence,stepIndexEdit)) if (getTied(sequence,stepIndexEdit))
tiedWarning = tiedWarningInit;
tiedWarning = (long) (tiedWarningTime * sampleRate / displayRefreshStepSkips);
else else
attributes[sequence][stepIndexEdit] ^= ATT_MSK_SLIDE; attributes[sequence][stepIndexEdit] ^= ATT_MSK_SLIDE;
} }
@@ -1005,8 +999,6 @@ struct SemiModularSynth : Module {
displayState = DISP_NORMAL; displayState = DISP_NORMAL;
clockTrigger.reset(); clockTrigger.reset();
} }
else
resetLight -= (resetLight / lightLambda) * engineGetSampleTime();
//********** Outputs and lights ********** //********** Outputs and lights **********
@@ -1027,135 +1019,142 @@ struct SemiModularSynth : Module {
outputs[GATE1_OUTPUT].value = (editingGate > 0ul) ? 10.0f : 0.0f; outputs[GATE1_OUTPUT].value = (editingGate > 0ul) ? 10.0f : 0.0f;
outputs[GATE2_OUTPUT].value = (editingGate > 0ul) ? 10.0f : 0.0f; outputs[GATE2_OUTPUT].value = (editingGate > 0ul) ? 10.0f : 0.0f;
} }
if (slideStepsRemain > 0ul)
slideStepsRemain--;
// Step/phrase lights
if (infoCopyPaste != 0l) {
for (int i = 0; i < 16; i++) {
if ( (i >= stepIndexEdit && i < (stepIndexEdit + countCP)) || (countCP == 16) )
lights[STEP_PHRASE_LIGHTS + (i<<1)].value = 0.5f;// Green when copy interval
else
lights[STEP_PHRASE_LIGHTS + (i<<1)].value = 0.0f; // Green (nothing)
lights[STEP_PHRASE_LIGHTS + (i<<1) + 1].value = 0.0f;// Red (nothing)
}
}
else {
for (int i = 0; i < 16; i++) {
if (editingLength > 0ul) {
// Length (green)
if (editingSequence)
lights[STEP_PHRASE_LIGHTS + (i<<1)].value = ((i < lengths[sequence]) ? 0.5f : 0.0f);
else
lights[STEP_PHRASE_LIGHTS + (i<<1)].value = ((i < phrases) ? 0.5f : 0.0f);
// Nothing (red)
lights[STEP_PHRASE_LIGHTS + (i<<1) + 1].value = 0.0f;
}
else {
// Run cursor (green)
if (editingSequence)
lights[STEP_PHRASE_LIGHTS + (i<<1)].value = ((running && (i == stepIndexRun)) ? 1.0f : 0.0f);
else {
float green = ((running && (i == phraseIndexRun)) ? 1.0f : 0.0f);
green += ((running && (i == stepIndexRun) && i != phraseIndexEdit) ? 0.1f : 0.0f);
lights[STEP_PHRASE_LIGHTS + (i<<1)].value = clamp(green, 0.0f, 1.0f);
}
// Edit cursor (red)
if (editingSequence)
lights[STEP_PHRASE_LIGHTS + (i<<1) + 1].value = (i == stepIndexEdit ? 1.0f : 0.0f);
lightRefreshCounter++;
if (lightRefreshCounter > displayRefreshStepSkips) {
lightRefreshCounter = 0;

// Step/phrase lights
if (infoCopyPaste != 0l) {
for (int i = 0; i < 16; i++) {
if ( (i >= stepIndexEdit && i < (stepIndexEdit + countCP)) || (countCP == 16) )
lights[STEP_PHRASE_LIGHTS + (i<<1)].value = 0.5f;// Green when copy interval
else else
lights[STEP_PHRASE_LIGHTS + (i<<1) + 1].value = (i == phraseIndexEdit ? 1.0f : 0.0f);
lights[STEP_PHRASE_LIGHTS + (i<<1)].value = 0.0f; // Green (nothing)
lights[STEP_PHRASE_LIGHTS + (i<<1) + 1].value = 0.0f;// Red (nothing)
} }
} }
}
// Octave lights
float octCV = 0.0f;
if (editingSequence)
octCV = cv[sequence][stepIndexEdit];
else
octCV = cv[phrase[phraseIndexEdit]][stepIndexRun];
int octLightIndex = (int) floor(octCV + 3.0f);
for (int i = 0; i < 7; i++) {
if (!editingSequence && (!attached || !running))// no oct lights when song mode and either (detached [1] or stopped [2])
// [1] makes no sense, can't mod steps and stepping though seq that may not be playing
// [2] CV is set to 0V when not running and in song mode, so cv[][] makes no sense to display
lights[OCTAVE_LIGHTS + i].value = 0.0f;
else { else {
if (tiedWarning > 0l) {
bool warningFlashState = calcWarningFlash(tiedWarning, tiedWarningInit);
lights[OCTAVE_LIGHTS + i].value = (warningFlashState && (i == (6 - octLightIndex))) ? 1.0f : 0.0f;
for (int i = 0; i < 16; i++) {
if (editingLength > 0ul) {
// Length (green)
if (editingSequence)
lights[STEP_PHRASE_LIGHTS + (i<<1)].value = ((i < lengths[sequence]) ? 0.5f : 0.0f);
else
lights[STEP_PHRASE_LIGHTS + (i<<1)].value = ((i < phrases) ? 0.5f : 0.0f);
// Nothing (red)
lights[STEP_PHRASE_LIGHTS + (i<<1) + 1].value = 0.0f;
}
else {
// Run cursor (green)
if (editingSequence)
lights[STEP_PHRASE_LIGHTS + (i<<1)].value = ((running && (i == stepIndexRun)) ? 1.0f : 0.0f);
else {
float green = ((running && (i == phraseIndexRun)) ? 1.0f : 0.0f);
green += ((running && (i == stepIndexRun) && i != phraseIndexEdit) ? 0.1f : 0.0f);
lights[STEP_PHRASE_LIGHTS + (i<<1)].value = clamp(green, 0.0f, 1.0f);
}
// Edit cursor (red)
if (editingSequence)
lights[STEP_PHRASE_LIGHTS + (i<<1) + 1].value = (i == stepIndexEdit ? 1.0f : 0.0f);
else
lights[STEP_PHRASE_LIGHTS + (i<<1) + 1].value = (i == phraseIndexEdit ? 1.0f : 0.0f);
}
} }
else
lights[OCTAVE_LIGHTS + i].value = (i == (6 - octLightIndex) ? 1.0f : 0.0f);
} }
}
// Keyboard lights
float cvValOffset;
if (editingSequence)
cvValOffset = cv[sequence][stepIndexEdit] + 10.0f;//to properly handle negative note voltages
else
cvValOffset = cv[phrase[phraseIndexEdit]][stepIndexRun] + 10.0f;//to properly handle negative note voltages
int keyLightIndex = (int) clamp( roundf( (cvValOffset-floor(cvValOffset)) * 12.0f ), 0.0f, 11.0f);
for (int i = 0; i < 12; i++) {
if (!editingSequence && (!attached || !running))// no keyboard lights when song mode and either (detached [1] or stopped [2])
// [1] makes no sense, can't mod steps and stepping though seq that may not be playing
// [2] CV is set to 0V when not running and in song mode, so cv[][] makes no sense to display
lights[KEY_LIGHTS + i].value = 0.0f;
else {
if (tiedWarning > 0l) {
bool warningFlashState = calcWarningFlash(tiedWarning, tiedWarningInit);
lights[KEY_LIGHTS + i].value = (warningFlashState && i == keyLightIndex) ? 1.0f : 0.0f;
// Octave lights
float octCV = 0.0f;
if (editingSequence)
octCV = cv[sequence][stepIndexEdit];
else
octCV = cv[phrase[phraseIndexEdit]][stepIndexRun];
int octLightIndex = (int) floor(octCV + 3.0f);
for (int i = 0; i < 7; i++) {
if (!editingSequence && (!attached || !running))// no oct lights when song mode and either (detached [1] or stopped [2])
// [1] makes no sense, can't mod steps and stepping though seq that may not be playing
// [2] CV is set to 0V when not running and in song mode, so cv[][] makes no sense to display
lights[OCTAVE_LIGHTS + i].value = 0.0f;
else {
if (tiedWarning > 0l) {
bool warningFlashState = calcWarningFlash(tiedWarning, (long) (tiedWarningTime * sampleRate / displayRefreshStepSkips));
lights[OCTAVE_LIGHTS + i].value = (warningFlashState && (i == (6 - octLightIndex))) ? 1.0f : 0.0f;
}
else
lights[OCTAVE_LIGHTS + i].value = (i == (6 - octLightIndex) ? 1.0f : 0.0f);
} }
}
// Keyboard lights
float cvValOffset;
if (editingSequence)
cvValOffset = cv[sequence][stepIndexEdit] + 10.0f;//to properly handle negative note voltages
else
cvValOffset = cv[phrase[phraseIndexEdit]][stepIndexRun] + 10.0f;//to properly handle negative note voltages
int keyLightIndex = (int) clamp( roundf( (cvValOffset-floor(cvValOffset)) * 12.0f ), 0.0f, 11.0f);
for (int i = 0; i < 12; i++) {
if (!editingSequence && (!attached || !running))// no keyboard lights when song mode and either (detached [1] or stopped [2])
// [1] makes no sense, can't mod steps and stepping though seq that may not be playing
// [2] CV is set to 0V when not running and in song mode, so cv[][] makes no sense to display
lights[KEY_LIGHTS + i].value = 0.0f;
else { else {
if (editingGate > 0ul && editingGateKeyLight != -1)
lights[KEY_LIGHTS + i].value = (i == editingGateKeyLight ? ((float) editingGate / (float)(gateTime * engineGetSampleRate())) : 0.0f);
else
lights[KEY_LIGHTS + i].value = (i == keyLightIndex ? 1.0f : 0.0f);
if (tiedWarning > 0l) {
bool warningFlashState = calcWarningFlash(tiedWarning, (long) (tiedWarningTime * sampleRate / displayRefreshStepSkips));
lights[KEY_LIGHTS + i].value = (warningFlashState && i == keyLightIndex) ? 1.0f : 0.0f;
}
else {
if (editingGate > 0ul && editingGateKeyLight != -1)
lights[KEY_LIGHTS + i].value = (i == editingGateKeyLight ? ((float) editingGate / (float)(gateTime * sampleRate / displayRefreshStepSkips)) : 0.0f);
else
lights[KEY_LIGHTS + i].value = (i == keyLightIndex ? 1.0f : 0.0f);
}
} }
}
// Gate1, Gate1Prob, Gate2, Slide and Tied lights
int attributesVal = attributes[sequence][stepIndexEdit];
if (!editingSequence)
attributesVal = attributes[phrase[phraseIndexEdit]][stepIndexRun];
//
lights[GATE1_LIGHT].value = ((attributesVal & ATT_MSK_GATE1) != 0) ? 1.0f : 0.0f;
lights[GATE1_PROB_LIGHT].value = ((attributesVal & ATT_MSK_GATE1P) != 0) ? 1.0f : 0.0f;
lights[GATE2_LIGHT].value = ((attributesVal & ATT_MSK_GATE2) != 0) ? 1.0f : 0.0f;
lights[SLIDE_LIGHT].value = ((attributesVal & ATT_MSK_SLIDE) != 0) ? 1.0f : 0.0f;
if (tiedWarning > 0l) {
bool warningFlashState = calcWarningFlash(tiedWarning, (long) (tiedWarningTime * sampleRate / displayRefreshStepSkips));
lights[TIE_LIGHT].value = (warningFlashState) ? 1.0f : 0.0f;
} }
}
else
lights[TIE_LIGHT].value = ((attributesVal & ATT_MSK_TIED) != 0) ? 1.0f : 0.0f;

// Attach light
lights[ATTACH_LIGHT].value = (running && attached) ? 1.0f : 0.0f;
// Reset light
lights[RESET_LIGHT].value = resetLight;
resetLight -= (resetLight / lightLambda) * engineGetSampleTime() * displayRefreshStepSkips;
// Run light
lights[RUN_LIGHT].value = lights[RUN_LIGHT].value = running ? 1.0f : 0.0f;
if (editingLength > 0ul)
editingLength--;
if (editingGate > 0ul)
editingGate--;
if (infoCopyPaste != 0l) {
if (infoCopyPaste > 0l)
infoCopyPaste --;
if (infoCopyPaste < 0l)
infoCopyPaste ++;
}
if (tiedWarning > 0l)
tiedWarning--;
}// lightRefreshCounter
// Gate1, Gate1Prob, Gate2, Slide and Tied lights
int attributesVal = attributes[sequence][stepIndexEdit];
if (!editingSequence)
attributesVal = attributes[phrase[phraseIndexEdit]][stepIndexRun];
//
lights[GATE1_LIGHT].value = ((attributesVal & ATT_MSK_GATE1) != 0) ? 1.0f : 0.0f;
lights[GATE1_PROB_LIGHT].value = ((attributesVal & ATT_MSK_GATE1P) != 0) ? 1.0f : 0.0f;
lights[GATE2_LIGHT].value = ((attributesVal & ATT_MSK_GATE2) != 0) ? 1.0f : 0.0f;
lights[SLIDE_LIGHT].value = ((attributesVal & ATT_MSK_SLIDE) != 0) ? 1.0f : 0.0f;
if (tiedWarning > 0l) {
bool warningFlashState = calcWarningFlash(tiedWarning, tiedWarningInit);
lights[TIE_LIGHT].value = (warningFlashState) ? 1.0f : 0.0f;
}
else
lights[TIE_LIGHT].value = ((attributesVal & ATT_MSK_TIED) != 0) ? 1.0f : 0.0f;

// Attach light
lights[ATTACH_LIGHT].value = (running && attached) ? 1.0f : 0.0f;
// Reset light
lights[RESET_LIGHT].value = resetLight;
// Run light
lights[RUN_LIGHT].value = lights[RUN_LIGHT].value = running ? 1.0f : 0.0f;

if (editingLength > 0ul)
editingLength--;
if (editingGate > 0ul)
editingGate--;
if (infoCopyPaste != 0l) {
if (infoCopyPaste > 0l)
infoCopyPaste --;
if (infoCopyPaste < 0l)
infoCopyPaste ++;
}
if (slideStepsRemain > 0ul)
slideStepsRemain--;
if (clockIgnoreOnReset > 0l) if (clockIgnoreOnReset > 0l)
clockIgnoreOnReset--; clockIgnoreOnReset--;
if (tiedWarning > 0l)
tiedWarning--;


// VCO // VCO
@@ -1346,42 +1345,37 @@ struct SemiModularSynthWidget : ModuleWidget {
nvgText(vg, textPos.x, textPos.y, "~~~", NULL); nvgText(vg, textPos.x, textPos.y, "~~~", NULL);
nvgFillColor(vg, textColor); nvgFillColor(vg, textColor);
if (module->infoCopyPaste != 0l) { if (module->infoCopyPaste != 0l) {
if (module->infoCopyPaste > 0l) {// if copy display "CPY"
if (module->infoCopyPaste > 0l)
snprintf(displayStr, 4, "CPY"); snprintf(displayStr, 4, "CPY");
}
else {// if paste display "PST"
else
snprintf(displayStr, 4, "PST"); snprintf(displayStr, 4, "PST");
}
} }
else {
if (module->displayState == SemiModularSynth::DISP_MODE) {
if (module->editingSequence)
runModeToStr(module->runModeSeq[module->sequence]);
else
runModeToStr(module->runModeSong);
}
else if (module->editingLength > 0ul) {
if (module->editingSequence)
snprintf(displayStr, 4, "L%2u", (unsigned) module->lengths[module->sequence]);
else
snprintf(displayStr, 4, "L%2u", (unsigned) module->phrases);
}
else if (module->displayState == SemiModularSynth::DISP_TRANSPOSE) {
snprintf(displayStr, 4, "+%2u", (unsigned) abs(module->transposeOffset));
if (module->transposeOffset < 0)
displayStr[0] = '-';
}
else if (module->displayState == SemiModularSynth::DISP_ROTATE) {
snprintf(displayStr, 4, ")%2u", (unsigned) abs(module->rotateOffset));
if (module->rotateOffset < 0)
displayStr[0] = '(';
}
else {// DISP_NORMAL
snprintf(displayStr, 4, " %2u", (unsigned) (module->editingSequence ?
module->sequence : module->phrase[module->phraseIndexEdit]) + 1 );
}
else if (module->editingLength > 0ul) {
if (module->editingSequence)
snprintf(displayStr, 4, "L%2u", (unsigned) module->lengths[module->sequence]);
else
snprintf(displayStr, 4, "L%2u", (unsigned) module->phrases);
}
else if (module->displayState == SemiModularSynth::DISP_MODE) {
if (module->editingSequence)
runModeToStr(module->runModeSeq[module->sequence]);
else
runModeToStr(module->runModeSong);
}
else if (module->displayState == SemiModularSynth::DISP_TRANSPOSE) {
snprintf(displayStr, 4, "+%2u", (unsigned) abs(module->transposeOffset));
if (module->transposeOffset < 0)
displayStr[0] = '-';
}
else if (module->displayState == SemiModularSynth::DISP_ROTATE) {
snprintf(displayStr, 4, ")%2u", (unsigned) abs(module->rotateOffset));
if (module->rotateOffset < 0)
displayStr[0] = '(';
}
else {// DISP_NORMAL
snprintf(displayStr, 4, " %2u", (unsigned) (module->editingSequence ?
module->sequence : module->phrase[module->phraseIndexEdit]) + 1 );
} }
displayStr[3] = 0;// more safety
nvgText(vg, textPos.x, textPos.y, displayStr, NULL); nvgText(vg, textPos.x, textPos.y, displayStr, NULL);
} }
}; };
@@ -1758,7 +1752,11 @@ RACK_PLUGIN_MODEL_INIT(ImpromptuModular, SemiModularSynth) {


/*CHANGE LOG /*CHANGE LOG


0.6.11:
step optimization of lights refresh

0.6.10: 0.6.10:
unlock gates when tied (turn off when press tied, but allow to be turned back on)
allow main knob to also change length when length editing is active allow main knob to also change length when length editing is active


0.6.9: 0.6.9:


+ 30
- 26
plugins/community/repos/ImpromptuModular/src/Tact.cpp View File

@@ -10,7 +10,6 @@




#include "ImpromptuModular.hpp" #include "ImpromptuModular.hpp"
#include "dsp/digital.hpp"


namespace rack_plugin_ImpromptuModular { namespace rack_plugin_ImpromptuModular {


@@ -70,7 +69,7 @@ struct Tact : Module {
SchmittTrigger recallTriggers[2]; SchmittTrigger recallTriggers[2];
PulseGenerator eocPulses[2]; PulseGenerator eocPulses[2];
float paramReadRequest[2]; float paramReadRequest[2];
int lightRefreshCounter;
inline bool isLinked(void) {return params[LINK_PARAM].value > 0.5f;} inline bool isLinked(void) {return params[LINK_PARAM].value > 0.5f;}
inline bool isExpSliding(void) {return params[EXP_PARAM].value > 0.5f;} inline bool isExpSliding(void) {return params[EXP_PARAM].value > 0.5f;}
@@ -91,6 +90,7 @@ struct Tact : Module {
eocPulses[i].reset(); eocPulses[i].reset();
paramReadRequest[i] = -10.0f;// -10.0f when no request being made, value to read otherwize paramReadRequest[i] = -10.0f;// -10.0f when no request being made, value to read otherwize
} }
lightRefreshCounter = 0;
onReset(); onReset();
} }
@@ -186,7 +186,6 @@ struct Tact : Module {
void step() override { void step() override {
float sampleRate = engineGetSampleRate(); float sampleRate = engineGetSampleRate();
float sampleTime = engineGetSampleTime(); float sampleTime = engineGetSampleTime();
long initInfoStore = (long) (storeInfoTime * sampleRate);
// Scheduled reset (just the parts that do not have a place below in rest of function) // Scheduled reset (just the parts that do not have a place below in rest of function)
if (scheduledReset) { if (scheduledReset) {
@@ -207,7 +206,7 @@ struct Tact : Module {
if (storeTriggers[i].process(params[STORE_PARAMS + i].value)) { if (storeTriggers[i].process(params[STORE_PARAMS + i].value)) {
if ( !(i == 1 && isLinked()) ) {// ignore right channel store-button press when linked if ( !(i == 1 && isLinked()) ) {// ignore right channel store-button press when linked
storeCV[i] = cv[i]; storeCV[i] = cv[i];
infoStore = initInfoStore * (i == 0 ? 1l : -1l);
infoStore = (long) (storeInfoTime * sampleRate / displayRefreshStepSkips) * (i == 0 ? 1l : -1l);
} }
} }
} }
@@ -288,32 +287,37 @@ struct Tact : Module {
} }
// Tactile lights
if (infoStore > 0l)
setTLightsStore(0, infoStore, initInfoStore);
else
setTLights(0);
if (infoStore < 0l)
setTLightsStore(1, infoStore * -1l, initInfoStore);
else
setTLights(1);
// CV input lights
for (int i = 0; i < 2; i++)
lights[CVIN_LIGHTS + i * 2].value = infoCVinLight[i];
for (int i = 0; i < 2; i++) {
infoCVinLight[i] -= (infoCVinLight[i] / lightLambda) * engineGetSampleTime();
lightRefreshCounter++;
if (lightRefreshCounter > displayRefreshStepSkips) {
lightRefreshCounter = 0;

// Tactile lights
if (infoStore > 0l)
setTLightsStore(0, infoStore, (long) (storeInfoTime * sampleRate / displayRefreshStepSkips) );
else
setTLights(0);
if (infoStore < 0l)
setTLightsStore(1, infoStore * -1l, (long) (storeInfoTime * sampleRate / displayRefreshStepSkips) );
else
setTLights(1);
if (infoStore != 0l) {
if (infoStore > 0l)
infoStore --;
if (infoStore < 0l)
infoStore ++;
}
// CV input lights
for (int i = 0; i < 2; i++)
lights[CVIN_LIGHTS + i * 2].value = infoCVinLight[i];
for (int i = 0; i < 2; i++) {
infoCVinLight[i] -= (infoCVinLight[i] / lightLambda) * sampleTime * displayRefreshStepSkips;
}
} }
if (isLinked()) { if (isLinked()) {
cv[1] = clamp(params[TACT_PARAMS + 1].value, 0.0f, 10.0f); cv[1] = clamp(params[TACT_PARAMS + 1].value, 0.0f, 10.0f);
} }
if (infoStore != 0l) {
if (infoStore > 0l)
infoStore --;
if (infoStore < 0l)
infoStore ++;
}
scheduledReset = false; scheduledReset = false;
} }


+ 14
- 8
plugins/community/repos/ImpromptuModular/src/TwelveKey.cpp View File

@@ -15,7 +15,6 @@




#include "ImpromptuModular.hpp" #include "ImpromptuModular.hpp"
#include "dsp/digital.hpp"


namespace rack_plugin_ImpromptuModular { namespace rack_plugin_ImpromptuModular {


@@ -54,6 +53,7 @@ struct TwelveKey : Module {
//float gateLight = 0.0f; //float gateLight = 0.0f;
unsigned long noteLightCounter;// 0 when no key to light, downward step counter timer when key lit unsigned long noteLightCounter;// 0 when no key to light, downward step counter timer when key lit
int lastKeyPressed;// 0 to 11 int lastKeyPressed;// 0 to 11
int lightRefreshCounter;


SchmittTrigger keyTriggers[12]; SchmittTrigger keyTriggers[12];
@@ -72,6 +72,7 @@ struct TwelveKey : Module {
stateInternal = inputs[GATE_INPUT].active ? false : true; stateInternal = inputs[GATE_INPUT].active ? false : true;
noteLightCounter = 0ul; noteLightCounter = 0ul;
lastKeyPressed = 0; lastKeyPressed = 0;
lightRefreshCounter = 0;
} }


void onRandomize() override { void onRandomize() override {
@@ -145,7 +146,7 @@ struct TwelveKey : Module {
if (keyTriggers[i].process(params[KEY_PARAMS + i].value)) { if (keyTriggers[i].process(params[KEY_PARAMS + i].value)) {
cv = ((float)(octaveNum - 4)) + ((float) i) / 12.0f; cv = ((float)(octaveNum - 4)) + ((float) i) / 12.0f;
stateInternal = true; stateInternal = true;
noteLightCounter = (unsigned long) (noteLightTime * engineGetSampleRate());
noteLightCounter = (unsigned long) (noteLightTime * engineGetSampleRate() / displayRefreshStepSkips);
lastKeyPressed = i; lastKeyPressed = i;
} }
} }
@@ -182,12 +183,17 @@ struct TwelveKey : Module {
// Octave output // Octave output
outputs[OCT_OUTPUT].value = round( (float)(octaveNum + 1) ); outputs[OCT_OUTPUT].value = round( (float)(octaveNum + 1) );
// Key lights
for (int i = 0; i < 12; i++)
lights[KEY_LIGHTS + i].value = (( i == lastKeyPressed && (noteLightCounter > 0ul || params[KEY_PARAMS + i].value > 0.5f)) ? 1.0f : 0.0f);
if (noteLightCounter > 0ul)
noteLightCounter--;
lightRefreshCounter++;
if (lightRefreshCounter > displayRefreshStepSkips) {
lightRefreshCounter = 0;

// Key lights
for (int i = 0; i < 12; i++)
lights[KEY_LIGHTS + i].value = (( i == lastKeyPressed && (noteLightCounter > 0ul || params[KEY_PARAMS + i].value > 0.5f)) ? 1.0f : 0.0f);
if (noteLightCounter > 0ul)
noteLightCounter--;
}
} }
}; };




+ 45
- 38
plugins/community/repos/ImpromptuModular/src/WriteSeq32.cpp View File

@@ -9,7 +9,7 @@




#include "ImpromptuModular.hpp" #include "ImpromptuModular.hpp"
#include "dsp/digital.hpp"
#include "PhraseSeqUtil.hpp"


namespace rack_plugin_ImpromptuModular { namespace rack_plugin_ImpromptuModular {


@@ -79,7 +79,7 @@ struct WriteSeq32 : Module {
int pendingPaste;// 0 = nothing to paste, 1 = paste on clk, 2 = paste on seq, destination channel in next msbits int pendingPaste;// 0 = nothing to paste, 1 = paste on clk, 2 = paste on seq, destination channel in next msbits
long clockIgnoreOnReset; long clockIgnoreOnReset;
const float clockIgnoreOnResetDuration = 0.001f;// disable clock on powerup and reset for 1 ms (so that the first step plays) const float clockIgnoreOnResetDuration = 0.001f;// disable clock on powerup and reset for 1 ms (so that the first step plays)
int lightRefreshCounter;
SchmittTrigger clockTrigger; SchmittTrigger clockTrigger;
SchmittTrigger resetTrigger; SchmittTrigger resetTrigger;
@@ -115,6 +115,7 @@ struct WriteSeq32 : Module {
pendingPaste = 0; pendingPaste = 0;
clockIgnoreOnReset = (long) (clockIgnoreOnResetDuration * engineGetSampleRate()); clockIgnoreOnReset = (long) (clockIgnoreOnResetDuration * engineGetSampleRate());
resetOnRun = false; resetOnRun = false;
lightRefreshCounter = 0;
} }


void onRandomize() override { void onRandomize() override {
@@ -258,7 +259,7 @@ struct WriteSeq32 : Module {
// Copy button // Copy button
if (copyTrigger.process(params[COPY_PARAM].value)) { if (copyTrigger.process(params[COPY_PARAM].value)) {
infoCopyPaste = (long) (copyPasteInfoTime * engineGetSampleRate());
infoCopyPaste = (long) (copyPasteInfoTime * engineGetSampleRate() / displayRefreshStepSkips);
for (int s = 0; s < 32; s++) { for (int s = 0; s < 32; s++) {
cvCPbuffer[s] = cv[indexChannel][s]; cvCPbuffer[s] = cv[indexChannel][s];
gateCPbuffer[s] = gates[indexChannel][s]; gateCPbuffer[s] = gates[indexChannel][s];
@@ -269,7 +270,7 @@ struct WriteSeq32 : Module {
if (pasteTrigger.process(params[PASTE_PARAM].value)) { if (pasteTrigger.process(params[PASTE_PARAM].value)) {
if (params[PASTESYNC_PARAM].value < 0.5f || indexChannel == 3) { if (params[PASTESYNC_PARAM].value < 0.5f || indexChannel == 3) {
// Paste realtime, no pending to schedule // Paste realtime, no pending to schedule
infoCopyPaste = (long) (-1 * copyPasteInfoTime * engineGetSampleRate());
infoCopyPaste = (long) (-1 * copyPasteInfoTime * engineGetSampleRate() / displayRefreshStepSkips);
for (int s = 0; s < 32; s++) { for (int s = 0; s < 32; s++) {
cv[indexChannel][s] = cvCPbuffer[s]; cv[indexChannel][s] = cvCPbuffer[s];
gates[indexChannel][s] = gateCPbuffer[s]; gates[indexChannel][s] = gateCPbuffer[s];
@@ -367,7 +368,7 @@ struct WriteSeq32 : Module {
// Pending paste on clock or end of seq // Pending paste on clock or end of seq
if ( ((pendingPaste&0x3) == 1) || ((pendingPaste&0x3) == 2 && indexStep == 0) ) { if ( ((pendingPaste&0x3) == 1) || ((pendingPaste&0x3) == 2 && indexStep == 0) ) {
int pasteChannel = pendingPaste>>2; int pasteChannel = pendingPaste>>2;
infoCopyPaste = (long) (-1 * copyPasteInfoTime * engineGetSampleRate());
infoCopyPaste = (long) (-1 * copyPasteInfoTime * engineGetSampleRate() / displayRefreshStepSkips);
for (int s = 0; s < 32; s++) { for (int s = 0; s < 32; s++) {
cv[pasteChannel][s] = cvCPbuffer[s]; cv[pasteChannel][s] = cvCPbuffer[s];
gates[pasteChannel][s] = gateCPbuffer[s]; gates[pasteChannel][s] = gateCPbuffer[s];
@@ -411,40 +412,46 @@ struct WriteSeq32 : Module {
} }
} }


int index = (indexChannel == 3 ? indexStepStage : indexStep);
// Window lights
for (int i = 0; i < 4; i++) {
lights[WINDOW_LIGHTS + i].value = ((i == (index >> 3))?1.0f:0.0f);
}
// Step and gate lights
for (int index8 = 0, iGate = 0; index8 < 8; index8++) {
lights[STEP_LIGHTS + index8].value = (index8 == (index&0x7)) ? 1.0f : 0.0f;
iGate = (index&0x18) | index8;
lights[GATE_LIGHTS + index8].value = (gates[indexChannel][iGate] && iGate < numSteps) ? 1.0f : 0.0f;
}
lightRefreshCounter++;
if (lightRefreshCounter > displayRefreshStepSkips) {
lightRefreshCounter = 0;

int index = (indexChannel == 3 ? indexStepStage : indexStep);
// Window lights
for (int i = 0; i < 4; i++) {
lights[WINDOW_LIGHTS + i].value = ((i == (index >> 3))?1.0f:0.0f);
}
// Step and gate lights
for (int index8 = 0, iGate = 0; index8 < 8; index8++) {
lights[STEP_LIGHTS + index8].value = (index8 == (index&0x7)) ? 1.0f : 0.0f;
iGate = (index&0x18) | index8;
lights[GATE_LIGHTS + index8].value = (gates[indexChannel][iGate] && iGate < numSteps) ? 1.0f : 0.0f;
}
// Channel lights
lights[CHANNEL_LIGHTS + 0].value = (indexChannel == 0) ? 1.0f : 0.0f;// green
lights[CHANNEL_LIGHTS + 1].value = (indexChannel == 1) ? 1.0f : 0.0f;// yellow
lights[CHANNEL_LIGHTS + 2].value = (indexChannel == 2) ? 1.0f : 0.0f;// orange
lights[CHANNEL_LIGHTS + 3].value = (indexChannel == 3) ? 1.0f : 0.0f;// blue
// Channel lights
lights[CHANNEL_LIGHTS + 0].value = (indexChannel == 0) ? 1.0f : 0.0f;// green
lights[CHANNEL_LIGHTS + 1].value = (indexChannel == 1) ? 1.0f : 0.0f;// yellow
lights[CHANNEL_LIGHTS + 2].value = (indexChannel == 2) ? 1.0f : 0.0f;// orange
lights[CHANNEL_LIGHTS + 3].value = (indexChannel == 3) ? 1.0f : 0.0f;// blue
// Run light
lights[RUN_LIGHT].value = running;
// Write allowed light
lights[WRITE_LIGHT + 0].value = (canEdit)?1.0f:0.0f;
lights[WRITE_LIGHT + 1].value = (canEdit)?0.0f:1.0f;
// Pending paste light
lights[PENDING_LIGHT].value = (pendingPaste == 0 ? 0.0f : 1.0f);
if (infoCopyPaste != 0l) {
if (infoCopyPaste > 0l)
infoCopyPaste --;
if (infoCopyPaste < 0l)
infoCopyPaste ++;
}
// Run light
lights[RUN_LIGHT].value = running ? 1.0f : 0.0f;
// Write allowed light
lights[WRITE_LIGHT + 0].value = (canEdit)?1.0f:0.0f;
lights[WRITE_LIGHT + 1].value = (canEdit)?0.0f:1.0f;
// Pending paste light
lights[PENDING_LIGHT].value = (pendingPaste == 0 ? 0.0f : 1.0f);
if (infoCopyPaste != 0l) {
if (infoCopyPaste > 0l)
infoCopyPaste --;
if (infoCopyPaste < 0l)
infoCopyPaste ++;
}
}// lightRefreshCounter
if (clockIgnoreOnReset > 0l) if (clockIgnoreOnReset > 0l)
clockIgnoreOnReset--; clockIgnoreOnReset--;
} }


+ 33
- 26
plugins/community/repos/ImpromptuModular/src/WriteSeq64.cpp View File

@@ -9,7 +9,7 @@




#include "ImpromptuModular.hpp" #include "ImpromptuModular.hpp"
#include "dsp/digital.hpp"
#include "PhraseSeqUtil.hpp"


namespace rack_plugin_ImpromptuModular { namespace rack_plugin_ImpromptuModular {


@@ -82,6 +82,7 @@ struct WriteSeq64 : Module {
const float clockIgnoreOnResetDuration = 0.001f;// disable clock on powerup and reset for 1 ms (so that the first step plays) const float clockIgnoreOnResetDuration = 0.001f;// disable clock on powerup and reset for 1 ms (so that the first step plays)
int stepKnob = 0; int stepKnob = 0;
int stepsKnob = 0; int stepsKnob = 0;
int lightRefreshCounter;


SchmittTrigger clock12Trigger; SchmittTrigger clock12Trigger;
@@ -121,6 +122,7 @@ struct WriteSeq64 : Module {
pendingPaste = 0; pendingPaste = 0;
clockIgnoreOnReset = (long) (clockIgnoreOnResetDuration * engineGetSampleRate()); clockIgnoreOnReset = (long) (clockIgnoreOnResetDuration * engineGetSampleRate());
resetOnRun = false; resetOnRun = false;
lightRefreshCounter = 0;
} }


void onRandomize() override { void onRandomize() override {
@@ -280,7 +282,7 @@ struct WriteSeq64 : Module {
// Copy button // Copy button
if (copyTrigger.process(params[COPY_PARAM].value)) { if (copyTrigger.process(params[COPY_PARAM].value)) {
infoCopyPaste = (long) (copyPasteInfoTime * engineGetSampleRate());
infoCopyPaste = (long) (copyPasteInfoTime * engineGetSampleRate() / displayRefreshStepSkips);
for (int s = 0; s < 64; s++) { for (int s = 0; s < 64; s++) {
cvCPbuffer[s] = cv[indexChannel][s]; cvCPbuffer[s] = cv[indexChannel][s];
gateCPbuffer[s] = gates[indexChannel][s]; gateCPbuffer[s] = gates[indexChannel][s];
@@ -292,7 +294,7 @@ struct WriteSeq64 : Module {
if (pasteTrigger.process(params[PASTE_PARAM].value)) { if (pasteTrigger.process(params[PASTE_PARAM].value)) {
if (params[PASTESYNC_PARAM].value < 0.5f || indexChannel == 4) { if (params[PASTESYNC_PARAM].value < 0.5f || indexChannel == 4) {
// Paste realtime, no pending to schedule // Paste realtime, no pending to schedule
infoCopyPaste = (long) (-1 * copyPasteInfoTime * engineGetSampleRate());
infoCopyPaste = (long) (-1 * copyPasteInfoTime * engineGetSampleRate() / displayRefreshStepSkips);
for (int s = 0; s < 64; s++) { for (int s = 0; s < 64; s++) {
cv[indexChannel][s] = cvCPbuffer[s]; cv[indexChannel][s] = cvCPbuffer[s];
gates[indexChannel][s] = gateCPbuffer[s]; gates[indexChannel][s] = gateCPbuffer[s];
@@ -395,7 +397,7 @@ struct WriteSeq64 : Module {
if ( ((pendingPaste&0x3) == 1) || ((pendingPaste&0x3) == 2 && indexStep[indexChannel] == 0) ) { if ( ((pendingPaste&0x3) == 1) || ((pendingPaste&0x3) == 2 && indexStep[indexChannel] == 0) ) {
if ( (clk12step && (indexChannel == 0 || indexChannel == 1)) || if ( (clk12step && (indexChannel == 0 || indexChannel == 1)) ||
(clk34step && (indexChannel == 2 || indexChannel == 3)) ) { (clk34step && (indexChannel == 2 || indexChannel == 3)) ) {
infoCopyPaste = (long) (-1 * copyPasteInfoTime * engineGetSampleRate());
infoCopyPaste = (long) (-1 * copyPasteInfoTime * engineGetSampleRate() / displayRefreshStepSkips);
int pasteChannel = pendingPaste>>2; int pasteChannel = pendingPaste>>2;
for (int s = 0; s < 64; s++) { for (int s = 0; s < 64; s++) {
cv[pasteChannel][s] = cvCPbuffer[s]; cv[pasteChannel][s] = cvCPbuffer[s];
@@ -420,8 +422,6 @@ struct WriteSeq64 : Module {
clock34Trigger.reset(); clock34Trigger.reset();
clockIgnoreOnReset = (long) (clockIgnoreOnResetDuration * engineGetSampleRate()); clockIgnoreOnReset = (long) (clockIgnoreOnResetDuration * engineGetSampleRate());
} }
else
resetLight -= (resetLight / lightLambda) * engineGetSampleTime();
//********** Outputs and lights ********** //********** Outputs and lights **********
@@ -452,28 +452,35 @@ struct WriteSeq64 : Module {
} }
} }
// Gate light
lights[GATE_LIGHT].value = gates[indexChannel][indexStep[indexChannel]] ? 1.0f : 0.0f;
// Reset light
lights[RESET_LIGHT].value = resetLight;
lightRefreshCounter++;
if (lightRefreshCounter > displayRefreshStepSkips) {
lightRefreshCounter = 0;


// Run light
lights[RUN_LIGHT].value = running;
// Write allowed light
lights[WRITE_LIGHT + 0].value = (canEdit)?1.0f:0.0f;
lights[WRITE_LIGHT + 1].value = (canEdit)?0.0f:1.0f;
// Pending paste light
lights[PENDING_LIGHT].value = (pendingPaste == 0 ? 0.0f : 1.0f);
// Gate light
lights[GATE_LIGHT].value = gates[indexChannel][indexStep[indexChannel]] ? 1.0f : 0.0f;
// Reset light
lights[RESET_LIGHT].value = resetLight;
resetLight -= (resetLight / lightLambda) * engineGetSampleTime() * displayRefreshStepSkips;

// Run light
lights[RUN_LIGHT].value = running ? 1.0f : 0.0f;
// Write allowed light
lights[WRITE_LIGHT + 0].value = (canEdit)?1.0f:0.0f;
lights[WRITE_LIGHT + 1].value = (canEdit)?0.0f:1.0f;
// Pending paste light
lights[PENDING_LIGHT].value = (pendingPaste == 0 ? 0.0f : 1.0f);
if (infoCopyPaste != 0l) {
if (infoCopyPaste > 0l)
infoCopyPaste --;
if (infoCopyPaste < 0l)
infoCopyPaste ++;
}
}// lightRefreshCounter
if (infoCopyPaste != 0l) {
if (infoCopyPaste > 0l)
infoCopyPaste --;
if (infoCopyPaste < 0l)
infoCopyPaste ++;
}
if (clockIgnoreOnReset > 0l) if (clockIgnoreOnReset > 0l)
clockIgnoreOnReset--; clockIgnoreOnReset--;
} }


+ 52
- 13
vst2_bin/plugins/ImpromptuModular/README.md View File

@@ -2,7 +2,7 @@


Modules for [VCV Rack](https://vcvrack.com), available in the [plugin manager](https://vcvrack.com/plugins.html). Modules for [VCV Rack](https://vcvrack.com), available in the [plugin manager](https://vcvrack.com/plugins.html).


Version 0.6.9
Version 0.6.10


[//]: # (!!!!!UPDATE VERSION NUMBER IN MAKEFILE ALSO!!!!! 120% Zoom for jpgs) [//]: # (!!!!!UPDATE VERSION NUMBER IN MAKEFILE ALSO!!!!! 120% Zoom for jpgs)


@@ -19,7 +19,8 @@ Impromptu Modular is not a single-person endeavor:
* Thanks to **Xavier Belmont** for suggesting improvements to the modules, for testing/bug-reports, for the concept design of the SMS16 module and the blank panel, and for graciously providing the dark panels of all modules. * Thanks to **Xavier Belmont** for suggesting improvements to the modules, for testing/bug-reports, for the concept design of the SMS16 module and the blank panel, and for graciously providing the dark panels of all modules.
* Thanks to **Steve Baker** for many fruitful discussions regarding the BPM Detection method in Clocked, testing and improvements that were suggested for that module. * Thanks to **Steve Baker** for many fruitful discussions regarding the BPM Detection method in Clocked, testing and improvements that were suggested for that module.
* Thanks to **Omri Cohen** for testing and suggesting improvements to the modules, and for the [PhraseSeq16/32 tutorial](https://www.youtube.com/watch?v=N8_rMNzsS7w). * Thanks to **Omri Cohen** for testing and suggesting improvements to the modules, and for the [PhraseSeq16/32 tutorial](https://www.youtube.com/watch?v=N8_rMNzsS7w).
* Thanks also to **Latif Fital**, **Alfredo Santamaria**, **Nay Seven**, **Alberto Zamora**, **Clément Foulc** for suggesting improvements to the modules, bug reports and testing.
* Thanks to **Latif Karoumi** for [testing](https://www.youtube.com/watch?v=5PZCXvWlFZM) and suggesting improvement to the modules, particularly the advanced gate modes in the GateSeq64 and PhraseSeq sequencers.
* Thanks also to **Pyer (Pierre Collard)**, **Alfredo Santamaria**, **Nay Seven**, **Alberto Zamora**, **Clément Foulc**, **Espen Storo**, **Wouter Spekkink**, **John Melcher** for suggesting improvements to the modules, bug reports and testing.






@@ -65,7 +66,7 @@ Such sequencers have two main inputs that allow the capturing of (pitch) CVs, as


When **AUTOSTEP** is activated, the sequencer automatically advances one step right on each write. For example, to automatically capture the notes played on a keyboard, send the midi keyboard's CV into the sequencer's CV IN, and send the keyboard's gate signal into the sequencer's Write input. With Autostep activated, each key-press will be automatically entered in sequence. An alternative way of automatically stepping the sequencer each time a note is entered is to send the gate signal of the keyboard to both the write and ">" inputs. When **AUTOSTEP** is activated, the sequencer automatically advances one step right on each write. For example, to automatically capture the notes played on a keyboard, send the midi keyboard's CV into the sequencer's CV IN, and send the keyboard's gate signal into the sequencer's Write input. With Autostep activated, each key-press will be automatically entered in sequence. An alternative way of automatically stepping the sequencer each time a note is entered is to send the gate signal of the keyboard to both the write and ">" inputs.


When Run is activated, the sequencer automatically starts playing in the current step position, provided **RESET on RUN** is not checked in the right-click menu; sequencers will start at the first step when this option is checked. All edge sensitive inputs have a threshold of 1V. In all sequencers, the duration of the gates corresponds to the pulse width (high time) of the clock signal.
When Run is activated, the sequencer automatically starts playing in the current step position, provided **RESET on RUN** is not checked in the right-click menu; sequencers will start at the first step when this option is checked. All edge sensitive inputs have a threshold of 1V. In all sequencers, the duration of the gates normally corresponds to the pulse width (high time) of the clock signal. When sequencers offer an **Advanced gate mode** and this mode is activated, the pulse width of the clock signal has no effect on the sequencer.


Many modules feature an **Expansion panel** to provide additional CV inputs for the module (available in the right-click menu of the module). An extra 4 HP is added on the right side of the module, thus it is advisable to first make room in your Rack for this. Many modules feature an **Expansion panel** to provide additional CV inputs for the module (available in the right-click menu of the module). An extra 4 HP is added on the right side of the module, thus it is advisable to first make room in your Rack for this.


@@ -141,14 +142,14 @@ The PW and Swing **CV inputs** (some are available in the expansion panel) are 0


### External synchronization <a id="clocked-sync"></a> ### External synchronization <a id="clocked-sync"></a>


By default, the clock's BPM input is level sensitive and follows [Rack standards for BPM CVs](https://vcvrack.com/manual/VoltageStandards.html#pitch-and-frequencies). For synchronizing Clocked an external clock signal, an optional setting is available in the right-click menu called "Use **BPM Detection** (as opposed to **BPM CV**)". When using this option in a chain of Clocked modules, all modules must have the option checked. The green LED to the right of the main BPM display will light up when this mode is enabled and a cable is connected to the BPM input.
By default, the clock's BPM input is level sensitive and follows [Rack standards for BPM CVs](https://vcvrack.com/manual/VoltageStandards.html#pitch-and-frequencies). For synchronizing Clocked to an external clock signal, an optional setting is available using the MODE button located below the BPM input jack, and selecting a mode other than "CV". When using a chain of Clocked modules, all modules must have the same mode setting. The LED to the right of the MODE button will light up when the sync mode is enabled and a cable is connected to the BPM input. When no cable is connected to the BPM input jack, the MODE button has no effect and the BPM CV mode is implicit. When a cable is connected, the possible settings are: P4, P8, P12, P24, where the number indicates the number of pulses per step of the external clock source.


When using BPM detection, Clocked syncs itself to the incoming clock pulse, and will stay synchronized, as opposed to just calculating the BPM from the external source. This means that it will not drift (or that it will drift in time with the incoming pulses if they drift), and it should stay perfectly synchronized over time; it also allows for latency compensation. Here are a few points to keep in mind when using BPM Detection.
When using external clock synchronization, Clocked syncs itself to the incoming clock pulse, and will stay synchronized, as opposed to just calculating the BPM from the external source. This means that it will not drift (or that it will drift in time with the incoming pulses if they drift), and it should stay perfectly synchronized over time; it also allows for latency compensation. Here are a few points to keep in mind when using clock synchronization.


1. When using the BPM detection mode, Clocked can not be manually turned on, it will autostart on the first pulse it receives.
1. Clocked can not be manually turned on in clock sync mode, it will autostart on the first pulse it receives.
1. Clocked will automatically stop when the pulses stop, but in order to detect this, it take a small amount of time. To stop the clock quickly, you can simply send a pulse to the RUN CV input, and if the clock is running, it will turn off. 1. Clocked will automatically stop when the pulses stop, but in order to detect this, it take a small amount of time. To stop the clock quickly, you can simply send a pulse to the RUN CV input, and if the clock is running, it will turn off.
1. The external clock must be capable of sending clocks at a minumum of 4 pulses per quarter note (PPQN) and should not have any swing. 1. The external clock must be capable of sending clocks at a minumum of 4 pulses per quarter note (PPQN) and should not have any swing.
1. For low clock BPMs, synchronization may take some time if the external clock changes markedly from the last BPM it was synchronized to. Making gradual tempo changes is always recommended, and increasing the PPQN setting may also help. An other method is to first prime Clocked with is correct BPM to let it learn the new BPM, so that all further runs at that BPM will sync perfectly.
1. For low clock BPMs, synchronization may take some time if the external clock changes markedly from the last BPM it was synchronized to. Making gradual tempo changes is always recommended, and increasing the PPQN setting may also help. An other method consists in priming Clocked with is correct BPM first, to let it learn the new BPM, so that all further runs at that BPM will sync perfectly.


([Back to module list](#modules)) ([Back to module list](#modules))


@@ -158,7 +159,7 @@ When using BPM detection, Clocked syncs itself to the incoming clock pulse, and


![IM](res/img/PhraseSeq16.jpg) ![IM](res/img/PhraseSeq16.jpg)


A 16 phrase sequencer module, where each phrase is an index into a set of 16 sequences of 16 steps (maximum). CVs can be entered via a CV input when using an external keyboard controller or via the built-in keyboard on the module itself. If you need a 256-step sequence in a single module, this is the sequencer for you! With two separate gates per step, gate 2 is perfect for using as an accent if desired. When notes are entered with the *right mouse button* instead of the left button, the sequencer automatically moves to the next step.
A 16 phrase sequencer module, where each phrase is an index into a set of 16 sequences of 16 steps (maximum). CVs can be entered via a CV input when using an external keyboard controller or via the built-in keyboard on the module itself. If you need a 256-step sequence in a single module, this is the sequencer for you! With two separate gates per step, gate 2 is perfect for using as an accent if desired. When notes are entered with the **right mouse button** instead of the left button, the sequencer automatically moves to the next step.


The following block diagram shows how sequences and phrases relate to each other to create a song. In the diagram, a 12-bar blues pattern is created by setting the song length to 12, the step lengths to 8 (not visible in the figure), and then creating 4 sequences. The 12 phrases are indexes into the 4 sequences that were created. (Not sure anyone plays blues in a modular synth, but it shows the idea at least!) The following block diagram shows how sequences and phrases relate to each other to create a song. In the diagram, a 12-bar blues pattern is created by setting the song length to 12, the step lengths to 8 (not visible in the figure), and then creating 4 sequences. The 12 phrases are indexes into the 4 sequences that were created. (Not sure anyone plays blues in a modular synth, but it shows the idea at least!)


@@ -176,7 +177,7 @@ Familiarity with the Fundamental SEQ-3 sequencer is recommended, as some operati


* **ATTACH**: Allows the edit head to follow the run head (Attach on). The position of the edit head is shown with a red LED, and the position of the run head is shown with a green LED. When in Seq mode, the actual content of the step corresponding to the edit head position (i.e. note, oct, gates, slide) can be modified in real time whether the sequencer is running or not. The edit head automatically follows the run head when Attach is on, or can manually positioned by using the < and > buttons when Attach is off. * **ATTACH**: Allows the edit head to follow the run head (Attach on). The position of the edit head is shown with a red LED, and the position of the run head is shown with a green LED. When in Seq mode, the actual content of the step corresponding to the edit head position (i.e. note, oct, gates, slide) can be modified in real time whether the sequencer is running or not. The edit head automatically follows the run head when Attach is on, or can manually positioned by using the < and > buttons when Attach is off.


* **MODE**: This controls the run mode of the sequences and the song (one setting for each sequence and one for the song). The modes are: FWD (forward), REV (reverse), PPG (ping-pong, also called forward-reverse), BRN (Brownian random), RND (random), FW2 (forward, play twice), FW3 (play three times) and FW4 (four times). For example, setting the run mode to FWD for sequences and to RND for the song will play the phrases that are part of a song randomly, and the probability of a given phrase playing is proportional to the number of times it appears in the song. For sequences, the FW2, FW3 and FW4 modes can be used to repeat sequences more easily without consuming additional phrases in the song. These last three modes are not available for the song's run mode however.
* **MODE**: This controls the run mode of the sequences and the song (one setting for each sequence and one for the song). The modes are: FWD (forward), REV (reverse), PPG (ping-pong, also called forward-reverse), BRN (Brownian random), RND (random), FW2 (forward, play twice), FW3 (play three times) and FW4 (four times). For example, setting the run mode to FWD for sequences and to RND for the song will play the phrases that are part of a song randomly, and the probability of a given phrase playing is proportional to the number of times it appears in the song. For sequences, the FW2, FW3 and FW4 modes can be used to repeat sequences more easily without consuming additional phrases in the song. These last three modes are not available for the song's run mode however. Holding the MODE button for **two seconds** allows the selection of the clock resolution, and is the mechanism used to enable the [advanced gate mode](#advanced-gate-mode-ps).


* **TRAN/ROT**: Transpose/Rotate the currently selected sequence up-down/left-right by a given number of semi-tones/steps. The main knob is used to set the transposition/rotation amount. Only available in Seq mode. * **TRAN/ROT**: Transpose/Rotate the currently selected sequence up-down/left-right by a given number of semi-tones/steps. The main knob is used to set the transposition/rotation amount. Only available in Seq mode.


@@ -188,7 +189,22 @@ Familiarity with the Fundamental SEQ-3 sequencer is recommended, as some operati


* **SLIDE**: Portamento between CVs of successive steps. Slide can be activated for a given step using the slide button. The slide duration can be set using the slide knob. The slide duration can range from 0 to T seconds, where T is the duration of a clock period (the default is 10% of T). This knob's setting is not memorized for each step and applies to the sequencer as a whole. * **SLIDE**: Portamento between CVs of successive steps. Slide can be activated for a given step using the slide button. The slide duration can be set using the slide knob. The slide duration can range from 0 to T seconds, where T is the duration of a clock period (the default is 10% of T). This knob's setting is not memorized for each step and applies to the sequencer as a whole.


* **TIED STEP**: When CVs are intended to be held across subsequent steps, this button can be used to tie the CV of the previous step to the current step; when tied, the gates of the current step are automatically turned off. If the CV of the head note changes, all consecutive tied notes are updated automatically.
* **TIED STEP**: When CVs are intended to be held across subsequent steps, this button can be used to tie the CV of the previous step to the current step. When tied is turned on for a step, the gates of that step are automatically turned off, but can be manually turned back on if desired. When tied, if the CV of the head note changes, all consecutive tied notes are updated automatically.


### Advanced gate mode<a id="advanced-gate-mode-ps"></a>

Holding the MODE button for **two seconds** allows the selection of the clock resolution, in number of pulses per step (PPS). When set to a value greater than 1, which unlocks the advanced gate mode, the sequencer will skip this many clock pulses before advancing to the next step. In such cases, a mutliplied clock must be supplied in order to keep the same tempo in the sequencer. In advanced gate mode, the pulse width of the clock is not used and has no effect on the gates.

In the advanced gate mode, the Gate1 and Gate2 lights will be a different color, and the onboard keyboard can be used not only to enter note values, but also to select one of the 12 types of gates for a given step. Advanced gates can only be set while in Seq mode and when the sequencer is stopped. Here are the different gate types and their minimum PPS requirements.

![IM](res/img/AdvancedGateDetails.jpg)

All PPS settings will work for the half and full gates (the D and F keys) as well as triggers (the B key). A full gate remains high during the entire step, and if the next step's gate is active, then the gate continues without interruption into that next step. When PPS requirements are not met, the sequencer will not allow invalid gate types to be entered on the keyboard. For example, if PPS is set to 6, then the 75% gate (the E key) can not be selected. Selecting a PPS value of 12 or 24 will ensure that all gate types can be used (i.e. that all PPS requirements are met irrespective of the gate type chosen).

The gate type for a given step can be selected during a short time interval after a given gate has just been turned on using the Gate1 or Gate2 buttons. If a gate is already turned on and its gate type is to be edited, clicking the gate button twice will allow it to be edited while keeping it in the same state. The onboard keyboard will temporarily show a yellow/orange light corresponding to the current gate type for that step; during this time the gate type can be changed.

Since the editing time for the advanced gate mode is kept rather short (4s), holding the Gate2 button for 2s will set that default time interval to 400s. Holding Gate1 for 2s will revert to the default time of 4s. The extended time feature is useful when the gate modes for multiple steps of a sequence are to be editied or reviewed in a single pass, for example.


([Back to module list](#modules)) ([Back to module list](#modules))


@@ -216,15 +232,22 @@ When the 1x32 configuration is selected, only the top channel outputs are used (


A 64 step gate sequencer with the ability to define **probabilities** for each step. A configuration switch allows the sequencer to output quad 16 step sequences, dual 32 step sequences or single 64 step sequences. To see the sequencer in action and for a tutorial on how it works, please see [this segment](https://www.youtube.com/watch?v=bjqWwTKqERQ&t=6111s) of Nigel Sixsmith's Talking Rackheads episode 10. A 64 step gate sequencer with the ability to define **probabilities** for each step. A configuration switch allows the sequencer to output quad 16 step sequences, dual 32 step sequences or single 64 step sequences. To see the sequencer in action and for a tutorial on how it works, please see [this segment](https://www.youtube.com/watch?v=bjqWwTKqERQ&t=6111s) of Nigel Sixsmith's Talking Rackheads episode 10.


When activating a given step by clicking it once, it will turn green showing that the step is on. Clicking the step again turns it yellow, and the main display shows the probability associated with this step. While the probability remains shown, the probability can be adjusted with the main knob, in 0.02 increments, between 0 and 1. When a yellow step is selected, clicking it again will turn it off.
When activating a given step by clicking it once, it will turn green showing that the step is on. Clicking the _"p"_ button turns it yellow, and the main display shows the probability associated with this step. While the probability remains shown, the probability can be adjusted with the main knob, in 0.02 increments, between 0 and 1. When a yellow step is selected, clicking the _"p"_ button again will turn it off.


This sequencer also features the song mode found in [PhraseSeq16](#phrase-seq-16); 16 phrases can be defined, where a phrase is an index into a set of 16 sequences. In GateSeq64, the song steps are shown using the fourth row of steps, overlapped with the actual sequence progression in lighter shades in the lights. This sequencer also features the song mode found in [PhraseSeq16](#phrase-seq-16); 16 phrases can be defined, where a phrase is an index into a set of 16 sequences. In GateSeq64, the song steps are shown using the fourth row of steps, overlapped with the actual sequence progression in lighter shades in the lights.


The **SEQ** CV input and run **MODES** are identical to those found in PhraseSeq16, and selecting sequence lengths is done in the same manner as described in [PhraseSeq32](#phrase-seq-32). Copy-pasting **ALL** also copies the run mode and length of a given sequence, along with gate states and probabilities, whereas only gates and probabilities are copied when **ROW** is selected.
The **SEQ** CV input and run **MODES** are identical to those found in PhraseSeq16, and selecting sequence lengths is done in the same manner as described in [PhraseSeq32](#phrase-seq-32). Copy-pasting ALL also copies the run mode and length of a given sequence, along with gate states and probabilities, whereas only gates and probabilities are copied when 4 or ROW are selected.


When running in the 4x16 configuration, each of the four rows is sent to the four **GATE** output jacks (jacks 1 to 4, with jack 1 being the top-most jack). In the 2x32 configuration, jacks 1 and 3 are used, and in the 1x64 configuration, only jack 1 is used (top-most jack). The pulse width of the gates emitted corresponds to the pulse width of the clock. When running in the 4x16 configuration, each of the four rows is sent to the four **GATE** output jacks (jacks 1 to 4, with jack 1 being the top-most jack). In the 2x32 configuration, jacks 1 and 3 are used, and in the 1x64 configuration, only jack 1 is used (top-most jack). The pulse width of the gates emitted corresponds to the pulse width of the clock.


Although no **write** capabilities appear in the main part of the module, automatically storing patterns into the sequencer can be performed using the CV inputs in the **expansion panel**. A write cursor is implicitly stepped forward on each write, and can be repositioned at the first step by pressing the reset button, or at an arbitrary step by simply clicking that given step.
Although no **write** capabilities appear in the main part of the module, automatically storing patterns into the sequencer can be performed using the CV inputs in the **expansion panel**. The cursor is stepped forward on each write, and can be repositioned at the first step by pressing the reset button, or at an arbitrary step by simply clicking that given step. When the cursor is not flashing, clicking any step will make it appear.


### Advanced gate mode<a id="advanced-gate-mode-gs"></a>

Holding the MODE button for **two seconds** allows the selection of the clock resolution, in number of pulses per step (PPS). When set to a value greater than 1, which unlocks the advanced gate mode, the sequencer will skip this many clock pulses before advancing to the next step. In such cases, a mutliplied clock must be supplied in order to keep the same tempo in the sequencer. In advanced gate mode, the pulse width of the clock is not used and has no effect on the gates.

The PPS be a multiple of 4 for the first three gate types, while the PPS be a multiple of 6 for the last five gate types. A chosen gate type not meeting its required pulse rate will have a red LED beside it to indicate this (normally it is green). When a gate type is red, the sequencer will still emit a (possibly empty) gate pattern for that step, but with no guarantee that it will match the type that was selected. All gate types can be used when selecting a PPS value of 12 or 24.


([Back to module list](#modules)) ([Back to module list](#modules))


@@ -260,6 +283,8 @@ Here are a few more details on some of the uses of the buttons. The sequencer us


**FILL**: plays continuous triggers for the given channel as long as the button is kept pressed. By default the fills are not written to memory and are only for playback; however, an option to allow the writing of fill steps to memory is available in the right-click menu. **FILL**: plays continuous triggers for the given channel as long as the button is kept pressed. By default the fills are not written to memory and are only for playback; however, an option to allow the writing of fill steps to memory is available in the right-click menu.


The BIG and DEL buttons are **quantized to the nearest beat**. Without quantization, button presses typically affect the current beat (step) of the sequencer. With quantized buttons, they affect the nearest beat. For example, pressing the big button 1 microsecond before a beat would normally record the beat in the current step and not the next one that is about to occur, which is actually the closest. For the quantization to work properly, the sequencer must recieve a stable clock of at least 30 BPM. When this is not the case, the option is automatically disabled internally. When manually advancing the clock to program a sequence in non real-time, for example, the option has no effect and the current step is always the target of a button press.

([Back to module list](#modules)) ([Back to module list](#modules))




@@ -315,3 +340,17 @@ WriteSeq64 has dual clock inputs, where each controls a pair of channels. When n
Ideas: The first part of the famous [Piano Phase](https://en.wikipedia.org/wiki/Piano_Phase) piece by Steve Reich can be easily programmed into the sequencer by entering the twelve notes into channel 1 with a keyboard, setting STEPS to 12, copy-pasting channel 1 into channel 3, and then driving each clock input with two LFOs that have ever so slightly different frequencies. Exercise left to the reader! Ideas: The first part of the famous [Piano Phase](https://en.wikipedia.org/wiki/Piano_Phase) piece by Steve Reich can be easily programmed into the sequencer by entering the twelve notes into channel 1 with a keyboard, setting STEPS to 12, copy-pasting channel 1 into channel 3, and then driving each clock input with two LFOs that have ever so slightly different frequencies. Exercise left to the reader!


([Back to module list](#modules)) ([Back to module list](#modules))


# Hall of Fame <a id="hall-of-fame"></a>

Here are a few videos featuring Impromptu Modular, which I find particularly very inspiring and interesting (listed in no particular order). Many talented Rackheads have made tracks using Impromptu modules, and this list could in fact be quite long. I have no formal criteria for why a track ends up in the list or doesn't.

* **Nigel Sixsmith**, [Talking Rackheads episode 8, PS16 MegaPatch Play Out](https://www.youtube.com/watch?v=KOpo2oUPTjg&t=5504s)

* **Omri Cohen**, [Arvo Pärt - Spiegel im Spiegel VCV Rack Cover](https://www.youtube.com/watch?v=6bm4LjRYDmI)

* **Isaac Hu**, [In the Hall of the Mountain King](https://www.youtube.com/watch?v=fxYc0H5i6HA)

* **John Melcher**, [Steppes](https://www.youtube.com/watch?v=ruo4s_Hyhrw)


+ 813
- 0
vst2_bin/plugins/ImpromptuModular/res/AdvancedGateDetails.svg View File

@@ -0,0 +1,813 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="220"
height="183"
viewBox="0 0 58.208549 48.418911"
version="1.1"
id="svg321"
sodipodi:docname="AdvancedGateDetails.svg"
inkscape:version="0.92.3 (2405546, 2018-03-11)">
<metadata
id="metadata325">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title />
</cc:Work>
</rdf:RDF>
</metadata>
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1920"
inkscape:window-height="1017"
id="namedview323"
showgrid="false"
inkscape:zoom="3.1174557"
inkscape:cx="144.03182"
inkscape:cy="84.993533"
inkscape:window-x="-8"
inkscape:window-y="-8"
inkscape:window-maximized="1"
inkscape:current-layer="svg321"
showguides="true"
inkscape:guide-bbox="true" />
<defs
id="defs89">
<filter
height="1"
width="1"
y="0"
x="0"
filterUnits="objectBoundingBox"
id="a">
<feColorMatrix
values="0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 1 0"
in="SourceGraphic"
id="feColorMatrix2" />
</filter>
<filter
height="1"
width="1"
y="0"
x="0"
filterUnits="objectBoundingBox"
id="b">
<feColorMatrix
values="0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 1 0"
in="SourceGraphic"
id="feColorMatrix5" />
</filter>
<filter
height="1"
width="1"
y="0"
x="0"
filterUnits="objectBoundingBox"
id="c">
<feColorMatrix
values="0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 1 0"
in="SourceGraphic"
id="feColorMatrix8" />
</filter>
<filter
height="1"
width="1"
y="0"
x="0"
filterUnits="objectBoundingBox"
id="d">
<feColorMatrix
values="0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 1 0"
in="SourceGraphic"
id="feColorMatrix11" />
</filter>
<filter
height="1"
width="1"
y="0"
x="0"
filterUnits="objectBoundingBox"
id="e">
<feColorMatrix
values="0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 1 0"
in="SourceGraphic"
id="feColorMatrix14" />
</filter>
<filter
height="1"
width="1"
y="0"
x="0"
filterUnits="objectBoundingBox"
id="f">
<feColorMatrix
values="0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 1 0"
in="SourceGraphic"
id="feColorMatrix17" />
</filter>
<filter
height="1"
width="1"
y="0"
x="0"
filterUnits="objectBoundingBox"
id="g">
<feColorMatrix
values="0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 1 0"
in="SourceGraphic"
id="feColorMatrix20" />
</filter>
<filter
height="1"
width="1"
y="0"
x="0"
filterUnits="objectBoundingBox"
id="h">
<feColorMatrix
values="0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 1 0"
in="SourceGraphic"
id="feColorMatrix23" />
</filter>
<filter
height="1"
width="1"
y="0"
x="0"
filterUnits="objectBoundingBox"
id="i">
<feColorMatrix
values="0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 1 0"
in="SourceGraphic"
id="feColorMatrix26" />
</filter>
<filter
height="1"
width="1"
y="0"
x="0"
filterUnits="objectBoundingBox"
id="j">
<feColorMatrix
values="0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 1 0"
in="SourceGraphic"
id="feColorMatrix29" />
</filter>
<filter
height="1"
width="1"
y="0"
x="0"
filterUnits="objectBoundingBox"
id="k">
<feColorMatrix
values="0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 1 0"
in="SourceGraphic"
id="feColorMatrix32" />
</filter>
<filter
height="1"
width="1"
y="0"
x="0"
filterUnits="objectBoundingBox"
id="l">
<feColorMatrix
values="0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 1 0"
in="SourceGraphic"
id="feColorMatrix35" />
</filter>
<filter
height="1"
width="1"
y="0"
x="0"
filterUnits="objectBoundingBox"
id="m">
<feColorMatrix
values="0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 1 0"
in="SourceGraphic"
id="feColorMatrix38" />
</filter>
<filter
height="1"
width="1"
y="0"
x="0"
filterUnits="objectBoundingBox"
id="n">
<feColorMatrix
values="0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 1 0"
in="SourceGraphic"
id="feColorMatrix41" />
</filter>
<filter
height="1"
width="1"
y="0"
x="0"
filterUnits="objectBoundingBox"
id="o">
<feColorMatrix
values="0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 1 0"
in="SourceGraphic"
id="feColorMatrix44" />
</filter>
<filter
height="1"
width="1"
y="0"
x="0"
filterUnits="objectBoundingBox"
id="p">
<feColorMatrix
values="0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 1 0"
in="SourceGraphic"
id="feColorMatrix47" />
</filter>
<filter
height="1"
width="1"
y="0"
x="0"
filterUnits="objectBoundingBox"
id="q">
<feColorMatrix
values="0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 1 0"
in="SourceGraphic"
id="feColorMatrix50" />
</filter>
<filter
height="1"
width="1"
y="0"
x="0"
filterUnits="objectBoundingBox"
id="r">
<feColorMatrix
values="0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 1 0"
in="SourceGraphic"
id="feColorMatrix53" />
</filter>
<filter
height="1"
width="1"
y="0"
x="0"
filterUnits="objectBoundingBox"
id="s">
<feColorMatrix
values="0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 1 0"
in="SourceGraphic"
id="feColorMatrix56" />
</filter>
<filter
height="1"
width="1"
y="0"
x="0"
filterUnits="objectBoundingBox"
id="t">
<feColorMatrix
values="0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 1 0"
in="SourceGraphic"
id="feColorMatrix59" />
</filter>
<filter
height="1"
width="1"
y="0"
x="0"
filterUnits="objectBoundingBox"
id="u">
<feColorMatrix
values="0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 1 0"
in="SourceGraphic"
id="feColorMatrix62" />
</filter>
<filter
height="1"
width="1"
y="0"
x="0"
filterUnits="objectBoundingBox"
id="v">
<feColorMatrix
values="0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 1 0"
in="SourceGraphic"
id="feColorMatrix65" />
</filter>
<filter
height="1"
width="1"
y="0"
x="0"
filterUnits="objectBoundingBox"
id="w">
<feColorMatrix
values="0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 1 0"
in="SourceGraphic"
id="feColorMatrix68" />
</filter>
<filter
height="1"
width="1"
y="0"
x="0"
filterUnits="objectBoundingBox"
id="x">
<feColorMatrix
values="0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 1 0"
in="SourceGraphic"
id="feColorMatrix71" />
</filter>
<filter
height="1"
width="1"
y="0"
x="0"
filterUnits="objectBoundingBox"
id="y">
<feColorMatrix
values="0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 1 0"
in="SourceGraphic"
id="feColorMatrix74" />
</filter>
<filter
height="1"
width="1"
y="0"
x="0"
filterUnits="objectBoundingBox"
id="z">
<feColorMatrix
values="0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 1 0"
in="SourceGraphic"
id="feColorMatrix77" />
</filter>
<filter
height="1"
width="1"
y="0"
x="0"
filterUnits="objectBoundingBox"
id="A">
<feColorMatrix
values="0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 1 0"
in="SourceGraphic"
id="feColorMatrix80" />
</filter>
<filter
height="1"
width="1"
y="0"
x="0"
filterUnits="objectBoundingBox"
id="B">
<feColorMatrix
values="0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 1 0"
in="SourceGraphic"
id="feColorMatrix83" />
</filter>
<filter
height="1"
width="1"
y="0"
x="0"
filterUnits="objectBoundingBox"
id="C">
<feColorMatrix
values="0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 1 0"
in="SourceGraphic"
id="feColorMatrix86" />
</filter>
</defs>
<text
id="text95"
word-spacing="0"
letter-spacing="0"
font-size="10.583"
font-weight="400"
y="34.662903"
x="64.782875"
style="font-weight:400;font-size:10.58300018px;line-height:1.25;font-family:sans-serif;letter-spacing:0;word-spacing:0;stroke-width:0.26499999" />
<rect
y="12.95517"
x="2.9069221"
height="22.225079"
width="52.916325"
rx="0.60890275"
ry="0.60897696"
id="rect117"
style="fill:#333333;stroke-width:1.000314;paint-order:markers stroke fill" />
<path
d="m 15.230183,13.882545 0.03219,9.537557 2.327392,1.289025 v 9.560551 h -6.358203 v -9.560551 l 2.370542,-1.289025 v -9.537557 z m 22.398581,0 0.03288,9.537557 2.326708,1.289025 v 9.560551 h -6.35821 v -9.560551 l 2.370543,-1.289025 v -9.537557 z m 5.806151,0 -0.03288,9.537557 -2.326707,1.289025 v 9.560551 h 6.35752 v -9.560551 l -2.369854,-1.289025 v -9.537557 z m -35.6499211,0 0.03288,9.537557 2.3267081,1.289025 v 9.560551 H 3.7863718 v -9.560551 l -0.0055,-10.826582 h 2.3753371 z m 13.2465461,0 -0.03288,9.537557 -2.326707,1.289025 v 9.560551 h 6.35752 v -9.560551 l 0.0048,-10.826582 h -2.375337 z m 9.152035,0 0.03288,9.537557 2.326708,1.289025 v 9.560551 h -6.357525 v -9.560551 l -0.0055,-10.826582 h 2.376022 z m 20.69653,0 -0.03288,9.537557 -2.326707,1.289025 v 9.560551 h 6.35752 v -9.560551 l 0.0048,-10.826582 h -2.375337 z"
id="path119"
inkscape:connector-curvature="0"
style="fill:#999999;stroke:#000000;stroke-width:0.26458582"
sodipodi:nodetypes="ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc" />
<g
id="g169"
transform="matrix(0.26458426,0,0,0.26458426,-6.0187653,3.9901329)">
<g
id="g58">
<line
id="line52"
style="fill:none;stroke:#a8a8a8;stroke-linecap:round;stroke-linejoin:round"
y2="128.75461"
x2="58.888561"
y1="122.37562"
x1="58.888561" />
<line
id="line54"
style="fill:none;stroke:#a8a8a8;stroke-linecap:round;stroke-linejoin:round"
y2="128.75461"
x2="53.964432"
y1="122.37562"
x1="53.964432" />
<line
id="line56"
style="fill:none;stroke:#a8a8a8;stroke-linecap:round;stroke-linejoin:round"
y2="128.75461"
x2="49.040298"
y1="122.37562"
x1="49.040298" />
</g>
<path
id="path96"
style="fill:none;stroke:#272727;stroke-linecap:round;stroke-linejoin:round"
transform="translate(-14.81094,-16.5)"
d="m 54.003,145.25461 v -5.2617 a 1.11729,1.11729 0 0 1 1.1173,-1.11729 h 2.68954 a 1.11729,1.11729 0 0 1 1.11729,1.11729 v 4.1444 a 1.1173,1.1173 0 0 0 1.1173,1.1173 H 73.6995"
inkscape:connector-curvature="0" />
</g>
<path
inkscape:connector-curvature="0"
d="M 11.854922,38.056573 V 36.66441 a 0.29561735,0.29561735 0 0 1 0.29562,-0.295618 h 2.014447 a 0.29561735,0.29561735 0 0 1 0.295617,0.295618 v 1.096543 a 0.29561999,0.29561999 0 0 0 0.29562,0.29562 h 2.310085"
style="fill:none;stroke:#333333;stroke-width:0.26458427;stroke-linecap:round;stroke-linejoin:round"
id="path98" />
<g
id="g183"
transform="matrix(0.26458426,0,0,0.26458426,-10.607803,3.9901329)">
<g
id="g74">
<line
id="line68"
style="fill:none;stroke:#a8a8a8;stroke-linecap:round;stroke-linejoin:round"
y2="128.75461"
x2="132.77985"
y1="122.37562"
x1="132.77985" />
<line
id="line70"
style="fill:none;stroke:#a8a8a8;stroke-linecap:round;stroke-linejoin:round"
y2="128.75461"
x2="122.93159"
y1="122.37562"
x1="122.93159" />
<line
id="line72"
style="fill:none;stroke:#a8a8a8;stroke-linecap:round;stroke-linejoin:round"
y2="128.75461"
x2="118.00746"
y1="122.37562"
x1="118.00746" />
</g>
<path
id="path100"
style="fill:none;stroke:#333333;stroke-linecap:round;stroke-linejoin:round"
transform="translate(-14.81094,-16.5)"
d="m 127.89428,145.25461 v -5.2617 a 1.11729,1.11729 0 0 1 1.11729,-1.11729 h 12.5378 a 1.11729,1.11729 0 0 1 1.1173,1.11729 v 4.1444 a 1.11729,1.11729 0 0 0 1.11729,1.1173 h 3.80684"
inkscape:connector-curvature="0" />
</g>
<path
inkscape:connector-curvature="0"
d="m 26.749066,38.056572 v -1.392163 a 0.29561735,0.29561735 0 0 1 0.295618,-0.295617 h 4.620152 a 0.29561735,0.29561735 0 0 1 0.29562,0.295617 v 1.392163"
style="fill:none;stroke:#333333;stroke-width:0.26458427;stroke-linecap:round;stroke-linejoin:round"
id="path102" />
<g
id="g194"
transform="matrix(0.26458426,0,0,0.26458426,-13.778451,3.9901329)">
<line
id="line4"
style="fill:none;stroke:#a8a8a8;stroke-linecap:round;stroke-linejoin:round"
y2="128.75461"
x2="201.02985"
y1="122.37562"
x1="201.02985" />
<path
id="path104"
style="fill:none;stroke:#333333;stroke-linecap:round;stroke-linejoin:round"
transform="translate(-14.81094,-16.5)"
d="m 196.14428,145.25461 v -5.2617 a 1.11729,1.11729 0 0 1 1.11729,-1.11729 h 2.68954 a 1.11729,1.11729 0 0 1 1.1173,1.11729 v 4.1444 a 1.11729,1.11729 0 0 0 1.11729,1.1173 h 2.68954 a 1.1173,1.1173 0 0 0 1.1173,-1.1173 v -4.1444 a 1.11729,1.11729 0 0 1 1.11729,-1.11729 h 2.68954 a 1.11729,1.11729 0 0 1 1.1173,1.11729 v 4.1444 a 1.11729,1.11729 0 0 0 1.11729,1.1173 h 3.80684"
inkscape:connector-curvature="0" />
</g>
<g
id="g140"
transform="matrix(0.26458426,0,0,0.26458426,-3.8684196,9.9159005)">
<g
id="g14">
<line
id="line8"
style="fill:none;stroke:#a8a8a8;stroke-linecap:round;stroke-linejoin:round"
y2="6.8789902"
x2="136.35571"
y1="0.5"
x1="136.35571" />
<line
id="line10"
style="fill:none;stroke:#a8a8a8;stroke-linecap:round;stroke-linejoin:round"
y2="6.8789902"
x2="149.48431"
y1="0.5"
x1="149.48431" />
<line
id="line12"
style="fill:none;stroke:#a8a8a8;stroke-linecap:round;stroke-linejoin:round"
y2="6.8789902"
x2="142.92365"
y1="0.5"
x1="142.92365" />
</g>
<path
id="path32"
style="fill:none;stroke:#333333;stroke-linecap:round;stroke-linejoin:round"
transform="translate(-14.81094,-16.5)"
d="m 144.59874,23.379 v 0 -5.2617 A 1.1173,1.1173 0 0 1 145.716,17 h 1.04816 a 1.1173,1.1173 0 0 1 1.1173,1.1173 v 4.14439 a 1.11729,1.11729 0 0 0 1.11729,1.1173 h 15.29647"
inkscape:connector-curvature="0" />
</g>
<g
id="g126"
transform="matrix(0.26458426,0,0,0.26458426,36.52507,36.236497)">
<g
id="g40">
<line
id="line34"
style="fill:none;stroke:#a8a8a8;stroke-linecap:round;stroke-linejoin:round"
y2="6.8789902"
x2="39.228661"
y1="0.5"
x1="39.228661" />
<line
id="line36"
style="fill:none;stroke:#a8a8a8;stroke-linecap:round;stroke-linejoin:round"
y2="6.8789902"
x2="24.45627"
y1="0.5"
x1="24.45627" />
<line
id="line38"
style="fill:none;stroke:#a8a8a8;stroke-linecap:round;stroke-linejoin:round"
y2="6.8789902"
x2="19.532141"
y1="0.5"
x1="19.532141" />
</g>
<path
transform="translate(-14.81094,-16.5)"
id="path84"
style="fill:none;stroke:#333333;stroke-linecap:round;stroke-linejoin:round"
d="m 34.34308,23.379 h 8.731 a 1.11729,1.11729 0 0 0 1.11729,-1.1173 V 18.1173 A 1.1173,1.1173 0 0 1 45.30864,17 h 2.68954 a 1.11729,1.11729 0 0 1 1.11729,1.1173 v 4.14439 a 1.1173,1.1173 0 0 0 1.1173,1.1173 h 3.80683"
inkscape:connector-curvature="0" />
</g>
<g
id="g358"
transform="translate(-6.7638893,-11.032398)">
<g
id="g22-4"
transform="matrix(0.26458426,0,0,0.26458426,-2.729345,16.582661)">
<line
id="line16-2"
style="fill:none;stroke:#a8a8a8;stroke-linecap:round;stroke-linejoin:round"
y2="23.37899"
x2="179.22273"
y1="17"
x1="179.22273" />
<line
id="line18-9"
style="fill:none;stroke:#a8a8a8;stroke-linecap:round;stroke-linejoin:round"
y2="23.37899"
x2="198.91927"
y1="17"
x1="198.91927" />
<line
id="line20-1"
style="fill:none;stroke:#a8a8a8;stroke-linecap:round;stroke-linejoin:round"
y2="23.37899"
x2="192.3586"
y1="17"
x1="192.3586" />
</g>
<path
id="path108-6"
style="fill:none;stroke:#333333;stroke-width:0.26458427;stroke-linecap:round;stroke-linejoin:round"
d="m 44.690171,22.768376 h 1.432131 a 0.30500215,0.30500215 0 0 0 0.305,-0.304999 v -1.077784 a 0.30499951,0.30499951 0 0 1 0.304999,-0.305 h 0.258565 a 0.30499951,0.30499951 0 0 1 0.305,0.305 v 1.077781 a 0.30499951,0.30499951 0 0 0 0.304999,0.305 h 2.300695"
inkscape:connector-curvature="0" />
</g>
<g
id="g351"
transform="translate(-8.8805593,-11.032398)">
<g
id="g30-4"
transform="matrix(0.26458426,0,0,0.26458426,-2.729345,16.582661)">
<line
id="line24-4"
style="fill:none;stroke:#a8a8a8;stroke-linecap:round;stroke-linejoin:round"
y2="23.37899"
x2="215.2529"
y1="17"
x1="215.2529" />
<line
id="line26-9"
style="fill:none;stroke:#a8a8a8;stroke-linecap:round;stroke-linejoin:round"
y2="23.37899"
x2="221.82082"
y1="17"
x1="221.82082" />
<line
id="line28-5"
style="fill:none;stroke:#a8a8a8;stroke-linecap:round;stroke-linejoin:round"
y2="23.37899"
x2="234.94942"
y1="17"
x1="234.94942" />
</g>
<path
id="path110-3"
style="fill:none;stroke:#333333;stroke-width:0.26458427;stroke-linecap:round;stroke-linejoin:round"
d="m 54.223185,22.768376 h 3.169259 a 0.30499951,0.30499951 0 0 0 0.304999,-0.304999 v -1.077784 a 0.30499951,0.30499951 0 0 1 0.305,-0.305 h 0.258567 a 0.30499951,0.30499951 0 0 1 0.305,0.305 v 1.077781 a 0.30499951,0.30499951 0 0 0 0.304999,0.305 h 0.563565"
inkscape:connector-curvature="0" />
</g>
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:10.58337021px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458427"
x="7.3508749"
y="42.490799"
id="text245"><tspan
sodipodi:role="line"
id="tspan243"
x="7.3508749"
y="52.146332"
style="stroke-width:0.26458427" /></text>
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:2.82223225px;line-height:1.25;font-family:Sniglet;-inkscape-font-specification:'Sniglet, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458427"
x="3.1753566"
y="3.9635246"
id="text249"><tspan
sodipodi:role="line"
id="tspan247"
x="3.1753566"
y="3.9635246"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:2.82223225px;font-family:Sniglet;-inkscape-font-specification:'Sniglet, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;stroke-width:0.26458427">PULSES PER STEPS (PPS) - REQUIREMENTS</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:2.82223225px;line-height:1.25;font-family:Sniglet;-inkscape-font-specification:'Sniglet, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458427"
x="12.148573"
y="41.321423"
id="text249-5-3"><tspan
sodipodi:role="line"
id="tspan247-9-6"
x="12.148573"
y="41.321423"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:2.82223225px;font-family:Sniglet;-inkscape-font-specification:'Sniglet, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;stroke-width:0.26458427">ALL</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:2.82223225px;line-height:1.25;font-family:Sniglet;-inkscape-font-specification:'Sniglet, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458427"
x="19.675114"
y="8.6498356"
id="text249-5-8"><tspan
sodipodi:role="line"
id="tspan247-9-4"
x="19.675114"
y="8.6498356"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:2.82223225px;font-family:Sniglet;-inkscape-font-specification:'Sniglet, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;stroke-width:0.26458427">MULTIPLES OF 6</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:2.82223225px;line-height:1.25;font-family:Sniglet;-inkscape-font-specification:'Sniglet, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458427"
x="49.741993"
y="41.321423"
id="text249-5-3-8"><tspan
sodipodi:role="line"
id="tspan247-9-6-2"
x="49.741993"
y="41.321423"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:2.82223225px;font-family:Sniglet;-inkscape-font-specification:'Sniglet, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;stroke-width:0.26458427">ALL</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:2.82223225px;line-height:1.25;font-family:Sniglet;-inkscape-font-specification:'Sniglet, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458427"
x="27.327574"
y="41.332336"
id="text249-5-3-3"><tspan
sodipodi:role="line"
id="tspan247-9-6-7"
x="27.327574"
y="41.332336"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:2.82223225px;font-family:Sniglet;-inkscape-font-specification:'Sniglet, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;stroke-width:0.26458427">ALL</tspan></text>
<g
id="g198-7"
transform="matrix(0.26458426,0,0,0.26458426,-49.235941,-22.33047)">
<line
id="line6-2"
style="fill:none;stroke:#a8a8a8;stroke-linecap:round;stroke-linejoin:round"
y2="128.75461"
x2="236.47182"
y1="122.37562"
x1="236.47182" />
<path
id="path106-8"
style="fill:none;stroke:#333333;stroke-linecap:round;stroke-linejoin:round"
transform="translate(-14.81094,-16.5)"
d="m 231.58623,145.25461 v -5.2617 a 1.11729,1.11729 0 0 1 1.1173,-1.11729 h 1.04816 a 1.11729,1.11729 0 0 1 1.1173,1.11729 v 4.1444 a 1.11729,1.11729 0 0 0 1.11729,1.1173 h 1.04816 a 1.1173,1.1173 0 0 0 1.1173,-1.1173 v -4.1444 a 1.11729,1.11729 0 0 1 1.1173,-1.11729 h 1.04816 a 1.11729,1.11729 0 0 1 1.11729,1.11729 v 4.1444 a 1.1173,1.1173 0 0 0 1.1173,1.1173 H 243.6 a 1.1173,1.1173 0 0 0 1.1173,-1.1173 v -4.1444 a 1.11729,1.11729 0 0 1 1.11729,-1.11729 h 1.04816 A 1.11729,1.11729 0 0 1 248,139.99291 v 4.1444 a 1.1173,1.1173 0 0 0 1.1173,1.1173 h 2.16545"
inkscape:connector-curvature="0" />
</g>
<g
id="g1007"
transform="translate(-45.905405,-6.2726063)">
<line
id="line6-2-3"
style="fill:none;stroke:#a8a8a8;stroke-width:0.26458427;stroke-linecap:round;stroke-linejoin:round"
y2="18.029966"
x2="66.630135"
y1="16.342186"
x1="66.630135" />
<line
id="line6-2-3-3"
style="fill:none;stroke:#a8a8a8;stroke-width:0.26458427;stroke-linecap:round;stroke-linejoin:round"
y2="18.029966"
x2="61.418743"
y1="16.342186"
x1="61.418743" />
<path
sodipodi:nodetypes="ccccccccssccccccsc"
id="path106-8-9"
style="fill:none;stroke:#333333;stroke-width:0.26458427;stroke-linecap:round;stroke-linejoin:round"
d="m 61.418742,18.029966 c 2.141299,0 -0.951586,0 1.441511,0 0.163266,0 0.295619,-0.132353 0.29562,-0.295619 v -1.096544 c 0,-0.163266 0.132354,-0.295618 0.29562,-0.295617 h 0.277326 c 0.163265,0 0.295618,0.132352 0.295618,0.295617 v 1.096544 c 10e-7,0.163266 0.132354,0.295619 0.29562,0.295619 h 0.27734 c 0.163266,0 0.295619,-0.132353 0.29562,-0.295619 v -1.096544 c 0,-0.163265 0.132352,-0.295617 0.295617,-0.295617 h 0.277327 c 0.16326,6e-6 0.295606,0.132357 0.295606,0.295617 v 1.096544 c 10e-7,0.163266 0.132354,0.295619 0.29562,0.295619 h 0.572944"
inkscape:connector-curvature="0" />
</g>
<path
inkscape:connector-curvature="0"
d="m 49.106667,36.368793 v 1.392161 a 0.29562,0.29562 0 0 0 0.29562,0.295619 h 4.915769"
style="fill:none;stroke:#333333;stroke-width:0.26458427;stroke-linecap:round;stroke-linejoin:round"
id="path94-9" />
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:10.58337021px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458427"
x="5.1611581"
y="41.337326"
id="text1040"><tspan
sodipodi:role="line"
id="tspan1038"
x="5.1611581"
y="41.337326"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:2.82223225px;font-family:Sniglet;-inkscape-font-specification:'Sniglet, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;stroke-width:0.26458427">M4</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:10.58337021px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458427"
x="20.233774"
y="41.303471"
id="text1040-4"><tspan
sodipodi:role="line"
id="tspan1038-5"
x="20.233774"
y="41.303471"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:2.82223225px;font-family:Sniglet;-inkscape-font-specification:'Sniglet, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;stroke-width:0.26458427">M4</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:10.58337021px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458427"
x="38.53788"
y="41.303474"
id="text1040-8"><tspan
sodipodi:role="line"
id="tspan1038-4"
x="38.53788"
y="41.303474"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:2.82223225px;font-family:Sniglet;-inkscape-font-specification:'Sniglet, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;stroke-width:0.26458427">M4</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:10.58337021px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458427"
x="17.403894"
y="46.498486"
id="text1072"><tspan
sodipodi:role="line"
id="tspan1070"
x="17.403894"
y="46.498486"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:2.82223225px;font-family:Sniglet;-inkscape-font-specification:'Sniglet, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;stroke-width:0.26458427">M4: MULTIPLES OF 4</tspan></text>
</svg>

+ 1
- 1
vst2_bin/plugins/ImpromptuModular/res/dark/Clocked_dark.svg
File diff suppressed because it is too large
View File


+ 1
- 1
vst2_bin/plugins/ImpromptuModular/res/dark/GateSeq64_dark.svg
File diff suppressed because it is too large
View File


+ 1
- 1
vst2_bin/plugins/ImpromptuModular/res/dark/PhraseSeq16_dark.svg
File diff suppressed because it is too large
View File


+ 1
- 1
vst2_bin/plugins/ImpromptuModular/res/dark/PhraseSeq32_dark.svg
File diff suppressed because it is too large
View File


BIN
vst2_bin/plugins/ImpromptuModular/res/img/AdvancedGateDetails.jpg View File

Before After
Width: 340  |  Height: 282  |  Size: 28KB

BIN
vst2_bin/plugins/ImpromptuModular/res/img/Clocked.jpg View File

Before After
Width: 594  |  Height: 456  |  Size: 84KB Width: 594  |  Height: 456  |  Size: 83KB

BIN
vst2_bin/plugins/ImpromptuModular/res/img/GateSeq64.jpg View File

Before After
Width: 432  |  Height: 456  |  Size: 48KB Width: 432  |  Height: 456  |  Size: 53KB

BIN
vst2_bin/plugins/ImpromptuModular/res/img/PhraseSeq16.jpg View File

Before After
Width: 540  |  Height: 456  |  Size: 71KB Width: 539  |  Height: 456  |  Size: 73KB

BIN
vst2_bin/plugins/ImpromptuModular/res/img/PhraseSeq32.jpg View File

Before After
Width: 576  |  Height: 456  |  Size: 84KB Width: 576  |  Height: 456  |  Size: 87KB

+ 1
- 1
vst2_bin/plugins/ImpromptuModular/res/light/Clocked.svg
File diff suppressed because it is too large
View File


+ 0
- 931
vst2_bin/plugins/ImpromptuModular/res/light/EngTest1.svg
File diff suppressed because it is too large
View File


+ 1
- 1
vst2_bin/plugins/ImpromptuModular/res/light/GateSeq64.svg
File diff suppressed because it is too large
View File


+ 1
- 1
vst2_bin/plugins/ImpromptuModular/res/light/PhraseSeq16.svg
File diff suppressed because it is too large
View File


+ 1
- 1
vst2_bin/plugins/ImpromptuModular/res/light/PhraseSeq32.svg
File diff suppressed because it is too large
View File


Loading…
Cancel
Save