diff --git a/tracker/gioui/keybindings.go b/tracker/gioui/keybindings.go index ff5cd74..cda4689 100644 --- a/tracker/gioui/keybindings.go +++ b/tracker/gioui/keybindings.go @@ -215,37 +215,37 @@ func (t *Tracker) KeyEvent(e key.Event, gtx C) { t.Solo().Toggle() // Integers case "InstrumentVoicesAdd": - t.Model.InstrumentVoices().Int().Add(1) + t.Model.InstrumentVoices().Add(1) case "InstrumentVoicesSubtract": - t.Model.InstrumentVoices().Int().Add(-1) + t.Model.InstrumentVoices().Add(-1) case "TrackVoicesAdd": - t.TrackVoices().Int().Add(1) + t.TrackVoices().Add(1) case "TrackVoicesSubtract": - t.TrackVoices().Int().Add(-1) + t.TrackVoices().Add(-1) case "SongLengthAdd": - t.SongLength().Int().Add(1) + t.SongLength().Add(1) case "SongLengthSubtract": - t.SongLength().Int().Add(-1) + t.SongLength().Add(-1) case "BPMAdd": - t.BPM().Int().Add(1) + t.BPM().Add(1) case "BPMSubtract": - t.BPM().Int().Add(-1) + t.BPM().Add(-1) case "RowsPerPatternAdd": - t.RowsPerPattern().Int().Add(1) + t.RowsPerPattern().Add(1) case "RowsPerPatternSubtract": - t.RowsPerPattern().Int().Add(-1) + t.RowsPerPattern().Add(-1) case "RowsPerBeatAdd": - t.RowsPerBeat().Int().Add(1) + t.RowsPerBeat().Add(1) case "RowsPerBeatSubtract": - t.RowsPerBeat().Int().Add(-1) + t.RowsPerBeat().Add(-1) case "StepAdd": - t.Step().Int().Add(1) + t.Step().Add(1) case "StepSubtract": - t.Step().Int().Add(-1) + t.Step().Add(-1) case "OctaveAdd": - t.Octave().Int().Add(1) + t.Octave().Add(1) case "OctaveSubtract": - t.Octave().Int().Add(-1) + t.Octave().Add(-1) // Other miscellaneous case "Paste": gtx.Execute(clipboard.ReadCmd{Tag: t}) diff --git a/tracker/gioui/note_editor.go b/tracker/gioui/note_editor.go index 794c33d..ded7b63 100644 --- a/tracker/gioui/note_editor.go +++ b/tracker/gioui/note_editor.go @@ -76,7 +76,7 @@ type NoteEditor struct { func NewNoteEditor(model *tracker.Model) *NoteEditor { ret := &NoteEditor{ - TrackVoices: NewNumberInput(model.TrackVoices().Int()), + TrackVoices: NewNumberInput(model.TrackVoices()), NewTrackBtn: NewActionClickable(model.AddTrack()), DeleteTrackBtn: NewActionClickable(model.DeleteTrack()), SplitTrackBtn: NewActionClickable(model.SplitTrack()), diff --git a/tracker/gioui/numericupdown.go b/tracker/gioui/numericupdown.go index 374c238..0911f76 100644 --- a/tracker/gioui/numericupdown.go +++ b/tracker/gioui/numericupdown.go @@ -84,7 +84,7 @@ func (s *NumericUpDown) Update(gtx layout.Context) { case pointer.Drag: var deltaCoord float32 deltaCoord = e.Position.X - e.Position.Y - s.NumberInput.dragStartXY - s.NumberInput.Int.Set(s.NumberInput.dragStartValue + int(deltaCoord/pxPerStep+0.5)) + s.NumberInput.Int.SetValue(s.NumberInput.dragStartValue + int(deltaCoord/pxPerStep+0.5)) } } } diff --git a/tracker/gioui/oscilloscope.go b/tracker/gioui/oscilloscope.go index aa671bf..b3843e4 100644 --- a/tracker/gioui/oscilloscope.go +++ b/tracker/gioui/oscilloscope.go @@ -47,8 +47,8 @@ func NewOscilloscope(model *tracker.Model) *OscilloscopeState { return &OscilloscopeState{ onceBtn: NewBoolClickable(model.SignalAnalyzer().Once()), wrapBtn: NewBoolClickable(model.SignalAnalyzer().Wrap()), - lengthInBeatsNumber: NewNumberInput(model.SignalAnalyzer().LengthInBeats().Int()), - triggerChannelNumber: NewNumberInput(model.SignalAnalyzer().TriggerChannel().Int()), + lengthInBeatsNumber: NewNumberInput(model.SignalAnalyzer().LengthInBeats()), + triggerChannelNumber: NewNumberInput(model.SignalAnalyzer().TriggerChannel()), } } diff --git a/tracker/gioui/songpanel.go b/tracker/gioui/songpanel.go index fcc6017..dd8ef30 100644 --- a/tracker/gioui/songpanel.go +++ b/tracker/gioui/songpanel.go @@ -39,11 +39,11 @@ type SongPanel struct { func NewSongPanel(model *tracker.Model) *SongPanel { ret := &SongPanel{ - BPM: NewNumberInput(model.BPM().Int()), - RowsPerPattern: NewNumberInput(model.RowsPerPattern().Int()), - RowsPerBeat: NewNumberInput(model.RowsPerBeat().Int()), - Step: NewNumberInput(model.Step().Int()), - SongLength: NewNumberInput(model.SongLength().Int()), + BPM: NewNumberInput(model.BPM()), + RowsPerPattern: NewNumberInput(model.RowsPerPattern()), + RowsPerBeat: NewNumberInput(model.RowsPerBeat()), + Step: NewNumberInput(model.Step()), + SongLength: NewNumberInput(model.SongLength()), Scope: NewOscilloscope(model), MenuBar: NewMenuBar(model), PlayBar: NewPlayBar(model), @@ -61,7 +61,7 @@ func NewSongPanel(model *tracker.Model) *SongPanel { func (s *SongPanel) Update(gtx C, t *Tracker) { for s.WeightingTypeBtn.Clicked(gtx) { - t.Model.DetectorWeighting().Int().Set((t.DetectorWeighting().Value() + 1) % int(tracker.NumWeightingTypes)) + t.Model.DetectorWeighting().SetValue((t.DetectorWeighting().Value() + 1) % int(tracker.NumWeightingTypes)) } for s.OversamplingBtn.Clicked(gtx) { t.Model.Oversampling().SetValue(!t.Oversampling().Value()) diff --git a/tracker/gioui/tracker.go b/tracker/gioui/tracker.go index b14e2ad..81b67d0 100644 --- a/tracker/gioui/tracker.go +++ b/tracker/gioui/tracker.go @@ -71,8 +71,8 @@ var ZoomFactors = []float32{.25, 1. / 3, .5, 2. / 3, .75, .8, 1, 1.1, 1.25, 1.5, func NewTracker(model *tracker.Model) *Tracker { t := &Tracker{ - OctaveNumberInput: NewNumberInput(model.Octave().Int()), - InstrumentVoices: NewNumberInput(model.InstrumentVoices().Int()), + OctaveNumberInput: NewNumberInput(model.Octave()), + InstrumentVoices: NewNumberInput(model.InstrumentVoices()), TopHorizontalSplit: &Split{Ratio: -.5, MinSize1: 180, MinSize2: 180}, BottomHorizontalSplit: &Split{Ratio: -.6, MinSize1: 180, MinSize2: 180}, diff --git a/tracker/gioui/unit_editor.go b/tracker/gioui/unit_editor.go index 4696d5e..37d7205 100644 --- a/tracker/gioui/unit_editor.go +++ b/tracker/gioui/unit_editor.go @@ -208,22 +208,22 @@ func (pe *UnitEditor) command(e key.Event, t *Tracker) { if sel == nil { return } - i := (&tracker.Int{IntData: sel}) + i := tracker.MakeInt(sel) if e.Modifiers.Contain(key.ModShift) { - i.Set(i.Value() - sel.LargeStep()) + i.SetValue(i.Value() - sel.LargeStep()) } else { - i.Set(i.Value() - 1) + i.SetValue(i.Value() - 1) } case key.NameRightArrow: sel := params.SelectedItem() if sel == nil { return } - i := (&tracker.Int{IntData: sel}) + i := tracker.MakeInt(sel) if e.Modifiers.Contain(key.ModShift) { - i.Set(i.Value() + sel.LargeStep()) + i.SetValue(i.Value() + sel.LargeStep()) } else { - i.Set(i.Value() + 1) + i.SetValue(i.Value() + 1) } case key.NameEscape: t.InstrumentEditor.unitDragList.Focus() @@ -286,7 +286,7 @@ func (p ParameterStyle) Layout(gtx C) D { } if ev, ok := e.(pointer.Event); ok && ev.Kind == pointer.Scroll { delta := math.Min(math.Max(float64(ev.Scroll.Y), -1), 1) - tracker.Int{IntData: p.w.Parameter}.Add(-int(delta)) + tracker.MakeInt(p.w.Parameter).Add(-int(delta)) } } gtx.Constraints.Min.X = gtx.Dp(unit.Dp(200)) @@ -306,7 +306,7 @@ func (p ParameterStyle) Layout(gtx C) D { event.Op(gtx.Ops, &p.w.floatWidget) } dims := sliderStyle.Layout(gtx) - tracker.Int{IntData: p.w.Parameter}.Set(int(p.w.floatWidget.Value*float32(ra.Max-ra.Min) + float32(ra.Min) + 0.5)) + tracker.MakeInt(p.w.Parameter).SetValue(int(p.w.floatWidget.Value*float32(ra.Max-ra.Min) + float32(ra.Min) + 0.5)) return dims case tracker.BoolParameter: gtx.Constraints.Min.X = gtx.Dp(unit.Dp(60)) @@ -318,9 +318,9 @@ func (p ParameterStyle) Layout(gtx C) D { defer pointer.PassOp{}.Push(gtx.Ops).Pop() dims := layout.Center.Layout(gtx, boolStyle.Layout) if p.w.boolWidget.Value { - tracker.Int{IntData: p.w.Parameter}.Set(ra.Max) + tracker.MakeInt(p.w.Parameter).SetValue(ra.Max) } else { - tracker.Int{IntData: p.w.Parameter}.Set(ra.Min) + tracker.MakeInt(p.w.Parameter).SetValue(ra.Min) } return dims case tracker.IDParameter: @@ -334,7 +334,7 @@ func (p ParameterStyle) Layout(gtx C) D { instrItems[i].IconBytes = icons.NavigationChevronRight instrItems[i].Doer = tracker.MakeEnabledAction((tracker.DoFunc)(func() { if id, ok := p.tracker.Instruments().FirstID(i); ok { - tracker.Int{IntData: p.w.Parameter}.Set(id) + tracker.MakeInt(p.w.Parameter).SetValue(id) } })) } @@ -351,7 +351,7 @@ func (p ParameterStyle) Layout(gtx C) D { unitItems[j].Text = buildUnitLabel(j, unit) unitItems[j].IconBytes = icons.NavigationChevronRight unitItems[j].Doer = tracker.MakeEnabledAction((tracker.DoFunc)(func() { - tracker.Int{IntData: p.w.Parameter}.Set(id) + tracker.MakeInt(p.w.Parameter).SetValue(id) })) } } diff --git a/tracker/int.go b/tracker/int.go index 63ef405..0c1459a 100644 --- a/tracker/int.go +++ b/tracker/int.go @@ -5,147 +5,151 @@ import ( ) type ( + // Int represents an integer value in the tracker model e.g. BPM, song + // length, etc. It is a wrapper around an IntValue interface that provides + // methods to manipulate the value, but Int guard that all changes are + // within the range of the underlying IntValue implementation and that + // SetValue is not called when the value is unchanged. Int struct { - IntData + value IntValue } - IntData interface { + IntValue interface { Value() int - Range() intRange - - setValue(int) - change(kind string) func() + SetValue(int) bool // returns true if the value was changed + Range() IntRange } - intRange struct { + IntRange struct { Min, Max int } - InstrumentVoices Model - TrackVoices Model - SongLength Model - BPM Model - RowsPerPattern Model - RowsPerBeat Model - Step Model - Octave Model - + InstrumentVoices Model + TrackVoices Model + SongLength Model + BPM Model + RowsPerPattern Model + RowsPerBeat Model + Step Model + Octave Model DetectorWeighting Model ) +func MakeInt(value IntValue) Int { + return Int{value} +} + func (v Int) Add(delta int) (ok bool) { + return v.SetValue(v.Value() + delta) +} + +func (v Int) SetValue(value int) (ok bool) { r := v.Range() - value := r.Clamp(v.Value() + delta) + value = r.Clamp(value) if value == v.Value() || value < r.Min || value > r.Max { return false } - defer v.change("Add")() - v.setValue(value) - return true + return v.value.SetValue(value) } -func (v Int) Set(value int) (ok bool) { - r := v.Range() - value = v.Range().Clamp(value) - if value == v.Value() || value < r.Min || value > r.Max { - return false +func (v Int) Range() IntRange { + if v.value == nil { + return IntRange{0, 0} } - defer v.change("Set")() - v.setValue(value) - return true + return v.value.Range() } -func (r intRange) Clamp(value int) int { +func (v Int) Value() int { + if v.value == nil { + return 0 + } + return v.value.Value() +} + +func (r IntRange) Clamp(value int) int { return max(min(value, r.Max), r.Min) } // Model methods -func (m *Model) InstrumentVoices() *InstrumentVoices { return (*InstrumentVoices)(m) } -func (m *Model) TrackVoices() *TrackVoices { return (*TrackVoices)(m) } -func (m *Model) SongLength() *SongLength { return (*SongLength)(m) } -func (m *Model) BPM() *BPM { return (*BPM)(m) } -func (m *Model) RowsPerPattern() *RowsPerPattern { return (*RowsPerPattern)(m) } -func (m *Model) RowsPerBeat() *RowsPerBeat { return (*RowsPerBeat)(m) } -func (m *Model) Step() *Step { return (*Step)(m) } -func (m *Model) Octave() *Octave { return (*Octave)(m) } -func (m *Model) DetectorWeighting() *DetectorWeighting { return (*DetectorWeighting)(m) } +func (m *Model) BPM() Int { return MakeInt((*BPM)(m)) } +func (m *Model) InstrumentVoices() Int { return MakeInt((*InstrumentVoices)(m)) } +func (m *Model) TrackVoices() Int { return MakeInt((*TrackVoices)(m)) } +func (m *Model) SongLength() Int { return MakeInt((*SongLength)(m)) } +func (m *Model) RowsPerPattern() Int { return MakeInt((*RowsPerPattern)(m)) } +func (m *Model) RowsPerBeat() Int { return MakeInt((*RowsPerBeat)(m)) } +func (m *Model) Step() Int { return MakeInt((*Step)(m)) } +func (m *Model) Octave() Int { return MakeInt((*Octave)(m)) } +func (m *Model) DetectorWeighting() Int { return MakeInt((*DetectorWeighting)(m)) } // BeatsPerMinuteInt -func (v *BPM) Int() Int { return Int{v} } -func (v *BPM) Value() int { return v.d.Song.BPM } -func (v *BPM) setValue(value int) { v.d.Song.BPM = value } -func (v *BPM) Range() intRange { return intRange{1, 999} } -func (v *BPM) change(kind string) func() { - return (*Model)(v).change("BPMInt."+kind, SongChange, MinorChange) +func (v *BPM) Value() int { return v.d.Song.BPM } +func (v *BPM) SetValue(value int) bool { + defer (*Model)(v).change("BPMInt", SongChange, MinorChange)() + v.d.Song.BPM = value + return true } +func (v *BPM) Range() IntRange { return IntRange{1, 999} } // RowsPerPatternInt -func (v *RowsPerPattern) Int() Int { return Int{v} } -func (v *RowsPerPattern) Value() int { return v.d.Song.Score.RowsPerPattern } -func (v *RowsPerPattern) setValue(value int) { v.d.Song.Score.RowsPerPattern = value } -func (v *RowsPerPattern) Range() intRange { return intRange{1, 256} } -func (v *RowsPerPattern) change(kind string) func() { - return (*Model)(v).change("RowsPerPatternInt."+kind, SongChange, MinorChange) +func (v *RowsPerPattern) Value() int { return v.d.Song.Score.RowsPerPattern } +func (v *RowsPerPattern) SetValue(value int) bool { + defer (*Model)(v).change("RowsPerPatternInt", SongChange, MinorChange)() + v.d.Song.Score.RowsPerPattern = value + return true } +func (v *RowsPerPattern) Range() IntRange { return IntRange{1, 256} } // SongLengthInt -func (v *SongLength) Int() Int { return Int{v} } -func (v *SongLength) Value() int { return v.d.Song.Score.Length } -func (v *SongLength) setValue(value int) { v.d.Song.Score.Length = value } -func (v *SongLength) Range() intRange { return intRange{1, math.MaxInt32} } -func (v *SongLength) change(kind string) func() { - return (*Model)(v).change("SongLengthInt."+kind, SongChange, MinorChange) +func (v *SongLength) Value() int { return v.d.Song.Score.Length } +func (v *SongLength) SetValue(value int) bool { + defer (*Model)(v).change("SongLengthInt", SongChange, MinorChange)() + v.d.Song.Score.Length = value + return true } +func (v *SongLength) Range() IntRange { return IntRange{1, math.MaxInt32} } // StepInt -func (v *Step) Int() Int { return Int{v} } -func (v *Step) Value() int { return v.d.Step } -func (v *Step) setValue(value int) { v.d.Step = value } -func (v *Step) Range() intRange { return intRange{0, 8} } -func (v *Step) change(kind string) func() { - return (*Model)(v).change("StepInt"+kind, NoChange, MinorChange) +func (v *Step) Value() int { return v.d.Step } +func (v *Step) SetValue(value int) bool { + defer (*Model)(v).change("StepInt", NoChange, MinorChange)() + v.d.Step = value + return true } +func (v *Step) Range() IntRange { return IntRange{0, 8} } // OctaveInt -func (v *Octave) Int() Int { return Int{v} } -func (v *Octave) Value() int { return v.d.Octave } -func (v *Octave) setValue(value int) { v.d.Octave = value } -func (v *Octave) Range() intRange { return intRange{0, 9} } -func (v *Octave) change(kind string) func() { return func() {} } +func (v *Octave) Value() int { return v.d.Octave } +func (v *Octave) SetValue(value int) bool { v.d.Octave = value; return true } +func (v *Octave) Range() IntRange { return IntRange{0, 9} } // RowsPerBeatInt -func (v *RowsPerBeat) Int() Int { return Int{v} } -func (v *RowsPerBeat) Value() int { return v.d.Song.RowsPerBeat } -func (v *RowsPerBeat) setValue(value int) { v.d.Song.RowsPerBeat = value } -func (v *RowsPerBeat) Range() intRange { return intRange{1, 32} } -func (v *RowsPerBeat) change(kind string) func() { - return (*Model)(v).change("RowsPerBeatInt."+kind, SongChange, MinorChange) +func (v *RowsPerBeat) Value() int { return v.d.Song.RowsPerBeat } +func (v *RowsPerBeat) SetValue(value int) bool { + defer (*Model)(v).change("RowsPerBeatInt", SongChange, MinorChange)() + v.d.Song.RowsPerBeat = value + return true } +func (v *RowsPerBeat) Range() IntRange { return IntRange{1, 32} } // ModelLoudnessType -func (v *DetectorWeighting) Int() Int { return Int{v} } func (v *DetectorWeighting) Value() int { return int(v.weightingType) } -func (v *DetectorWeighting) setValue(value int) { +func (v *DetectorWeighting) SetValue(value int) bool { v.weightingType = WeightingType(value) TrySend(v.broker.ToDetector, MsgToDetector{HasWeightingType: true, WeightingType: WeightingType(value)}) + return true } -func (v *DetectorWeighting) Range() intRange { return intRange{0, int(NumLoudnessTypes) - 1} } -func (v *DetectorWeighting) change(kind string) func() { return func() {} } +func (v *DetectorWeighting) Range() IntRange { return IntRange{0, int(NumLoudnessTypes) - 1} } // InstrumentVoicesInt -func (v *InstrumentVoices) Int() Int { - return Int{v} -} - func (v *InstrumentVoices) Value() int { if v.d.InstrIndex < 0 || v.d.InstrIndex >= len(v.d.Song.Patch) { return 1 @@ -153,10 +157,11 @@ func (v *InstrumentVoices) Value() int { return max(v.d.Song.Patch[v.d.InstrIndex].NumVoices, 1) } -func (m *InstrumentVoices) setValue(value int) { +func (m *InstrumentVoices) SetValue(value int) bool { if m.d.InstrIndex < 0 || m.d.InstrIndex >= len(m.d.Song.Patch) { - return + return false } + defer (*Model)(m).change("InstrumentVoices", SongChange, MinorChange)() voiceIndex := m.d.Song.Patch.FirstVoiceForInstrument(m.d.InstrIndex) voiceRange := Range{voiceIndex, voiceIndex + m.d.Song.Patch[m.d.InstrIndex].NumVoices} ranges := MakeSetLength(voiceRange, value) @@ -164,22 +169,15 @@ func (m *InstrumentVoices) setValue(value int) { if !ok { m.changeCancel = true } + return ok } -func (v *InstrumentVoices) Range() intRange { - return intRange{1, (*Model)(v).remainingVoices(true, v.linkInstrTrack) + v.Value()} -} - -func (v *InstrumentVoices) change(kind string) func() { - return (*Model)(v).change("InstrumentVoices."+kind, SongChange, MinorChange) +func (v *InstrumentVoices) Range() IntRange { + return IntRange{1, (*Model)(v).remainingVoices(true, v.linkInstrTrack) + v.Value()} } // TrackVoicesInt -func (v *TrackVoices) Int() Int { - return Int{v} -} - func (v *TrackVoices) Value() int { t := v.d.Cursor.Track if t < 0 || t >= len(v.d.Song.Score.Tracks) { @@ -188,7 +186,8 @@ func (v *TrackVoices) Value() int { return max(v.d.Song.Score.Tracks[t].NumVoices, 1) } -func (m *TrackVoices) setValue(value int) { +func (m *TrackVoices) SetValue(value int) bool { + defer (*Model)(m).change("TrackVoices", SongChange, MinorChange)() voiceIndex := m.d.Song.Score.FirstVoiceForTrack(m.d.Cursor.Track) voiceRange := Range{voiceIndex, voiceIndex + m.d.Song.Score.Tracks[m.d.Cursor.Track].NumVoices} ranges := MakeSetLength(voiceRange, value) @@ -196,16 +195,13 @@ func (m *TrackVoices) setValue(value int) { if !ok { m.changeCancel = true } + return ok } -func (v *TrackVoices) Range() intRange { +func (v *TrackVoices) Range() IntRange { t := v.d.Cursor.Track if t < 0 || t >= len(v.d.Song.Score.Tracks) { - return intRange{1, 1} + return IntRange{1, 1} } - return intRange{1, (*Model)(v).remainingVoices(v.linkInstrTrack, true) + v.d.Song.Score.Tracks[t].NumVoices} -} - -func (v *TrackVoices) change(kind string) func() { - return (*Model)(v).change("TrackVoices."+kind, SongChange, MinorChange) + return IntRange{1, (*Model)(v).remainingVoices(v.linkInstrTrack, true) + v.d.Song.Score.Tracks[t].NumVoices} } diff --git a/tracker/model_test.go b/tracker/model_test.go index 773de16..65ab4bc 100644 --- a/tracker/model_test.go +++ b/tracker/model_test.go @@ -46,14 +46,14 @@ func (mwc *myWriteCloser) Close() error { func (s *modelFuzzState) Iterate(yield func(string, func(p string, t *testing.T)) bool, seed int) { // Ints - s.IterateInt("InstrumentVoices", s.model.InstrumentVoices().Int(), yield, seed) - s.IterateInt("TrackVoices", s.model.TrackVoices().Int(), yield, seed) - s.IterateInt("SongLength", s.model.SongLength().Int(), yield, seed) - s.IterateInt("BPM", s.model.BPM().Int(), yield, seed) - s.IterateInt("RowsPerPattern", s.model.RowsPerPattern().Int(), yield, seed) - s.IterateInt("RowsPerBeat", s.model.RowsPerBeat().Int(), yield, seed) - s.IterateInt("Step", s.model.Step().Int(), yield, seed) - s.IterateInt("Octave", s.model.Octave().Int(), yield, seed) + s.IterateInt("InstrumentVoices", s.model.InstrumentVoices(), yield, seed) + s.IterateInt("TrackVoices", s.model.TrackVoices(), yield, seed) + s.IterateInt("SongLength", s.model.SongLength(), yield, seed) + s.IterateInt("BPM", s.model.BPM(), yield, seed) + s.IterateInt("RowsPerPattern", s.model.RowsPerPattern(), yield, seed) + s.IterateInt("RowsPerBeat", s.model.RowsPerBeat(), yield, seed) + s.IterateInt("Step", s.model.Step(), yield, seed) + s.IterateInt("Octave", s.model.Octave(), yield, seed) // Lists s.IterateList("Instruments", s.model.Instruments().List(), yield, seed) s.IterateList("Units", s.model.Units().List(), yield, seed) @@ -135,7 +135,7 @@ func (s *modelFuzzState) Iterate(yield func(string, func(p string, t *testing.T) func (s *modelFuzzState) IterateInt(name string, i tracker.Int, yield func(string, func(p string, t *testing.T)) bool, seed int) { r := i.Range() yield(name+".Set", func(p string, t *testing.T) { - i.Set(seed%(r.Max-r.Min+10) - 5 + r.Min) + i.SetValue(seed%(r.Max-r.Min+10) - 5 + r.Min) }) yield(name+".Value", func(p string, t *testing.T) { if v := i.Value(); v < r.Min || v > r.Max { diff --git a/tracker/params.go b/tracker/params.go index ee699d3..950b9ad 100644 --- a/tracker/params.go +++ b/tracker/params.go @@ -12,7 +12,7 @@ import ( type ( Parameter interface { - IntData + IntValue Type() ParameterType Name() string Hint() ParameterHint @@ -61,12 +61,6 @@ const ( func (m *Model) Params() *Params { return (*Params)(m) } -// parameter methods - -func (p parameter) change(kind string) func() { - return p.m.change("Parameter."+kind, PatchChange, MinorChange) -} - // ParamList func (pl *Params) List() List { return List{pl} } @@ -151,17 +145,21 @@ func (pl *Params) Iterate(yield ParamYieldFunc) { // NamedParameter -func (p NamedParameter) Name() string { return p.up.Name } -func (p NamedParameter) Range() intRange { return intRange{Min: p.up.MinValue, Max: p.up.MaxValue} } -func (p NamedParameter) Value() int { return p.unit.Parameters[p.up.Name] } -func (p NamedParameter) setValue(value int) { p.unit.Parameters[p.up.Name] = value } +func (p NamedParameter) Name() string { return p.up.Name } +func (p NamedParameter) Range() IntRange { return IntRange{Min: p.up.MinValue, Max: p.up.MaxValue} } +func (p NamedParameter) Value() int { return p.unit.Parameters[p.up.Name] } +func (p NamedParameter) SetValue(value int) bool { + defer p.m.change("Parameter"+p.Name(), PatchChange, MinorChange)() + p.unit.Parameters[p.up.Name] = value + return true +} func (p NamedParameter) Reset() { v, ok := defaultUnits[p.unit.Type].Parameters[p.up.Name] if !ok || p.unit.Parameters[p.up.Name] == v { return } - defer p.parameter.change("Reset")() + defer p.m.change("Reset"+p.Name(), PatchChange, MinorChange)() p.unit.Parameters[p.up.Name] = v } @@ -220,7 +218,7 @@ func (p NamedParameter) Unit() sointu.Unit { func (p GmDlsEntryParameter) Name() string { return "sample" } func (p GmDlsEntryParameter) Type() ParameterType { return IntegerParameter } -func (p GmDlsEntryParameter) Range() intRange { return intRange{Min: 0, Max: len(GmDlsEntries)} } +func (p GmDlsEntryParameter) Range() IntRange { return IntRange{Min: 0, Max: len(GmDlsEntries)} } func (p GmDlsEntryParameter) LargeStep() int { return 16 } func (p GmDlsEntryParameter) Reset() { return } @@ -232,15 +230,17 @@ func (p GmDlsEntryParameter) Value() int { return 0 } -func (p GmDlsEntryParameter) setValue(v int) { +func (p GmDlsEntryParameter) SetValue(v int) bool { if v < 1 || v > len(GmDlsEntries) { - return + return false } + defer p.m.change("GmDlsEntryParameter", PatchChange, MinorChange)() e := GmDlsEntries[v-1] p.unit.Parameters["samplestart"] = e.Start p.unit.Parameters["loopstart"] = e.LoopStart p.unit.Parameters["looplength"] = e.LoopLength p.unit.Parameters["transpose"] = 64 + e.SuggestedTranspose + return true } func (p GmDlsEntryParameter) Hint() ParameterHint { @@ -265,15 +265,17 @@ func (p DelayTimeParameter) Value() int { return p.unit.VarArgs[p.index] } -func (p DelayTimeParameter) setValue(v int) { +func (p DelayTimeParameter) SetValue(v int) bool { + defer p.m.change("DelayTimeParameter", PatchChange, MinorChange)() p.unit.VarArgs[p.index] = v + return true } -func (p DelayTimeParameter) Range() intRange { +func (p DelayTimeParameter) Range() IntRange { if p.unit.Parameters["notetracking"] == 2 { - return intRange{Min: 1, Max: 576} + return IntRange{Min: 1, Max: 576} } - return intRange{Min: 1, Max: 65535} + return IntRange{Min: 1, Max: 65535} } func (p DelayTimeParameter) Hint() ParameterHint { @@ -325,7 +327,7 @@ func (p DelayTimeParameter) Hint() ParameterHint { func (p DelayLinesParameter) Name() string { return "delaylines" } func (p DelayLinesParameter) Type() ParameterType { return IntegerParameter } -func (p DelayLinesParameter) Range() intRange { return intRange{Min: 1, Max: 32} } +func (p DelayLinesParameter) Range() IntRange { return IntRange{Min: 1, Max: 32} } func (p DelayLinesParameter) LargeStep() int { return 4 } func (p DelayLinesParameter) Reset() { return } @@ -341,7 +343,8 @@ func (p DelayLinesParameter) Value() int { return val } -func (p DelayLinesParameter) setValue(v int) { +func (p DelayLinesParameter) SetValue(v int) bool { + defer p.m.change("DelayLinesParameter", PatchChange, MinorChange)() targetLines := v if p.unit.Parameters["stereo"] == 1 { targetLines *= 2 @@ -350,13 +353,14 @@ func (p DelayLinesParameter) setValue(v int) { p.unit.VarArgs = append(p.unit.VarArgs, 1) } p.unit.VarArgs = p.unit.VarArgs[:targetLines] + return true } // ReverbParameter func (p ReverbParameter) Name() string { return "reverb" } func (p ReverbParameter) Type() ParameterType { return IntegerParameter } -func (p ReverbParameter) Range() intRange { return intRange{Min: 0, Max: len(reverbs)} } +func (p ReverbParameter) Range() IntRange { return IntRange{Min: 0, Max: len(reverbs)} } func (p ReverbParameter) LargeStep() int { return 1 } func (p ReverbParameter) Reset() { return } @@ -367,15 +371,17 @@ func (p ReverbParameter) Value() int { return i + 1 } -func (p ReverbParameter) setValue(v int) { +func (p ReverbParameter) SetValue(v int) bool { if v < 1 || v > len(reverbs) { - return + return false } + defer p.m.change("ReverbParameter", PatchChange, MinorChange)() entry := reverbs[v-1] p.unit.Parameters["stereo"] = entry.stereo p.unit.Parameters["notetracking"] = 0 p.unit.VarArgs = make([]int, len(entry.varArgs)) copy(p.unit.VarArgs, entry.varArgs) + return true } func (p ReverbParameter) Hint() ParameterHint { diff --git a/tracker/scopemodel.go b/tracker/scopemodel.go index ee51074..0acbf3f 100644 --- a/tracker/scopemodel.go +++ b/tracker/scopemodel.go @@ -67,10 +67,10 @@ func NewScopeModel(broker *Broker, bpm int) *ScopeModel { func (s *ScopeModel) Waveform() RingBuffer[[2]float32] { return s.waveForm } -func (s *ScopeModel) Once() Bool { return MakeEnabledBool((*SignalOnce)(s)) } -func (s *ScopeModel) Wrap() Bool { return MakeEnabledBool((*SignalWrap)(s)) } -func (s *ScopeModel) LengthInBeats() *SignalLengthInBeats { return (*SignalLengthInBeats)(s) } -func (s *ScopeModel) TriggerChannel() *TriggerChannel { return (*TriggerChannel)(s) } +func (s *ScopeModel) Once() Bool { return MakeEnabledBool((*SignalOnce)(s)) } +func (s *ScopeModel) Wrap() Bool { return MakeEnabledBool((*SignalWrap)(s)) } +func (s *ScopeModel) LengthInBeats() Int { return MakeInt((*SignalLengthInBeats)(s)) } +func (s *ScopeModel) TriggerChannel() Int { return MakeInt((*TriggerChannel)(s)) } func (m *SignalOnce) Value() bool { return m.once } func (m *SignalOnce) SetValue(val bool) { m.once = val } @@ -78,22 +78,17 @@ func (m *SignalOnce) SetValue(val bool) { m.once = val } func (m *SignalWrap) Value() bool { return m.wrap } func (m *SignalWrap) SetValue(val bool) { m.wrap = val } -func (m *SignalLengthInBeats) Int() Int { return Int{m} } func (m *SignalLengthInBeats) Value() int { return m.lengthInBeats } -func (m *SignalLengthInBeats) setValue(val int) { +func (m *SignalLengthInBeats) SetValue(val int) bool { m.lengthInBeats = val (*ScopeModel)(m).updateBufferLength() + return true } -func (m *SignalLengthInBeats) Enabled() bool { return true } -func (m *SignalLengthInBeats) Range() intRange { return intRange{1, 999} } -func (m *SignalLengthInBeats) change(string) func() { return func() {} } +func (m *SignalLengthInBeats) Range() IntRange { return IntRange{1, 999} } -func (m *TriggerChannel) Int() Int { return Int{m} } -func (m *TriggerChannel) Value() int { return m.triggerChannel } -func (m *TriggerChannel) setValue(val int) { m.triggerChannel = val } -func (m *TriggerChannel) Enabled() bool { return true } -func (m *TriggerChannel) Range() intRange { return intRange{0, vm.MAX_VOICES} } -func (m *TriggerChannel) change(string) func() { return func() {} } +func (m *TriggerChannel) Value() int { return m.triggerChannel } +func (m *TriggerChannel) SetValue(val int) bool { m.triggerChannel = val; return true } +func (m *TriggerChannel) Range() IntRange { return IntRange{0, vm.MAX_VOICES} } func (s *ScopeModel) ProcessAudioBuffer(bufPtr *sointu.AudioBuffer) { if s.wrap {