nimphea/boards/daisy_patch

Search:
Group by:
Source   Edit  

nimphea_patch

Nim wrapper for Electro-Smith Daisy Patch development board.

The Daisy Patch is a complete Eurorack-format module featuring:

  • 4 CV/Knob inputs (with gate inputs)
  • OLED display (128x64)
  • Rotary encoder
  • MIDI I/O
  • Gate inputs/outputs
  • Audio I/O with AK4556 codec

Example:

import nimphea/boards/daisy_patch

var patch: DaisyPatch

proc audioCallback(input, output: ptr ptr cfloat, size: csize_t) {.cdecl.} =
  for i in 0..<size:
    output[0][i] = input[0][i]  # Passthrough
    output[1][i] = input[1][i]

proc main() =
  patch.init()
  patch.startAudio(audioCallback)
  
  while true:
    patch.processAllControls()
    let knob1 = patch.getKnobValue(CTRL_1)
    patch.delayMs(10)

Types

Ak4556 {.importcpp: "daisy::Ak4556", header: "per/ak4556.h".} = object
AK4556 codec (used by Patch for additional audio channels) Source   Edit  
AnalogControl {.importcpp: "daisy::AnalogControl", header: "hid/ctrl.h".} = object
Analog control (knob/CV input) wrapper Source   Edit  
DaisyPatch {.importcpp: "daisy::DaisyPatch", header: "daisy_patch.h".} = object
  seed*: DaisySeed           ## Underlying Seed board
  codec*: Ak4556             ## Secondary AK4556 codec
  encoder*: Encoder          ## Rotary encoder
  controls*: array[4, AnalogControl] ## Four CV/Knob controls
  gate_input*: array[2, GateIn] ## Two gate inputs
  midi*: MidiUartHandler     ## MIDI handler
  display*: OledDisplay128x64Spi ## OLED display
  gate_output*: GPIO         ## Gate output
Daisy Patch board handle Source   Edit  
GateIn {.importcpp: "daisy::GateIn", header: "hid/gatein.h".} = object
Gate input wrapper Source   Edit  
MidiUartHandler {.importcpp: "daisy::MidiHandler<daisy::MidiUartTransport>",
                  header: "hid/midi.h".} = object
MIDI UART handler Source   Edit  
MidiUartTransport {.importcpp: "daisy::MidiUartTransport", header: "hid/midi.h".} = object
MIDI UART transport Source   Edit  
PatchCtrl = enum
  CTRL_1 = 0,               ## Control 1
  CTRL_2 = 1,               ## Control 2
  CTRL_3 = 2,               ## Control 3
  CTRL_4 = 3,               ## Control 4
  CTRL_LAST = 4              ## Sentinel value
Control identifiers for CV/Knob inputs Source   Edit  
PatchGateInput = enum
  GATE_IN_1 = 0,            ## Gate input 1
  GATE_IN_2 = 1,            ## Gate input 2
  GATE_IN_LAST = 2           ## Sentinel value
Gate input identifiers Source   Edit  

Procs

proc audioBlockSize(this: var DaisyPatch): csize_t {.
    importcpp: "#.AudioBlockSize()", ...raises: [], tags: [], forbids: [].}

Get audio block size

Returns: Block size in samples per channel

Source   Edit  
proc audioCallbackRate(this: var DaisyPatch): cfloat {.
    importcpp: "#.AudioCallbackRate()", ...raises: [], tags: [], forbids: [].}

Get audio callback rate

Returns: Callback rate in Hz

Source   Edit  
proc audioSampleRate(this: var DaisyPatch): cfloat {.
    importcpp: "#.AudioSampleRate()", ...raises: [], tags: [], forbids: [].}

Get current audio sample rate

Returns: Sample rate in Hz

Source   Edit  
proc changeAudioCallback(this: var DaisyPatch; cb: AudioCallback) {.
    importcpp: "#.ChangeAudioCallback(#)", ...raises: [], tags: [], forbids: [].}

Switch to a different audio callback

Parameters:

  • cb - New audio callback function
Source   Edit  
proc delay(this: var DaisyPatch; milliseconds: int) {.inline, ...raises: [],
    tags: [], forbids: [].}
Delay execution (convenience wrapper) Source   Edit  
proc delayMs(this: var DaisyPatch; del: csize_t) {.importcpp: "#.DelayMs(#)",
    ...raises: [], tags: [], forbids: [].}

Wait for specified milliseconds

Parameters:

  • del - Delay time in milliseconds
Source   Edit  
proc displayControls(this: var DaisyPatch; invert: bool = true) {.
    importcpp: "#.DisplayControls(#)", ...raises: [], tags: [], forbids: [].}

Display control values on OLED

Helper function for debugging/visualization.

Parameters:

  • invert - Invert display colors
Source   Edit  
proc encoderFallingEdge(this: var DaisyPatch): bool {.
    importcpp: "#.encoder.FallingEdge()", ...raises: [], tags: [], forbids: [].}

Detect encoder button release (falling edge)

Returns: true if button was just released

Source   Edit  
proc encoderPressed(this: var DaisyPatch): bool {.
    importcpp: "#.encoder.Pressed()", ...raises: [], tags: [], forbids: [].}

Check if encoder button is currently pressed

Returns: true if button is pressed

Note: Call processDigitalControls() first to update state.

Source   Edit  
proc encoderRisingEdge(this: var DaisyPatch): bool {.
    importcpp: "#.encoder.RisingEdge()", ...raises: [], tags: [], forbids: [].}

Detect encoder button press (rising edge)

Returns: true if button was just pressed

Use case: Menu selection, mode switching

Example:

if patch.encoderRisingEdge():
  currentMenu = (currentMenu + 1) mod NUM_MENUS

Source   Edit  
proc encoderTimeHeldMs(this: var DaisyPatch): cfloat {.
    importcpp: "#.encoder.TimeHeldMs()", ...raises: [], tags: [], forbids: [].}

Get how long encoder button has been held

Returns: Time in milliseconds

Use case: Long-press detection

Example:

if patch.encoderPressed() and patch.encoderTimeHeldMs() > 1000.0:
  # Long press detected (>1 second)

Source   Edit  
proc fallingEdge(enc: var Encoder): bool {.importcpp: "#.FallingEdge()",
    header: "hid/encoder.h", ...raises: [], tags: [], forbids: [].}
Check if encoder button has falling edge Source   Edit  
proc gateInputState(this: var DaisyPatch; input: PatchGateInput): bool {.
    importcpp: "#.gate_input[#].State()", ...raises: [], tags: [], forbids: [].}

Get current state of gate input

Parameters:

  • input - Gate input identifier (GATE_IN_1 or GATE_IN_2)

Returns: true if gate is currently high

Source   Edit  
proc gateInputTrig(this: var DaisyPatch; input: PatchGateInput): bool {.
    importcpp: "#.gate_input[#].Trig()", ...raises: [], tags: [], forbids: [].}

Check if gate input has triggered (rising edge)

Parameters:

  • input - Gate input identifier (GATE_IN_1 or GATE_IN_2)

Returns: true if gate just went high

Example:

if patch.gateInputTrig(GATE_IN_1):
  # Gate 1 triggered

Source   Edit  
proc getKnobValue(this: var DaisyPatch; k: PatchCtrl): cfloat {.
    importcpp: "#.GetKnobValue(#)", ...raises: [], tags: [], forbids: [].}

Get value for a control knob

Parameters:

  • k - Control identifier (CTRL_1 to CTRL_4)

Returns: Value from 0.0 to 1.0

Source   Edit  
proc increment(enc: var Encoder): int32 {.importcpp: "#.Increment()",
    header: "hid/encoder.h", ...raises: [], tags: [], forbids: [].}
Get encoder increment since last call Source   Edit  
proc init(this: var DaisyPatch; boost: bool = false) {.importcpp: "#.Init(#)",
    ...raises: [], tags: [], forbids: [].}

Initialize the Daisy Patch board

Parameters:

  • boost - Enable CPU boost mode (480MHz vs 400MHz)
Source   Edit  
proc pressed(enc: var Encoder): bool {.importcpp: "#.Pressed()",
                                       header: "hid/encoder.h", ...raises: [],
                                       tags: [], forbids: [].}
Check if encoder button is pressed Source   Edit  
proc processAllControls(this: var DaisyPatch) {.
    importcpp: "#.ProcessAllControls()", ...raises: [], tags: [], forbids: [].}

Process both analog and digital controls

Convenience method. Call regularly (e.g., in main loop).

Source   Edit  
proc processAnalogControls(this: var DaisyPatch) {.
    importcpp: "#.ProcessAnalogControls()", ...raises: [], tags: [], forbids: [].}

Process analog control inputs

Call regularly (e.g., in main loop) for smooth control reads.

Source   Edit  
proc processDigitalControls(this: var DaisyPatch) {.
    importcpp: "#.ProcessDigitalControls()", ...raises: [], tags: [], forbids: [].}

Process digital control inputs (encoder)

Call regularly (e.g., in main loop).

Source   Edit  
proc risingEdge(enc: var Encoder): bool {.importcpp: "#.RisingEdge()",
    header: "hid/encoder.h", ...raises: [], tags: [], forbids: [].}
Check if encoder button has rising edge Source   Edit  
proc setAudioBlockSize(this: var DaisyPatch; size: csize_t) {.
    importcpp: "#.SetAudioBlockSize(#)", ...raises: [], tags: [], forbids: [].}

Set audio block size

Call before startAudio(). Defaults to 48 samples.

Parameters:

  • size - Block size in samples per channel
Source   Edit  
proc setAudioSampleRate(this: var DaisyPatch; samplerate: SampleRate) {.
    importcpp: "#.SetAudioSampleRate(#)", ...raises: [], tags: [], forbids: [].}

Set audio sample rate

Call before startAudio().

Parameters:

  • samplerate - Target sample rate
Source   Edit  
proc startAdc(this: var DaisyPatch) {.importcpp: "#.StartAdc()", ...raises: [],
                                      tags: [], forbids: [].}
Start analog-to-digital conversion for controls Source   Edit  
proc startAudio(this: var DaisyPatch; cb: AudioCallback) {.
    importcpp: "#.StartAudio(#)", ...raises: [], tags: [], forbids: [].}

Start audio processing with callback

Parameters:

  • cb - Audio callback function
Source   Edit  
proc stopAdc(this: var DaisyPatch) {.importcpp: "#.StopAdc()", ...raises: [],
                                     tags: [], forbids: [].}
Stop analog-to-digital conversion Source   Edit  
proc stopAudio(this: var DaisyPatch) {.importcpp: "#.StopAudio()", ...raises: [],
                                       tags: [], forbids: [].}
Stop audio processing Source   Edit  
proc timeHeldMs(enc: var Encoder): cfloat {.importcpp: "#.TimeHeldMs()",
    header: "hid/encoder.h", ...raises: [], tags: [], forbids: [].}
Get time encoder button has been held Source   Edit  
proc toggleGateOutput(this: var DaisyPatch) {.
    importcpp: "#.gate_output.Toggle()", ...raises: [], tags: [], forbids: [].}

Toggle gate output state

Switches between HIGH and LOW.

Source   Edit  
proc writeGateOutput(this: var DaisyPatch; state: bool) {.
    importcpp: "#.gate_output.Write(#)", ...raises: [], tags: [], forbids: [].}

Write to gate output

Parameters:

  • state - true = HIGH (gate on), false = LOW (gate off)

Voltage levels: LOW = 0V, HIGH = 3.3V (can drive gate inputs)

Example:

# Trigger gate output
patch.writeGateOutput(true)
patch.seed.delay(5)
patch.writeGateOutput(false)

Source   Edit