DaisyLegio
Wrapper for the Daisy Legio Eurorack module (Virt Iter Legio by Olivia Artz Modular + Noise Engineering).
Hardware Overview:
- Compact Eurorack utility module
- Audio I/O: Stereo in/out (24-bit, up to 96kHz)
- Controls: 1 encoder, 3 CV inputs (pitch + 2 knobs), 1 gate input
- Switches: 2 three-position switches
- LEDs: 2 RGB LEDs (PWM controlled)
Key Features:
- Encoder with button for menu navigation
- CV inputs for pitch and modulation
- Gate input for triggering
- Three-position switches for mode selection
Usage Example:
import nimphea import nimphea/boards/daisy_legio proc audioCallback(input, output: AudioBuffer, size: int) {.cdecl.} = for i in 0..<size: let inL = input[0][i] let inR = input[1][i] output[0][i] = inL output[1][i] = inR proc main() = var legio: DaisyLegio legio.init() legio.startAudio(audioCallback) while true: legio.processAllControls() # Read encoder let increment = legio.encoder.increment() let pressed = legio.encoder.pressed() # Read CV inputs let pitch = legio.getKnobValue(CONTROL_PITCH.cint) let knobTop = legio.getKnobValue(CONTROL_KNOB_TOP.cint) let knobBottom = legio.getKnobValue(CONTROL_KNOB_BOTTOM.cint) # Update LEDs legio.setLed(LED_LEFT.csize_t, pitch, 0.0, 0.0) legio.setLed(LED_RIGHT.csize_t, 0.0, knobTop, knobBottom) legio.updateLeds() legio.delayMs(10) when isMainModule: main()
Types
AnalogControl {.importcpp: "daisy::AnalogControl", header: "hid/ctrl.h".} = object
- Analog control (knob/CV input) wrapper Source Edit
DaisyLegio {.importcpp: "daisy::DaisyLegio".} = object seed*: DaisySeed ## Underlying Seed board encoder*: Encoder ## Rotary encoder with button gate*: GateIn ## Gate input leds*: array[2, RgbLed] ## 2 RGB LEDs (PWM controlled) controls*: array[3, AnalogControl] ## 3 CV inputs (pitch + 2 knobs) sw*: array[2, Switch3] ## 2 three-position switches
-
Daisy Legio board handle
Contains all hardware peripherals pre-configured for the Legio platform.
Source Edit Encoder {.importcpp: "daisy::Encoder", header: "hid/encoder.h".} = object
- Quadrature encoder with button Source Edit
LegioControl {.importcpp: "daisy::DaisyLegio::LEGIO_CONTROLS", size: 4.} = enum CONTROL_PITCH = 0, CONTROL_KNOB_TOP = 1, CONTROL_KNOB_BOTTOM = 2, CONTROL_LAST = 3
- Control input identifiers (3 CV inputs) Source Edit
LegioLed {.importcpp: "daisy::DaisyLegio::LEGIO_LEDS", size: 4.} = enum LED_LEFT = 0, LED_RIGHT = 1, LED_LAST = 2
- LED identifiers (2 RGB LEDs) Source Edit
LegioSwitch {.importcpp: "daisy::DaisyLegio::LEGIO_TOGGLE3", size: 4.} = enum SW_LEFT = 0, SW_RIGHT = 1, SW_LAST = 2
- Three-position switch identifiers (2 switches) Source Edit
Procs
proc audioBlockSize(this: var DaisyLegio): csize_t {. importcpp: "#.AudioBlockSize()", ...raises: [], tags: [], forbids: [].}
-
Get current audio callback block size
Returns: Samples per channel
Source Edit proc audioCallbackRate(this: var DaisyLegio): cfloat {. importcpp: "#.AudioCallbackRate()", ...raises: [], tags: [], forbids: [].}
-
Get audio callback rate in Hz
Returns: Callback rate (samplerate / blocksize)
Source Edit proc audioSampleRate(this: var DaisyLegio): cfloat {. importcpp: "#.AudioSampleRate()", ...raises: [], tags: [], forbids: [].}
-
Get current audio sample rate in Hz
Returns: Sample rate as float (e.g., 48000.0)
Source Edit proc delayMs(this: var DaisyLegio; del: csize_t) {.importcpp: "#.DelayMs(#)", ...raises: [], tags: [], forbids: [].}
-
Delay for specified milliseconds
Parameters:
- del - Delay time in milliseconds
proc fallingEdge(this: var Encoder): bool {.importcpp: "#.FallingEdge()", ...raises: [], tags: [], forbids: [].}
-
Check if encoder button was just released (falling edge)
Returns: true on the first frame of button release, false otherwise
Source Edit proc gate(this: var DaisyLegio): bool {.importcpp: "#.Gate()", ...raises: [], tags: [], forbids: [].}
-
Read gate input state
Returns: true if gate is HIGH, false if LOW
Source Edit proc getKnobValue(this: var DaisyLegio; idx: cint): cfloat {. importcpp: "#.GetKnobValue(#)", ...raises: [], tags: [], forbids: [].}
-
Read normalized CV input value
Parameters:
- idx - Control index (CONTROL_PITCH, CONTROL_KNOB_TOP, CONTROL_KNOB_BOTTOM)
Returns: Normalized value (0.0 to 1.0)
Note: Call processAnalogControls() first to update values.
Example:
let pitch = legio.getKnobValue(CONTROL_PITCH.cint) let knobTop = legio.getKnobValue(CONTROL_KNOB_TOP.cint)
Source Edit proc increment(this: var Encoder): cint {.importcpp: "#.Increment()", ...raises: [], tags: [], forbids: [].}
-
Read encoder rotation since last call
Returns: Number of clicks (positive = clockwise, negative = counter-clockwise)
Example:
let delta = legio.encoder.increment() if delta > 0: echo "Rotated clockwise" elif delta < 0: echo "Rotated counter-clockwise"
Source Edit proc init(this: var DaisyLegio; boost: bool = false) {.importcpp: "#.Init(#)", ...raises: [], tags: [], forbids: [].}
-
Initialize the Daisy Legio board
Configures all hardware peripherals:
- Audio codec (AK4556, 24-bit/48kHz)
- ADC inputs for CV
- Gate input
- Encoder
- RGB LEDs (PWM)
- Three-position switches
Parameters:
- boost - Enable CPU boost mode (400MHz, default: false for 480MHz)
proc processAllControls(this: var DaisyLegio) {. importcpp: "#.ProcessAllControls()", ...raises: [], tags: [], forbids: [].}
-
Update all controls (digital + analog)
Convenience function that calls both:
- processDigitalControls()
- processAnalogControls()
proc processAnalogControls(this: var DaisyLegio) {. importcpp: "#.ProcessAnalogControls()", ...raises: [], tags: [], forbids: [].}
-
Normalize CV inputs to range (0.0, 1.0)
Call once per main loop iteration.
Source Edit proc processDigitalControls(this: var DaisyLegio) {. importcpp: "#.ProcessDigitalControls()", ...raises: [], tags: [], forbids: [].}
-
Update encoder and switch states
Call once per main loop iteration.
Source Edit proc read(this: var Switch3): cint {.importcpp: "#.Read()", ...raises: [], tags: [], forbids: [].}
-
Read three-position switch state
Returns:
- 0 - Down position
- 1 - Center position
- 2 - Up position
Example:
let swPos = legio.sw[0].read() case swPos of 0: echo "Down" of 1: echo "Center" of 2: echo "Up" else: discard
Source Edit proc risingEdge(this: var Encoder): bool {.importcpp: "#.RisingEdge()", ...raises: [], tags: [], forbids: [].}
-
Check if encoder button was just pressed (rising edge)
Returns: true on the first frame of button press, false otherwise
Source Edit proc setAudioBlockSize(this: var DaisyLegio; size: csize_t) {. importcpp: "#.SetAudioBlockSize(#)", ...raises: [], tags: [], forbids: [].}
-
Set audio callback block size (samples per channel)
Parameters:
- size - Number of samples per channel (default: 48)
proc setAudioSampleRate(this: var DaisyLegio; samplerate: SampleRate) {. importcpp: "#.SetAudioSampleRate(#)", ...raises: [], tags: [], forbids: [].}
-
Set audio sample rate (audio must be stopped first)
Parameters:
- samplerate - Sample rate (SAI_48KHZ, SAI_96KHZ, etc.)
proc setLed(this: var DaisyLegio; idx: csize_t; red: cfloat; green: cfloat; blue: cfloat) {.importcpp: "#.SetLed(#, #, #, #)", ...raises: [], tags: [], forbids: [].}
-
Set RGB LED color
Parameters:
- idx - LED index (LED_LEFT, LED_RIGHT)
- red - Red brightness (0.0 to 1.0)
- green - Green brightness (0.0 to 1.0)
- blue - Blue brightness (0.0 to 1.0)
Example:
legio.setLed(LED_LEFT.csize_t, 1.0, 0.0, 0.0) # Bright red
Source Edit proc startAdc(this: var DaisyLegio) {.importcpp: "#.StartAdc()", ...raises: [], tags: [], forbids: [].}
-
Start analog-to-digital conversion for CV inputs
Must be called to enable CV input reading.
Source Edit proc startAudio(this: var DaisyLegio; callback: AudioCallback) {....raises: [], tags: [], forbids: [].}
-
Start audio processing with callback
Parameters:
- callback - Audio callback function (non-interleaved stereo: proc(input, output: AudioBuffer, size: int))
Example:
proc audioCallback(input, output: AudioBuffer, size: int) {.cdecl.} = for i in 0..<size: let inL = input[0][i] let inR = input[1][i] output[0][i] = inL output[1][i] = inR legio.startAudio(audioCallback)
Source Edit proc stopAdc(this: var DaisyLegio) {.importcpp: "#.StopAdc()", ...raises: [], tags: [], forbids: [].}
- Stop analog-to-digital conversion Source Edit
proc stopAudio(this: var DaisyLegio) {.importcpp: "#.StopAudio()", ...raises: [], tags: [], forbids: [].}
- Stop audio processing Source Edit
proc updateExample(this: var DaisyLegio) {.importcpp: "#.UpdateExample()", ...raises: [], tags: [], forbids: [].}
-
Hardware test function
Tests all inputs and outputs:
- Each input (gate, encoder, switches, CV) changes LED colors
Call once per main loop for hardware verification.
Source Edit proc updateLeds(this: var DaisyLegio) {.importcpp: "#.UpdateLeds()", ...raises: [], tags: [], forbids: [].}
-
Update LED PWM state
Must be called after setLed() to apply changes. Call once per main loop iteration.
Source Edit