diff --git a/go4k/tracker/keyevent.go b/go4k/tracker/keyevent.go index d302bef..c69309f 100644 --- a/go4k/tracker/keyevent.go +++ b/go4k/tracker/keyevent.go @@ -3,15 +3,110 @@ package tracker import ( "gioui.org/io/key" "os" + "strconv" ) +var noteMap = map[string]byte{ + "Z": 0, + "S": 1, + "X": 2, + "D": 3, + "C": 4, + "V": 5, + "G": 6, + "B": 7, + "H": 8, + "N": 9, + "J": 10, + "M": 11, + "Q": 12, + "2": 13, + "W": 14, + "3": 15, + "E": 16, + "R": 17, + "5": 18, + "T": 19, + "6": 20, + "Y": 21, + "7": 22, + "U": 23, +} + // 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 key.NameEscape: os.Exit(0) + case key.NameUpArrow: + t.CursorRow = (t.CursorRow + t.song.PatternRows() - 1) % t.song.PatternRows() + return true + case key.NameDownArrow: + t.CursorRow = (t.CursorRow + 1) % t.song.PatternRows() + return true + case key.NameLeftArrow: + if t.CursorColumn == 0 { + t.ActiveTrack = (t.ActiveTrack + len(t.song.Tracks) - 1) % len(t.song.Tracks) + t.CursorColumn = 2 + } else { + t.CursorColumn-- + } + return true + case key.NameRightArrow: + if t.CursorColumn == 2 { + t.ActiveTrack = (t.ActiveTrack + 1) % len(t.song.Tracks) + t.CursorColumn = 0 + } else { + t.CursorColumn++ + } + return true + case key.NameTab: + if e.Modifiers.Contain(key.ModShift) { + t.ActiveTrack = (t.ActiveTrack + len(t.song.Tracks) - 1) % len(t.song.Tracks) + } else { + t.ActiveTrack = (t.ActiveTrack + 1) % len(t.song.Tracks) + } + t.CursorColumn = 0 + return true } } return false } + +// setCurrent sets the (note) value in current pattern under cursor to iv +func (t *Tracker) setCurrent(iv byte) { + t.song.Patterns[t.song.Tracks[t.ActiveTrack].Sequence[t.DisplayPattern]][t.CursorRow] = iv +} + +// getCurrent returns the current (note) value in current pattern under the cursor +func (t *Tracker) getCurrent() byte { + return t.song.Patterns[t.song.Tracks[t.ActiveTrack].Sequence[t.DisplayPattern]][t.CursorRow] +} + +// NotePressed handles incoming key presses while in the note column +func (t *Tracker) NotePressed(val byte) { + t.setCurrent(getNoteValue(t.CurrentOctave, val)) +} + +// NumberPressed handles incoming presses while in either of the hex number columns +func (t *Tracker) NumberPressed(iv byte) { + val := t.getCurrent() + if t.CursorColumn == 1 { + val = ((iv & 0xF) << 4) | (val & 0xF) + } else if t.CursorColumn == 2 { + val = (val & 0xF0) | (iv & 0xF) + } + t.setCurrent(val) +} diff --git a/go4k/tracker/music.go b/go4k/tracker/music.go index 4a30d6b..c16b2c0 100644 --- a/go4k/tracker/music.go +++ b/go4k/tracker/music.go @@ -19,6 +19,7 @@ var notes = []string{ "B-", } +// valueAsNote returns the textual representation of a note value func valueAsNote(val byte) string { octave := (val - baseNote) / 12 oNote := (val - baseNote) % 12 @@ -27,3 +28,8 @@ func valueAsNote(val byte) string { } return fmt.Sprintf("%s%d", notes[oNote], octave) } + +// noteValue return the note value for a particular note and octave combination +func getNoteValue(octave, note byte) byte { + return baseNote + (octave * 12) + note +} diff --git a/go4k/tracker/tracker.go b/go4k/tracker/tracker.go index 83eb644..e0037ce 100644 --- a/go4k/tracker/tracker.go +++ b/go4k/tracker/tracker.go @@ -12,11 +12,13 @@ type Tracker struct { CursorColumn int DisplayPattern int ActiveTrack int + CurrentOctave byte } func New() *Tracker { return &Tracker{ - QuitButton: new(widget.Clickable), - song: defaultSong, + QuitButton: new(widget.Clickable), + CurrentOctave: 4, + song: defaultSong, } }