mirror of
https://github.com/vsariola/sointu.git
synced 2025-05-28 03:10:24 -04:00
refactor: implement Order and Pattern types: slices returning default values for out of bound indices
This commit is contained in:
parent
ce6e5d4942
commit
a2723829da
21
order.go
Normal file
21
order.go
Normal file
@ -0,0 +1,21 @@
|
||||
package sointu
|
||||
|
||||
// Order is the pattern order for a track, in practice just a slice of integers,
|
||||
// but provides convenience functions that return -1 values for indices out of
|
||||
// bounds of the array, and functions to increase the size of the slice only by
|
||||
// necessary amount when a new item is added, filling the unused slots with -1s.
|
||||
type Order []int
|
||||
|
||||
func (s Order) Get(index int) int {
|
||||
if index < 0 || index >= len(s) {
|
||||
return -1
|
||||
}
|
||||
return s[index]
|
||||
}
|
||||
|
||||
func (s *Order) Set(index, value int) {
|
||||
for len(*s) <= index {
|
||||
*s = append(*s, -1)
|
||||
}
|
||||
(*s)[index] = value
|
||||
}
|
21
pattern.go
Normal file
21
pattern.go
Normal file
@ -0,0 +1,21 @@
|
||||
package sointu
|
||||
|
||||
// Pattern represents a single pattern of note, in practice just a slice of bytes,
|
||||
// but provides convenience functions that return 1 values (hold) for indices out of
|
||||
// bounds of the array, and functions to increase the size of the slice only by
|
||||
// necessary amount when a new item is added, filling the unused slots with 1s.
|
||||
type Pattern []byte
|
||||
|
||||
func (s Pattern) Get(index int) byte {
|
||||
if index < 0 || index >= len(s) {
|
||||
return 1
|
||||
}
|
||||
return s[index]
|
||||
}
|
||||
|
||||
func (s *Pattern) Set(index int, value byte) {
|
||||
for len(*s) <= index {
|
||||
*s = append(*s, 1)
|
||||
}
|
||||
(*s)[index] = value
|
||||
}
|
10
track.go
10
track.go
@ -2,17 +2,17 @@ package sointu
|
||||
|
||||
type Track struct {
|
||||
NumVoices int
|
||||
Effect bool `yaml:",omitempty"`
|
||||
Order []int `yaml:",flow"`
|
||||
Patterns [][]byte `yaml:",flow"`
|
||||
Effect bool `yaml:",omitempty"`
|
||||
Order Order `yaml:",flow"`
|
||||
Patterns []Pattern `yaml:",flow"`
|
||||
}
|
||||
|
||||
func (t *Track) Copy() Track {
|
||||
order := make([]int, len(t.Order))
|
||||
copy(order, t.Order)
|
||||
patterns := make([][]byte, len(t.Patterns))
|
||||
patterns := make([]Pattern, len(t.Patterns))
|
||||
for i, oldPat := range t.Patterns {
|
||||
newPat := make([]byte, len(oldPat))
|
||||
newPat := make(Pattern, len(oldPat))
|
||||
copy(newPat, oldPat)
|
||||
patterns[i] = newPat
|
||||
}
|
||||
|
@ -63,7 +63,7 @@ var defaultSong = sointu.Song{
|
||||
RowsPerPattern: 16,
|
||||
Length: 1,
|
||||
Tracks: []sointu.Track{
|
||||
{NumVoices: 1, Order: []int{0}, Patterns: [][]byte{{72, 0}}},
|
||||
{NumVoices: 1, Order: sointu.Order{0}, Patterns: []sointu.Pattern{{72, 0}}},
|
||||
},
|
||||
},
|
||||
Patch: sointu.Patch{defaultInstrument,
|
||||
|
@ -418,10 +418,7 @@ func (te *TrackEditor) layoutTracks(gtx C, t *Tracker) D {
|
||||
for row := firstRow; row <= lastRow; row++ {
|
||||
pat := row / t.Song().Score.RowsPerPattern
|
||||
patRow := row % t.Song().Score.RowsPerPattern
|
||||
s := -1
|
||||
if pat >= 0 && pat < len(trk.Order) {
|
||||
s = trk.Order[pat]
|
||||
}
|
||||
s := trk.Order.Get(pat)
|
||||
if s < 0 {
|
||||
op.Offset(f32.Pt(0, trackRowHeight)).Add(gtx.Ops)
|
||||
continue
|
||||
@ -442,10 +439,7 @@ func (te *TrackEditor) layoutTracks(gtx C, t *Tracker) D {
|
||||
}
|
||||
var c byte = 1
|
||||
if s >= 0 && s < len(trk.Patterns) {
|
||||
pattern := trk.Patterns[s]
|
||||
if patRow >= 0 && patRow < len(pattern) {
|
||||
c = pattern[patRow]
|
||||
}
|
||||
c = trk.Patterns[s].Get(patRow)
|
||||
}
|
||||
if trk.Effect {
|
||||
var text string
|
||||
|
@ -212,7 +212,7 @@ func (m *Model) AddTrack(after bool) {
|
||||
copy(newTracks[m.cursor.Track+1:], m.song.Score.Tracks[m.cursor.Track:])
|
||||
newTracks[m.cursor.Track] = sointu.Track{
|
||||
NumVoices: 1,
|
||||
Patterns: [][]byte{},
|
||||
Patterns: []sointu.Pattern{},
|
||||
}
|
||||
m.song.Score.Tracks = newTracks
|
||||
m.clampPositions()
|
||||
@ -335,18 +335,11 @@ func (m *Model) CanDeleteInstrument() bool {
|
||||
|
||||
func (m *Model) Note() byte {
|
||||
trk := m.song.Score.Tracks[m.cursor.Track]
|
||||
if m.cursor.Pattern < 0 || m.cursor.Pattern >= len(trk.Order) {
|
||||
pat := trk.Order.Get(m.cursor.Pattern)
|
||||
if pat < 0 || pat >= len(trk.Patterns) {
|
||||
return 1
|
||||
}
|
||||
p := trk.Order[m.cursor.Pattern]
|
||||
if p < 0 || p >= len(trk.Patterns) {
|
||||
return 1
|
||||
}
|
||||
pat := trk.Patterns[p]
|
||||
if m.cursor.Row < 0 || m.cursor.Row >= len(pat) {
|
||||
return 1
|
||||
}
|
||||
return pat[m.cursor.Row]
|
||||
return trk.Patterns[pat].Get(m.cursor.Row)
|
||||
}
|
||||
|
||||
// SetCurrentNote sets the (note) value in current pattern under cursor to iv
|
||||
@ -356,11 +349,7 @@ func (m *Model) SetNote(iv byte) {
|
||||
if m.cursor.Pattern < 0 || m.cursor.Row < 0 {
|
||||
return
|
||||
}
|
||||
for len(tracks[m.cursor.Track].Order) <= m.cursor.Pattern {
|
||||
tracks[m.cursor.Track].Order = append(tracks[m.cursor.Track].Order, -1)
|
||||
}
|
||||
order := tracks[m.cursor.Track].Order
|
||||
patIndex := order[m.cursor.Pattern]
|
||||
patIndex := tracks[m.cursor.Track].Order.Get(m.cursor.Pattern)
|
||||
if patIndex < 0 {
|
||||
patIndex = len(tracks[m.cursor.Track].Patterns)
|
||||
for _, pi := range tracks[m.cursor.Track].Order {
|
||||
@ -368,26 +357,18 @@ func (m *Model) SetNote(iv byte) {
|
||||
patIndex = pi + 1 // we find a pattern that is not in the pattern table nor in the order list i.e. completely new pattern
|
||||
}
|
||||
}
|
||||
tracks[m.cursor.Track].Order[m.cursor.Pattern] = patIndex
|
||||
tracks[m.cursor.Track].Order.Set(m.cursor.Pattern, patIndex)
|
||||
}
|
||||
for len(tracks[m.cursor.Track].Patterns) <= patIndex {
|
||||
tracks[m.cursor.Track].Patterns = append(tracks[m.cursor.Track].Patterns, nil)
|
||||
}
|
||||
patterns := tracks[m.cursor.Track].Patterns
|
||||
for len(patterns[patIndex]) <= m.cursor.Row {
|
||||
patterns[patIndex] = append(patterns[patIndex], 1)
|
||||
}
|
||||
patterns[patIndex][m.cursor.Row] = iv
|
||||
tracks[m.cursor.Track].Patterns[patIndex].Set(m.cursor.Row, iv)
|
||||
m.notifyScoreChange()
|
||||
}
|
||||
|
||||
func (m *Model) SetCurrentPattern(pat int) {
|
||||
m.saveUndo("SetCurrentPattern", 0)
|
||||
track := &m.song.Score.Tracks[m.cursor.Track]
|
||||
for len(track.Order) <= m.cursor.Pattern {
|
||||
track.Order = append(track.Order, -1)
|
||||
}
|
||||
track.Order[m.cursor.Pattern] = pat
|
||||
m.song.Score.Tracks[m.cursor.Track].Order.Set(m.cursor.Pattern, pat)
|
||||
m.computePatternUseCounts()
|
||||
m.notifyScoreChange()
|
||||
}
|
||||
|
@ -38,7 +38,7 @@ func TestOscillatSine(t *testing.T) {
|
||||
sointu.Unit{Type: "mulp", Parameters: map[string]int{"stereo": 0}},
|
||||
sointu.Unit{Type: "out", Parameters: map[string]int{"stereo": 1, "gain": 128}},
|
||||
}}}
|
||||
tracks := []sointu.Track{{NumVoices: 1, Order: []int{0}, Patterns: [][]byte{{64, 0, 68, 0, 32, 0, 0, 0, 75, 0, 78, 0, 0, 0, 0, 0}}}}
|
||||
tracks := []sointu.Track{{NumVoices: 1, Order: []int{0}, Patterns: []sointu.Pattern{{64, 0, 68, 0, 32, 0, 0, 0, 75, 0, 78, 0, 0, 0, 0, 0}}}}
|
||||
song := sointu.Song{BPM: 100, RowsPerBeat: 4, Score: sointu.Score{RowsPerPattern: 16, Length: 1, Tracks: tracks}, Patch: patch}
|
||||
synth, err := bridge.Synth(patch)
|
||||
if err != nil {
|
||||
|
@ -10,7 +10,7 @@ import (
|
||||
// fixPatternLength makes sure that every pattern is the same length. During
|
||||
// composing. Patterns shorter than the given length are padded with 1 / "hold";
|
||||
// patterns longer than the given length are cropped.
|
||||
func fixPatternLength(patterns [][]byte, fixedLength int) [][]int {
|
||||
func fixPatternLength(patterns []sointu.Pattern, fixedLength int) [][]int {
|
||||
patternData := make([]int, len(patterns)*fixedLength)
|
||||
ret := make([][]int, len(patterns))
|
||||
for i, pat := range patterns {
|
||||
|
@ -14,11 +14,11 @@ func TestPatternReusing(t *testing.T) {
|
||||
Length: 2,
|
||||
RowsPerPattern: 8,
|
||||
Tracks: []sointu.Track{{
|
||||
Patterns: [][]byte{{64, 1, 1, 1, 0, 0, 0, 0}, {72, 0, 0, 0, 0, 0, 0, 0}},
|
||||
Order: []int{0, 1},
|
||||
Patterns: []sointu.Pattern{{64, 1, 1, 1, 0, 0, 0, 0}, {72, 0, 0, 0, 0, 0, 0, 0}},
|
||||
Order: sointu.Order{0, 1},
|
||||
}, {
|
||||
Patterns: [][]byte{{64, 1, 1, 1, 0, 0, 0, 0}, {84, 0, 0, 0, 0, 0, 0, 0}},
|
||||
Order: []int{0, 1},
|
||||
Patterns: []sointu.Pattern{{64, 1, 1, 1, 0, 0, 0, 0}, {84, 0, 0, 0, 0, 0, 0, 0}},
|
||||
Order: sointu.Order{0, 1},
|
||||
}},
|
||||
},
|
||||
}
|
||||
@ -42,10 +42,10 @@ func TestUnnecessaryHolds(t *testing.T) {
|
||||
Length: 2,
|
||||
RowsPerPattern: 8,
|
||||
Tracks: []sointu.Track{{
|
||||
Patterns: [][]byte{{64, 1, 1, 1, 0, 1, 0, 0}, {72, 0, 1, 0, 1, 0, 0, 0}},
|
||||
Patterns: []sointu.Pattern{{64, 1, 1, 1, 0, 1, 0, 0}, {72, 0, 1, 0, 1, 0, 0, 0}},
|
||||
Order: []int{0, 1},
|
||||
}, {
|
||||
Patterns: [][]byte{{64, 1, 1, 1, 0, 0, 1, 0}, {84, 0, 0, 0, 1, 1, 0, 0}},
|
||||
Patterns: []sointu.Pattern{{64, 1, 1, 1, 0, 0, 1, 0}, {84, 0, 0, 0, 1, 1, 0, 0}},
|
||||
Order: []int{0, 1},
|
||||
}}},
|
||||
}
|
||||
@ -69,10 +69,10 @@ func TestDontCares(t *testing.T) {
|
||||
Length: 2,
|
||||
RowsPerPattern: 8,
|
||||
Tracks: []sointu.Track{{
|
||||
Patterns: [][]byte{{64, 1, 1, 1, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0}},
|
||||
Patterns: []sointu.Pattern{{64, 1, 1, 1, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0}},
|
||||
Order: []int{0, 1},
|
||||
}, {
|
||||
Patterns: [][]byte{{64, 1, 1, 1, 1, 1, 1, 1}, {1, 1, 1, 0, 0, 0, 0, 0}},
|
||||
Patterns: []sointu.Pattern{{64, 1, 1, 1, 1, 1, 1, 1}, {1, 1, 1, 0, 0, 0, 0, 0}},
|
||||
Order: []int{0, 1},
|
||||
}},
|
||||
},
|
||||
|
Loading…
Reference in New Issue
Block a user