nimphea/boards/daisy_legio

Search:
Group by:
Source   Edit  

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  
Switch3 {.importcpp: "daisy::Switch3", header: "hid/switch3.h".} = object
Three-position switch 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
Source   Edit  
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)
Source   Edit  
proc pressed(this: var Encoder): bool {.importcpp: "#.Pressed()", ...raises: [],
                                        tags: [], forbids: [].}

Check if encoder button is currently pressed

Returns: true if button is pressed, false otherwise

Source   Edit  
proc processAllControls(this: var DaisyLegio) {.
    importcpp: "#.ProcessAllControls()", ...raises: [], tags: [], forbids: [].}

Update all controls (digital + analog)

Convenience function that calls both:

  • processDigitalControls()
  • processAnalogControls()
Source   Edit  
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)
Source   Edit  
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.)
Source   Edit  
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