mirror of
https://github.com/vsariola/sointu.git
synced 2025-05-28 03:10:24 -04:00
88 lines
3.2 KiB
Go
88 lines
3.2 KiB
Go
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"]
|
|
}
|