diff --git a/patch.go b/patch.go index 1e16148..c30f7c6 100644 --- a/patch.go +++ b/patch.go @@ -465,21 +465,19 @@ func (p Patch) FindUnit(id int) (instrIndex int, unitIndex int, err error) { return 0, 0, fmt.Errorf("could not find a unit with id %v", id) } -func FindParamForModulationPort(unitName string, index int) *UnitParameter { - // qm210: couldn't see a function yet that matches the parameter index to the modulateable param. - // Not sure whether *UnitParameters is good here, would this make them mutable? +func FindParamForModulationPort(unitName string, index int) (UnitParameter, bool) { unitType, ok := UnitTypes[unitName] if !ok { - return nil + return UnitParameter{}, false } for _, param := range unitType { - if index == 0 { - return ¶m + if !param.CanModulate { + continue } - if param.CanModulate { - index-- + if index == 0 { + return param, true } + index-- } - // index outside range - return nil + return UnitParameter{}, false } diff --git a/tracker/derived.go b/tracker/derived.go index e57c8b6..95a4a3c 100644 --- a/tracker/derived.go +++ b/tracker/derived.go @@ -1,6 +1,7 @@ package tracker import ( + "fmt" "github.com/vsariola/sointu" "iter" "slices" @@ -13,10 +14,34 @@ import ( */ type ( + derivedModelData struct { + // map Unit by ID, other entities by their respective index + forUnit map[int]derivedForUnit + forTrack []derivedForTrack + forPattern []derivedForPattern + } + derivedForUnit struct { - unit *sointu.Unit - instrument *sointu.Instrument - sends []*sointu.Unit + unit sointu.Unit + instrument sointu.Instrument + instrumentIndex int + unitIndex int + + // map param by Name + forParameter map[string]derivedForParameter + } + + derivedForParameter struct { + sendTooltip string + sendSources []sendSourceData + } + + sendSourceData struct { + unitId int + paramName string + amount int + instrumentIndex int + instrumentName string } derivedForTrack struct { @@ -25,68 +50,75 @@ type ( title string } - derivedModelData struct { - // map unit by ID - forUnit map[int]derivedForUnit - // map track by index - forTrack map[int]derivedForTrack + derivedForPattern struct { + useCount []int } ) // public access functions -func (m *Model) forUnitById(id int) *derivedForUnit { +func (m *Model) InstrumentForUnit(id int) (sointu.Instrument, int, bool) { forUnit, ok := m.derived.forUnit[id] if !ok { - return nil + return sointu.Instrument{}, -1, false } - return &forUnit + return forUnit.instrument, forUnit.instrumentIndex, true } -func (m *Model) InstrumentForUnit(id int) *sointu.Instrument { - fu := m.forUnitById(id) - if fu == nil { - return nil - } - return fu.instrument +func (m *Model) UnitInfo(id int) (instrName string, units []sointu.Unit, unitIndex int, ok bool) { + fu, ok := m.derived.forUnit[id] + return fu.instrument.Name, fu.instrument.Units, fu.unitIndex, ok } -func (m *Model) UnitById(id int) *sointu.Unit { - fu := m.forUnitById(id) - if fu == nil { - return nil +func (m *Model) UnitHintInfo(id int) (instrIndex int, unitType string, ok bool) { + fu, ok := m.derived.forUnit[id] + return fu.instrumentIndex, fu.unit.Type, ok +} + +func (m *Model) ParameterInfo(unitId int, paramName string) (isSendTarget bool, tooltip string, exists bool) { + du, ok1 := m.derived.forUnit[unitId] + if !ok1 { + return false, "", false + } + dp, ok2 := du.forParameter[paramName] + if !ok2 { + return false, "", false } - return fu.unit + return len(dp.sendSources) > 0, dp.sendTooltip, true } -func (m *Model) SendTargetsForUnit(id int) []*sointu.Unit { - fu := m.forUnitById(id) - if fu == nil { - return nil +func (m *Model) TrackTitle(index int) string { + if index < 0 || index > len(m.derived.forTrack) { + return "" } - return fu.sends + return m.derived.forTrack[index].title } -func (m *Model) forTrackByIndex(index int) *derivedForTrack { - forTrack, ok := m.derived.forTrack[index] - if !ok { +func (m *Model) PatternUseCount(index int) []int { + if index < 0 || index > len(m.derived.forPattern) { return nil } - return &forTrack + return m.derived.forPattern[index].useCount } -func (m *Model) TrackTitle(index int) string { - ft := m.forTrackByIndex(index) - if ft == nil { - return "" +func (m *Model) PatternUnique(t, p int) bool { + if t < 0 || t > len(m.derived.forPattern) { + return false } - return ft.title + forPattern := m.derived.forPattern[t] + if p < 0 || p >= len(forPattern.useCount) { + return false + } + return forPattern.useCount[p] == 1 } // public getters with further model information func (m *Model) TracksWithSameInstrumentAsCurrent() []int { currentTrack := m.d.Cursor.Track + if currentTrack > len(m.derived.forTrack) { + return nil + } return m.derived.forTrack[currentTrack].tracksWithSameInstrument } @@ -105,41 +137,68 @@ func (m *Model) CountNextTracksForCurrentInstrument() int { func (m *Model) initDerivedData() { m.derived = derivedModelData{ - forUnit: make(map[int]derivedForUnit), - forTrack: make(map[int]derivedForTrack), + forUnit: make(map[int]derivedForUnit), + forTrack: make([]derivedForTrack, 0), + forPattern: make([]derivedForPattern, 0), } m.updateDerivedScoreData() m.updateDerivedPatchData() } func (m *Model) updateDerivedScoreData() { - for index, _ := range m.d.Song.Score.Tracks { + m.derived.forTrack = m.derived.forTrack[:0] + m.derived.forPattern = m.derived.forPattern[:0] + for index, track := range m.d.Song.Score.Tracks { firstInstr, lastInstr, _ := m.instrumentRangeFor(index) - m.derived.forTrack[index] = derivedForTrack{ - instrumentRange: []int{firstInstr, lastInstr}, - tracksWithSameInstrument: slices.Collect(m.tracksWithSameInstrument(index)), - title: m.buildTrackTitle(index), - } + m.derived.forTrack = append( + m.derived.forTrack, + derivedForTrack{ + instrumentRange: []int{firstInstr, lastInstr}, + tracksWithSameInstrument: slices.Collect(m.tracksWithSameInstrument(index)), + title: m.buildTrackTitle(index), + }, + ) + m.derived.forPattern = append( + m.derived.forPattern, + derivedForPattern{ + useCount: m.calcPatternUseCounts(track), + }, + ) } } func (m *Model) updateDerivedPatchData() { - for _, instr := range m.d.Song.Patch { - for _, unit := range instr.Units { + clear(m.derived.forUnit) + for i, instr := range m.d.Song.Patch { + for u, unit := range instr.Units { m.derived.forUnit[unit.ID] = derivedForUnit{ - unit: &unit, - instrument: &instr, - sends: slices.Collect(m.collectSendsTo(unit)), + unit: unit, + unitIndex: u, + instrument: instr, + instrumentIndex: i, + forParameter: make(map[string]derivedForParameter), } + m.updateDerivedParameterData(unit) + } + } +} + +func (m *Model) updateDerivedParameterData(unit sointu.Unit) { + fu, _ := m.derived.forUnit[unit.ID] + for name := range fu.unit.Parameters { + sendSources := slices.Collect(m.collectSendSources(unit, name)) + fu.forParameter[name] = derivedForParameter{ + sendSources: sendSources, + sendTooltip: m.buildSendTargetTooltip(fu.instrumentIndex, sendSources), } } } // internals... -func (m *Model) collectSendsTo(unit sointu.Unit) iter.Seq[*sointu.Unit] { - return func(yield func(*sointu.Unit) bool) { - for _, instr := range m.d.Song.Patch { +func (m *Model) collectSendSources(unit sointu.Unit, paramName string) iter.Seq[sendSourceData] { + return func(yield func(sendSourceData) bool) { + for i, instr := range m.d.Song.Patch { for _, u := range instr.Units { if u.Type != "send" { continue @@ -148,7 +207,19 @@ func (m *Model) collectSendsTo(unit sointu.Unit) iter.Seq[*sointu.Unit] { if !ok || targetId != unit.ID { continue } - if !yield(&u) { + port := u.Parameters["port"] + unitParam, ok := sointu.FindParamForModulationPort(unit.Type, port) + if !ok || unitParam.Name != paramName { + continue + } + sourceData := sendSourceData{ + unitId: u.ID, + paramName: paramName, + instrumentIndex: i, + instrumentName: instr.Name, + amount: u.Parameters["amount"], + } + if !yield(sourceData) { return } } @@ -156,8 +227,34 @@ func (m *Model) collectSendsTo(unit sointu.Unit) iter.Seq[*sointu.Unit] { } } +func (m *Model) buildSendTargetTooltip(ownInstrIndex int, sendSources []sendSourceData) string { + if len(sendSources) == 0 { + return "" + } + amounts := "" + for _, sendSource := range sendSources { + sourceInfo := "" + if sendSource.instrumentIndex != ownInstrIndex { + sourceInfo = fmt.Sprintf(" from \"%s\"", sendSource.instrumentName) + } + if amounts == "" { + amounts = fmt.Sprintf("x %d%s", sendSource.amount, sourceInfo) + } else { + amounts = fmt.Sprintf("%s, x %d%s", amounts, sendSource.amount, sourceInfo) + } + } + count := "1 send" + if len(sendSources) > 1 { + count = fmt.Sprintf("%d sends", len(sendSources)) + } + return fmt.Sprintf("%s [%s]", count, amounts) +} + func (m *Model) instrumentRangeFor(trackIndex int) (int, int, error) { track := m.d.Song.Score.Tracks[trackIndex] + if track.NumVoices <= 0 { + return 0, 0, fmt.Errorf("track %d has no voices", trackIndex) + } firstVoice := m.d.Song.Score.FirstVoiceForTrack(trackIndex) lastVoice := firstVoice + track.NumVoices - 1 firstIndex, err1 := m.d.Song.Patch.InstrumentForVoice(firstVoice) @@ -233,3 +330,24 @@ func (m *Model) tracksWithSameInstrument(trackIndex int) iter.Seq[int] { } } } + +func (m *Model) calcPatternUseCounts(track sointu.Track) []int { + result := make([]int, len(m.d.Song.Score.Tracks)) + for j, _ := range result { + result[j] = 0 + } + for j := 0; j < m.d.Song.Score.Length; j++ { + if j >= len(track.Order) { + break + } + p := track.Order[j] + for len(result) <= p { + result = append(result, 0) + } + if p < 0 { + continue + } + result[p]++ + } + return result +} diff --git a/tracker/gioui/note_editor.go b/tracker/gioui/note_editor.go index f03dea0..0006878 100644 --- a/tracker/gioui/note_editor.go +++ b/tracker/gioui/note_editor.go @@ -293,7 +293,7 @@ func (te *NoteEditor) layoutTracks(gtx C, t *Tracker) D { } // draw the corresponding "fake cursors" for instrument-track-groups (for polyphony) if hasTrackMidiIn { - for trackIndex := range t.Model.TracksWithSameInstrumentAsCurrent() { + for _, trackIndex := range t.Model.TracksWithSameInstrumentAsCurrent() { if x == trackIndex && y == cursor.Y { te.paintColumnCell(gtx, x, t, cursorNeighborForTrackMidiInColor) } @@ -310,7 +310,7 @@ func (te *NoteEditor) layoutTracks(gtx C, t *Tracker) D { paint.ColorOp{Color: trackerPatMarker}.Add(gtx.Ops) widget.Label{}.Layout(gtx, t.Theme.Shaper, trackerFont, trackerFontSize, patternIndexToString(s), op.CallOp{}) } - if row == 1 && t.Model.Notes().Unique(x, s) { // draw a * if the pattern is unique + if row == 1 && t.Model.PatternUnique(x, s) { // draw a * if the pattern is unique paint.ColorOp{Color: mediumEmphasisTextColor}.Add(gtx.Ops) widget.Label{}.Layout(gtx, t.Theme.Shaper, trackerFont, trackerFontSize, "*", op.CallOp{}) } diff --git a/tracker/gioui/theme.go b/tracker/gioui/theme.go index bc93b00..1df08c7 100644 --- a/tracker/gioui/theme.go +++ b/tracker/gioui/theme.go @@ -74,3 +74,4 @@ var warningColor = color.NRGBA{R: 251, G: 192, B: 45, A: 255} var dialogBgColor = color.NRGBA{R: 0, G: 0, B: 0, A: 224} var paramIsSendTargetColor = color.NRGBA{R: 120, G: 120, B: 210, A: 255} +var paramValueInvalidColor = color.NRGBA{R: 120, G: 120, B: 120, A: 190} diff --git a/tracker/gioui/unit_editor.go b/tracker/gioui/unit_editor.go index 0e711f7..bd65463 100644 --- a/tracker/gioui/unit_editor.go +++ b/tracker/gioui/unit_editor.go @@ -14,16 +14,14 @@ import ( "gioui.org/widget" "gioui.org/widget/material" "gioui.org/x/component" - sointu "github.com/vsariola/sointu" + "github.com/vsariola/sointu" "github.com/vsariola/sointu/tracker" "golang.org/x/exp/shiny/materialdesign/icons" "golang.org/x/text/cases" "golang.org/x/text/language" "image" "io" - "iter" "math" - "slices" ) type UnitEditor struct { @@ -252,7 +250,6 @@ type ParameterStyle struct { Theme *material.Theme SendTargetTheme *material.Theme Focus bool - sends []sointu.Unit } func (t *Tracker) ParamStyle(th *material.Theme, paramWidget *ParameterWidget) ParameterStyle { @@ -271,7 +268,7 @@ func (t *Tracker) ParamStyle(th *material.Theme, paramWidget *ParameterWidget) P } func (p ParameterStyle) Layout(gtx C) D { - sends := slices.Collect(p.findSends()) + isSendTarget, info := p.tryDerivedParameterInfo() return layout.Flex{Axis: layout.Horizontal, Alignment: layout.Middle}.Layout(gtx, layout.Rigid(func(gtx C) D { gtx.Constraints.Min.X = gtx.Dp(unit.Dp(110)) @@ -302,7 +299,7 @@ func (p ParameterStyle) Layout(gtx C) D { } sliderStyle := material.Slider(p.Theme, &p.w.floatWidget) sliderStyle.Color = p.Theme.Fg - if len(sends) > 0 { + if isSendTarget { sliderStyle.Color = paramIsSendTargetColor } r := image.Rectangle{Max: gtx.Constraints.Min} @@ -348,16 +345,14 @@ func (p ParameterStyle) Layout(gtx C) D { var unitItems []MenuItem instrName := "" unitName := "" - targetI, targetU, err := p.tracker.FindUnit(p.w.Parameter.Value()) - if err == nil { - targetInstrument := p.tracker.Instrument(targetI) - instrName = targetInstrument.Name - units := targetInstrument.Units - unitName = unitNameFor(targetU, units[targetU]) + targetInstrName, units, targetUnitIndex, ok := p.tracker.UnitInfo(p.w.Parameter.Value()) + if ok { + instrName = targetInstrName + unitName = buildUnitLabel(targetUnitIndex, units[targetUnitIndex]) unitItems = make([]MenuItem, len(units)) for j, unit := range units { id := unit.ID - unitItems[j].Text = unitNameFor(j, unit) + unitItems[j].Text = buildUnitLabel(j, unit) unitItems[j].IconBytes = icons.NavigationChevronRight unitItems[j].Doer = tracker.Allow(func() { tracker.Int{IntData: p.w.Parameter}.Set(id) @@ -378,8 +373,12 @@ func (p ParameterStyle) Layout(gtx C) D { }), layout.Rigid(func(gtx C) D { if p.w.Parameter.Type() != tracker.IDParameter { - label := Label(p.w.Parameter.Hint(), white, p.tracker.Theme.Shaper) - info := p.buildSendTargetTooltip(sends) + color := white + hint := p.w.Parameter.Hint() + if !hint.Valid { + color = paramValueInvalidColor + } + label := Label(hint.Label, color, p.tracker.Theme.Shaper) if info == "" { return label(gtx) } @@ -391,7 +390,7 @@ func (p ParameterStyle) Layout(gtx C) D { ) } -func unitNameFor(index int, u sointu.Unit) string { +func buildUnitLabel(index int, u sointu.Unit) string { text := u.Type if u.Comment != "" { text = fmt.Sprintf("%s \"%s\"", text, u.Comment) @@ -399,47 +398,11 @@ func unitNameFor(index int, u sointu.Unit) string { return fmt.Sprintf("%d: %s", index, text) } -func (p ParameterStyle) findSends() iter.Seq[sointu.Unit] { - return func(yield func(sointu.Unit) bool) { - param, ok := (p.w.Parameter).(tracker.NamedParameter) - if !ok { - return - } - for _, send := range p.sends { - port := send.Parameters["port"] - unitParam := sointu.FindParamForModulationPort(param.Unit().Type, port) - if unitParam.Name != param.Name() { - continue - } - if !yield(send) { - return - } - } - } -} - -func (p ParameterStyle) buildSendTargetTooltip(sends []sointu.Unit) string { - if len(sends) == 0 { - return "" - } - targetParam := (p.w.Parameter).(tracker.NamedParameter) - targetInstr := p.tracker.Model.InstrumentForUnit(targetParam.Unit().ID) - amounts := "" - for i := 0; i < len(sends); i++ { - sourceInstr := p.tracker.Model.InstrumentForUnit(sends[0].ID) - sourceInfo := "" - if sourceInstr != targetInstr { - sourceInfo = fmt.Sprintf(" from \"%s\"", sourceInstr.Name) - } - if amounts == "" { - amounts = fmt.Sprintf("x %d%s", sends[i].Parameters["amount"], sourceInfo) - } else { - amounts = fmt.Sprintf("%s, x %d%s", amounts, sends[i].Parameters["amount"], sourceInfo) - } - } - count := "1 send" - if len(sends) > 1 { - count = fmt.Sprintf("%d sends") +func (p ParameterStyle) tryDerivedParameterInfo() (isSendTarget bool, sendInfo string) { + param, ok := (p.w.Parameter).(tracker.NamedParameter) + if !ok { + return false, "" } - return fmt.Sprintf("%s [%s]", count, amounts) + isSendTarget, sendInfo, _ = p.tracker.ParameterInfo(param.Unit().ID, param.Name()) + return isSendTarget, sendInfo } diff --git a/tracker/model.go b/tracker/model.go index 02dba9e..89dbf19 100644 --- a/tracker/model.go +++ b/tracker/model.go @@ -68,8 +68,6 @@ type ( // reordering or deleting instrument can delete track) linkInstrTrack bool - cachePatternUseCount [][]int - voiceLevels [vm.MAX_VOICES]float32 signalAnalyzer *ScopeModel @@ -234,7 +232,6 @@ func (m *Model) change(kind string, t ChangeType, severity ChangeSeverity) func( m.d.ChangedSinceSave = true m.d.ChangedSinceRecovery = true if m.changeType&ScoreChange != 0 { - m.updatePatternUseCount() m.d.Cursor.SongPos = m.d.Song.Score.Clamp(m.d.Cursor.SongPos) m.d.Cursor2.SongPos = m.d.Song.Score.Clamp(m.d.Cursor2.SongPos) m.updateDerivedScoreData() @@ -343,7 +340,7 @@ func (m *Model) UnmarshalRecovery(bytes []byte) { } m.d.ChangedSinceRecovery = false trySend(m.broker.ToPlayer, any(m.d.Song.Copy())) - m.updatePatternUseCount() + m.initDerivedData() } func (m *Model) ProcessMsg(msg MsgToModel) { @@ -409,20 +406,6 @@ func (n NoteID) NoteOff() { trySend(n.model.broker.ToPlayer, any(NoteOffMsg{n})) } -func (m *Model) FindUnit(id int) (instrIndex, unitIndex int, err error) { - // TODO: this only used for choosing send target; find a better way for this - return m.d.Song.Patch.FindUnit(id) -} - -func (m *Model) Instrument(index int) sointu.Instrument { - // TODO: this only used for choosing send target; find a better way for this - // we make a copy just so that the gui can't accidentally modify the song - if index < 0 || index >= len(m.d.Song.Patch) { - return sointu.Instrument{} - } - return m.d.Song.Patch[index].Copy() -} - func (d *modelData) Copy() modelData { ret := *d ret.Song = d.Song.Copy() @@ -568,30 +551,6 @@ func (m *Model) fixUnitParams() { } } -func (m *Model) updatePatternUseCount() { - for i, track := range m.d.Song.Score.Tracks { - for len(m.cachePatternUseCount) <= i { - m.cachePatternUseCount = append(m.cachePatternUseCount, nil) - } - for j := range m.cachePatternUseCount[i] { - m.cachePatternUseCount[i][j] = 0 - } - for j := 0; j < m.d.Song.Score.Length; j++ { - if j >= len(track.Order) { - break - } - p := track.Order[j] - for len(m.cachePatternUseCount[i]) <= p { - m.cachePatternUseCount[i] = append(m.cachePatternUseCount[i], 0) - } - if p < 0 { - continue - } - m.cachePatternUseCount[i][p]++ - } - } -} - func clamp(a, min, max int) int { if a > max { return max diff --git a/tracker/params.go b/tracker/params.go index c6092cb..ee699d3 100644 --- a/tracker/params.go +++ b/tracker/params.go @@ -15,7 +15,7 @@ type ( IntData Type() ParameterType Name() string - Hint() string + Hint() ParameterHint LargeStep() int Reset() } @@ -44,6 +44,11 @@ type ( ParamYieldFunc func(param Parameter) bool ParameterType int + + ParameterHint struct { + Label string + Valid bool + } ) const ( @@ -170,31 +175,34 @@ func (p NamedParameter) Type() ParameterType { return IntegerParameter } -func (p NamedParameter) Hint() string { +func (p NamedParameter) Hint() ParameterHint { val := p.Value() + label := strconv.Itoa(val) if p.up.DisplayFunc != nil { valueInUnits, units := p.up.DisplayFunc(val) - return fmt.Sprintf("%d / %s %s", val, valueInUnits, units) + label = fmt.Sprintf("%d / %s %s", val, valueInUnits, units) } - if p.unit.Type == "send" && p.up.Name == "voice" && val == 0 { - targetIndex, _, err := p.m.FindUnit(p.unit.Parameters["target"]) - if err == nil && targetIndex != p.m.d.InstrIndex { - return "all" - } - return "self" - } - if p.unit.Type == "send" && p.up.Name == "port" { - instrIndex, unitIndex, err := p.m.FindUnit(p.unit.Parameters["target"]) - if err != nil { - return strconv.Itoa(val) + 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" + } } - portList := sointu.Ports[p.m.d.Song.Patch[instrIndex].Units[unitIndex].Type] - if val < 0 || val >= len(portList) { - return strconv.Itoa(val) + 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 = fmt.Sprintf(portList[val]) } - return fmt.Sprintf(portList[val]) } - return strconv.Itoa(val) + return ParameterHint{label, true} } func (p NamedParameter) LargeStep() int { @@ -235,11 +243,12 @@ func (p GmDlsEntryParameter) setValue(v int) { p.unit.Parameters["transpose"] = 64 + e.SuggestedTranspose } -func (p GmDlsEntryParameter) Hint() string { +func (p GmDlsEntryParameter) Hint() ParameterHint { + label := "0 / custom" if v := p.Value(); v > 0 { - return fmt.Sprintf("%v / %v", v, GmDlsEntries[v-1].Name) + label = fmt.Sprintf("%v / %v", v, GmDlsEntries[v-1].Name) } - return "0 / custom" + return ParameterHint{label, true} } // DelayTimeParameter @@ -267,7 +276,7 @@ func (p DelayTimeParameter) Range() intRange { return intRange{Min: 1, Max: 65535} } -func (p DelayTimeParameter) Hint() string { +func (p DelayTimeParameter) Hint() ParameterHint { val := p.Value() var text string switch p.unit.Parameters["notetracking"] { @@ -309,7 +318,7 @@ func (p DelayTimeParameter) Hint() string { text += " L" } } - return text + return ParameterHint{text, true} } // DelayLinesParameter @@ -319,7 +328,10 @@ func (p DelayLinesParameter) Type() ParameterType { return IntegerParameter } func (p DelayLinesParameter) Range() intRange { return intRange{Min: 1, Max: 32} } func (p DelayLinesParameter) LargeStep() int { return 4 } func (p DelayLinesParameter) Reset() { return } -func (p DelayLinesParameter) Hint() string { return strconv.Itoa(p.Value()) } + +func (p DelayLinesParameter) Hint() ParameterHint { + return ParameterHint{strconv.Itoa(p.Value()), true} +} func (p DelayLinesParameter) Value() int { val := len(p.unit.VarArgs) @@ -366,10 +378,11 @@ func (p ReverbParameter) setValue(v int) { copy(p.unit.VarArgs, entry.varArgs) } -func (p ReverbParameter) Hint() string { +func (p ReverbParameter) Hint() ParameterHint { i := p.Value() + label := "0 / custom" if i > 0 { - return fmt.Sprintf("%v / %v", i, reverbs[i-1].name) + label = fmt.Sprintf("%v / %v", i, reverbs[i-1].name) } - return "0 / custom" + return ParameterHint{label, true} } diff --git a/tracker/player.go b/tracker/player.go index 6137ece..e018bb6 100644 --- a/tracker/player.go +++ b/tracker/player.go @@ -11,8 +11,8 @@ import ( type ( // Player is the audio player for the tracker, run in a separate thread. It // is controlled by messages from the model and MIDI messages via the - // context, typically from the VSTI host. The player sends messages to the - // model via the playerMessages channel. The model sends messages to the + // context, typically from the VSTI host. The player sendTargets messages to the + // model via the playerMessages channel. The model sendTargets messages to the // player via the modelMessages channel. Player struct { synth sointu.Synth // the synth used to render audio @@ -344,7 +344,7 @@ func (p *Player) compileOrUpdateSynth() { } } -// all sends from player are always non-blocking, to ensure that the player thread cannot end up in a dead-lock +// all sendTargets from player are always non-blocking, to ensure that the player thread cannot end up in a dead-lock func (p *Player) send(message interface{}) { trySend(p.broker.ToModel, MsgToModel{HasPanicPosLevels: true, Panic: p.synth == nil, SongPosition: p.songPos, VoiceLevels: p.voiceLevels, Data: message}) } diff --git a/tracker/presets.go b/tracker/presets.go index 3cf7bfd..96ec4ba 100644 --- a/tracker/presets.go +++ b/tracker/presets.go @@ -21,7 +21,7 @@ type ( LoopStart int // loop start offset in words LoopLength int // loop length in words SuggestedTranspose int // suggested transpose in semitones, so that all samples play at same pitch - Name string // sample name + Name string // sample Name } InstrumentPresetYieldFunc func(index int, item string) (ok bool) diff --git a/tracker/table.go b/tracker/table.go index a270218..82b78d2 100644 --- a/tracker/table.go +++ b/tracker/table.go @@ -576,13 +576,6 @@ func (m *Notes) LowNibble() bool { return m.d.LowNibble } -func (m *Notes) Unique(t, p int) bool { - if t < 0 || t >= len(m.cachePatternUseCount) || p < 0 || p >= len(m.cachePatternUseCount[t]) { - return false - } - return m.cachePatternUseCount[t][p] == 1 -} - func (m *Notes) SetValue(p Point, val byte) { defer m.change("SetValue", MinorChange)() if p.Y < 0 || p.X < 0 || p.X >= len(m.d.Song.Score.Tracks) {