Files
sointu/tracker/bool.go
2026-01-21 20:45:03 +02:00

382 lines
11 KiB
Go

package tracker
import (
"fmt"
)
type (
Bool struct {
value BoolValue
}
BoolValue interface {
Value() bool
SetValue(bool)
}
Panic Model
IsRecording Model
Playing Model
Effect Model
TrackMidiIn Model
UnitSearching Model
UnitDisabled Model
LoopToggle Model
Mute Model
Solo Model
Oversampling Model
InstrEditor Model
InstrPresets Model
InstrComment Model
Thread1 Model
Thread2 Model
Thread3 Model
Thread4 Model
simpleBool bool
)
func MakeBool(value BoolValue) Bool {
return Bool{value: value}
}
func MakeBoolFromPtr(value *bool) Bool {
return Bool{value: (*simpleBool)(value)}
}
func (v Bool) Toggle() {
v.SetValue(!v.Value())
}
func (v Bool) SetValue(value bool) {
if v.Enabled() && v.Value() != value {
v.value.SetValue(value)
}
}
func (v Bool) Value() bool {
if v.value == nil {
return false
}
return v.value.Value()
}
func (v Bool) Enabled() bool {
if v.value == nil {
return false
}
e, ok := v.value.(Enabler)
if !ok {
return true
}
return e.Enabled()
}
func (v *simpleBool) Value() bool { return bool(*v) }
func (v *simpleBool) SetValue(value bool) { *v = simpleBool(value) }
// Thread methods
func (m *Model) getThreadsBit(bit int) bool {
if m.d.InstrIndex < 0 || m.d.InstrIndex >= len(m.d.Song.Patch) {
return false
}
mask := m.d.Song.Patch[m.d.InstrIndex].ThreadMaskM1 + 1
return mask&(1<<bit) != 0
}
func (m *Model) setThreadsBit(bit int, value bool) {
if m.d.InstrIndex < 0 || m.d.InstrIndex >= len(m.d.Song.Patch) {
return
}
defer (*Model)(m).change("ThreadBitMask", PatchChange, MinorChange)()
mask := m.d.Song.Patch[m.d.InstrIndex].ThreadMaskM1 + 1
if value {
mask |= (1 << bit)
} else {
mask &^= (1 << bit)
}
m.d.Song.Patch[m.d.InstrIndex].ThreadMaskM1 = max(mask-1, -1) // -1 has all threads disabled, we warn about that
m.warnAboutCrossThreadSends()
m.warnNoMultithreadSupport()
m.warnNoThread()
}
func (m *Model) warnAboutCrossThreadSends() {
for i, instr := range m.d.Song.Patch {
for _, unit := range instr.Units {
if unit.Type == "send" {
targetID, ok := unit.Parameters["target"]
if !ok {
continue
}
it, _, err := m.d.Song.Patch.FindUnit(targetID)
if err != nil {
continue
}
if instr.ThreadMaskM1 != m.d.Song.Patch[it].ThreadMaskM1 {
m.Alerts().AddNamed("CrossThreadSend", fmt.Sprintf("Instrument %d '%s' has a send to instrument %d '%s' but they are not on the same threads, which may cause issues", i+1, instr.Name, it+1, m.d.Song.Patch[it].Name), Warning)
return
}
}
}
}
m.Alerts().ClearNamed("CrossThreadSend")
}
func (m *Model) warnNoMultithreadSupport() {
for _, instr := range m.d.Song.Patch {
if instr.ThreadMaskM1 > 0 && !m.synthers[m.syntherIndex].SupportsMultithreading() {
m.Alerts().AddNamed("NoMultithreadSupport", "The current synth does not support multithreading and the patch was configured to use more than one thread", Warning)
return
}
}
m.Alerts().ClearNamed("NoMultithreadSupport")
}
func (m *Model) warnNoThread() {
for i, instr := range m.d.Song.Patch {
if instr.ThreadMaskM1 == -1 {
m.Alerts().AddNamed("NoThread", fmt.Sprintf("Instrument %d '%s' is not rendered on any thread", i+1, instr.Name), Warning)
return
}
}
m.Alerts().ClearNamed("NoThread")
}
func (m *Model) Thread1() Bool { return MakeBool((*Thread1)(m)) }
func (m *Thread1) Value() bool { return (*Model)(m).getThreadsBit(0) }
func (m *Thread1) SetValue(val bool) { (*Model)(m).setThreadsBit(0, val) }
func (m *Model) Thread2() Bool { return MakeBool((*Thread2)(m)) }
func (m *Thread2) Value() bool { return (*Model)(m).getThreadsBit(1) }
func (m *Thread2) SetValue(val bool) { (*Model)(m).setThreadsBit(1, val) }
func (m *Model) Thread3() Bool { return MakeBool((*Thread3)(m)) }
func (m *Thread3) Value() bool { return (*Model)(m).getThreadsBit(2) }
func (m *Thread3) SetValue(val bool) { (*Model)(m).setThreadsBit(2, val) }
func (m *Model) Thread4() Bool { return MakeBool((*Thread4)(m)) }
func (m *Thread4) Value() bool { return (*Model)(m).getThreadsBit(3) }
func (m *Thread4) SetValue(val bool) { (*Model)(m).setThreadsBit(3, val) }
// Panic methods
func (m *Model) Panic() Bool { return MakeBool((*Panic)(m)) }
func (m *Panic) Value() bool { return m.panic }
func (m *Panic) SetValue(val bool) { (*Model)(m).setPanic(val) }
// IsRecording methods
func (m *Model) IsRecording() Bool { return MakeBool((*IsRecording)(m)) }
func (m *IsRecording) Value() bool { return (*Model)(m).recording }
func (m *IsRecording) SetValue(val bool) {
m.recording = val
m.instrEnlarged = val
TrySend(m.broker.ToPlayer, any(RecordingMsg{val}))
}
// Playing methods
func (m *Model) Playing() Bool { return MakeBool((*Playing)(m)) }
func (m *Playing) Value() bool { return m.playing }
func (m *Playing) SetValue(val bool) {
m.playing = val
if m.playing {
(*Model)(m).setPanic(false)
TrySend(m.broker.ToPlayer, any(StartPlayMsg{m.d.Cursor.SongPos}))
} else {
TrySend(m.broker.ToPlayer, any(IsPlayingMsg{val}))
}
}
func (m *Playing) Enabled() bool { return m.playing || !m.instrEnlarged }
// InstrEnlarged methods
func (m *Model) InstrEnlarged() Bool { return MakeBoolFromPtr(&m.instrEnlarged) }
// InstrEditor methods
func (m *Model) InstrEditor() Bool { return MakeBool((*InstrEditor)(m)) }
func (m *InstrEditor) Value() bool { return m.d.InstrumentTab == InstrumentEditorTab }
func (m *InstrEditor) SetValue(val bool) {
if val {
m.d.InstrumentTab = InstrumentEditorTab
}
}
func (m *Model) InstrComment() Bool { return MakeBool((*InstrComment)(m)) }
func (m *InstrComment) Value() bool { return m.d.InstrumentTab == InstrumentCommentTab }
func (m *InstrComment) SetValue(val bool) {
if val {
m.d.InstrumentTab = InstrumentCommentTab
}
}
func (m *Model) InstrPresets() Bool { return MakeBool((*InstrPresets)(m)) }
func (m *InstrPresets) Value() bool { return m.d.InstrumentTab == InstrumentPresetsTab }
func (m *InstrPresets) SetValue(val bool) {
if val {
m.d.InstrumentTab = InstrumentPresetsTab
}
}
// Follow methods
func (m *Model) Follow() Bool { return MakeBoolFromPtr(&m.follow) }
// TrackMidiIn (Midi Input for notes in the tracks)
func (m *Model) TrackMidiIn() Bool { return MakeBool((*TrackMidiIn)(m)) }
func (m *TrackMidiIn) Value() bool { return m.broker.mIDIEventsToGUI.Load() }
func (m *TrackMidiIn) SetValue(val bool) { m.broker.mIDIEventsToGUI.Store(val) }
// Effect methods
func (m *Model) Effect() Bool { return MakeBool((*Effect)(m)) }
func (m *Effect) Value() bool {
if m.d.Cursor.Track < 0 || m.d.Cursor.Track >= len(m.d.Song.Score.Tracks) {
return false
}
return m.d.Song.Score.Tracks[m.d.Cursor.Track].Effect
}
func (m *Effect) SetValue(val bool) {
if m.d.Cursor.Track < 0 || m.d.Cursor.Track >= len(m.d.Song.Score.Tracks) {
return
}
m.d.Song.Score.Tracks[m.d.Cursor.Track].Effect = val
}
// Oversampling methods
func (m *Model) Oversampling() Bool { return MakeBool((*Oversampling)(m)) }
func (m *Oversampling) Value() bool { return m.oversampling }
func (m *Oversampling) SetValue(val bool) {
m.oversampling = val
TrySend(m.broker.ToDetector, MsgToDetector{HasOversampling: true, Oversampling: val})
}
// UnitSearching methods
func (m *Model) UnitSearching() Bool { return MakeBool((*UnitSearching)(m)) }
func (m *UnitSearching) Value() bool { return m.d.UnitSearching }
func (m *UnitSearching) SetValue(val bool) {
m.d.UnitSearching = val
if m.d.InstrIndex < 0 || m.d.InstrIndex >= len(m.d.Song.Patch) {
m.d.UnitSearchString = ""
return
}
if m.d.UnitIndex < 0 || m.d.UnitIndex >= len(m.d.Song.Patch[m.d.InstrIndex].Units) {
m.d.UnitSearchString = ""
return
}
m.d.UnitSearchString = m.d.Song.Patch[m.d.InstrIndex].Units[m.d.UnitIndex].Type
}
// UnitDisabled methods
func (m *Model) UnitDisabled() Bool { return MakeBool((*UnitDisabled)(m)) }
func (m *UnitDisabled) Value() bool {
if m.d.InstrIndex < 0 || m.d.InstrIndex >= len(m.d.Song.Patch) {
return false
}
if m.d.UnitIndex < 0 || m.d.UnitIndex >= len(m.d.Song.Patch[m.d.InstrIndex].Units) {
return false
}
return m.d.Song.Patch[m.d.InstrIndex].Units[m.d.UnitIndex].Disabled
}
func (m *UnitDisabled) SetValue(val bool) {
if m.d.InstrIndex < 0 || m.d.InstrIndex >= len(m.d.Song.Patch) {
return
}
l := ((*Model)(m)).Units().List()
r := l.listRange()
defer (*Model)(m).change("UnitDisabledSet", PatchChange, MajorChange)()
for i := r.Start; i < r.End; i++ {
m.d.Song.Patch[m.d.InstrIndex].Units[i].Disabled = val
}
}
func (m *UnitDisabled) Enabled() bool {
if m.d.InstrIndex < 0 || m.d.InstrIndex >= len(m.d.Song.Patch) {
return false
}
if len(m.d.Song.Patch[m.d.InstrIndex].Units) == 0 {
return false
}
return true
}
// LoopToggle methods
func (m *Model) LoopToggle() Bool { return MakeBool((*LoopToggle)(m)) }
func (m *LoopToggle) Value() bool { return m.loop.Length > 0 }
func (t *LoopToggle) SetValue(val bool) {
m := (*Model)(t)
newLoop := Loop{}
if val {
l := m.OrderRows().List()
r := l.listRange()
newLoop = Loop{r.Start, r.End - r.Start}
}
m.setLoop(newLoop)
}
// UniquePatterns methods
func (m *Model) UniquePatterns() Bool { return MakeBoolFromPtr(&m.uniquePatterns) }
// Mute methods
func (m *Model) Mute() Bool { return MakeBool((*Mute)(m)) }
func (m *Mute) Value() bool {
if m.d.InstrIndex < 0 || m.d.InstrIndex >= len(m.d.Song.Patch) {
return false
}
return m.d.Song.Patch[m.d.InstrIndex].Mute
}
func (m *Mute) SetValue(val bool) {
if m.d.InstrIndex < 0 || m.d.InstrIndex >= len(m.d.Song.Patch) {
return
}
defer (*Model)(m).change("Mute", PatchChange, MinorChange)()
a, b := min(m.d.InstrIndex, m.d.InstrIndex2), max(m.d.InstrIndex, m.d.InstrIndex2)
for i := a; i <= b; i++ {
if i < 0 || i >= len(m.d.Song.Patch) {
continue
}
m.d.Song.Patch[i].Mute = val
}
}
func (m *Mute) Enabled() bool { return m.d.InstrIndex >= 0 && m.d.InstrIndex < len(m.d.Song.Patch) }
// Solo methods
func (m *Model) Solo() Bool { return MakeBool((*Solo)(m)) }
func (m *Solo) Value() bool {
a, b := min(m.d.InstrIndex, m.d.InstrIndex2), max(m.d.InstrIndex, m.d.InstrIndex2)
for i := range m.d.Song.Patch {
if i < 0 || i >= len(m.d.Song.Patch) {
continue
}
if (i >= a && i <= b) == m.d.Song.Patch[i].Mute {
return false
}
}
return true
}
func (m *Solo) SetValue(val bool) {
defer (*Model)(m).change("Solo", PatchChange, MinorChange)()
a, b := min(m.d.InstrIndex, m.d.InstrIndex2), max(m.d.InstrIndex, m.d.InstrIndex2)
for i := range m.d.Song.Patch {
if i < 0 || i >= len(m.d.Song.Patch) {
continue
}
m.d.Song.Patch[i].Mute = !(i >= a && i <= b) && val
}
}
func (m *Solo) Enabled() bool { return m.d.InstrIndex >= 0 && m.d.InstrIndex < len(m.d.Song.Patch) }
// LinkInstrTrack methods
func (m *Model) LinkInstrTrack() Bool { return MakeBoolFromPtr(&m.linkInstrTrack) }