87 lines
2.8 KiB
Go
87 lines
2.8 KiB
Go
package dsp
|
|
|
|
import "fmt"
|
|
|
|
// Op type categories for static (compile-time / editor) type propagation.
|
|
// These mirror the runtime dispatch in the synthetic graph evaluator and the
|
|
// frontend's inferNodeTypes (web/src/lib/synthTypes.ts) — keep the three in
|
|
// sync; a parity test guards the Go/TS pair.
|
|
var (
|
|
// reductionOps collapse an array (or scalar) to a single scalar.
|
|
reductionOps = map[string]bool{
|
|
"index": true, "length": true, "sum": true,
|
|
"mean": true, "min": true, "max": true,
|
|
}
|
|
// arrayProducerOps require an array input and yield an array.
|
|
arrayProducerOps = map[string]bool{
|
|
"fft": true, "slice": true,
|
|
}
|
|
// scalarOnlyOps reject array inputs and yield a scalar. Stateful filters
|
|
// plus lua (whose state/closure cannot be broadcast per array lane).
|
|
scalarOnlyOps = map[string]bool{
|
|
"moving_average": true, "rms": true, "lowpass": true,
|
|
"derivative": true, "integrate": true, "lua": true,
|
|
}
|
|
// statefulOps accumulate state across evaluations, so a single-shot trace
|
|
// (fresh state) only approximates their true running value. lua is included
|
|
// because a script may persist state in its state table.
|
|
statefulOps = map[string]bool{
|
|
"moving_average": true, "rms": true, "lowpass": true,
|
|
"derivative": true, "integrate": true, "lua": true,
|
|
}
|
|
)
|
|
|
|
// IsStateful reports whether an op accumulates state across evaluations. The
|
|
// editor's live/debug trace flags such nodes as "approx" since it evaluates a
|
|
// single tick with fresh state.
|
|
func IsStateful(op string) bool { return statefulOps[op] }
|
|
|
|
// OpOutputType reports the output ValType of an op given its input types, and
|
|
// an error if the inputs are definitely incompatible with the op. Inputs may be
|
|
// ValUnknown (a source whose real type is not yet known at compile time); such
|
|
// inputs never trigger an error — runtime Sample typing is authoritative.
|
|
func OpOutputType(op string, in []ValType) (ValType, error) {
|
|
switch {
|
|
case reductionOps[op]:
|
|
return ValScalar, nil
|
|
|
|
case arrayProducerOps[op]:
|
|
for _, t := range in {
|
|
if t == ValScalar {
|
|
return ValUnknown, fmt.Errorf("%s requires an array input", op)
|
|
}
|
|
}
|
|
return ValArray, nil
|
|
|
|
case scalarOnlyOps[op]:
|
|
for _, t := range in {
|
|
if t == ValArray {
|
|
return ValUnknown, fmt.Errorf("%s does not accept an array input", op)
|
|
}
|
|
}
|
|
return ValScalar, nil
|
|
|
|
default:
|
|
// Elementwise stateless ops (gain, offset, add, subtract, multiply,
|
|
// divide, clamp, threshold, expr): array if any input is an array,
|
|
// scalar if all inputs are definitely scalar, otherwise unknown.
|
|
anyArray, anyUnknown := false, false
|
|
for _, t := range in {
|
|
switch t {
|
|
case ValArray:
|
|
anyArray = true
|
|
case ValUnknown:
|
|
anyUnknown = true
|
|
}
|
|
}
|
|
switch {
|
|
case anyArray:
|
|
return ValArray, nil
|
|
case anyUnknown:
|
|
return ValUnknown, nil
|
|
default:
|
|
return ValScalar, nil
|
|
}
|
|
}
|
|
}
|