func (ps Processers) Framer(n int) (Framer, error) {
	if len(ps) < n {
		return nil, fmt.Errorf("processer slice has size %d, expected at least %d inputs", len(ps), n)
	}
	framer, ok := ps[n].(Framer)
	if !ok {
		return nil, fmt.Errorf("processer #%d does not implement the Framer interface", n)
	}
	return framer, nil
}
Framer is a convenience method that checks that input #n exist and implements the Framer interface.
package dsp
import (
	"bytes"
	"errors"
	"fmt"
	"reflect"
	"strings"
)
var ErrNoFunc = errors.New("no ProcFunc set")
var ErrOOB = errors.New("frame index out of bounds")
type Value interface {
}
type Processer interface {
}
type Framer interface {
	Get(int) (Value, error)
}
type OneValuer interface {
	Get() (Value, error)
}
type Inputter interface {
	SetInputs(...Processer)
}
type Resetter interface {
	Reset()
}
type ProcFunc func(int, ...Processer) (Value, error)
type Proc struct {
	f      ProcFunc
	inputs []Processer
	cache  *cache
}
func NewProc(bufSize int, f ProcFunc) *Proc {
	return &Proc{
		f:     f,
		cache: newCache(bufSize),
	}
}
func (bp *Proc) SetInputs(inputs ...Processer) {
	bp.inputs = inputs
}
func (bp *Proc) Reset() {
	bp.cache.clear()
}
func (bp *Proc) Get(idx int) (Value, error) {
	if idx < 0 {
		return nil, ErrOOB
	}
	val, ok := bp.cache.get(idx)
	if ok {
		return val, nil
	}
	if bp.f != nil {
		v, e := bp.f(idx, bp.inputs...)
		if e != nil {
			return nil, e
		}
		bp.cache.set(idx, v)
		return v, nil
	}
	return nil, ErrNoFunc
}
func (bp *Proc) SetCache(idx int, val Value) {
	bp.cache.set(idx, val)
}
func (bp *Proc) GetCache(idx int) (Value, bool) {
	val, ok := bp.cache.get(idx)
	return val, ok
}
func (bp *Proc) ClearCache() {
	bp.cache.clear()
}
func (bp *Proc) Inputs() []Processer {
	return bp.inputs
}
func (bp *Proc) Framer(n int) Framer {
	return bp.inputs[n].(Framer)
}
func (bp *Proc) OneValuer(n int) OneValuer {
	return bp.inputs[n].(OneValuer)
}
type OneProcFunc func(...Processer) (Value, error)
type OneProc struct {
	f      OneProcFunc
	inputs []Processer
	cache  Value
}
func NewOneProc(f OneProcFunc) *OneProc {
	return &OneProc{f: f}
}
func (bp *OneProc) SetInputs(inputs ...Processer) {
	bp.inputs = inputs
}
func (bp *OneProc) Reset() {
	bp.cache = Value(nil)
}
func (bp *OneProc) Get() (Value, error) {
	if bp.cache != nil {
		return bp.cache, nil
	}
	if bp.f != nil {
		v, e := bp.f(bp.inputs...)
		if e != nil {
			return nil, e
		}
		bp.cache = v
		return v, nil
	}
	return nil, ErrNoFunc
}
func (bp *OneProc) Inputs() []Processer {
	return bp.inputs
}
func (bp *OneProc) Framer(n int) Framer {
	return bp.inputs[n].(Framer)
}
func (bp *OneProc) OneValuer(n int) OneValuer {
	return bp.inputs[n].(OneValuer)
}
type App struct {
	Name   string
	procs  map[string]Node
	inputs map[Node][]Node
}
type Node struct {
	name string
	typ  Processer
}
func (n Node) Name() string {
	return n.name
}
func (n Node) Proc(idx int) Processer {
	return n.typ
}
func (n Node) Get(idx int) (Value, error) {
	return n.typ.(Framer).Get(idx)
}
func (n Node) GetOne() (Value, error) {
	return n.typ.(OneValuer).Get()
}
func NewApp(name string) *App {
	return &App{
		Name:   name,
		procs:  make(map[string]Node),
		inputs: make(map[Node][]Node),
	}
}
func (app *App) NodeByName(name string) Node {
	proc, ok := app.procs[name]
	if !ok {
		panic(fmt.Errorf("no processor named [%s] in builder graph", name))
	}
	return proc
}
func (app *App) NodesByName(names ...string) ([]Node, error) {
	if len(names) == 0 {
		return nil, fmt.Errorf("the names argument is empty, need at least one name")
	}
	nodes := []Node{}
	for _, name := range names {
		node, ok := app.procs[name]
		if !ok {
			return nil, fmt.Errorf("no processor named [%s] in builder graph", name)
		}
		nodes = append(nodes, node)
	}
	return nodes, nil
}
func (app *App) Add(name string, p Processer) Node {
	nodeName := strings.TrimSpace(name)
	_, ok := app.procs[nodeName]
	if ok {
		panic(fmt.Errorf("there is already a processor named [%s] in builder graph", nodeName))
	}
	n := Node{name: nodeName, typ: p}
	app.procs[nodeName] = n
	return n
}
func (app *App) Connect(to Node, from ...Node) Node {
	inputter, ok := to.typ.(Inputter)
	if !ok {
		panic("tried to set inputs on a processor that does not implement the Inputter interface")
	}
	inputs := []Processer{}
	for _, in := range from {
		inputs = append(inputs, in.typ)
	}
	inputter.SetInputs(inputs...)
	app.inputs[to] = from
	return to
}
func (app *App) Chain(nodes ...Node) Node {
	for k := 0; k < len(nodes)-1; k++ {
		inputter, ok := nodes[k].typ.(Inputter)
		if !ok {
			panic("tried to set inputs on a processor that does not implement the Inputter interface")
		}
		inputter.SetInputs(nodes[k+1].typ)
		app.inputs[nodes[k]] = []Node{nodes[k+1]}
	}
	return nodes[0]
}
func (app *App) Reset() {
	for _, node := range app.procs {
		res, ok := node.typ.(Resetter)
		if ok {
			res.Reset()
		}
	}
}
func (app *App) String() string {
	var buf bytes.Buffer
	for name, node := range app.procs {
		buf.WriteString(fmt.Sprintf("proc: %s, inputs: | ", name))
		for _, in := range app.inputs[node] {
			buf.WriteString(fmt.Sprintf("%s | ", in))
		}
		buf.WriteString("\n")
	}
	return buf.String()
}
func Get(p Processer, idx int) (val Value, err error) {
	switch input := p.(type) {
	case Framer:
		val, err = input.Get(idx)
		if err != nil {
			return nil, err
		}
	case OneValuer:
		val, err = input.Get()
		if err != nil {
			return nil, err
		}
	default:
		return nil, fmt.Errorf("unsupported input type %s", reflect.TypeOf(p))
	}
	return val, nil
}
type Processers []Processer
func (ps Processers) Get(idx int) (Value, error) {
	framer, ok := ps[0].(Framer)
	if !ok {
		return nil, fmt.Errorf("processor does not implement the Framer interface")
	}
	return framer.Get(idx)
}