mirror of
https://github.com/vsariola/sointu.git
synced 2025-07-20 22:14:35 -04:00
feat!: display the parameters as knobs in a grid
Also removed the negbandpass & neghighpass parameters and replaced them with bandpass & highpass set to -1, to fit the switches better to the GUI. Closes #51, closes #173
This commit is contained in:
parent
c3caa8de11
commit
666af9433e
@ -8,6 +8,7 @@ import (
|
||||
|
||||
"github.com/vsariola/sointu"
|
||||
"github.com/vsariola/sointu/vm"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
type (
|
||||
@ -20,6 +21,7 @@ type (
|
||||
up *sointu.UnitParameter
|
||||
index int
|
||||
vtable parameterVtable
|
||||
port int
|
||||
}
|
||||
|
||||
parameterVtable interface {
|
||||
@ -29,12 +31,13 @@ type (
|
||||
Type(*Parameter) ParameterType
|
||||
Name(*Parameter) string
|
||||
Hint(*Parameter) ParameterHint
|
||||
Info(*Parameter) (string, bool) // additional info for the parameter, used to display send targets
|
||||
LargeStep(*Parameter) int
|
||||
Reset(*Parameter)
|
||||
RoundToGrid(*Parameter, int, bool) int
|
||||
}
|
||||
|
||||
Params Model
|
||||
Params Model
|
||||
ParamVertList Model
|
||||
|
||||
// different parameter vtables to handle different types of parameters.
|
||||
// Casting struct{} to interface does not cause allocations.
|
||||
namedParameter struct{}
|
||||
@ -54,7 +57,8 @@ type (
|
||||
)
|
||||
|
||||
const (
|
||||
IntegerParameter ParameterType = iota
|
||||
NoParameter ParameterType = iota
|
||||
IntegerParameter
|
||||
BoolParameter
|
||||
IDParameter
|
||||
)
|
||||
@ -67,6 +71,12 @@ func (p *Parameter) Value() int {
|
||||
}
|
||||
return p.vtable.Value(p)
|
||||
}
|
||||
func (p *Parameter) Port() (int, bool) {
|
||||
if p.port <= 0 {
|
||||
return 0, false
|
||||
}
|
||||
return p.port - 1, true
|
||||
}
|
||||
func (p *Parameter) SetValue(value int) bool {
|
||||
if p.vtable == nil {
|
||||
return false
|
||||
@ -78,15 +88,35 @@ func (p *Parameter) SetValue(value int) bool {
|
||||
}
|
||||
return p.vtable.SetValue(p, value)
|
||||
}
|
||||
func (p *Parameter) Add(delta int, snapToGrid bool) bool {
|
||||
if p.vtable == nil {
|
||||
return false
|
||||
}
|
||||
newVal := p.Value() + delta
|
||||
if snapToGrid && p.vtable != nil {
|
||||
newVal = p.vtable.RoundToGrid(p, newVal, delta > 0)
|
||||
}
|
||||
return p.SetValue(newVal)
|
||||
}
|
||||
|
||||
func (p *Parameter) Range() IntRange {
|
||||
if p.vtable == nil {
|
||||
return IntRange{}
|
||||
}
|
||||
return p.vtable.Range(p)
|
||||
}
|
||||
func (p *Parameter) Neutral() int {
|
||||
if p.vtable == nil {
|
||||
return 0
|
||||
}
|
||||
if p.up != nil {
|
||||
return p.up.Neutral
|
||||
}
|
||||
return 0
|
||||
}
|
||||
func (p *Parameter) Type() ParameterType {
|
||||
if p.vtable == nil {
|
||||
return IntegerParameter
|
||||
return NoParameter
|
||||
}
|
||||
return p.vtable.Type(p)
|
||||
}
|
||||
@ -102,97 +132,172 @@ func (p *Parameter) Hint() ParameterHint {
|
||||
}
|
||||
return p.vtable.Hint(p)
|
||||
}
|
||||
func (p *Parameter) Info() (string, bool) {
|
||||
if p.vtable == nil {
|
||||
return "", false
|
||||
}
|
||||
return p.vtable.Info(p)
|
||||
}
|
||||
func (p *Parameter) LargeStep() int {
|
||||
if p.vtable == nil {
|
||||
return 1
|
||||
}
|
||||
return p.vtable.LargeStep(p)
|
||||
}
|
||||
func (p *Parameter) Reset() {
|
||||
if p.vtable == nil {
|
||||
return
|
||||
}
|
||||
p.vtable.Reset(p)
|
||||
}
|
||||
func (p *Parameter) UnitID() int {
|
||||
if p.unit == nil {
|
||||
return 0
|
||||
}
|
||||
return p.unit.ID
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
func (m *Model) ParamVertList() *ParamVertList { return (*ParamVertList)(m) }
|
||||
func (pt *ParamVertList) List() List { return List{pt} }
|
||||
func (pt *ParamVertList) Selected() int { return pt.d.ParamIndex }
|
||||
func (pt *ParamVertList) Selected2() int { return pt.d.ParamIndex }
|
||||
func (pt *ParamVertList) SetSelected(index int) { pt.d.ParamIndex = index }
|
||||
func (pt *ParamVertList) SetSelected2(index int) {}
|
||||
func (pt *ParamVertList) Count() int { return (*Params)(pt).Width() }
|
||||
|
||||
// Model and Params methods
|
||||
|
||||
func (m *Model) Params() *Params { return (*Params)(m) }
|
||||
func (pl *Params) List() List { return List{pl} }
|
||||
func (pl *Params) Selected() int { return pl.d.ParamIndex }
|
||||
func (pl *Params) Selected2() int { return pl.Selected() }
|
||||
func (pl *Params) SetSelected(value int) { pl.d.ParamIndex = max(min(value, pl.Count()-1), 0) }
|
||||
func (pl *Params) SetSelected2(value int) {}
|
||||
|
||||
func (pl *Params) Count() int {
|
||||
count := 0
|
||||
for range pl.Iterate {
|
||||
count++
|
||||
}
|
||||
return count
|
||||
func (m *Model) Params() *Params { return (*Params)(m) }
|
||||
func (pt *Params) Table() Table { return Table{pt} }
|
||||
func (pt *Params) Cursor() Point { return Point{pt.d.ParamIndex, pt.d.UnitIndex} }
|
||||
func (pt *Params) Cursor2() Point { return Point{pt.d.ParamIndex, pt.d.UnitIndex2} }
|
||||
func (pt *Params) SetCursor(p Point) {
|
||||
pt.d.ParamIndex = max(min(p.X, pt.Width()-1), 0)
|
||||
pt.d.UnitIndex = max(min(p.Y, pt.Height()-1), 0)
|
||||
}
|
||||
|
||||
func (pl *Params) SelectedItem() (ret Parameter) {
|
||||
index := pl.Selected()
|
||||
for param := range pl.Iterate {
|
||||
if index == 0 {
|
||||
ret = param
|
||||
}
|
||||
index--
|
||||
}
|
||||
return
|
||||
func (pt *Params) SetCursor2(p Point) {
|
||||
pt.d.ParamIndex = max(min(p.X, pt.Width()-1), 0)
|
||||
pt.d.UnitIndex2 = max(min(p.Y, pt.Height()-1), 0)
|
||||
}
|
||||
|
||||
func (pl *Params) Iterate(yield ParamYieldFunc) {
|
||||
if pl.d.InstrIndex < 0 || pl.d.InstrIndex >= len(pl.d.Song.Patch) {
|
||||
return
|
||||
func (pt *Params) Width() int {
|
||||
if pt.d.InstrIndex < 0 || pt.d.InstrIndex >= len(pt.derived.patch) {
|
||||
return 0
|
||||
}
|
||||
if pl.d.UnitIndex < 0 || pl.d.UnitIndex >= len(pl.d.Song.Patch[pl.d.InstrIndex].Units) {
|
||||
return
|
||||
// TODO: we hack the +1 so that we always have one extra cell to draw the
|
||||
// comments. Refactor the gioui side so that we can specify the width and
|
||||
// height regardless of the underlying table size
|
||||
return pt.derived.patch[pt.d.InstrIndex].paramsWidth + 1
|
||||
}
|
||||
func (pt *Params) RowWidth(y int) int {
|
||||
if pt.d.InstrIndex < 0 || pt.d.InstrIndex >= len(pt.derived.patch) || y < 0 || y >= len(pt.derived.patch[pt.d.InstrIndex].params) {
|
||||
return 0
|
||||
}
|
||||
unit := &pl.d.Song.Patch[pl.d.InstrIndex].Units[pl.d.UnitIndex]
|
||||
unitType, ok := sointu.UnitTypes[unit.Type]
|
||||
if !ok {
|
||||
return
|
||||
return len(pt.derived.patch[pt.d.InstrIndex].params[y])
|
||||
}
|
||||
func (pt *Params) Height() int { return (*Model)(pt).Units().Count() }
|
||||
func (pt *Params) MoveCursor(dx, dy int) (ok bool) {
|
||||
p := pt.Cursor()
|
||||
p.X += dx
|
||||
p.Y += dy
|
||||
pt.SetCursor(p)
|
||||
return p == pt.Cursor()
|
||||
}
|
||||
func (pt *Params) Item(p Point) Parameter {
|
||||
if pt.d.InstrIndex < 0 || pt.d.InstrIndex >= len(pt.derived.patch) || p.Y < 0 || p.Y >= len(pt.derived.patch[pt.d.InstrIndex].params) || p.X < 0 || p.X >= len(pt.derived.patch[pt.d.InstrIndex].params[p.Y]) {
|
||||
return Parameter{}
|
||||
}
|
||||
for i, up := range unitType {
|
||||
if !up.CanSet {
|
||||
continue
|
||||
}
|
||||
if unit.Type == "oscillator" && unit.Parameters["type"] != sointu.Sample && (up.Name == "samplestart" || up.Name == "loopstart" || up.Name == "looplength") {
|
||||
continue // don't show the sample related params unless necessary
|
||||
}
|
||||
if !yield(Parameter{m: (*Model)(pl), unit: unit, up: &unitType[i], vtable: &namedParameter{}}) {
|
||||
return
|
||||
}
|
||||
}
|
||||
if unit.Type == "oscillator" && unit.Parameters["type"] == sointu.Sample {
|
||||
if !yield(Parameter{m: (*Model)(pl), unit: unit, vtable: &gmDlsEntryParameter{}}) {
|
||||
return
|
||||
}
|
||||
}
|
||||
if unit.Type == "delay" {
|
||||
if unit.Parameters["stereo"] == 1 && len(unit.VarArgs)%2 == 1 {
|
||||
unit.VarArgs = append(unit.VarArgs, 1)
|
||||
}
|
||||
if !yield(Parameter{m: (*Model)(pl), unit: unit, vtable: &reverbParameter{}}) {
|
||||
return
|
||||
}
|
||||
if !yield(Parameter{m: (*Model)(pl), unit: unit, vtable: &delayLinesParameter{}}) {
|
||||
return
|
||||
}
|
||||
for i := range unit.VarArgs {
|
||||
if !yield(Parameter{m: (*Model)(pl), unit: unit, index: i, vtable: &delayTimeParameter{}}) {
|
||||
return
|
||||
return pt.derived.patch[pt.d.InstrIndex].params[p.Y][p.X]
|
||||
}
|
||||
func (pt *Params) clear(p Point) {
|
||||
q := pt.Item(p)
|
||||
q.Reset()
|
||||
}
|
||||
func (pt *Params) set(p Point, value int) {
|
||||
q := pt.Item(p)
|
||||
q.SetValue(value)
|
||||
}
|
||||
func (pt *Params) add(rect Rect, delta int, largeStep bool) (ok bool) {
|
||||
for y := rect.TopLeft.Y; y <= rect.BottomRight.Y; y++ {
|
||||
for x := rect.TopLeft.X; x <= rect.BottomRight.X; x++ {
|
||||
p := Point{x, y}
|
||||
q := pt.Item(p)
|
||||
if !q.Add(delta, largeStep) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
type paramsTable struct {
|
||||
Params [][]int `yaml:",flow"`
|
||||
}
|
||||
|
||||
func (pt *Params) marshal(rect Rect) (data []byte, ok bool) {
|
||||
width := rect.BottomRight.X - rect.TopLeft.X + 1
|
||||
height := rect.BottomRight.Y - rect.TopLeft.Y + 1
|
||||
var table = paramsTable{Params: make([][]int, 0, width)}
|
||||
for x := 0; x < width; x++ {
|
||||
table.Params = append(table.Params, make([]int, 0, rect.BottomRight.Y-rect.TopLeft.Y+1))
|
||||
for y := 0; y < height; y++ {
|
||||
p := pt.Item(Point{x + rect.TopLeft.X, y + rect.TopLeft.Y})
|
||||
table.Params[x] = append(table.Params[x], p.Value())
|
||||
}
|
||||
}
|
||||
ret, err := yaml.Marshal(table)
|
||||
if err != nil {
|
||||
return nil, false
|
||||
}
|
||||
return ret, true
|
||||
}
|
||||
func (pt *Params) unmarshal(data []byte) (paramsTable, bool) {
|
||||
var table paramsTable
|
||||
yaml.Unmarshal(data, &table)
|
||||
if len(table.Params) == 0 {
|
||||
return paramsTable{}, false
|
||||
}
|
||||
for i := 0; i < len(table.Params); i++ {
|
||||
if len(table.Params[i]) > 0 {
|
||||
return table, true
|
||||
}
|
||||
}
|
||||
return paramsTable{}, false
|
||||
}
|
||||
|
||||
func (pt *Params) unmarshalAtCursor(data []byte) (ret bool) {
|
||||
table, ok := pt.unmarshal(data)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
for i := 0; i < len(table.Params); i++ {
|
||||
for j, q := range table.Params[i] {
|
||||
x := i + pt.Cursor().X
|
||||
y := j + pt.Cursor().Y
|
||||
p := pt.Item(Point{x, y})
|
||||
ret = p.SetValue(q) || ret
|
||||
}
|
||||
}
|
||||
return ret
|
||||
}
|
||||
func (pt *Params) unmarshalRange(rect Rect, data []byte) (ret bool) {
|
||||
table, ok := pt.unmarshal(data)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
if len(table.Params) == 0 || len(table.Params[0]) == 0 {
|
||||
return false
|
||||
}
|
||||
width := rect.BottomRight.X - rect.TopLeft.X + 1
|
||||
height := rect.BottomRight.Y - rect.TopLeft.Y + 1
|
||||
if len(table.Params) < width {
|
||||
return false
|
||||
}
|
||||
for x := 0; x < width; x++ {
|
||||
for y := 0; y < height; y++ {
|
||||
if len(table.Params[0]) < height {
|
||||
return false
|
||||
}
|
||||
p := pt.Item(Point{x + rect.TopLeft.X, y + rect.TopLeft.Y})
|
||||
ret = p.SetValue(table.Params[x][y]) || ret
|
||||
}
|
||||
}
|
||||
return ret
|
||||
}
|
||||
func (pt *Params) change(kind string, severity ChangeSeverity) func() {
|
||||
return (*Model)(pt).change(kind, PatchChange, severity)
|
||||
}
|
||||
func (pt *Params) cancel() {
|
||||
pt.changeCancel = true
|
||||
}
|
||||
|
||||
// namedParameter vtable
|
||||
@ -207,15 +312,21 @@ func (n *namedParameter) Range(p *Parameter) IntRange {
|
||||
return IntRange{Min: p.up.MinValue, Max: p.up.MaxValue}
|
||||
}
|
||||
func (n *namedParameter) Type(p *Parameter) ParameterType {
|
||||
if p.up == nil || !p.up.CanSet {
|
||||
return NoParameter
|
||||
}
|
||||
if p.unit.Type == "send" && p.up.Name == "target" {
|
||||
return IDParameter
|
||||
}
|
||||
if p.up.MinValue == 0 && p.up.MaxValue == 1 {
|
||||
if p.up.MinValue >= -1 && p.up.MaxValue <= 1 {
|
||||
return BoolParameter
|
||||
}
|
||||
return IntegerParameter
|
||||
}
|
||||
func (n *namedParameter) Name(p *Parameter) string {
|
||||
if p.up.Name == "notetracking" {
|
||||
return "tracking" // notetracking does not fit in the UI
|
||||
}
|
||||
return p.up.Name
|
||||
}
|
||||
func (n *namedParameter) Hint(p *Parameter) ParameterHint {
|
||||
@ -223,39 +334,15 @@ func (n *namedParameter) Hint(p *Parameter) ParameterHint {
|
||||
label := strconv.Itoa(val)
|
||||
if p.up.DisplayFunc != nil {
|
||||
valueInUnits, units := p.up.DisplayFunc(val)
|
||||
label = fmt.Sprintf("%d / %s %s", val, valueInUnits, units)
|
||||
}
|
||||
if p.unit.Type == "send" {
|
||||
instrIndex, targetType, ok := p.m.UnitHintInfo(p.unit.Parameters["target"])
|
||||
if p.up.Name == "voice" && val == 0 {
|
||||
if ok && instrIndex != p.m.d.InstrIndex {
|
||||
label = "all"
|
||||
} else {
|
||||
label = "self"
|
||||
}
|
||||
}
|
||||
if p.up.Name == "port" {
|
||||
if !ok {
|
||||
return ParameterHint{label, false}
|
||||
}
|
||||
portList := sointu.Ports[targetType]
|
||||
if val < 0 || val >= len(portList) {
|
||||
return ParameterHint{label, false}
|
||||
}
|
||||
label = portList[val]
|
||||
}
|
||||
label = fmt.Sprintf("%s %s", valueInUnits, units)
|
||||
}
|
||||
return ParameterHint{label, true}
|
||||
}
|
||||
func (n *namedParameter) Info(p *Parameter) (string, bool) {
|
||||
sendInfo, ok := p.m.ParameterInfo(p.unit.ID, p.up.Name)
|
||||
return sendInfo, ok
|
||||
}
|
||||
func (n *namedParameter) LargeStep(p *Parameter) int {
|
||||
func (n *namedParameter) RoundToGrid(p *Parameter, val int, up bool) int {
|
||||
if p.up.Name == "transpose" {
|
||||
return 12
|
||||
return roundToGrid(val-64, 12, up) + 64
|
||||
}
|
||||
return 16
|
||||
return roundToGrid(val, 16, up)
|
||||
}
|
||||
func (n *namedParameter) Reset(p *Parameter) {
|
||||
v, ok := defaultUnits[p.unit.Type].Parameters[p.up.Name]
|
||||
@ -301,22 +388,36 @@ func (g *gmDlsEntryParameter) Name(p *Parameter) string {
|
||||
return "sample"
|
||||
}
|
||||
func (g *gmDlsEntryParameter) Hint(p *Parameter) ParameterHint {
|
||||
label := "0 / custom"
|
||||
label := "custom"
|
||||
if v := g.Value(p); v > 0 {
|
||||
label = fmt.Sprintf("%v / %v", v, GmDlsEntries[v-1].Name)
|
||||
label = GmDlsEntries[v-1].Name
|
||||
}
|
||||
return ParameterHint{label, true}
|
||||
}
|
||||
func (g *gmDlsEntryParameter) Info(p *Parameter) (string, bool) {
|
||||
return "", false
|
||||
}
|
||||
func (g *gmDlsEntryParameter) LargeStep(p *Parameter) int {
|
||||
return 16
|
||||
func (g *gmDlsEntryParameter) RoundToGrid(p *Parameter, val int, up bool) int {
|
||||
return roundToGrid(val, 16, up)
|
||||
}
|
||||
func (g *gmDlsEntryParameter) Reset(p *Parameter) {}
|
||||
|
||||
// delayTimeParameter vtable
|
||||
|
||||
var delayNoteTrackGrid, delayBpmTrackGrid []int
|
||||
|
||||
func init() {
|
||||
for st := -30; st <= 30; st++ {
|
||||
gridVal := int(math.Exp2(float64(st)/12)*10787 + 0.5)
|
||||
delayNoteTrackGrid = append(delayNoteTrackGrid, gridVal)
|
||||
}
|
||||
for i := 0; i < 16; i++ {
|
||||
delayBpmTrackGrid = append(delayBpmTrackGrid, 1<<i)
|
||||
delayBpmTrackGrid = append(delayBpmTrackGrid, 3<<i)
|
||||
delayBpmTrackGrid = append(delayBpmTrackGrid, 9<<i)
|
||||
}
|
||||
slices.Sort(delayBpmTrackGrid)
|
||||
}
|
||||
|
||||
func (d *delayTimeParameter) Type(p *Parameter) ParameterType { return IntegerParameter }
|
||||
func (d *delayTimeParameter) Name(p *Parameter) string { return "delaytime" }
|
||||
func (d *delayTimeParameter) Value(p *Parameter) int {
|
||||
if p.index < 0 || p.index >= len(p.unit.VarArgs) {
|
||||
return 1
|
||||
@ -334,23 +435,17 @@ func (d *delayTimeParameter) Range(p *Parameter) IntRange {
|
||||
}
|
||||
return IntRange{Min: 1, Max: 65535}
|
||||
}
|
||||
func (d *delayTimeParameter) Type(p *Parameter) ParameterType {
|
||||
return IntegerParameter
|
||||
}
|
||||
func (d *delayTimeParameter) Name(p *Parameter) string {
|
||||
return "delaytime"
|
||||
}
|
||||
func (d *delayTimeParameter) Hint(p *Parameter) ParameterHint {
|
||||
val := d.Value(p)
|
||||
var text string
|
||||
switch p.unit.Parameters["notetracking"] {
|
||||
default:
|
||||
case 0:
|
||||
text = fmt.Sprintf("%v / %.3f rows", val, float32(val)/float32(p.m.d.Song.SamplesPerRow()))
|
||||
text = fmt.Sprintf("%.3f rows", float32(val)/float32(p.m.d.Song.SamplesPerRow()))
|
||||
case 1:
|
||||
relPitch := float64(val) / 10787
|
||||
semitones := -math.Log2(relPitch) * 12
|
||||
text = fmt.Sprintf("%v / %.3f st", val, semitones)
|
||||
text = fmt.Sprintf("%.3f st", semitones)
|
||||
case 2:
|
||||
k := 0
|
||||
v := val
|
||||
@ -372,7 +467,7 @@ func (d *delayTimeParameter) Hint(p *Parameter) ParameterHint {
|
||||
text = fmt.Sprintf(" (1/%d dotted)", 1<<(5-k))
|
||||
}
|
||||
}
|
||||
text = fmt.Sprintf("%v / %.3f beats%s", val, float32(val)/48.0, text)
|
||||
text = fmt.Sprintf("%.3f beats%s", float32(val)/48.0, text)
|
||||
}
|
||||
if p.unit.Parameters["stereo"] == 1 {
|
||||
if p.index < len(p.unit.VarArgs)/2 {
|
||||
@ -383,11 +478,15 @@ func (d *delayTimeParameter) Hint(p *Parameter) ParameterHint {
|
||||
}
|
||||
return ParameterHint{text, true}
|
||||
}
|
||||
func (d *delayTimeParameter) Info(p *Parameter) (string, bool) {
|
||||
return "", false
|
||||
}
|
||||
func (d *delayTimeParameter) LargeStep(p *Parameter) int {
|
||||
return 16
|
||||
func (d *delayTimeParameter) RoundToGrid(p *Parameter, val int, up bool) int {
|
||||
switch p.unit.Parameters["notetracking"] {
|
||||
default:
|
||||
return roundToGrid(val, 16, up)
|
||||
case 1:
|
||||
return roundToSliceGrid(val, delayNoteTrackGrid, up)
|
||||
case 2:
|
||||
return roundToSliceGrid(val, delayBpmTrackGrid, up)
|
||||
}
|
||||
}
|
||||
func (d *delayTimeParameter) Reset(p *Parameter) {}
|
||||
|
||||
@ -412,21 +511,13 @@ func (d *delayLinesParameter) SetValue(p *Parameter, v int) bool {
|
||||
p.unit.VarArgs = p.unit.VarArgs[:targetLines]
|
||||
return true
|
||||
}
|
||||
func (d *delayLinesParameter) Range(p *Parameter) IntRange {
|
||||
return IntRange{Min: 1, Max: 32}
|
||||
}
|
||||
func (d *delayLinesParameter) Type(p *Parameter) ParameterType {
|
||||
return IntegerParameter
|
||||
}
|
||||
func (d *delayLinesParameter) Name(p *Parameter) string {
|
||||
return "delaylines"
|
||||
}
|
||||
func (d *delayLinesParameter) Range(p *Parameter) IntRange { return IntRange{Min: 1, Max: 32} }
|
||||
func (d *delayLinesParameter) Type(p *Parameter) ParameterType { return IntegerParameter }
|
||||
func (d *delayLinesParameter) Name(p *Parameter) string { return "delaylines" }
|
||||
func (r *delayLinesParameter) RoundToGrid(p *Parameter, val int, up bool) int { return val }
|
||||
func (d *delayLinesParameter) Hint(p *Parameter) ParameterHint {
|
||||
return ParameterHint{strconv.Itoa(d.Value(p)), true}
|
||||
}
|
||||
func (d *delayLinesParameter) Info(p *Parameter) (string, bool) {
|
||||
return "", false
|
||||
}
|
||||
func (d *delayLinesParameter) LargeStep(p *Parameter) int {
|
||||
return 4
|
||||
}
|
||||
@ -452,27 +543,51 @@ func (r *reverbParameter) SetValue(p *Parameter, v int) bool {
|
||||
copy(p.unit.VarArgs, entry.varArgs)
|
||||
return true
|
||||
}
|
||||
func (r *reverbParameter) Range(p *Parameter) IntRange {
|
||||
return IntRange{Min: 0, Max: len(reverbs)}
|
||||
}
|
||||
func (r *reverbParameter) Type(p *Parameter) ParameterType {
|
||||
return IntegerParameter
|
||||
}
|
||||
func (r *reverbParameter) Name(p *Parameter) string {
|
||||
return "reverb"
|
||||
}
|
||||
func (r *reverbParameter) Range(p *Parameter) IntRange { return IntRange{Min: 0, Max: len(reverbs)} }
|
||||
func (r *reverbParameter) Type(p *Parameter) ParameterType { return IntegerParameter }
|
||||
func (r *reverbParameter) Name(p *Parameter) string { return "reverb" }
|
||||
func (r *reverbParameter) RoundToGrid(p *Parameter, val int, up bool) int { return val }
|
||||
func (r *reverbParameter) Reset(p *Parameter) {}
|
||||
func (r *reverbParameter) Hint(p *Parameter) ParameterHint {
|
||||
i := r.Value(p)
|
||||
label := "0 / custom"
|
||||
label := "custom"
|
||||
if i > 0 {
|
||||
label = fmt.Sprintf("%v / %v", i, reverbs[i-1].name)
|
||||
label = reverbs[i-1].name
|
||||
}
|
||||
return ParameterHint{label, true}
|
||||
}
|
||||
func (r *reverbParameter) Info(p *Parameter) (string, bool) {
|
||||
return "", false
|
||||
|
||||
func roundToGrid(value, grid int, up bool) int {
|
||||
if up {
|
||||
return value + mod(-value, grid)
|
||||
}
|
||||
return value - mod(value, grid)
|
||||
}
|
||||
func (r *reverbParameter) LargeStep(p *Parameter) int {
|
||||
return 1
|
||||
|
||||
func mod(a, b int) int {
|
||||
m := a % b
|
||||
if a < 0 && b < 0 {
|
||||
m -= b
|
||||
}
|
||||
if a < 0 && b > 0 {
|
||||
m += b
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
func roundToSliceGrid(value int, grid []int, up bool) int {
|
||||
if up {
|
||||
for _, v := range grid {
|
||||
if value < v {
|
||||
return v
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for i := len(grid) - 1; i >= 0; i-- {
|
||||
if value > grid[i] {
|
||||
return grid[i]
|
||||
}
|
||||
}
|
||||
}
|
||||
return value
|
||||
}
|
||||
func (r *reverbParameter) Reset(p *Parameter) {}
|
||||
|
Reference in New Issue
Block a user