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
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
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
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 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)
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
proc setAudioSampleRate(this: var DaisyPatch; samplerate: SampleRate) {. importcpp: "#.SetAudioSampleRate(#)", ...raises: [], tags: [], forbids: [].}
-
Set audio sample rate
Call before startAudio().
Parameters:
- samplerate - Target sample rate
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
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