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"] }