mirror of
https://github.com/vsariola/sointu.git
synced 2025-05-28 03:10:24 -04:00
feat(tracker): change keyboard shortcuts to mimic old trackers
This commit is contained in:
parent
b4a63ce362
commit
91b7850bf7
@ -28,6 +28,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||
- Empty patch should not crash the native synth ([#148][i148])
|
||||
|
||||
### Changed
|
||||
- The keyboard shortcuts are now again closer to what they were old trackers
|
||||
([#151][i151])
|
||||
- The stand-alone apps now output floating point sound, as made possible by
|
||||
upgrading oto-library to latest version. This way the tracker sound output
|
||||
matches the compiled output better, as usually compiled intros output sound in
|
||||
@ -237,6 +239,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||
[i148]: https://github.com/vsariola/sointu/issues/148
|
||||
[i149]: https://github.com/vsariola/sointu/issues/149
|
||||
[i150]: https://github.com/vsariola/sointu/issues/150
|
||||
[i151]: https://github.com/vsariola/sointu/issues/151
|
||||
[i154]: https://github.com/vsariola/sointu/issues/154
|
||||
[i158]: https://github.com/vsariola/sointu/issues/158
|
||||
[i162]: https://github.com/vsariola/sointu/issues/162
|
||||
|
@ -275,18 +275,75 @@ func (m *Model) RemoveUnused() Action {
|
||||
})
|
||||
}
|
||||
|
||||
func (m *Model) Rewind() Action {
|
||||
func (m *Model) PlayFromCurrentPosition() Action {
|
||||
return Action{
|
||||
allowed: func() bool {
|
||||
return m.playing || !m.instrEnlarged
|
||||
},
|
||||
allowed: func() bool { return !m.instrEnlarged },
|
||||
do: func() {
|
||||
m.setPanic(false)
|
||||
m.setLoop(Loop{})
|
||||
m.playing = true
|
||||
m.send(StartPlayMsg{m.d.Cursor.SongPos})
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (m *Model) PlayFromSongStart() Action {
|
||||
return Action{
|
||||
allowed: func() bool { return !m.instrEnlarged },
|
||||
do: func() {
|
||||
m.setPanic(false)
|
||||
m.setLoop(Loop{})
|
||||
m.playing = true
|
||||
m.send(StartPlayMsg{})
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (m *Model) PlaySelected() Action {
|
||||
return Action{
|
||||
allowed: func() bool { return !m.instrEnlarged },
|
||||
do: func() {
|
||||
m.setPanic(false)
|
||||
m.playing = true
|
||||
l := m.OrderRows().List()
|
||||
a, b := l.listRange()
|
||||
newLoop := Loop{a, b - a + 1}
|
||||
m.setLoop(newLoop)
|
||||
m.send(StartPlayMsg{sointu.SongPos{OrderRow: a, PatternRow: 0}})
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (m *Model) PlayFromLoopStart() Action {
|
||||
return Action{
|
||||
allowed: func() bool { return !m.instrEnlarged },
|
||||
do: func() {
|
||||
m.setPanic(false)
|
||||
if m.loop == (Loop{}) {
|
||||
m.PlaySelected().Do()
|
||||
return
|
||||
}
|
||||
m.playing = true
|
||||
m.send(StartPlayMsg{sointu.SongPos{OrderRow: m.loop.Start, PatternRow: 0}})
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (m *Model) StopPlaying() Action {
|
||||
return Action{
|
||||
allowed: func() bool { return true },
|
||||
do: func() {
|
||||
if !m.playing {
|
||||
m.setPanic(true)
|
||||
m.setLoop(Loop{})
|
||||
return
|
||||
}
|
||||
m.playing = false
|
||||
(*Model)(m).send(IsPlayingMsg{false})
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (m *Model) AddOrderRow(before bool) Action {
|
||||
return Allow(func() {
|
||||
defer m.change("AddOrderRowAction", ScoreChange, MinorChange)()
|
||||
@ -394,8 +451,9 @@ func (m *Model) completeAction(checkSave bool) {
|
||||
}
|
||||
switch m.dialog {
|
||||
case NewSongChanges, NewSongSaveExplorer:
|
||||
c := m.change("NewSong", SongChange|LoopChange, MajorChange)
|
||||
c := m.change("NewSong", SongChange, MajorChange)
|
||||
m.resetSong()
|
||||
m.setLoop(Loop{})
|
||||
c()
|
||||
m.d.ChangedSinceSave = false
|
||||
m.dialog = NoDialog
|
||||
@ -408,3 +466,17 @@ func (m *Model) completeAction(checkSave bool) {
|
||||
m.dialog = NoDialog
|
||||
}
|
||||
}
|
||||
|
||||
func (m *Model) setPanic(val bool) {
|
||||
if m.panic != val {
|
||||
m.panic = val
|
||||
m.send(PanicMsg{val})
|
||||
}
|
||||
}
|
||||
|
||||
func (m *Model) setLoop(newLoop Loop) {
|
||||
if m.loop != newLoop {
|
||||
m.loop = newLoop
|
||||
m.send(newLoop)
|
||||
}
|
||||
}
|
||||
|
@ -51,8 +51,7 @@ func (m *Model) LoopToggle() *LoopToggle { return (*LoopToggle)(m) }
|
||||
func (m *Panic) Bool() Bool { return Bool{m} }
|
||||
func (m *Panic) Value() bool { return m.panic }
|
||||
func (m *Panic) setValue(val bool) {
|
||||
m.panic = val
|
||||
(*Model)(m).send(PanicMsg{val})
|
||||
(*Model)(m).setPanic(val)
|
||||
}
|
||||
func (m *Panic) Enabled() bool { return true }
|
||||
|
||||
@ -74,6 +73,7 @@ func (m *Playing) Value() bool { return m.playing }
|
||||
func (m *Playing) setValue(val bool) {
|
||||
m.playing = val
|
||||
if m.playing {
|
||||
(*Model)(m).setPanic(false)
|
||||
(*Model)(m).send(StartPlayMsg{m.d.Cursor.SongPos})
|
||||
} else {
|
||||
(*Model)(m).send(IsPlayingMsg{val})
|
||||
@ -98,9 +98,9 @@ func (m *CommentExpanded) Enabled() bool { return true }
|
||||
// NoteTracking methods
|
||||
|
||||
func (m *NoteTracking) Bool() Bool { return Bool{m} }
|
||||
func (m *NoteTracking) Value() bool { return m.playing && m.noteTracking }
|
||||
func (m *NoteTracking) Value() bool { return m.noteTracking }
|
||||
func (m *NoteTracking) setValue(val bool) { m.noteTracking = val }
|
||||
func (m *NoteTracking) Enabled() bool { return m.playing }
|
||||
func (m *NoteTracking) Enabled() bool { return true }
|
||||
|
||||
// Effect methods
|
||||
|
||||
@ -173,16 +173,15 @@ func (m *UnitDisabled) Enabled() bool {
|
||||
// LoopToggle methods
|
||||
|
||||
func (m *LoopToggle) Bool() Bool { return Bool{m} }
|
||||
func (m *LoopToggle) Value() bool { return m.d.Loop.Length > 0 }
|
||||
func (m *LoopToggle) Value() bool { return m.loop.Length > 0 }
|
||||
func (t *LoopToggle) setValue(val bool) {
|
||||
m := (*Model)(t)
|
||||
defer m.change("SetLoopAction", LoopChange, MinorChange)()
|
||||
if !val {
|
||||
m.d.Loop = Loop{}
|
||||
return
|
||||
}
|
||||
newLoop := Loop{}
|
||||
if val {
|
||||
l := m.OrderRows().List()
|
||||
a, b := l.listRange()
|
||||
m.d.Loop = Loop{a, b - a + 1}
|
||||
newLoop = Loop{a, b - a + 1}
|
||||
}
|
||||
m.setLoop(newLoop)
|
||||
}
|
||||
func (m *LoopToggle) Enabled() bool { return true }
|
||||
|
@ -121,19 +121,34 @@ func (t *Tracker) KeyEvent(e key.Event, gtx C) {
|
||||
case "F3":
|
||||
t.InstrumentEditor.Focus()
|
||||
return
|
||||
case "F5":
|
||||
t.SongPanel.RewindBtn.Action.Do()
|
||||
t.SongPanel.NoteTracking.Bool.Set(!e.Modifiers.Contain(key.ModCtrl))
|
||||
case "Space":
|
||||
t.NoteTracking().Bool().Set(e.Modifiers.Contain(key.ModShift))
|
||||
t.Playing().Bool().Toggle()
|
||||
return
|
||||
case "F6", "Space":
|
||||
t.SongPanel.PlayingBtn.Bool.Toggle()
|
||||
t.SongPanel.NoteTracking.Bool.Set(!e.Modifiers.Contain(key.ModCtrl))
|
||||
case "F5":
|
||||
t.NoteTracking().Bool().Set(e.Modifiers.Contain(key.ModShift))
|
||||
if e.Modifiers.Contain(key.ModCtrl) {
|
||||
t.Model.PlayFromSongStart().Do()
|
||||
} else {
|
||||
t.Model.PlayFromCurrentPosition().Do()
|
||||
}
|
||||
return
|
||||
case "F6":
|
||||
t.NoteTracking().Bool().Set(e.Modifiers.Contain(key.ModShift))
|
||||
if e.Modifiers.Contain(key.ModCtrl) {
|
||||
t.Model.PlayFromLoopStart().Do()
|
||||
} else {
|
||||
t.Model.PlaySelected().Do()
|
||||
}
|
||||
return
|
||||
case "F7":
|
||||
t.SongPanel.RecordBtn.Bool.Toggle()
|
||||
t.IsRecording().Bool().Toggle()
|
||||
return
|
||||
case "F8":
|
||||
t.SongPanel.NoteTracking.Bool.Toggle()
|
||||
t.StopPlaying().Do()
|
||||
return
|
||||
case "F9":
|
||||
t.NoteTracking().Bool().Toggle()
|
||||
return
|
||||
case "F12":
|
||||
t.Panic().Bool().Toggle()
|
||||
|
@ -56,7 +56,7 @@ func NewSongPanel(model *tracker.Model) *SongPanel {
|
||||
RecordBtn: NewBoolClickable(model.IsRecording().Bool()),
|
||||
NoteTracking: NewBoolClickable(model.NoteTracking().Bool()),
|
||||
PlayingBtn: NewBoolClickable(model.Playing().Bool()),
|
||||
RewindBtn: NewActionClickable(model.Rewind()),
|
||||
RewindBtn: NewActionClickable(model.PlayFromSongStart()),
|
||||
}
|
||||
ret.fileMenuItems = []MenuItem{
|
||||
{IconBytes: icons.ContentClear, Text: "New Song", ShortcutText: shortcutKey + "N", Doer: model.NewSong()},
|
||||
@ -105,10 +105,10 @@ func (t *SongPanel) layoutSongOptions(gtx C, tr *Tracker) D {
|
||||
in := layout.UniformInset(unit.Dp(1))
|
||||
|
||||
panicBtnStyle := ToggleButton(gtx, tr.Theme, t.PanicBtn, "Panic (F12)")
|
||||
rewindBtnStyle := ActionIcon(gtx, tr.Theme, t.RewindBtn, icons.AVFastRewind, "Rewind\n(F5)")
|
||||
playBtnStyle := ToggleIcon(gtx, tr.Theme, t.PlayingBtn, icons.AVPlayArrow, icons.AVStop, "Play (F6 / Space)", "Stop (F6 / Space)")
|
||||
rewindBtnStyle := ActionIcon(gtx, tr.Theme, t.RewindBtn, icons.AVFastRewind, "Rewind\n(Ctrl+F5)")
|
||||
playBtnStyle := ToggleIcon(gtx, tr.Theme, t.PlayingBtn, icons.AVPlayArrow, icons.AVStop, "Play (F5 / Space)", "Stop (F8)")
|
||||
recordBtnStyle := ToggleIcon(gtx, tr.Theme, t.RecordBtn, icons.AVFiberManualRecord, icons.AVFiberSmartRecord, "Record (F7)", "Stop (F7)")
|
||||
noteTrackBtnStyle := ToggleIcon(gtx, tr.Theme, t.NoteTracking, icons.ActionSpeakerNotesOff, icons.ActionSpeakerNotes, "Follow\nOff\n(F8)", "Follow\nOn\n(F8)")
|
||||
noteTrackBtnStyle := ToggleIcon(gtx, tr.Theme, t.NoteTracking, icons.ActionSpeakerNotesOff, icons.ActionSpeakerNotes, "Follow\nOff", "Follow\nOn")
|
||||
loopBtnStyle := ToggleIcon(gtx, tr.Theme, t.LoopBtn, icons.NavigationArrowForward, icons.AVLoop, "Loop\nOff\n(Ctrl+L)", "Loop\nOn\n(Ctrl+L)")
|
||||
|
||||
return layout.Flex{Axis: layout.Vertical}.Layout(gtx,
|
||||
|
@ -36,7 +36,6 @@ type (
|
||||
ChangedSinceSave bool
|
||||
RecoveryFilePath string
|
||||
ChangedSinceRecovery bool
|
||||
Loop Loop
|
||||
}
|
||||
|
||||
Model struct {
|
||||
@ -59,6 +58,7 @@ type (
|
||||
recording bool
|
||||
playing bool
|
||||
playPosition sointu.SongPos
|
||||
loop Loop
|
||||
noteTracking bool
|
||||
quitted bool
|
||||
|
||||
@ -132,7 +132,6 @@ const (
|
||||
ScoreChange
|
||||
BPMChange
|
||||
RowsPerBeatChange
|
||||
LoopChange
|
||||
SongChange ChangeType = PatchChange | ScoreChange | BPMChange | RowsPerBeatChange
|
||||
)
|
||||
|
||||
@ -156,7 +155,7 @@ const maxUndo = 64
|
||||
func (m *Model) AverageVolume() Volume { return m.avgVolume }
|
||||
func (m *Model) PeakVolume() Volume { return m.peakVolume }
|
||||
func (m *Model) PlayPosition() sointu.SongPos { return m.playPosition }
|
||||
func (m *Model) Loop() Loop { return m.d.Loop }
|
||||
func (m *Model) Loop() Loop { return m.loop }
|
||||
func (m *Model) PlaySongRow() int { return m.d.Song.Score.SongRow(m.playPosition) }
|
||||
func (m *Model) ChangedSinceSave() bool { return m.d.ChangedSinceSave }
|
||||
func (m *Model) Dialog() Dialog { return m.dialog }
|
||||
@ -183,7 +182,7 @@ func NewModelPlayer(synther sointu.Synther, recoveryFilePath string) (*Model, *P
|
||||
modelMsgs: modelMessages,
|
||||
synther: synther,
|
||||
song: m.d.Song.Copy(),
|
||||
loop: m.d.Loop,
|
||||
loop: m.loop,
|
||||
avgVolumeMeter: VolumeAnalyzer{Attack: 0.3, Release: 0.3, Min: -100, Max: 20},
|
||||
peakVolumeMeter: VolumeAnalyzer{Attack: 1e-4, Release: 1, Min: -100, Max: 20},
|
||||
}
|
||||
@ -244,9 +243,6 @@ func (m *Model) change(kind string, t ChangeType, severity ChangeSeverity) func(
|
||||
if m.changeType&RowsPerBeatChange != 0 {
|
||||
m.send(RowsPerBeatMsg{m.d.Song.RowsPerBeat})
|
||||
}
|
||||
if m.changeType&LoopChange != 0 {
|
||||
m.send(m.d.Loop)
|
||||
}
|
||||
m.undoSkipCounter++
|
||||
var limit int
|
||||
switch m.changeSeverity {
|
||||
@ -322,7 +318,6 @@ func (m *Model) UnmarshalRecovery(bytes []byte) {
|
||||
}
|
||||
m.d.ChangedSinceRecovery = false
|
||||
m.send(m.d.Song.Copy())
|
||||
m.send(m.d.Loop)
|
||||
m.updatePatternUseCount()
|
||||
}
|
||||
|
||||
@ -402,7 +397,6 @@ func (m *Model) resetSong() {
|
||||
}
|
||||
m.d.FilePath = ""
|
||||
m.d.ChangedSinceSave = false
|
||||
m.d.Loop = Loop{}
|
||||
}
|
||||
|
||||
// send sends a message to the player
|
||||
|
@ -82,7 +82,7 @@ func (s *modelFuzzState) Iterate(yield func(string, func(p string, t *testing.T)
|
||||
s.IterateAction("AddOctave", s.model.AddOctave(), yield, seed)
|
||||
s.IterateAction("SubtractOctave", s.model.SubtractOctave(), yield, seed)
|
||||
s.IterateAction("EditNoteOff", s.model.EditNoteOff(), yield, seed)
|
||||
s.IterateAction("Rewind", s.model.Rewind(), yield, seed)
|
||||
s.IterateAction("Rewind", s.model.PlayFromSongStart(), yield, seed)
|
||||
s.IterateAction("AddOrderRowAfter", s.model.AddOrderRow(false), yield, seed)
|
||||
s.IterateAction("AddOrderRowBefore", s.model.AddOrderRow(true), yield, seed)
|
||||
s.IterateAction("DeleteOrderRowForward", s.model.DeleteOrderRow(false), yield, seed)
|
||||
|
Loading…
Reference in New Issue
Block a user