Download free music MP3s on genuine quality, the world’s largest online music catalogue, powered by your scrobbles. Free listening, videos, photos, The world’s largest online music catalogue, powered by your scrobbles. Free listening, videos, photos, stats, charts, biographies and concerts. stats, charts, biographies and concerts.
Monday, October 28, 2024
Arduino Euclidean Gate Sequencer – Part 4
In this posts I describe a version of the Arduino Euclidean Gate Sequencer that implements the same user interface and most of the features of HAGIWO's brilliant build. Part 1 covered all the theory and main functions of the code. Part …
HAGIWO's screen is shown above and has the following features:
6 channel outputs each with 16 steps.
16 different Euclidean patterns, represented by polygons on the display.
Patterns can be offset by up to 16 steps.
Patterns can have the number of steps reduced or limited down to a single step.
Channels can be muted.
There is a random mode that sets all parameters in a random, but still musically useful, way.
The menus are navigated by means of a rotary encoder and switch and it expects an external clock signal, as it is designed for use in a Eurorack setup.
I'm not bothered about the random mode, but I want to implement a built-in clock source with its own tempo control, similar to that shown in Part 3.
The Code
HAGIWO's code is implemented in a single loop function, using IO interrupts with the Enc rotary encoder library, which means the encoder has to be on pins 2 and 3 on an Uno.
I want to keep my timer-driven GATE outputs and will be polling the encoder using the library from Matthias Hertel (https://github.com/mathertel/RotaryEncoder) which means essentially I'm going to be doing a re-implementation rather than a port of HAGIWO's code to my hardware.
I've reused all the user interface design elements though (apart from the random mode) even though the implementation has been tweaked slightly.
Once again I have two main threads of code:
A 10mS tick that handles the GATE outputs and timings subject to the current selected tempo. I will also be using this to poll the rotary encoder too.
The main Arduino Loop() function which will manage the user interface, update the display and adjust the sequencer parameters as required.
By keeping the GATE handling in an interrupt routine it will not be affected by the slower I2C updates of the display.
I did wonder about a third level - perhaps a multiple of the main tick, to give me a thread for the encoder and one for the GATES, but it seems to work fine using the same 10mS tick for both.
The core sequencer parameters are:
seqTempo - tempo in beats per minute.
seqPatterns[] - pattern number for each channel (gate).
seqOffsets[] - start offset for each channel.
seqMute[] - if TRUE then channel is muted.
seqLimits[] - end step for each channel.
seqCounter[] - current playing step for each channel.
Timer Functions:
timerSetup() - initialise the TimerOne library with a 10mS tick to call timerLoop().
timerLoop() - calls the sequencerLoop() and uiScan() each tick.
Sequencer Functions:
sequencerSetup() - sets up the first patterns, tempo and any sequencer parameters that aren't pre-initialised in code.
sequencerLoop() - uses tempoTickCnt to decide if a sequencer step is to be enacted or not and output the GATEs as required. Called from the timerLoop().
Gate Functions (all called from the sequencerLoop()):
gateSetup() - initialses the IO for GATE outputs.
gateClear() - turns off all GATEs.
gateOutput() - turns on all active GATEs.
gateUpdate() - works out which GATEs should be on for a particular step in a pattern using patternStepOn().
Pattern Functions:
patternStepOn() - returns TRUE if the indicated step of the current active pattern shows a GATE should be on for this channel.
patternRefresh() - caches the current active patterns for each channel, taking into account any seqOffset[] setting.
User Interface and Menu Functions:
uiSetup() - initialise the OLED display and encoder.
uiScan() - high speed elements of the interface, called from the timer interrupt and used to scan the encoder.
uiLoop() - lower speed elements of the interface, called from the Arduino's main loop and handles the updates to the sequencer parameters from the encoder by calling updateMenu().
uiRequest() - used to signal a display refresh is required. Used when something changes due to the encoder or the playing step display needs updating.
uiUpdate() - redraws the screen to represent the current state of the interface and sequencer parameters.
updateMenu() - handles the main menu logic using the encoder increment/decrement and switch functions.
The main menu logic from the updateMenu() function has to manage 7 selection states as follows:
State 0: channel update
State 1: pattern update
State 2: offset update
State 3: limit update
State 4: mute on/off
State 5: reset sequence back to starting step
State 6: tempo update
Most of the states will add the (positive or negative) increment to the appropriate sequencer parameter and bounds check the result.
The exceptions are mute and reset which act immediately - mute being a toggle, and reset simply setting all seqCounters[] back to 0.
uiLoop() will act on the encoder switch using it to choose between moving between menus and selecting the functionality of one of the menu items.
uiUpdate() is the function that closely mirrors HAGIWO's original as it has to reproduce the interface on the display. As already mentioned, I've not implemented a random mode, but I have added a tempo indication which I show by adding a blocked-out rectangle across the centre of the display with the current bpm tempo value shown.
I've also added code so that the currently selected menu item has a solid triangle when in parameter changing mode. This is mostly as HAGIWO's original used an independent switch, so the sub-menu mode was active as long as the switch was pressed. As I'm using a switched encoder, I want the switch to be a latched toggle between modes rather than only active when pressed.
There is one bug that I can't quite see why it happens. When showing the tempo there is a block of interference on the bottom right-hand corner. I'm using display.print() at the coordinate point indicated for the tempo text, so I'm not sure what is causing that. Once the tempo display disappears as the menu selection moves on, it goes away... I'll look into that at some point.
Apart from these changes, I believe the interface is working as to HAGIWO's original.
When I first started messing around with Euclidean rhythms and thinking about an encoder and screen-based interface, I did think I'd just pick up and use the code from HAGIWO's project pretty much "as is". But in the end it was just the interface design I reused and reimplemented everything else myself so I could tweak and adjust things in the way I wanted.
So I consider this is another wheel dutifully reinvented here.
As always though, major thanks to HAGIWO for the project that inspired this one and for publishing the designs and code online for others to learn from.
No comments:
Post a Comment