Sample accurate CV renderer https://auti.sm/cv-engine
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
2026-09-15 12:38:29 +10:00
.gitignore Create .gitignore 2026-07-23 22:18:45 +01:00
calibration.go feat: engine and modulation 2026-06-25 21:56:29 +10:00
command.go fix: rm base36 limit 2026-09-15 12:38:29 +10:00
command_test.go fix: rm base36 limit 2026-09-15 12:38:29 +10:00
engine.go fix: rm base36 limit 2026-09-15 12:38:29 +10:00
go.mod feat: engine and modulation 2026-06-25 21:56:29 +10:00
go.sum feat: engine and modulation 2026-06-25 21:56:29 +10:00
LICENSE Create LICENSE 2026-08-19 17:11:44 +10:00
modulation.go feat: engine and modulation 2026-06-25 21:56:29 +10:00
modulation_test.go feat: engine and modulation 2026-06-25 21:56:29 +10:00
portaudio.go feat: engine and modulation 2026-06-25 21:56:29 +10:00
README.md Update README.md 2026-08-17 13:50:18 +10:00

CV Engine is an open-source sample accurate CV renderer written in Go. It allows programmatic output of CV when using a DC-coupled audio card. It's only dependency is PortAudio. It has a bespoke detune algorithm spirtually aligned with an out of tune guitar, and has easy configuration of many EDO. It's purpose for existing is so that you can create wrappers for various live-coding environments, or any other sequencing software that speaks OSC, and connect it to your CV synthesisers.

Atm, we have wrappers for the following OSC compatible live-coding environments:


api

import cv "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)

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.

edo

engine.SetEDO(19)

detune

engine.SetDetune(10)

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.

modulation

engine.SetLFO(cv.LFOConfig{
    Channel:   0,
    Wave:      cv.Sine,
    Frequency: 0.5,
    Amplitude: 2,
    Offset:    0,
})

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.