Files
sointu/tracker/stack.go
5684185+vsariola@users.noreply.github.com 4e295a3a2f drafting
2025-07-03 18:54:02 +03:00

139 lines
3.5 KiB
Go

package tracker
import (
"fmt"
"github.com/vsariola/sointu"
)
type (
SignalRail struct {
signals [][]Signal
scratch []signalScratch
error SignalError
}
SignalError struct {
InstrIndex, UnitIndex int
Err error
}
Signal struct {
PassThrough int
Send bool
StackUse sointu.StackUse
}
signalScratch struct {
instr, unit int
}
SignalRailType Model
)
func (m *Model) SignalRail() *SignalRailType {
return (*SignalRailType)(m)
}
func (s *SignalRailType) Item(u int) Signal {
i := s.d.InstrIndex
if i < 0 || u < 0 || i >= len(s.derived.rail.signals) || u >= len(s.derived.rail.signals[i]) {
return Signal{}
}
return s.derived.rail.signals[i][u]
}
func (s *SignalRailType) Error() SignalError {
i := s.d.InstrIndex
if i < 0 || i >= len(s.derived.rail.signals) {
return SignalError{}
}
if i == s.derived.rail.error.InstrIndex {
return s.derived.rail.error
}
return SignalError{}
}
func (s *SignalRailType) MaxWidth() int {
i := s.d.InstrIndex
if i < 0 || i >= len(s.derived.rail.signals) {
return 0
}
ret := 0
for _, signal := range s.derived.rail.signals[i] {
ret = max(ret, signal.PassThrough+max(len(signal.StackUse.Inputs), signal.StackUse.NumOutputs))
}
return ret
}
func (st *SignalRailType) update() {
s := &st.derived.rail
patch := st.d.Song.Patch
s.scratch = s.scratch[:0]
s.error = SignalError{}
for i, instr := range patch {
for len(s.signals) <= i {
s.signals = append(s.signals, make([]Signal, len(instr.Units)))
}
start := len(s.scratch)
for u, unit := range instr.Units {
for len(s.signals[i]) <= u {
s.signals[i] = append(s.signals[i], Signal{})
}
stackUse := unit.StackUse()
numInputs := len(stackUse.Inputs)
if len(s.scratch) < numInputs {
if s.error.Err == nil {
s.error.Err = fmt.Errorf("%s unit in instrument %d / %s needs %d inputs, but got only %d", unit.Type, i, instr.Name, numInputs, len(s.scratch))
s.error.InstrIndex = i
s.error.UnitIndex = u
}
s.scratch = s.scratch[:0]
} else {
s.scratch = s.scratch[:len(s.scratch)-numInputs]
}
s.signals[i][u] = Signal{
PassThrough: len(s.scratch),
StackUse: stackUse,
Send: unit.Type == "send",
}
for _ = range stackUse.NumOutputs {
s.scratch = append(s.scratch, signalScratch{instr: i, unit: u})
}
}
diff := len(s.scratch) - start
if instr.NumVoices > 1 && diff != 0 {
if diff < 0 {
morepop := (instr.NumVoices - 1) * diff
if morepop > len(s.scratch) {
if s.error.Err == nil {
s.error.Err = fmt.Errorf("each voice of instrument %d / %s consumes %d signals, but there was not enough signals available", i, instr.Name, -diff)
s.error.InstrIndex = i
s.error.UnitIndex = -1
}
s.scratch = s.scratch[:0]
} else {
s.scratch = s.scratch[:len(s.scratch)-morepop]
}
} else {
for range (instr.NumVoices - 1) * diff {
s.scratch = append(s.scratch, s.scratch[len(s.scratch)-diff])
}
}
}
}
if len(s.scratch) > 0 && s.error.Err == nil {
s.error.Err = fmt.Errorf("instrument %d / %s unit %d / %s leave a signal on stack ", s.scratch[0].instr, patch[s.scratch[0].instr].Name, s.scratch[0].unit, patch[s.scratch[0].instr].Units[s.scratch[0].unit].Type)
s.error.InstrIndex = s.scratch[0].instr
s.error.UnitIndex = s.scratch[0].unit
}
if s.error.Err != nil {
(*Model)(st).Alerts().AddNamed("SignalError", s.error.Error(), Error)
}
}
func (e *SignalError) Error() string {
return e.Err.Error()
}