mirror of
https://github.com/vsariola/sointu.git
synced 2025-05-28 03:10:24 -04:00
style: group types into fewer, logical files
This commit is contained in:
parent
0187cc66ec
commit
e4a2ed9f32
42
audio.go
42
audio.go
@ -7,28 +7,30 @@ import (
|
|||||||
"math"
|
"math"
|
||||||
)
|
)
|
||||||
|
|
||||||
// AudioBuffer is a buffer of stereo audio samples of variable length, each
|
type (
|
||||||
// sample represented by a slice of [2]float32. [0] is left channel, [1] is
|
// AudioBuffer is a buffer of stereo audio samples of variable length, each
|
||||||
// right
|
// sample represented by a slice of [2]float32. [0] is left channel, [1] is
|
||||||
type AudioBuffer [][2]float32
|
// right
|
||||||
|
AudioBuffer [][2]float32
|
||||||
|
|
||||||
// AudioOutput represents something where we can send audio e.g. audio output.
|
// AudioOutput represents something where we can send audio e.g. audio output.
|
||||||
// WriteAudio should block if not ready to accept audio e.g. buffer full.
|
// WriteAudio should block if not ready to accept audio e.g. buffer full.
|
||||||
type AudioOutput interface {
|
AudioOutput interface {
|
||||||
WriteAudio(buffer AudioBuffer) error
|
WriteAudio(buffer AudioBuffer) error
|
||||||
Close() error
|
Close() error
|
||||||
}
|
}
|
||||||
|
|
||||||
// AudioContext represents the low-level audio drivers. There should be at most
|
// AudioContext represents the low-level audio drivers. There should be at most
|
||||||
// one AudioContext at a time. The interface is implemented at least by
|
// one AudioContext at a time. The interface is implemented at least by
|
||||||
// oto.OtoContext, but in future we could also mock it.
|
// oto.OtoContext, but in future we could also mock it.
|
||||||
//
|
//
|
||||||
// AudioContext is used to create one or more AudioOutputs with Output(); each
|
// AudioContext is used to create one or more AudioOutputs with Output(); each
|
||||||
// can be used to output separate sound & closed when done.
|
// can be used to output separate sound & closed when done.
|
||||||
type AudioContext interface {
|
AudioContext interface {
|
||||||
Output() AudioOutput
|
Output() AudioOutput
|
||||||
Close() error
|
Close() error
|
||||||
}
|
}
|
||||||
|
)
|
||||||
|
|
||||||
// Wav converts a stereo signal of 32-bit floats (L R L R..., length should be
|
// Wav converts a stereo signal of 32-bit floats (L R L R..., length should be
|
||||||
// divisible by 2) into a valid WAV-file, returned as a []byte array.
|
// divisible by 2) into a valid WAV-file, returned as a []byte array.
|
||||||
|
@ -1,18 +0,0 @@
|
|||||||
package sointu
|
|
||||||
|
|
||||||
// Instrument includes a list of units consisting of the instrument, and the number of polyphonic voices for this instrument
|
|
||||||
type Instrument struct {
|
|
||||||
Name string `yaml:",omitempty"`
|
|
||||||
Comment string `yaml:",omitempty"`
|
|
||||||
NumVoices int
|
|
||||||
Units []Unit
|
|
||||||
}
|
|
||||||
|
|
||||||
// Copy makes a deep copy of an Instrument
|
|
||||||
func (instr *Instrument) Copy() Instrument {
|
|
||||||
units := make([]Unit, len(instr.Units))
|
|
||||||
for i, u := range instr.Units {
|
|
||||||
units[i] = u.Copy()
|
|
||||||
}
|
|
||||||
return Instrument{Name: instr.Name, Comment: instr.Comment, NumVoices: instr.NumVoices, Units: units}
|
|
||||||
}
|
|
23
order.go
23
order.go
@ -1,23 +0,0 @@
|
|||||||
package sointu
|
|
||||||
|
|
||||||
// Order is the pattern order for a track, in practice just a slice of integers,
|
|
||||||
// but provides convenience functions that return -1 values for indices out of
|
|
||||||
// bounds of the array, and functions to increase the size of the slice only by
|
|
||||||
// necessary amount when a new item is added, filling the unused slots with -1s.
|
|
||||||
type Order []int
|
|
||||||
|
|
||||||
// Get returns the value at index; or -1 is the index is out of range
|
|
||||||
func (s Order) Get(index int) int {
|
|
||||||
if index < 0 || index >= len(s) {
|
|
||||||
return -1
|
|
||||||
}
|
|
||||||
return s[index]
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set sets the value at index; appending -1s until the slice is long enough.
|
|
||||||
func (s *Order) Set(index, value int) {
|
|
||||||
for len(*s) <= index {
|
|
||||||
*s = append(*s, -1)
|
|
||||||
}
|
|
||||||
(*s)[index] = value
|
|
||||||
}
|
|
248
patch.go
248
patch.go
@ -6,8 +6,252 @@ import (
|
|||||||
"math"
|
"math"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Patch is simply a list of instruments used in a song
|
type (
|
||||||
type Patch []Instrument
|
// Patch is simply a list of instruments used in a song
|
||||||
|
Patch []Instrument
|
||||||
|
|
||||||
|
// Instrument includes a list of units consisting of the instrument, and the number of polyphonic voices for this instrument
|
||||||
|
Instrument struct {
|
||||||
|
Name string `yaml:",omitempty"`
|
||||||
|
Comment string `yaml:",omitempty"`
|
||||||
|
NumVoices int
|
||||||
|
Units []Unit
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unit is e.g. a filter, oscillator, envelope and its parameters
|
||||||
|
Unit struct {
|
||||||
|
// Type is the type of the unit, e.g. "add","oscillator" or "envelope".
|
||||||
|
// Always in lowercase. "" type should be ignored, no invalid types should
|
||||||
|
// be used.
|
||||||
|
Type string `yaml:",omitempty"`
|
||||||
|
|
||||||
|
// ID should be a unique ID for this unit, used by SEND units to target
|
||||||
|
// specific units. ID = 0 means that no ID has been given to a unit and thus
|
||||||
|
// cannot be targeted by SENDs. When possible, units that are not targeted
|
||||||
|
// by any SENDs should be cleaned from having IDs, e.g. to keep the exported
|
||||||
|
// data clean.
|
||||||
|
ID int `yaml:",omitempty"`
|
||||||
|
|
||||||
|
// Parameters is a map[string]int of parameters of a unit. For example, for
|
||||||
|
// an oscillator, unit.Type == "oscillator" and unit.Parameters["attack"]
|
||||||
|
// could be 64. Most parameters are either limites to 0 and 1 (e.g. stereo
|
||||||
|
// parameters) or between 0 and 128, inclusive.
|
||||||
|
Parameters map[string]int `yaml:",flow"`
|
||||||
|
|
||||||
|
// VarArgs is a list containing the variable number arguments that some
|
||||||
|
// units require, most notably the DELAY units. For example, for a DELAY
|
||||||
|
// unit, VarArgs is the delaytimes, in samples, of the different delaylines
|
||||||
|
// in the unit.
|
||||||
|
VarArgs []int `yaml:",flow,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnitParameter documents one parameter that an unit takes
|
||||||
|
UnitParameter struct {
|
||||||
|
Name string // thould be found with this name in the Unit.Parameters map
|
||||||
|
MinValue int // minimum value of the parameter, inclusive
|
||||||
|
MaxValue int // maximum value of the parameter, inclusive
|
||||||
|
CanSet bool // if this parameter can be set before hand i.e. through the gui
|
||||||
|
CanModulate bool // if this parameter can be modulated i.e. has a port number in "send" unit
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// UnitTypes documents all the available unit types and if they support stereo variant
|
||||||
|
// and what parameters they take.
|
||||||
|
var UnitTypes = map[string]([]UnitParameter){
|
||||||
|
"add": []UnitParameter{{Name: "stereo", MinValue: 0, MaxValue: 1, CanSet: true, CanModulate: false}},
|
||||||
|
"addp": []UnitParameter{{Name: "stereo", MinValue: 0, MaxValue: 1, CanSet: true, CanModulate: false}},
|
||||||
|
"pop": []UnitParameter{{Name: "stereo", MinValue: 0, MaxValue: 1, CanSet: true, CanModulate: false}},
|
||||||
|
"loadnote": []UnitParameter{{Name: "stereo", MinValue: 0, MaxValue: 1, CanSet: true, CanModulate: false}},
|
||||||
|
"mul": []UnitParameter{{Name: "stereo", MinValue: 0, MaxValue: 1, CanSet: true, CanModulate: false}},
|
||||||
|
"mulp": []UnitParameter{{Name: "stereo", MinValue: 0, MaxValue: 1, CanSet: true, CanModulate: false}},
|
||||||
|
"push": []UnitParameter{{Name: "stereo", MinValue: 0, MaxValue: 1, CanSet: true, CanModulate: false}},
|
||||||
|
"xch": []UnitParameter{{Name: "stereo", MinValue: 0, MaxValue: 1, CanSet: true, CanModulate: false}},
|
||||||
|
"distort": []UnitParameter{
|
||||||
|
{Name: "stereo", MinValue: 0, MaxValue: 1, CanSet: true, CanModulate: false},
|
||||||
|
{Name: "drive", MinValue: 0, MaxValue: 128, CanSet: true, CanModulate: true}},
|
||||||
|
"hold": []UnitParameter{
|
||||||
|
{Name: "stereo", MinValue: 0, MaxValue: 1, CanSet: true, CanModulate: false},
|
||||||
|
{Name: "holdfreq", MinValue: 0, MaxValue: 128, CanSet: true, CanModulate: true}},
|
||||||
|
"crush": []UnitParameter{
|
||||||
|
{Name: "stereo", MinValue: 0, MaxValue: 1, CanSet: true, CanModulate: false},
|
||||||
|
{Name: "resolution", MinValue: 0, MaxValue: 128, CanSet: true, CanModulate: true}},
|
||||||
|
"gain": []UnitParameter{
|
||||||
|
{Name: "stereo", MinValue: 0, MaxValue: 1, CanSet: true, CanModulate: false},
|
||||||
|
{Name: "gain", MinValue: 0, MaxValue: 128, CanSet: true, CanModulate: true}},
|
||||||
|
"invgain": []UnitParameter{
|
||||||
|
{Name: "stereo", MinValue: 0, MaxValue: 1, CanSet: true, CanModulate: false},
|
||||||
|
{Name: "invgain", MinValue: 0, MaxValue: 128, CanSet: true, CanModulate: true}},
|
||||||
|
"filter": []UnitParameter{
|
||||||
|
{Name: "stereo", MinValue: 0, MaxValue: 1, CanSet: true, CanModulate: false},
|
||||||
|
{Name: "frequency", MinValue: 0, MaxValue: 128, CanSet: true, CanModulate: true},
|
||||||
|
{Name: "resonance", MinValue: 0, MaxValue: 128, CanSet: true, CanModulate: true},
|
||||||
|
{Name: "lowpass", MinValue: 0, MaxValue: 1, CanSet: true, CanModulate: false},
|
||||||
|
{Name: "bandpass", MinValue: 0, MaxValue: 1, CanSet: true, CanModulate: false},
|
||||||
|
{Name: "highpass", MinValue: 0, MaxValue: 1, CanSet: true, CanModulate: false},
|
||||||
|
{Name: "negbandpass", MinValue: 0, MaxValue: 1, CanSet: true, CanModulate: false},
|
||||||
|
{Name: "neghighpass", MinValue: 0, MaxValue: 1, CanSet: true, CanModulate: false}},
|
||||||
|
"clip": []UnitParameter{{Name: "stereo", MinValue: 0, MaxValue: 1, CanSet: true, CanModulate: false}},
|
||||||
|
"pan": []UnitParameter{
|
||||||
|
{Name: "stereo", MinValue: 0, MaxValue: 1, CanSet: true, CanModulate: false},
|
||||||
|
{Name: "panning", MinValue: 0, MaxValue: 128, CanSet: true, CanModulate: true}},
|
||||||
|
"delay": []UnitParameter{
|
||||||
|
{Name: "stereo", MinValue: 0, MaxValue: 1, CanSet: true, CanModulate: false},
|
||||||
|
{Name: "pregain", MinValue: 0, MaxValue: 128, CanSet: true, CanModulate: true},
|
||||||
|
{Name: "dry", MinValue: 0, MaxValue: 128, CanSet: true, CanModulate: true},
|
||||||
|
{Name: "feedback", MinValue: 0, MaxValue: 128, CanSet: true, CanModulate: true},
|
||||||
|
{Name: "damp", MinValue: 0, MaxValue: 128, CanSet: true, CanModulate: true},
|
||||||
|
{Name: "notetracking", MinValue: 0, MaxValue: 2, CanSet: true, CanModulate: false},
|
||||||
|
{Name: "delaytime", MinValue: 0, MaxValue: -1, CanSet: false, CanModulate: true}},
|
||||||
|
"compressor": []UnitParameter{
|
||||||
|
{Name: "stereo", MinValue: 0, MaxValue: 1, CanSet: true, CanModulate: false},
|
||||||
|
{Name: "attack", MinValue: 0, MaxValue: 128, CanSet: true, CanModulate: true},
|
||||||
|
{Name: "release", MinValue: 0, MaxValue: 128, CanSet: true, CanModulate: true},
|
||||||
|
{Name: "invgain", MinValue: 0, MaxValue: 128, CanSet: true, CanModulate: true},
|
||||||
|
{Name: "threshold", MinValue: 0, MaxValue: 128, CanSet: true, CanModulate: true},
|
||||||
|
{Name: "ratio", MinValue: 0, MaxValue: 128, CanSet: true, CanModulate: true}},
|
||||||
|
"speed": []UnitParameter{},
|
||||||
|
"out": []UnitParameter{
|
||||||
|
{Name: "stereo", MinValue: 0, MaxValue: 1, CanSet: true, CanModulate: false},
|
||||||
|
{Name: "gain", MinValue: 0, MaxValue: 128, CanSet: true, CanModulate: true}},
|
||||||
|
"outaux": []UnitParameter{
|
||||||
|
{Name: "stereo", MinValue: 0, MaxValue: 1, CanSet: true, CanModulate: false},
|
||||||
|
{Name: "outgain", MinValue: 0, MaxValue: 128, CanSet: true, CanModulate: true},
|
||||||
|
{Name: "auxgain", MinValue: 0, MaxValue: 128, CanSet: true, CanModulate: true}},
|
||||||
|
"aux": []UnitParameter{
|
||||||
|
{Name: "stereo", MinValue: 0, MaxValue: 1, CanSet: true, CanModulate: false},
|
||||||
|
{Name: "gain", MinValue: 0, MaxValue: 128, CanSet: true, CanModulate: true},
|
||||||
|
{Name: "channel", MinValue: 0, MaxValue: 6, CanSet: true, CanModulate: false}},
|
||||||
|
"send": []UnitParameter{
|
||||||
|
{Name: "stereo", MinValue: 0, MaxValue: 1, CanSet: true, CanModulate: false},
|
||||||
|
{Name: "amount", MinValue: 0, MaxValue: 128, CanSet: true, CanModulate: true},
|
||||||
|
{Name: "voice", MinValue: 0, MaxValue: 32, CanSet: true, CanModulate: false},
|
||||||
|
{Name: "target", MinValue: 0, MaxValue: math.MaxInt32, CanSet: true, CanModulate: false},
|
||||||
|
{Name: "port", MinValue: 0, MaxValue: 7, CanSet: true, CanModulate: false},
|
||||||
|
{Name: "sendpop", MinValue: 0, MaxValue: 1, CanSet: true, CanModulate: false}},
|
||||||
|
"envelope": []UnitParameter{
|
||||||
|
{Name: "stereo", MinValue: 0, MaxValue: 1, CanSet: true, CanModulate: false},
|
||||||
|
{Name: "attack", MinValue: 0, MaxValue: 128, CanSet: true, CanModulate: true},
|
||||||
|
{Name: "decay", MinValue: 0, MaxValue: 128, CanSet: true, CanModulate: true},
|
||||||
|
{Name: "sustain", MinValue: 0, MaxValue: 128, CanSet: true, CanModulate: true},
|
||||||
|
{Name: "release", MinValue: 0, MaxValue: 128, CanSet: true, CanModulate: true},
|
||||||
|
{Name: "gain", MinValue: 0, MaxValue: 128, CanSet: true, CanModulate: true}},
|
||||||
|
"noise": []UnitParameter{
|
||||||
|
{Name: "stereo", MinValue: 0, MaxValue: 1, CanSet: true, CanModulate: false},
|
||||||
|
{Name: "shape", MinValue: 0, MaxValue: 128, CanSet: true, CanModulate: true},
|
||||||
|
{Name: "gain", MinValue: 0, MaxValue: 128, CanSet: true, CanModulate: true}},
|
||||||
|
"oscillator": []UnitParameter{
|
||||||
|
{Name: "stereo", MinValue: 0, MaxValue: 1, CanSet: true, CanModulate: false},
|
||||||
|
{Name: "transpose", MinValue: 0, MaxValue: 128, CanSet: true, CanModulate: true},
|
||||||
|
{Name: "detune", MinValue: 0, MaxValue: 128, CanSet: true, CanModulate: true},
|
||||||
|
{Name: "phase", MinValue: 0, MaxValue: 128, CanSet: true, CanModulate: true},
|
||||||
|
{Name: "color", MinValue: 0, MaxValue: 128, CanSet: true, CanModulate: true},
|
||||||
|
{Name: "shape", MinValue: 0, MaxValue: 128, CanSet: true, CanModulate: true},
|
||||||
|
{Name: "gain", MinValue: 0, MaxValue: 128, CanSet: true, CanModulate: true},
|
||||||
|
{Name: "frequency", MinValue: 0, MaxValue: -1, CanSet: false, CanModulate: true},
|
||||||
|
{Name: "type", MinValue: int(Sine), MaxValue: int(Sample), CanSet: true, CanModulate: false},
|
||||||
|
{Name: "lfo", MinValue: 0, MaxValue: 1, CanSet: true, CanModulate: false},
|
||||||
|
{Name: "unison", MinValue: 0, MaxValue: 3, CanSet: true, CanModulate: false},
|
||||||
|
{Name: "samplestart", MinValue: 0, MaxValue: 1720329, CanSet: true, CanModulate: false},
|
||||||
|
{Name: "loopstart", MinValue: 0, MaxValue: 65535, CanSet: true, CanModulate: false},
|
||||||
|
{Name: "looplength", MinValue: 0, MaxValue: 65535, CanSet: true, CanModulate: false}},
|
||||||
|
"loadval": []UnitParameter{
|
||||||
|
{Name: "stereo", MinValue: 0, MaxValue: 1, CanSet: true, CanModulate: false},
|
||||||
|
{Name: "value", MinValue: 0, MaxValue: 128, CanSet: true, CanModulate: true}},
|
||||||
|
"receive": []UnitParameter{
|
||||||
|
{Name: "stereo", MinValue: 0, MaxValue: 1, CanSet: true, CanModulate: false},
|
||||||
|
{Name: "left", MinValue: 0, MaxValue: -1, CanSet: false, CanModulate: true},
|
||||||
|
{Name: "right", MinValue: 0, MaxValue: -1, CanSet: false, CanModulate: true}},
|
||||||
|
"in": []UnitParameter{
|
||||||
|
{Name: "stereo", MinValue: 0, MaxValue: 1, CanSet: true, CanModulate: false},
|
||||||
|
{Name: "channel", MinValue: 0, MaxValue: 6, CanSet: true, CanModulate: false}},
|
||||||
|
"sync": []UnitParameter{},
|
||||||
|
}
|
||||||
|
|
||||||
|
// When unit.Type = "oscillator", its unit.Parameter["Type"] tells the type of
|
||||||
|
// the oscillator. There is five different oscillator types, so these consts
|
||||||
|
// just enumerate them.
|
||||||
|
const (
|
||||||
|
Sine = iota
|
||||||
|
Trisaw = iota
|
||||||
|
Pulse = iota
|
||||||
|
Gate = iota
|
||||||
|
Sample = iota
|
||||||
|
)
|
||||||
|
|
||||||
|
// Ports is static map allowing quickly finding the parameters of a unit that
|
||||||
|
// can be modulated. This is populated based on the UnitTypes list during
|
||||||
|
// init(). Thus, should be immutable, but Go not supporting that, then this will
|
||||||
|
// have to suffice: DO NOT EVER CHANGE THIS MAP.
|
||||||
|
var Ports = make(map[string]([]string))
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
for name, unitType := range UnitTypes {
|
||||||
|
unitPorts := make([]string, 0)
|
||||||
|
for _, param := range unitType {
|
||||||
|
if param.CanModulate {
|
||||||
|
unitPorts = append(unitPorts, param.Name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ports[name] = unitPorts
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy makes a deep copy of a unit.
|
||||||
|
func (u *Unit) Copy() Unit {
|
||||||
|
parameters := make(map[string]int)
|
||||||
|
for k, v := range u.Parameters {
|
||||||
|
parameters[k] = v
|
||||||
|
}
|
||||||
|
varArgs := make([]int, len(u.VarArgs))
|
||||||
|
copy(varArgs, u.VarArgs)
|
||||||
|
return Unit{Type: u.Type, Parameters: parameters, VarArgs: varArgs, ID: u.ID}
|
||||||
|
}
|
||||||
|
|
||||||
|
// StackChange returns how this unit will affect the signal stack. "pop" and
|
||||||
|
// "addp" and such will consume the topmost signal, and thus return -1 (or -2,
|
||||||
|
// if the unit is a stereo unit). On the other hand, "oscillator" and "envelope"
|
||||||
|
// will produce a signal, and thus return 1 (or 2, if the unit is a stereo
|
||||||
|
// unit). Effects that just change the topmost signal and will not change the
|
||||||
|
// number of signals on the stack and thus return 0.
|
||||||
|
func (u *Unit) StackChange() int {
|
||||||
|
switch u.Type {
|
||||||
|
case "addp", "mulp", "pop", "out", "outaux", "aux":
|
||||||
|
return -1 - u.Parameters["stereo"]
|
||||||
|
case "envelope", "oscillator", "push", "noise", "receive", "loadnote", "loadval", "in", "compressor":
|
||||||
|
return 1 + u.Parameters["stereo"]
|
||||||
|
case "pan":
|
||||||
|
return 1 - u.Parameters["stereo"]
|
||||||
|
case "speed":
|
||||||
|
return -1
|
||||||
|
case "send":
|
||||||
|
return (-1 - u.Parameters["stereo"]) * u.Parameters["sendpop"]
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// StackNeed returns the number of signals that should be on the stack before
|
||||||
|
// this unit is executed. Used to prevent stack underflow. Units producing
|
||||||
|
// signals do not care what is on the stack before and will return 0.
|
||||||
|
func (u *Unit) StackNeed() int {
|
||||||
|
switch u.Type {
|
||||||
|
case "", "envelope", "oscillator", "noise", "receive", "loadnote", "loadval", "in":
|
||||||
|
return 0
|
||||||
|
case "mulp", "mul", "add", "addp", "xch":
|
||||||
|
return 2 * (1 + u.Parameters["stereo"])
|
||||||
|
case "speed":
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
return 1 + u.Parameters["stereo"]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy makes a deep copy of an Instrument
|
||||||
|
func (instr *Instrument) Copy() Instrument {
|
||||||
|
units := make([]Unit, len(instr.Units))
|
||||||
|
for i, u := range instr.Units {
|
||||||
|
units[i] = u.Copy()
|
||||||
|
}
|
||||||
|
return Instrument{Name: instr.Name, Comment: instr.Comment, NumVoices: instr.NumVoices, Units: units}
|
||||||
|
}
|
||||||
|
|
||||||
// Copy makes a deep copy of a Patch.
|
// Copy makes a deep copy of a Patch.
|
||||||
func (p Patch) Copy() Patch {
|
func (p Patch) Copy() Patch {
|
||||||
|
23
pattern.go
23
pattern.go
@ -1,23 +0,0 @@
|
|||||||
package sointu
|
|
||||||
|
|
||||||
// Pattern represents a single pattern of note, in practice just a slice of bytes,
|
|
||||||
// but provides convenience functions that return 1 values (hold) for indices out of
|
|
||||||
// bounds of the array, and functions to increase the size of the slice only by
|
|
||||||
// necessary amount when a new item is added, filling the unused slots with 1s.
|
|
||||||
type Pattern []byte
|
|
||||||
|
|
||||||
// Get returns the value at index; or 1 is the index is out of range
|
|
||||||
func (s Pattern) Get(index int) byte {
|
|
||||||
if index < 0 || index >= len(s) {
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
return s[index]
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set sets the value at index; appending 1s until the slice is long enough.
|
|
||||||
func (s *Pattern) Set(index int, value byte) {
|
|
||||||
for len(*s) <= index {
|
|
||||||
*s = append(*s, 1)
|
|
||||||
}
|
|
||||||
(*s)[index] = value
|
|
||||||
}
|
|
49
score.go
49
score.go
@ -1,49 +0,0 @@
|
|||||||
package sointu
|
|
||||||
|
|
||||||
// Score represents the arrangement of notes in a song; just a list of tracks
|
|
||||||
// and RowsPerPattern and Length (in patterns) to know the desired length of a
|
|
||||||
// song in rows. If any of the tracks is too short, all the notes outside the
|
|
||||||
// range should be just considered as holding the last note.
|
|
||||||
type Score struct {
|
|
||||||
Tracks []Track
|
|
||||||
RowsPerPattern int // number of rows in each pattern
|
|
||||||
Length int // length of the song, in number of patterns
|
|
||||||
}
|
|
||||||
|
|
||||||
// Copy makes a deep copy of a Score.
|
|
||||||
func (l Score) Copy() Score {
|
|
||||||
tracks := make([]Track, len(l.Tracks))
|
|
||||||
for i, t := range l.Tracks {
|
|
||||||
tracks[i] = t.Copy()
|
|
||||||
}
|
|
||||||
return Score{Tracks: tracks, RowsPerPattern: l.RowsPerPattern, Length: l.Length}
|
|
||||||
}
|
|
||||||
|
|
||||||
// NumVoices returns the total number of voices used in the Score; summing the
|
|
||||||
// voices of every track
|
|
||||||
func (l Score) NumVoices() int {
|
|
||||||
ret := 0
|
|
||||||
for _, t := range l.Tracks {
|
|
||||||
ret += t.NumVoices
|
|
||||||
}
|
|
||||||
return ret
|
|
||||||
}
|
|
||||||
|
|
||||||
// FirstVoiceForTrack returns the index of the first voice of given track. For
|
|
||||||
// example, if the Score has three tracks (0, 1 and 2), with 1, 3, 2 voices,
|
|
||||||
// respectively, then FirstVoiceForTrack(0) returns 0, FirstVoiceForTrack(1)
|
|
||||||
// returns 1 and FirstVoiceForTrack(2) returns 4. Essentially computes just the
|
|
||||||
// cumulative sum.
|
|
||||||
func (l Score) FirstVoiceForTrack(track int) int {
|
|
||||||
ret := 0
|
|
||||||
for _, t := range l.Tracks[:track] {
|
|
||||||
ret += t.NumVoices
|
|
||||||
}
|
|
||||||
return ret
|
|
||||||
}
|
|
||||||
|
|
||||||
// LengthInRows returns just RowsPerPattern * Length, as the length is the
|
|
||||||
// length in the number of patterns.
|
|
||||||
func (l Score) LengthInRows() int {
|
|
||||||
return l.RowsPerPattern * l.Length
|
|
||||||
}
|
|
162
song.go
162
song.go
@ -4,17 +4,157 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Song includes a Score(the arrangement of notes in the song in one or more
|
type (
|
||||||
// tracks) and a Patch (the list of one or more instruments). Additionally, BPM
|
// Song includes a Score (the arrangement of notes in the song in one or more
|
||||||
// and RowsPerBeat fields set how fast the song should be played. Currently, BPM
|
// tracks) and a Patch (the list of one or more instruments). Additionally,
|
||||||
// is an integer as it offers already quite much granularity for controlling the
|
// BPM and RowsPerBeat fields set how fast the song should be played.
|
||||||
// playback speed, but this could be changed to a floating point in future if
|
// Currently, BPM is an integer as it offers already quite much granularity
|
||||||
// finer adjustments are necessary.
|
// for controlling the playback speed, but this could be changed to a
|
||||||
type Song struct {
|
// floating point in future if finer adjustments are necessary.
|
||||||
BPM int
|
Song struct {
|
||||||
RowsPerBeat int
|
BPM int
|
||||||
Score Score
|
RowsPerBeat int
|
||||||
Patch Patch
|
Score Score
|
||||||
|
Patch Patch
|
||||||
|
}
|
||||||
|
|
||||||
|
// Score represents the arrangement of notes in a song; just a list of
|
||||||
|
// tracks and RowsPerPattern and Length (in patterns) to know the desired
|
||||||
|
// length of a song in rows. If any of the tracks is too short, all the
|
||||||
|
// notes outside the range should be just considered as holding the last
|
||||||
|
// note.
|
||||||
|
Score struct {
|
||||||
|
Tracks []Track
|
||||||
|
RowsPerPattern int // number of rows in each pattern
|
||||||
|
Length int // length of the song, in number of patterns
|
||||||
|
}
|
||||||
|
|
||||||
|
// Track represents the patterns and orderlist for each track. Note that
|
||||||
|
// each track has its own patterns, so one track cannot use another tracks
|
||||||
|
// patterns. This makes the data more intuitive to humans, as the reusing of
|
||||||
|
// patterns over tracks is a rather rare occurence. However, the compiler
|
||||||
|
// will put all the patterns in one global table (identical patterns only
|
||||||
|
// appearing once), to optimize the runtime code.
|
||||||
|
Track struct {
|
||||||
|
// NumVoices is the number of voices this track triggers, cycling through
|
||||||
|
// the voices. When this track triggers a new voice, the previous should be
|
||||||
|
// released.
|
||||||
|
NumVoices int
|
||||||
|
|
||||||
|
// Effect hints the GUI if this is more of an effect track than a note
|
||||||
|
// track: if true, e.g. the GUI can display the values as hexadecimals
|
||||||
|
// instead of note values.
|
||||||
|
Effect bool `yaml:",omitempty"`
|
||||||
|
|
||||||
|
// Order is a list telling which pattern comes in which order in the song in
|
||||||
|
// this track.
|
||||||
|
Order Order `yaml:",flow"`
|
||||||
|
|
||||||
|
// Patterns is a list of Patterns for this track.
|
||||||
|
Patterns []Pattern `yaml:",flow"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pattern represents a single pattern of note, in practice just a slice of
|
||||||
|
// bytes, but provides convenience functions that return 1 values (hold) for
|
||||||
|
// indices out of bounds of the array, and functions to increase the size of
|
||||||
|
// the slice only by necessary amount when a new item is added, filling the
|
||||||
|
// unused slots with 1s.
|
||||||
|
Pattern []byte
|
||||||
|
|
||||||
|
// Order is the pattern order for a track, in practice just a slice of
|
||||||
|
// integers, but provides convenience functions that return -1 values for
|
||||||
|
// indices out of bounds of the array, and functions to increase the size of
|
||||||
|
// the slice only by necessary amount when a new item is added, filling the
|
||||||
|
// unused slots with -1s.
|
||||||
|
Order []int
|
||||||
|
)
|
||||||
|
|
||||||
|
// Get returns the value at index; or -1 is the index is out of range
|
||||||
|
func (s Order) Get(index int) int {
|
||||||
|
if index < 0 || index >= len(s) {
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
return s[index]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set sets the value at index; appending -1s until the slice is long enough.
|
||||||
|
func (s *Order) Set(index, value int) {
|
||||||
|
for len(*s) <= index {
|
||||||
|
*s = append(*s, -1)
|
||||||
|
}
|
||||||
|
(*s)[index] = value
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get returns the value at index; or 1 is the index is out of range
|
||||||
|
func (s Pattern) Get(index int) byte {
|
||||||
|
if index < 0 || index >= len(s) {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
return s[index]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set sets the value at index; appending 1s until the slice is long enough.
|
||||||
|
func (s *Pattern) Set(index int, value byte) {
|
||||||
|
for len(*s) <= index {
|
||||||
|
*s = append(*s, 1)
|
||||||
|
}
|
||||||
|
(*s)[index] = value
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy makes a deep copy of a Track.
|
||||||
|
func (t *Track) Copy() Track {
|
||||||
|
order := make([]int, len(t.Order))
|
||||||
|
copy(order, t.Order)
|
||||||
|
patterns := make([]Pattern, len(t.Patterns))
|
||||||
|
for i, oldPat := range t.Patterns {
|
||||||
|
newPat := make(Pattern, len(oldPat))
|
||||||
|
copy(newPat, oldPat)
|
||||||
|
patterns[i] = newPat
|
||||||
|
}
|
||||||
|
return Track{
|
||||||
|
NumVoices: t.NumVoices,
|
||||||
|
Effect: t.Effect,
|
||||||
|
Order: order,
|
||||||
|
Patterns: patterns,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy makes a deep copy of a Score.
|
||||||
|
func (l Score) Copy() Score {
|
||||||
|
tracks := make([]Track, len(l.Tracks))
|
||||||
|
for i, t := range l.Tracks {
|
||||||
|
tracks[i] = t.Copy()
|
||||||
|
}
|
||||||
|
return Score{Tracks: tracks, RowsPerPattern: l.RowsPerPattern, Length: l.Length}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NumVoices returns the total number of voices used in the Score; summing the
|
||||||
|
// voices of every track
|
||||||
|
func (l Score) NumVoices() int {
|
||||||
|
ret := 0
|
||||||
|
for _, t := range l.Tracks {
|
||||||
|
ret += t.NumVoices
|
||||||
|
}
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
// FirstVoiceForTrack returns the index of the first voice of given track. For
|
||||||
|
// example, if the Score has three tracks (0, 1 and 2), with 1, 3, 2 voices,
|
||||||
|
// respectively, then FirstVoiceForTrack(0) returns 0, FirstVoiceForTrack(1)
|
||||||
|
// returns 1 and FirstVoiceForTrack(2) returns 4. Essentially computes just the
|
||||||
|
// cumulative sum.
|
||||||
|
func (l Score) FirstVoiceForTrack(track int) int {
|
||||||
|
ret := 0
|
||||||
|
for _, t := range l.Tracks[:track] {
|
||||||
|
ret += t.NumVoices
|
||||||
|
}
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
// LengthInRows returns just RowsPerPattern * Length, as the length is the
|
||||||
|
// length in the number of patterns.
|
||||||
|
func (l Score) LengthInRows() int {
|
||||||
|
return l.RowsPerPattern * l.Length
|
||||||
}
|
}
|
||||||
|
|
||||||
// Copy makes a deep copy of a Score.
|
// Copy makes a deep copy of a Score.
|
||||||
|
56
synth.go
56
synth.go
@ -6,37 +6,39 @@ import (
|
|||||||
"math"
|
"math"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Synth represents a state of a synthesizer, compiled from a Patch.
|
type (
|
||||||
type Synth interface {
|
// Synth represents a state of a synthesizer, compiled from a Patch.
|
||||||
// Render tries to fill a stereo signal buffer with sound from the
|
Synth interface {
|
||||||
// synthesizer, until either the buffer is full or a given number of
|
// Render tries to fill a stereo signal buffer with sound from the
|
||||||
// timesteps is advanced. Normally, 1 sample = 1 unit of time, but speed
|
// synthesizer, until either the buffer is full or a given number of
|
||||||
// modulations may change this. It returns the number of samples filled (in
|
// timesteps is advanced. Normally, 1 sample = 1 unit of time, but speed
|
||||||
// stereo samples i.e. number of elements of AudioBuffer filled), the
|
// modulations may change this. It returns the number of samples filled (in
|
||||||
// number of sync outputs written, the number of time steps time advanced,
|
// stereo samples i.e. number of elements of AudioBuffer filled), the
|
||||||
// and a possible error.
|
// number of sync outputs written, the number of time steps time advanced,
|
||||||
Render(buffer AudioBuffer, maxtime int) (sample int, time int, err error)
|
// and a possible error.
|
||||||
|
Render(buffer AudioBuffer, maxtime int) (sample int, time int, err error)
|
||||||
|
|
||||||
// Update recompiles a patch, but should maintain as much as possible of its
|
// Update recompiles a patch, but should maintain as much as possible of its
|
||||||
// state as reasonable. For example, filters should keep their state and
|
// state as reasonable. For example, filters should keep their state and
|
||||||
// delaylines should keep their content. Every change in the Patch triggers
|
// delaylines should keep their content. Every change in the Patch triggers
|
||||||
// an Update and if the Patch would be started fresh every time, it would
|
// an Update and if the Patch would be started fresh every time, it would
|
||||||
// lead to very choppy audio.
|
// lead to very choppy audio.
|
||||||
Update(patch Patch, bpm int) error
|
Update(patch Patch, bpm int) error
|
||||||
|
|
||||||
// Trigger triggers a note for a given voice. Called between synth.Renders.
|
// Trigger triggers a note for a given voice. Called between synth.Renders.
|
||||||
Trigger(voice int, note byte)
|
Trigger(voice int, note byte)
|
||||||
|
|
||||||
// Release releases the currently playing note for a given voice. Called
|
// Release releases the currently playing note for a given voice. Called
|
||||||
// between synth.Renders.
|
// between synth.Renders.
|
||||||
Release(voice int)
|
Release(voice int)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SynthService compiles a given Patch into a Synth, throwing errors if the
|
// SynthService compiles a given Patch into a Synth, throwing errors if the
|
||||||
// Patch is malformed.
|
// Patch is malformed.
|
||||||
type SynthService interface {
|
SynthService interface {
|
||||||
Compile(patch Patch, bpm int) (Synth, error)
|
Compile(patch Patch, bpm int) (Synth, error)
|
||||||
}
|
}
|
||||||
|
)
|
||||||
|
|
||||||
// Render fills an stereo audio buffer using a Synth, disregarding all syncs and
|
// Render fills an stereo audio buffer using a Synth, disregarding all syncs and
|
||||||
// time limits.
|
// time limits.
|
||||||
|
44
track.go
44
track.go
@ -1,44 +0,0 @@
|
|||||||
package sointu
|
|
||||||
|
|
||||||
// Track represents the patterns and orderlist for each track. Note that each
|
|
||||||
// track has its own patterns, so one track cannot use another tracks patterns.
|
|
||||||
// This makes the data more intuitive to humans, as the reusing of patterns over
|
|
||||||
// tracks is a rather rare occurence. However, the compiler will put all the
|
|
||||||
// patterns in one global table (identical patterns only appearing once), to
|
|
||||||
// optimize the runtime code.
|
|
||||||
type Track struct {
|
|
||||||
// NumVoices is the number of voices this track triggers, cycling through
|
|
||||||
// the voices. When this track triggers a new voice, the previous should be
|
|
||||||
// released.
|
|
||||||
NumVoices int
|
|
||||||
|
|
||||||
// Effect hints the GUI if this is more of an effect track than a note
|
|
||||||
// track: if true, e.g. the GUI can display the values as hexadecimals
|
|
||||||
// instead of note values.
|
|
||||||
Effect bool `yaml:",omitempty"`
|
|
||||||
|
|
||||||
// Order is a list telling which pattern comes in which order in the song in
|
|
||||||
// this track.
|
|
||||||
Order Order `yaml:",flow"`
|
|
||||||
|
|
||||||
// Patterns is a list of Patterns for this track.
|
|
||||||
Patterns []Pattern `yaml:",flow"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// Copy makes a deep copy of a Track.
|
|
||||||
func (t *Track) Copy() Track {
|
|
||||||
order := make([]int, len(t.Order))
|
|
||||||
copy(order, t.Order)
|
|
||||||
patterns := make([]Pattern, len(t.Patterns))
|
|
||||||
for i, oldPat := range t.Patterns {
|
|
||||||
newPat := make(Pattern, len(oldPat))
|
|
||||||
copy(newPat, oldPat)
|
|
||||||
patterns[i] = newPat
|
|
||||||
}
|
|
||||||
return Track{
|
|
||||||
NumVoices: t.NumVoices,
|
|
||||||
Effect: t.Effect,
|
|
||||||
Order: order,
|
|
||||||
Patterns: patterns,
|
|
||||||
}
|
|
||||||
}
|
|
87
unit.go
87
unit.go
@ -1,87 +0,0 @@
|
|||||||
package sointu
|
|
||||||
|
|
||||||
// Unit is e.g. a filter, oscillator, envelope and its parameters
|
|
||||||
type Unit struct {
|
|
||||||
// Type is the type of the unit, e.g. "add","oscillator" or "envelope".
|
|
||||||
// Always in lowercase. "" type should be ignored, no invalid types should
|
|
||||||
// be used.
|
|
||||||
Type string `yaml:",omitempty"`
|
|
||||||
|
|
||||||
// ID should be a unique ID for this unit, used by SEND units to target
|
|
||||||
// specific units. ID = 0 means that no ID has been given to a unit and thus
|
|
||||||
// cannot be targeted by SENDs. When possible, units that are not targeted
|
|
||||||
// by any SENDs should be cleaned from having IDs, e.g. to keep the exported
|
|
||||||
// data clean.
|
|
||||||
ID int `yaml:",omitempty"`
|
|
||||||
|
|
||||||
// Parameters is a map[string]int of parameters of a unit. For example, for
|
|
||||||
// an oscillator, unit.Type == "oscillator" and unit.Parameters["attack"]
|
|
||||||
// could be 64. Most parameters are either limites to 0 and 1 (e.g. stereo
|
|
||||||
// parameters) or between 0 and 128, inclusive.
|
|
||||||
Parameters map[string]int `yaml:",flow"`
|
|
||||||
|
|
||||||
// VarArgs is a list containing the variable number arguments that some
|
|
||||||
// units require, most notably the DELAY units. For example, for a DELAY
|
|
||||||
// unit, VarArgs is the delaytimes, in samples, of the different delaylines
|
|
||||||
// in the unit.
|
|
||||||
VarArgs []int `yaml:",flow,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// When unit.Type = "oscillator", its unit.Parameter["Type"] tells the type of
|
|
||||||
// the oscillator. There is five different oscillator types, so these consts
|
|
||||||
// just enumerate them.
|
|
||||||
const (
|
|
||||||
Sine = iota
|
|
||||||
Trisaw = iota
|
|
||||||
Pulse = iota
|
|
||||||
Gate = iota
|
|
||||||
Sample = iota
|
|
||||||
)
|
|
||||||
|
|
||||||
// Copy makes a deep copy of a unit.
|
|
||||||
func (u *Unit) Copy() Unit {
|
|
||||||
parameters := make(map[string]int)
|
|
||||||
for k, v := range u.Parameters {
|
|
||||||
parameters[k] = v
|
|
||||||
}
|
|
||||||
varArgs := make([]int, len(u.VarArgs))
|
|
||||||
copy(varArgs, u.VarArgs)
|
|
||||||
return Unit{Type: u.Type, Parameters: parameters, VarArgs: varArgs, ID: u.ID}
|
|
||||||
}
|
|
||||||
|
|
||||||
// StackChange returns how this unit will affect the signal stack. "pop" and
|
|
||||||
// "addp" and such will consume the topmost signal, and thus return -1 (or -2,
|
|
||||||
// if the unit is a stereo unit). On the other hand, "oscillator" and "envelope"
|
|
||||||
// will produce a signal, and thus return 1 (or 2, if the unit is a stereo
|
|
||||||
// unit). Effects that just change the topmost signal and will not change the
|
|
||||||
// number of signals on the stack and thus return 0.
|
|
||||||
func (u *Unit) StackChange() int {
|
|
||||||
switch u.Type {
|
|
||||||
case "addp", "mulp", "pop", "out", "outaux", "aux":
|
|
||||||
return -1 - u.Parameters["stereo"]
|
|
||||||
case "envelope", "oscillator", "push", "noise", "receive", "loadnote", "loadval", "in", "compressor":
|
|
||||||
return 1 + u.Parameters["stereo"]
|
|
||||||
case "pan":
|
|
||||||
return 1 - u.Parameters["stereo"]
|
|
||||||
case "speed":
|
|
||||||
return -1
|
|
||||||
case "send":
|
|
||||||
return (-1 - u.Parameters["stereo"]) * u.Parameters["sendpop"]
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
// StackNeed returns the number of signals that should be on the stack before
|
|
||||||
// this unit is executed. Used to prevent stack underflow. Units producing
|
|
||||||
// signals do not care what is on the stack before and will return 0.
|
|
||||||
func (u *Unit) StackNeed() int {
|
|
||||||
switch u.Type {
|
|
||||||
case "", "envelope", "oscillator", "noise", "receive", "loadnote", "loadval", "in":
|
|
||||||
return 0
|
|
||||||
case "mulp", "mul", "add", "addp", "xch":
|
|
||||||
return 2 * (1 + u.Parameters["stereo"])
|
|
||||||
case "speed":
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
return 1 + u.Parameters["stereo"]
|
|
||||||
}
|
|
144
unittype.go
144
unittype.go
@ -1,144 +0,0 @@
|
|||||||
package sointu
|
|
||||||
|
|
||||||
import (
|
|
||||||
"math"
|
|
||||||
)
|
|
||||||
|
|
||||||
// UnitParameter documents one parameter that an unit takes
|
|
||||||
type UnitParameter struct {
|
|
||||||
Name string // thould be found with this name in the Unit.Parameters map
|
|
||||||
MinValue int // minimum value of the parameter, inclusive
|
|
||||||
MaxValue int // maximum value of the parameter, inclusive
|
|
||||||
CanSet bool // if this parameter can be set before hand i.e. through the gui
|
|
||||||
CanModulate bool // if this parameter can be modulated i.e. has a port number in "send" unit
|
|
||||||
}
|
|
||||||
|
|
||||||
// UnitTypes documents all the available unit types and if they support stereo variant
|
|
||||||
// and what parameters they take.
|
|
||||||
var UnitTypes = map[string]([]UnitParameter){
|
|
||||||
"add": []UnitParameter{{Name: "stereo", MinValue: 0, MaxValue: 1, CanSet: true, CanModulate: false}},
|
|
||||||
"addp": []UnitParameter{{Name: "stereo", MinValue: 0, MaxValue: 1, CanSet: true, CanModulate: false}},
|
|
||||||
"pop": []UnitParameter{{Name: "stereo", MinValue: 0, MaxValue: 1, CanSet: true, CanModulate: false}},
|
|
||||||
"loadnote": []UnitParameter{{Name: "stereo", MinValue: 0, MaxValue: 1, CanSet: true, CanModulate: false}},
|
|
||||||
"mul": []UnitParameter{{Name: "stereo", MinValue: 0, MaxValue: 1, CanSet: true, CanModulate: false}},
|
|
||||||
"mulp": []UnitParameter{{Name: "stereo", MinValue: 0, MaxValue: 1, CanSet: true, CanModulate: false}},
|
|
||||||
"push": []UnitParameter{{Name: "stereo", MinValue: 0, MaxValue: 1, CanSet: true, CanModulate: false}},
|
|
||||||
"xch": []UnitParameter{{Name: "stereo", MinValue: 0, MaxValue: 1, CanSet: true, CanModulate: false}},
|
|
||||||
"distort": []UnitParameter{
|
|
||||||
{Name: "stereo", MinValue: 0, MaxValue: 1, CanSet: true, CanModulate: false},
|
|
||||||
{Name: "drive", MinValue: 0, MaxValue: 128, CanSet: true, CanModulate: true}},
|
|
||||||
"hold": []UnitParameter{
|
|
||||||
{Name: "stereo", MinValue: 0, MaxValue: 1, CanSet: true, CanModulate: false},
|
|
||||||
{Name: "holdfreq", MinValue: 0, MaxValue: 128, CanSet: true, CanModulate: true}},
|
|
||||||
"crush": []UnitParameter{
|
|
||||||
{Name: "stereo", MinValue: 0, MaxValue: 1, CanSet: true, CanModulate: false},
|
|
||||||
{Name: "resolution", MinValue: 0, MaxValue: 128, CanSet: true, CanModulate: true}},
|
|
||||||
"gain": []UnitParameter{
|
|
||||||
{Name: "stereo", MinValue: 0, MaxValue: 1, CanSet: true, CanModulate: false},
|
|
||||||
{Name: "gain", MinValue: 0, MaxValue: 128, CanSet: true, CanModulate: true}},
|
|
||||||
"invgain": []UnitParameter{
|
|
||||||
{Name: "stereo", MinValue: 0, MaxValue: 1, CanSet: true, CanModulate: false},
|
|
||||||
{Name: "invgain", MinValue: 0, MaxValue: 128, CanSet: true, CanModulate: true}},
|
|
||||||
"filter": []UnitParameter{
|
|
||||||
{Name: "stereo", MinValue: 0, MaxValue: 1, CanSet: true, CanModulate: false},
|
|
||||||
{Name: "frequency", MinValue: 0, MaxValue: 128, CanSet: true, CanModulate: true},
|
|
||||||
{Name: "resonance", MinValue: 0, MaxValue: 128, CanSet: true, CanModulate: true},
|
|
||||||
{Name: "lowpass", MinValue: 0, MaxValue: 1, CanSet: true, CanModulate: false},
|
|
||||||
{Name: "bandpass", MinValue: 0, MaxValue: 1, CanSet: true, CanModulate: false},
|
|
||||||
{Name: "highpass", MinValue: 0, MaxValue: 1, CanSet: true, CanModulate: false},
|
|
||||||
{Name: "negbandpass", MinValue: 0, MaxValue: 1, CanSet: true, CanModulate: false},
|
|
||||||
{Name: "neghighpass", MinValue: 0, MaxValue: 1, CanSet: true, CanModulate: false}},
|
|
||||||
"clip": []UnitParameter{{Name: "stereo", MinValue: 0, MaxValue: 1, CanSet: true, CanModulate: false}},
|
|
||||||
"pan": []UnitParameter{
|
|
||||||
{Name: "stereo", MinValue: 0, MaxValue: 1, CanSet: true, CanModulate: false},
|
|
||||||
{Name: "panning", MinValue: 0, MaxValue: 128, CanSet: true, CanModulate: true}},
|
|
||||||
"delay": []UnitParameter{
|
|
||||||
{Name: "stereo", MinValue: 0, MaxValue: 1, CanSet: true, CanModulate: false},
|
|
||||||
{Name: "pregain", MinValue: 0, MaxValue: 128, CanSet: true, CanModulate: true},
|
|
||||||
{Name: "dry", MinValue: 0, MaxValue: 128, CanSet: true, CanModulate: true},
|
|
||||||
{Name: "feedback", MinValue: 0, MaxValue: 128, CanSet: true, CanModulate: true},
|
|
||||||
{Name: "damp", MinValue: 0, MaxValue: 128, CanSet: true, CanModulate: true},
|
|
||||||
{Name: "notetracking", MinValue: 0, MaxValue: 2, CanSet: true, CanModulate: false},
|
|
||||||
{Name: "delaytime", MinValue: 0, MaxValue: -1, CanSet: false, CanModulate: true}},
|
|
||||||
"compressor": []UnitParameter{
|
|
||||||
{Name: "stereo", MinValue: 0, MaxValue: 1, CanSet: true, CanModulate: false},
|
|
||||||
{Name: "attack", MinValue: 0, MaxValue: 128, CanSet: true, CanModulate: true},
|
|
||||||
{Name: "release", MinValue: 0, MaxValue: 128, CanSet: true, CanModulate: true},
|
|
||||||
{Name: "invgain", MinValue: 0, MaxValue: 128, CanSet: true, CanModulate: true},
|
|
||||||
{Name: "threshold", MinValue: 0, MaxValue: 128, CanSet: true, CanModulate: true},
|
|
||||||
{Name: "ratio", MinValue: 0, MaxValue: 128, CanSet: true, CanModulate: true}},
|
|
||||||
"speed": []UnitParameter{},
|
|
||||||
"out": []UnitParameter{
|
|
||||||
{Name: "stereo", MinValue: 0, MaxValue: 1, CanSet: true, CanModulate: false},
|
|
||||||
{Name: "gain", MinValue: 0, MaxValue: 128, CanSet: true, CanModulate: true}},
|
|
||||||
"outaux": []UnitParameter{
|
|
||||||
{Name: "stereo", MinValue: 0, MaxValue: 1, CanSet: true, CanModulate: false},
|
|
||||||
{Name: "outgain", MinValue: 0, MaxValue: 128, CanSet: true, CanModulate: true},
|
|
||||||
{Name: "auxgain", MinValue: 0, MaxValue: 128, CanSet: true, CanModulate: true}},
|
|
||||||
"aux": []UnitParameter{
|
|
||||||
{Name: "stereo", MinValue: 0, MaxValue: 1, CanSet: true, CanModulate: false},
|
|
||||||
{Name: "gain", MinValue: 0, MaxValue: 128, CanSet: true, CanModulate: true},
|
|
||||||
{Name: "channel", MinValue: 0, MaxValue: 6, CanSet: true, CanModulate: false}},
|
|
||||||
"send": []UnitParameter{
|
|
||||||
{Name: "stereo", MinValue: 0, MaxValue: 1, CanSet: true, CanModulate: false},
|
|
||||||
{Name: "amount", MinValue: 0, MaxValue: 128, CanSet: true, CanModulate: true},
|
|
||||||
{Name: "voice", MinValue: 0, MaxValue: 32, CanSet: true, CanModulate: false},
|
|
||||||
{Name: "target", MinValue: 0, MaxValue: math.MaxInt32, CanSet: true, CanModulate: false},
|
|
||||||
{Name: "port", MinValue: 0, MaxValue: 7, CanSet: true, CanModulate: false},
|
|
||||||
{Name: "sendpop", MinValue: 0, MaxValue: 1, CanSet: true, CanModulate: false}},
|
|
||||||
"envelope": []UnitParameter{
|
|
||||||
{Name: "stereo", MinValue: 0, MaxValue: 1, CanSet: true, CanModulate: false},
|
|
||||||
{Name: "attack", MinValue: 0, MaxValue: 128, CanSet: true, CanModulate: true},
|
|
||||||
{Name: "decay", MinValue: 0, MaxValue: 128, CanSet: true, CanModulate: true},
|
|
||||||
{Name: "sustain", MinValue: 0, MaxValue: 128, CanSet: true, CanModulate: true},
|
|
||||||
{Name: "release", MinValue: 0, MaxValue: 128, CanSet: true, CanModulate: true},
|
|
||||||
{Name: "gain", MinValue: 0, MaxValue: 128, CanSet: true, CanModulate: true}},
|
|
||||||
"noise": []UnitParameter{
|
|
||||||
{Name: "stereo", MinValue: 0, MaxValue: 1, CanSet: true, CanModulate: false},
|
|
||||||
{Name: "shape", MinValue: 0, MaxValue: 128, CanSet: true, CanModulate: true},
|
|
||||||
{Name: "gain", MinValue: 0, MaxValue: 128, CanSet: true, CanModulate: true}},
|
|
||||||
"oscillator": []UnitParameter{
|
|
||||||
{Name: "stereo", MinValue: 0, MaxValue: 1, CanSet: true, CanModulate: false},
|
|
||||||
{Name: "transpose", MinValue: 0, MaxValue: 128, CanSet: true, CanModulate: true},
|
|
||||||
{Name: "detune", MinValue: 0, MaxValue: 128, CanSet: true, CanModulate: true},
|
|
||||||
{Name: "phase", MinValue: 0, MaxValue: 128, CanSet: true, CanModulate: true},
|
|
||||||
{Name: "color", MinValue: 0, MaxValue: 128, CanSet: true, CanModulate: true},
|
|
||||||
{Name: "shape", MinValue: 0, MaxValue: 128, CanSet: true, CanModulate: true},
|
|
||||||
{Name: "gain", MinValue: 0, MaxValue: 128, CanSet: true, CanModulate: true},
|
|
||||||
{Name: "frequency", MinValue: 0, MaxValue: -1, CanSet: false, CanModulate: true},
|
|
||||||
{Name: "type", MinValue: int(Sine), MaxValue: int(Sample), CanSet: true, CanModulate: false},
|
|
||||||
{Name: "lfo", MinValue: 0, MaxValue: 1, CanSet: true, CanModulate: false},
|
|
||||||
{Name: "unison", MinValue: 0, MaxValue: 3, CanSet: true, CanModulate: false},
|
|
||||||
{Name: "samplestart", MinValue: 0, MaxValue: 1720329, CanSet: true, CanModulate: false},
|
|
||||||
{Name: "loopstart", MinValue: 0, MaxValue: 65535, CanSet: true, CanModulate: false},
|
|
||||||
{Name: "looplength", MinValue: 0, MaxValue: 65535, CanSet: true, CanModulate: false}},
|
|
||||||
"loadval": []UnitParameter{
|
|
||||||
{Name: "stereo", MinValue: 0, MaxValue: 1, CanSet: true, CanModulate: false},
|
|
||||||
{Name: "value", MinValue: 0, MaxValue: 128, CanSet: true, CanModulate: true}},
|
|
||||||
"receive": []UnitParameter{
|
|
||||||
{Name: "stereo", MinValue: 0, MaxValue: 1, CanSet: true, CanModulate: false},
|
|
||||||
{Name: "left", MinValue: 0, MaxValue: -1, CanSet: false, CanModulate: true},
|
|
||||||
{Name: "right", MinValue: 0, MaxValue: -1, CanSet: false, CanModulate: true}},
|
|
||||||
"in": []UnitParameter{
|
|
||||||
{Name: "stereo", MinValue: 0, MaxValue: 1, CanSet: true, CanModulate: false},
|
|
||||||
{Name: "channel", MinValue: 0, MaxValue: 6, CanSet: true, CanModulate: false}},
|
|
||||||
"sync": []UnitParameter{},
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ports is static map allowing quickly finding the parameters of a unit that
|
|
||||||
// can be modulated. This is populated based on the UnitTypes list during
|
|
||||||
// init(). Thus, should be immutable, but Go not supporting that, then this will
|
|
||||||
// have to suffice: DO NOT EVER CHANGE THIS MAP.
|
|
||||||
var Ports = make(map[string]([]string))
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
for name, unitType := range UnitTypes {
|
|
||||||
unitPorts := make([]string, 0)
|
|
||||||
for _, param := range unitType {
|
|
||||||
if param.CanModulate {
|
|
||||||
unitPorts = append(unitPorts, param.Name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ports[name] = unitPorts
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user