No description
Find a file
2026-08-01 13:33:32 +03:00
.gitignore Create .gitignore 2026-07-23 22:18:45 +01:00
calibration.go wip 2026-06-25 22:15:01 +10:00
command.go add voltage 2026-08-01 13:33:32 +03:00
engine.go add voltage 2026-08-01 13:33:32 +03:00
go.mod wip 2026-06-25 21:56:29 +10:00
go.sum wip 2026-06-25 21:56:29 +10:00
modulation.go wip 2026-06-25 22:15:01 +10:00
modulation_test.go wip 2026-06-25 21:56:29 +10:00
portaudio.go wip 2026-06-25 21:56:29 +10:00
README.md wip 2026-06-25 21:56:29 +10:00

cv-engine

sample-accurate CV renderer.

holds pitch. holds gate. retriggers. slides. lfos. envelopes.

go get source.source.auti.sm/australian-tactile-instruments/cv-engine

protocol-agnostic. no midi. no gui. frontends handle OSC, address parsing, arg layout.

api

import cv "source.source.auti.sm/australian-tactile-instruments/cv-engine"

engine, _ := cv.New(cv.Config{
    Channels:    16,
    Calibration: []cv.Calibration{{MinVolts: -10, MaxVolts: 10}},
})

out, _ := cv.OpenOutput(engine, cv.PortAudioConfig{
    DeviceName:      "ES-8",
    FramesPerBuffer: 32,
})
out.Start()
engine.RenderInterleaved(buffer) // portaudio callback

commands

engine.Trigger(cv.Command{
    Kind:    cv.PitchCommand,
    Channel: 0,
    Note:    7,
    Octave:  4,
    Slide:   50 * time.Millisecond,
})

engine.Trigger(cv.Command{
    Kind:     cv.GateCommand,
    Channel:  1,
    Gate:     true,
    Duration: 100 * time.Millisecond,
})

engine.Trigger(cv.Command{
    Kind:     cv.TriggerCommand,
    Channel:  8,
    Gate:     true,
    Duration: 5 * time.Millisecond,
})

pitch holds until next pitch. gate retriggers on overlap. trigger fires once.

nudge delays by sample frames. queued. applied inside RenderInterleaved.

edo

engine.SetEDO(19)     // 035. 0 = 12.

pitch voltage: note / EDO.

detune

engine.SetDetune(10)  // 035. 0 = neutral.

six virtual strings. fixed cent offsets. scaled by detune.

0-4     -16
5-8      +9
9-13     -5
14-18   +18
19-22   -11
23+      +6

deterministic. not chorus, not vibrato, not random.

pitch voltage: note / EDO + cents / 1200.

modulation

engine.SetLFO(cv.LFOConfig{
    Channel:   0,
    Wave:      cv.Sine,     // Sine, Triangle, Saw, ReverseSaw, Square
    Frequency: 0.5,         // Hz
    Amplitude: 2,           // volts peak
    Offset:    0,           // volts DC
})

engine.SetADSR(cv.ADSRConfig{
    Channel:      1,
    AttackTime:   10 * time.Millisecond,
    DecayTime:    50 * time.Millisecond,
    SustainLevel: 0.7,
    ReleaseTime:  200 * time.Millisecond,
    Amplitude:    5,
})

lfo runs continuously. adsr triggered by gate — gate-on fires attack, gate-off fires release. summed into channel voltage alongside pitch and gate.

ClearLFO(channel) and ClearADSR(channel) remove modulation.

deps

portaudio