This commit is contained in:
5684185+vsariola@users.noreply.github.com
2025-07-06 01:38:07 +03:00
parent 53af773815
commit e1aa9c0d26
11 changed files with 218 additions and 102 deletions

View File

@ -39,7 +39,7 @@ type (
derivedInstrument struct {
wires []Wire
rails []Rail
railsWidth int
railWidth int
params [][]Parameter
paramsWidth int
}
@ -59,7 +59,7 @@ func (s *Model) RailWidth() int {
if i < 0 || i >= len(s.derived.patch) {
return 0
}
return s.derived.patch[i].railsWidth
return s.derived.patch[i].railWidth
}
func (m *Model) Wires(yield func(wire Wire) bool) {
@ -121,9 +121,10 @@ func (m *Model) updateParams() {
for i, instr := range m.d.Song.Patch {
setSliceLength(&m.derived.patch[i].params, len(instr.Units))
paramsWidth := 0
for u, unit := range instr.Units {
m.derived.patch[i].params[u] = m.deriveParams(&unit, m.derived.patch[i].params[u])
paramsWidth = max(paramsWidth, len(m.derived.patch[i].params[u]))
for u := range instr.Units {
p := m.deriveParams(&instr.Units[u], m.derived.patch[i].params[u])
m.derived.patch[i].params[u] = p
paramsWidth = max(paramsWidth, len(p))
}
m.derived.patch[i].paramsWidth = paramsWidth
}
@ -264,7 +265,7 @@ func (m *Model) updateRails() {
scratch = append(scratch, stackElem{instr: i, unit: u})
}
}
m.derived.patch[i].railsWidth = maxWidth
m.derived.patch[i].railWidth = maxWidth
diff := len(scratch) - start
if instr.NumVoices > 1 && diff != 0 {
if diff < 0 {
@ -314,7 +315,7 @@ func (m *Model) updateWires() {
if err != nil {
continue
}
_, tX, ok := sointu.FindParamForModulationPort(m.d.Song.Patch[tI].Units[tU].Type, unit.Parameters["port"])
up, tX, ok := sointu.FindParamForModulationPort(m.d.Song.Patch[tI].Units[tU].Type, unit.Parameters["port"])
if !ok {
continue
}
@ -325,20 +326,28 @@ func (m *Model) updateWires() {
FromSet: true,
To: Point{X: tX, Y: tU},
ToSet: true,
Hint: "TBW",
})
} else {
// remote send
m.derived.patch[i].wires = append(m.derived.patch[i].wires, Wire{
From: u,
FromSet: true,
Hint: "TBW",
Hint: fmt.Sprintf("To instrument #%d (%s), unit #%d (%s), port %s", tI, m.d.Song.Patch[tI].Name, tU, m.d.Song.Patch[tI].Units[tU].Type, up.Name),
})
toPt := Point{X: tX, Y: tU}
hint := fmt.Sprintf("From instrument #%d (%s), send #%d", i, m.d.Song.Patch[i].Name, u)
for i, w := range m.derived.patch[tI].wires {
if !w.FromSet && w.ToSet && w.To == toPt {
m.derived.patch[tI].wires[i].Hint += "; " + hint
goto skipAppend
}
}
m.derived.patch[tI].wires = append(m.derived.patch[tI].wires, Wire{
To: Point{X: tX, Y: tU},
To: toPt,
ToSet: true,
Hint: "TBW",
Hint: hint,
})
skipAppend:
}
}
}

View File

@ -111,6 +111,8 @@ func (s FilledDragListStyle) Layout(gtx C, element, bg func(gtx C, i int) D) D {
case key.FocusEvent:
if !ke.Focus {
s.dragList.TrackerList.SetSelected2(s.dragList.TrackerList.Selected())
} else {
s.dragList.EnsureVisible(s.dragList.TrackerList.Selected())
}
case key.Event:
if ke.State != key.Press {

View File

@ -9,6 +9,7 @@ import (
"gioui.org/f32"
"gioui.org/gesture"
"gioui.org/io/event"
"gioui.org/io/key"
"gioui.org/io/pointer"
"gioui.org/layout"
"gioui.org/op/clip"
@ -142,8 +143,8 @@ func (k *KnobWidget) update(gtx C) {
break
}
if ev, ok := e.(pointer.Event); ok && ev.Kind == pointer.Scroll {
delta := math.Min(math.Max(float64(ev.Scroll.Y), -1), 1)
k.Value.SetValue(k.Value.Value() - int(delta))
delta := -int(math.Min(math.Max(float64(ev.Scroll.Y), -1), 1))
k.Value.Add(delta, ev.Modifiers.Contain(key.ModShortcut))
k.State.tipArea.Appear(gtx.Now)
}
}

View File

@ -120,7 +120,9 @@ func (oe *OrderEditor) Layout(gtx C) D {
table := FilledScrollTable(t.Theme, oe.scrollTable)
table.ColumnTitleHeight = orderTitleHeight
return table.Layout(gtx, cell, colTitle, rowTitle, nil, rowTitleBg)
return Surface{Gray: 24, Focus: oe.scrollTable.TreeFocused(gtx)}.Layout(gtx, func(gtx C) D {
return table.Layout(gtx, cell, colTitle, rowTitle, nil, rowTitleBg)
})
}
func (oe *OrderEditor) handleEvents(gtx C, t *Tracker) {

View File

@ -444,7 +444,7 @@ func (ul *UnitList) update(gtx C, t *Tracker) {
if e, ok := event.(key.Event); ok && e.State == key.Press {
switch e.Name {
case key.NameRightArrow:
t.PatchPanel.unitEditor.paramTable.Focus()
t.PatchPanel.unitEditor.paramTable.RowTitleList.Focus()
case key.NameDeleteBackward:
t.Units().SetSelectedType("")
t.UnitSearching().SetValue(true)

View File

@ -176,12 +176,17 @@ func (s *ScrollTableStyle) handleEvents(gtx layout.Context, p image.Point) {
}
case key.Event:
if e.State == key.Press {
s.ScrollTable.command(gtx, e)
s.ScrollTable.command(gtx, e, p)
}
case transfer.DataEvent:
if b, err := io.ReadAll(e.Open()); err == nil {
s.ScrollTable.Table.Paste(b)
}
case key.FocusEvent:
if e.Focus {
s.ScrollTable.ColTitleList.EnsureVisible(s.ScrollTable.Table.Cursor().X)
s.ScrollTable.RowTitleList.EnsureVisible(s.ScrollTable.Table.Cursor().Y)
}
}
}
@ -250,7 +255,7 @@ func (s *ScrollTableStyle) layoutColTitles(gtx C, p image.Point, fg, bg func(gtx
s.ColTitleStyle.Layout(gtx, fg, bg)
}
func (s *ScrollTable) command(gtx C, e key.Event) {
func (s *ScrollTable) command(gtx C, e key.Event, p image.Point) {
stepX := 1
stepY := 1
if e.Modifiers.Contain(key.ModAlt) {
@ -265,13 +270,13 @@ func (s *ScrollTable) command(gtx C, e key.Event) {
s.Table.Clear()
return
case key.NameUpArrow:
if !s.Table.MoveCursor(0, -stepY) && stepY == 1 {
if !s.Table.MoveCursor(0, -stepY) && stepY == 1 && p.Y > 0 {
s.ColTitleList.Focus()
}
case key.NameDownArrow:
s.Table.MoveCursor(0, stepY)
case key.NameLeftArrow:
if !s.Table.MoveCursor(-stepX, 0) && stepX == 1 {
if !s.Table.MoveCursor(-stepX, 0) && stepX == 1 && p.X > 0 {
s.RowTitleList.Focus()
}
case key.NameRightArrow:

View File

@ -98,7 +98,8 @@ type Theme struct {
Disabled LabelStyle
Error color.NRGBA
}
Error color.NRGBA
Error color.NRGBA
Divider color.NRGBA
}
Cursor CursorStyle
Selection CursorStyle

View File

@ -220,6 +220,7 @@ uniteditor:
disabled:
{ textsize: 12, color: *disabled, font: { style: 1 }, alignment: 2 }
error: *errorcolor
divider: { r: 255, g: 255, b: 255, a: 5 }
knob:
diameter: 36
value: { textsize: 12, color: *highemphasis }
@ -237,4 +238,4 @@ signalrail:
port:
diameter: 36
strokewidth: 4
color: *secondarycolor
color: { r: 32, g: 55, b: 58, a: 255 }

View File

@ -10,7 +10,6 @@ import (
"gioui.org/f32"
"gioui.org/io/clipboard"
"gioui.org/io/event"
"gioui.org/io/key"
"gioui.org/io/pointer"
"gioui.org/layout"
@ -127,22 +126,23 @@ func (pe *UnitEditor) update(gtx C, t *Tracker) {
item := params.Item(params.Cursor())
switch e.Name {
case key.NameLeftArrow:
if e.Modifiers.Contain(key.ModShortcut) {
item.SetValue(item.Value() - item.LargeStep())
} else {
item.SetValue(item.Value() - 1)
}
item.Add(-1, e.Modifiers.Contain(key.ModShortcut))
case key.NameRightArrow:
if e.Modifiers.Contain(key.ModShortcut) {
item.SetValue(item.Value() + item.LargeStep())
} else {
item.SetValue(item.Value() + 1)
}
item.Add(1, e.Modifiers.Contain(key.ModShortcut))
case key.NameDeleteBackward, key.NameDeleteForward:
item.Reset()
}
}
}
for {
e, ok := gtx.Event(key.Filter{Focus: pe.paramTable.RowTitleList, Name: key.NameLeftArrow})
if !ok {
break
}
if e, ok := e.(key.Event); ok && e.State == key.Press {
t.PatchPanel.unitList.dragList.Focus()
}
}
}
func (pe *UnitEditor) ChooseUnitType(t *Tracker) {
@ -174,10 +174,6 @@ func (pe *UnitEditor) layoutRack(gtx C) D {
coltitle := func(gtx C, x int) D {
return D{Size: image.Pt(cellWidth, columnTitleHeight)}
}
rowTitleBg := func(gtx C, j int) D {
paint.FillShape(gtx.Ops, t.Theme.NoteEditor.Play, clip.Rect{Max: image.Pt(gtx.Constraints.Max.X, 1)}.Op())
return D{}
}
rowtitle := func(gtx C, y int) D {
if y < 0 || y >= len(pe.Parameters) {
return D{}
@ -223,8 +219,9 @@ func (pe *UnitEditor) layoutRack(gtx C) D {
table.ColumnTitleHeight = 0
table.CellWidth = t.Theme.UnitEditor.Width
table.CellHeight = t.Theme.UnitEditor.Height
pe.drawBackGround(gtx)
pe.drawSignals(gtx, rowTitleWidth)
dims := table.Layout(gtx, cell, coltitle, rowtitle, nil, rowTitleBg)
dims := table.Layout(gtx, cell, coltitle, rowtitle, nil, nil)
return dims
}
@ -238,27 +235,71 @@ func (pe *UnitEditor) drawSignals(gtx C, rowTitleWidth int) {
defer clip.Rect(image.Rectangle{Max: gtx.Constraints.Max}).Push(gtx.Ops).Pop()
defer op.Offset(image.Pt(-colP.Offset, -rowP.Offset)).Push(gtx.Ops).Pop()
for wire := range t.Wires {
pe.drawSignal(gtx, wire, colP.First, rowP.First)
switch {
case wire.FromSet && !wire.ToSet:
pe.drawRemoteSendSignal(gtx, wire, colP.First, rowP.First)
case !wire.FromSet && wire.ToSet:
pe.drawRemoteReceiveSignal(gtx, wire, colP.First, rowP.First)
case wire.FromSet && wire.ToSet:
pe.drawSignal(gtx, wire, colP.First, rowP.First)
}
}
}
func (pe *UnitEditor) drawBackGround(gtx C) {
t := TrackerFromContext(gtx)
rowP := pe.paramTable.RowTitleList.List.Position
defer op.Offset(image.Pt(0, -rowP.Offset)).Push(gtx.Ops).Pop()
for range pe.paramTable.RowTitleList.List.Position.Count + 1 {
paint.FillShape(gtx.Ops, t.Theme.UnitEditor.Divider, clip.Rect{Max: image.Pt(gtx.Constraints.Max.X, 1)}.Op())
op.Offset(image.Pt(0, gtx.Dp(t.Theme.UnitEditor.Height))).Add(gtx.Ops)
}
}
func (pe *UnitEditor) drawRemoteSendSignal(gtx C, wire tracker.Wire, col, row int) {
sy := wire.From - row
t := TrackerFromContext(gtx)
defer op.Offset(image.Pt(0, (sy+1)*gtx.Dp(t.Theme.UnitEditor.Height)-gtx.Dp(16))).Push(gtx.Ops).Pop()
Label(t.Theme, &t.Theme.UnitEditor.WireHint, wire.Hint).Layout(gtx)
}
func (pe *UnitEditor) drawRemoteReceiveSignal(gtx C, wire tracker.Wire, col, row int) {
ex := wire.To.X - col
ey := wire.To.Y - row
t := TrackerFromContext(gtx)
width := float32(gtx.Dp(t.Theme.UnitEditor.Width))
height := float32(gtx.Dp(t.Theme.UnitEditor.Height))
topLeft := f32.Pt(float32(ex)*width, float32(ey)*height)
center := topLeft.Add(f32.Pt(width/2, height/2))
c := float32(gtx.Dp(t.Theme.Knob.Diameter)) / 2 / float32(math.Sqrt2)
from := f32.Pt(c, c).Add(center)
q := c
c1 := f32.Pt(c+q, c+q).Add(center)
o := float32(gtx.Dp(8))
c2 := f32.Pt(width-q, height-o).Add(topLeft)
to := f32.Pt(width, height-o).Add(topLeft)
var path clip.Path
path.Begin(gtx.Ops)
path.MoveTo(from)
path.CubeTo(c1, c2, to)
paint.FillShape(gtx.Ops, t.Theme.UnitEditor.WireColor,
clip.Stroke{
Path: path.End(),
Width: float32(gtx.Dp(t.Theme.SignalRail.LineWidth)),
}.Op())
defer op.Offset(image.Pt((ex+1)*gtx.Dp(t.Theme.UnitEditor.Width)+gtx.Dp(5), (ey+1)*gtx.Dp(t.Theme.UnitEditor.Height)-gtx.Dp(16))).Push(gtx.Ops).Pop()
Label(t.Theme, &t.Theme.UnitEditor.WireHint, wire.Hint).Layout(gtx)
}
func (pe *UnitEditor) drawSignal(gtx C, wire tracker.Wire, col, row int) {
sy := wire.From - row
ex := wire.To.X - col
ey := wire.To.Y - row
t := TrackerFromContext(gtx)
if wire.FromSet && !wire.ToSet {
defer op.Offset(image.Pt(0, (sy+1)*gtx.Dp(t.Theme.UnitEditor.Height)-gtx.Dp(16))).Push(gtx.Ops).Pop()
Label(t.Theme, &t.Theme.UnitEditor.WireHint, wire.Hint).Layout(gtx)
return
}
if !wire.FromSet && wire.ToSet {
Label(t.Theme, &t.Theme.UnitEditor.WireHint, wire.Hint).Layout(gtx)
return
}
diam := gtx.Dp(t.Theme.Knob.Diameter)
c := float32(diam) / 2 / float32(math.Sqrt2)
width := float32(gtx.Dp(t.Theme.UnitEditor.Width))
height := float32(gtx.Dp(t.Theme.UnitEditor.Height))
diam := gtx.Dp(t.Theme.Knob.Diameter)
from := f32.Pt(0, float32((sy+1)*gtx.Dp(t.Theme.UnitEditor.Height))-float32(gtx.Dp(t.Theme.SignalRail.SignalWidth)/2))
corner := f32.Pt(1, 1)
if ex > 0 {
@ -267,7 +308,6 @@ func (pe *UnitEditor) drawSignal(gtx C, wire tracker.Wire, col, row int) {
if sy < ey {
corner.Y = -corner.Y
}
c := float32(diam) / 2 / float32(math.Sqrt2)
topLeft := f32.Pt(float32(ex)*width, float32(ey)*height)
center := topLeft.Add(f32.Pt(width/2, height/2))
to := mulVec(corner, f32.Pt(c, c)).Add(center)
@ -277,7 +317,6 @@ func (pe *UnitEditor) drawSignal(gtx C, wire tracker.Wire, col, row int) {
p1 = f32.Pt(p2.X, (float32(sy)+0.5)*float32(gtx.Dp(t.Theme.UnitEditor.Height))+float32(diam)/2)
}
k := float32(width) / 4
//toTan := mulVec(corner, f32.Pt(-k, -k))
p2Tan := mulVec(corner, f32.Pt(-k, -k))
p1Tan := f32.Pt(k, p2Tan.Y)
fromTan := f32.Pt(k, 0)
@ -353,11 +392,10 @@ func (pe *UnitEditor) layoutUnitTypeChooser(gtx C) D {
}
func (t *UnitEditor) Tags(level int, yield TagYieldFunc) bool {
widget := event.Tag(t.paramTable)
if t.showingChooser() {
widget = event.Tag(t.searchList)
return yield(level, t.searchList) && yield(level+1, &t.commentEditor.widgetEditor)
}
return yield(level, widget) && yield(level+1, &t.commentEditor.widgetEditor)
return yield(level+1, t.paramTable.RowTitleList) && yield(level, t.paramTable) && yield(level+1, &t.commentEditor.widgetEditor)
}
type ParameterState struct {

View File

@ -30,8 +30,8 @@ type (
Type(*Parameter) ParameterType
Name(*Parameter) string
Hint(*Parameter) ParameterHint
LargeStep(*Parameter) int
Reset(*Parameter)
RoundToGrid(*Parameter, int, bool) int
}
Params Model
@ -87,6 +87,17 @@ 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{}
@ -111,12 +122,6 @@ func (p *Parameter) Hint() ParameterHint {
}
return p.vtable.Hint(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
@ -173,13 +178,24 @@ func (pt *Params) Item(p Point) Parameter {
return pt.derived.patch[pt.d.InstrIndex].params[p.Y][p.X]
}
func (pt *Params) clear(p Point) {
panic("NOT IMPLEMENTED")
q := pt.Item(p)
q.Reset()
}
func (pt *Params) set(p Point, value int) {
panic("NOT IMPLEMENTED")
q := pt.Item(p)
q.SetValue(value)
}
func (pt *Params) add(rect Rect, delta int) (ok bool) {
panic("NOT IMPLEMENTED")
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.SetValue(q.Value() + delta) {
return false
}
}
}
return true
}
func (pt *Params) marshal(rect Rect) (data []byte, ok bool) {
panic("NOT IMPLEMENTED")
@ -191,10 +207,10 @@ func (pt *Params) unmarshalRange(rect Rect, data []byte) (ok bool) {
panic("NOT IMPLEMENTED")
}
func (pt *Params) change(kind string, severity ChangeSeverity) func() {
panic("NOT IMPLEMENTED")
return (*Model)(pt).change(kind, PatchChange, severity)
}
func (pt *Params) cancel() {
panic("NOT IMPLEMENTED")
pt.changeCancel = true
}
// namedParameter vtable
@ -232,11 +248,12 @@ func (n *namedParameter) Hint(p *Parameter) ParameterHint {
}
return ParameterHint{label, true}
}
func (n *namedParameter) LargeStep(p *Parameter) int {
func (n *namedParameter) RoundToGrid(p *Parameter, val int, up bool) int {
grid := 16
if p.up.Name == "transpose" {
return 12
grid = 12
}
return 16
return roundToGrid(val, grid, up)
}
func (n *namedParameter) Reset(p *Parameter) {
v, ok := defaultUnits[p.unit.Type].Parameters[p.up.Name]
@ -288,13 +305,30 @@ func (g *gmDlsEntryParameter) Hint(p *Parameter) ParameterHint {
}
return ParameterHint{label, true}
}
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 := -48; st <= 48; 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
@ -312,12 +346,6 @@ 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
@ -350,7 +378,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 {
@ -361,8 +389,15 @@ func (d *delayTimeParameter) Hint(p *Parameter) ParameterHint {
}
return ParameterHint{text, true}
}
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) {}
@ -387,15 +422,10 @@ 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}
}
@ -424,15 +454,11 @@ 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 := "custom"
@ -441,7 +467,38 @@ func (r *reverbParameter) Hint(p *Parameter) ParameterHint {
}
return ParameterHint{label, true}
}
func (r *reverbParameter) LargeStep(p *Parameter) int {
return 1
func roundToGrid(value, grid int, up bool) int {
if up {
return value + mod(-value, grid)
}
return value - mod(value, grid)
}
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) {}