Skip to content

Commit

Permalink
fix: changes after review (see PR #176)
Browse files Browse the repository at this point in the history
  • Loading branch information
qm210 committed Nov 9, 2024
1 parent 6f1cb5e commit 7370839
Show file tree
Hide file tree
Showing 8 changed files with 204 additions and 182 deletions.
214 changes: 167 additions & 47 deletions tracker/derived.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package tracker

import (
"fmt"
"github.com/vsariola/sointu"
"iter"
"slices"
Expand All @@ -13,10 +14,37 @@ import (
*/

type (
derivedModelData struct {
// map unit by ID
forUnit map[int]derivedForUnit
// map track by index
forTrack map[int]derivedForTrack
// map pattern by index
forPattern map[int]derivedForPattern
}

derivedForUnit struct {
unit *sointu.Unit
instrument *sointu.Instrument
sends []*sointu.Unit
unit sointu.Unit
Name string
Instrument sointu.Instrument
instrumentIndex int
unitIndexInInstrument 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 {
Expand All @@ -25,62 +53,68 @@ 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) UnitDerivedData(id int) (derivedForUnit, bool) {
fu, ok := m.derived.forUnit[id]
return fu, ok
}

func (m *Model) UnitById(id int) *sointu.Unit {
fu := m.forUnitById(id)
if fu == nil {
return nil
func (m *Model) UnitName(id int) string {
forUnit, ok := m.derived.forUnit[id]
if !ok {
return "?"
}
return fu.unit
return forUnit.Name
}

func (m *Model) SendTargetsForUnit(id int) []*sointu.Unit {
fu := m.forUnitById(id)
if fu == nil {
return nil
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.sends
return len(dp.sendSources) > 0, dp.sendTooltip, true
}

func (m *Model) forTrackByIndex(index int) *derivedForTrack {
func (m *Model) TrackTitle(index int) string {
forTrack, ok := m.derived.forTrack[index]
if !ok {
return nil
return ""
}
return &forTrack
return forTrack.title
}

func (m *Model) TrackTitle(index int) string {
ft := m.forTrackByIndex(index)
if ft == nil {
return ""
func (m *Model) PatternUseCount(index int) []int {
forPattern, ok := m.derived.forPattern[index]
if !ok {
return []int{}
}
return forPattern.useCount
}

func (m *Model) PatternUnique(t, p int) bool {
forPattern, ok := m.derived.forPattern[t]
if !ok || p < 0 || p >= len(forPattern.useCount) {
return false
}
return ft.title
return forPattern.useCount[p] == 1
}

// public getters with further model information
Expand All @@ -105,41 +139,63 @@ 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(map[int]derivedForTrack),
forPattern: make(map[int]derivedForPattern),
}
m.updateDerivedScoreData()
m.updateDerivedPatchData()
}

func (m *Model) updateDerivedScoreData() {
for index, _ := range m.d.Song.Score.Tracks {
clear(m.derived.forTrack)
clear(m.derived.forPattern)
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.forPattern[index] = 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,
unitIndexInInstrument: u,
Name: buildUnitName(u, unit),
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
Expand All @@ -148,14 +204,57 @@ 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 := sointu.FindParamForModulationPort(unit.Type, port)
if unitParam.Name != paramName {
continue
}
sourceData := sendSourceData{
unitId: u.ID,
paramName: paramName,
instrumentIndex: i,
instrumentName: instr.Name,
amount: u.Parameters["amount"],
}
if !yield(sourceData) {
return
}
}
}
}
}

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 buildUnitName(index int, u sointu.Unit) string {
text := u.Type
if u.Comment != "" {
text = fmt.Sprintf("%s \"%s\"", text, u.Comment)
}
return fmt.Sprintf("%d: %s", index, text)
}

func (m *Model) instrumentRangeFor(trackIndex int) (int, int, error) {
track := m.d.Song.Score.Tracks[trackIndex]
firstVoice := m.d.Song.Score.FirstVoiceForTrack(trackIndex)
Expand Down Expand Up @@ -233,3 +332,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
}
4 changes: 2 additions & 2 deletions tracker/gioui/note_editor.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
Expand All @@ -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{})
}
Expand Down
Loading

0 comments on commit 7370839

Please sign in to comment.