mirror of
https://github.com/vsariola/sointu.git
synced 2025-05-28 03:10:24 -04:00
feat(tracker): implement simple undo / redo
This commit is contained in:
parent
b1df5bb4d5
commit
eb25ddd864
@ -45,18 +45,17 @@ var noteMap = map[string]int{
|
||||
// KeyEvent handles incoming key events and returns true if repaint is needed.
|
||||
func (t *Tracker) KeyEvent(e key.Event) bool {
|
||||
if e.State == key.Press {
|
||||
if t.CursorColumn == 0 {
|
||||
if val, ok := noteMap[e.Name]; ok {
|
||||
t.NotePressed(val)
|
||||
return true
|
||||
}
|
||||
} else {
|
||||
if iv, err := strconv.ParseInt(e.Name, 16, 8); err == nil {
|
||||
t.NumberPressed(byte(iv))
|
||||
return true
|
||||
}
|
||||
}
|
||||
switch e.Name {
|
||||
case "Z":
|
||||
if e.Modifiers.Contain(key.ModCtrl) {
|
||||
t.Undo()
|
||||
return true
|
||||
}
|
||||
case "Y":
|
||||
if e.Modifiers.Contain(key.ModCtrl) {
|
||||
t.Redo()
|
||||
return true
|
||||
}
|
||||
case "A":
|
||||
t.setCurrent(0)
|
||||
return true
|
||||
@ -114,6 +113,17 @@ func (t *Tracker) KeyEvent(e key.Event) bool {
|
||||
t.CursorColumn = 0
|
||||
return true
|
||||
}
|
||||
if t.CursorColumn == 0 {
|
||||
if val, ok := noteMap[e.Name]; ok {
|
||||
t.NotePressed(val)
|
||||
return true
|
||||
}
|
||||
} else {
|
||||
if iv, err := strconv.ParseInt(e.Name, 16, 8); err == nil {
|
||||
t.NumberPressed(byte(iv))
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
@ -135,6 +145,7 @@ func (t *Tracker) moveCursor(delta int) {
|
||||
|
||||
// setCurrent sets the (note) value in current pattern under cursor to iv
|
||||
func (t *Tracker) setCurrent(iv byte) {
|
||||
t.SaveUndo()
|
||||
t.song.Tracks[t.ActiveTrack].Patterns[t.song.Tracks[t.ActiveTrack].Sequence[t.DisplayPattern]][t.CursorRow] = iv
|
||||
}
|
||||
|
||||
|
@ -44,6 +44,8 @@ type Tracker struct {
|
||||
synth sointu.Synth
|
||||
playBuffer []float32
|
||||
closer chan struct{}
|
||||
undoStack []sointu.Song
|
||||
redoStack []sointu.Song
|
||||
}
|
||||
|
||||
func (t *Tracker) LoadSong(song sointu.Song) error {
|
||||
@ -59,6 +61,21 @@ func (t *Tracker) LoadSong(song sointu.Song) error {
|
||||
} else {
|
||||
t.synth = synth
|
||||
}
|
||||
if t.DisplayPattern >= song.SequenceLength() {
|
||||
t.DisplayPattern = song.SequenceLength() - 1
|
||||
}
|
||||
if t.CursorRow >= song.PatternRows() {
|
||||
t.CursorRow = song.PatternRows() - 1
|
||||
}
|
||||
if t.PlayPattern >= song.SequenceLength() {
|
||||
t.PlayPattern = song.SequenceLength() - 1
|
||||
}
|
||||
if t.PlayRow >= song.PatternRows() {
|
||||
t.PlayRow = song.PatternRows() - 1
|
||||
}
|
||||
if t.ActiveTrack >= len(song.Tracks) {
|
||||
t.ActiveTrack = len(song.Tracks) - 1
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -153,6 +170,7 @@ func (t *Tracker) ChangeOctave(delta int) bool {
|
||||
}
|
||||
|
||||
func (t *Tracker) ChangeBPM(delta int) bool {
|
||||
t.SaveUndo()
|
||||
newBPM := t.song.BPM + delta
|
||||
if newBPM < 1 {
|
||||
newBPM = 1
|
||||
@ -169,6 +187,7 @@ func (t *Tracker) ChangeBPM(delta int) bool {
|
||||
}
|
||||
|
||||
func (t *Tracker) AddTrack() {
|
||||
t.SaveUndo()
|
||||
if t.song.TotalTrackVoices() < t.song.Patch.TotalVoices() {
|
||||
seq := make([]byte, t.song.SequenceLength())
|
||||
patterns := [][]byte{make([]byte, t.song.PatternRows())}
|
||||
@ -181,6 +200,7 @@ func (t *Tracker) AddTrack() {
|
||||
}
|
||||
|
||||
func (t *Tracker) AddInstrument() {
|
||||
t.SaveUndo()
|
||||
if t.song.Patch.TotalVoices() < 32 {
|
||||
units := make([]sointu.Unit, len(defaultInstrument.Units))
|
||||
for i, defUnit := range defaultInstrument.Units {
|
||||
@ -220,6 +240,8 @@ func New(audioContext sointu.AudioContext) *Tracker {
|
||||
patternJump: make(chan int),
|
||||
ticked: make(chan struct{}),
|
||||
closer: make(chan struct{}),
|
||||
undoStack: []sointu.Song{},
|
||||
redoStack: []sointu.Song{},
|
||||
}
|
||||
t.Theme.Color.Primary = color.RGBA{R: 64, G: 64, B: 64, A: 255}
|
||||
go t.sequencerLoop(t.closer)
|
||||
|
37
tracker/undo.go
Normal file
37
tracker/undo.go
Normal file
@ -0,0 +1,37 @@
|
||||
package tracker
|
||||
|
||||
var undoSkip = map[string]int{
|
||||
"setNote": 10,
|
||||
}
|
||||
|
||||
const maxUndo = 256
|
||||
|
||||
func (t *Tracker) SaveUndo() {
|
||||
if len(t.undoStack) >= maxUndo {
|
||||
t.undoStack = t.undoStack[1:]
|
||||
}
|
||||
t.undoStack = append(t.undoStack, t.song.Copy())
|
||||
t.redoStack = t.redoStack[:0]
|
||||
}
|
||||
|
||||
func (t *Tracker) Undo() {
|
||||
if len(t.undoStack) > 0 {
|
||||
if len(t.redoStack) >= maxUndo {
|
||||
t.redoStack = t.redoStack[1:]
|
||||
}
|
||||
t.redoStack = append(t.redoStack, t.song.Copy())
|
||||
t.LoadSong(t.undoStack[len(t.undoStack)-1])
|
||||
t.undoStack = t.undoStack[:len(t.undoStack)-1]
|
||||
}
|
||||
}
|
||||
|
||||
func (t *Tracker) Redo() {
|
||||
if len(t.redoStack) > 0 {
|
||||
if len(t.undoStack) >= maxUndo {
|
||||
t.undoStack = t.undoStack[1:]
|
||||
}
|
||||
t.undoStack = append(t.undoStack, t.song.Copy())
|
||||
t.LoadSong(t.redoStack[len(t.redoStack)-1])
|
||||
t.redoStack = t.redoStack[:len(t.redoStack)-1]
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user