newyearkrendelki/music.go

143 lines
2.6 KiB
Go
Raw Normal View History

package main
import (
"cart/w4"
"math/rand"
)
const (
C int = iota
Cs
D
Ds
E
F
Fs
G
Gs
A
As
B
)
var frequencies = []uint{262, 277, 294, 311, 330, 349, 370, 392, 415, 440, 466, 494}
type Music struct {
KickProb [8]uint8
KickLine [8]bool
SnareProb [8]uint8
SnareLine [8]bool
LeadProb [8]uint8
LeadLine [8]uint
MainNoteIndex int
Frame uint64
CurrentPart uint8
CurrentLine uint8
Active bool
MutedFrames uint
}
func (m *Music) Start() {
m.GenLines()
m.Frame = 0
m.CurrentLine = 0
m.CurrentPart = 0
m.Active = true
}
func (m *Music) Pause() {
m.Active = false
}
func (m *Music) Resume() {
m.Active = true
m.MutedFrames = 0
}
func (m *Music) MuteFor(frames uint) {
m.MutedFrames = frames
}
var pentaScale = []int{0, 3, 5, 7, 10, 12}
func (m *Music) PentaNote(n int) uint {
pentaIndex := n % 6
octave := 1 + n/6
// w4.Trace("Octave: " + strconv.Itoa(octave))
noteIndex := (m.MainNoteIndex + pentaScale[pentaIndex]) % 12
return frequencies[noteIndex] * uint(octave)
}
func (m *Music) Update() {
if !m.Active {
return
}
if m.MutedFrames > 0 {
m.MutedFrames--
}
if m.Frame%15 == 0 {
if m.MutedFrames == 0 {
if m.KickLine[m.CurrentLine] {
w4.Tone(200|1<<16, 5, 60, w4.TONE_TRIANGLE)
}
if m.SnareLine[m.CurrentLine] {
w4.Tone(200|50<<16, 10<<8, 10, w4.TONE_NOISE)
}
if m.LeadLine[m.CurrentLine] != 0 {
w4.Tone(m.LeadLine[m.CurrentLine], 10|15<<8, 5, w4.TONE_MODE2|w4.TONE_PULSE2)
}
}
m.CurrentLine++
if m.CurrentLine == 8 {
m.CurrentLine = 0
m.CurrentPart++
if m.CurrentPart == 4 {
m.CurrentPart = 0
m.GenLines()
}
}
}
m.Frame++
}
func (m *Music) GenLines() {
m.KickLine = m.GenDrumLine(m.KickProb)
m.SnareLine = m.GenDrumLine(m.SnareProb)
m.LeadLine = m.GenLeadLine(m.LeadProb)
m.MainNoteIndex = rand.Intn(5)
}
func (m *Music) GenDrumLine(prob [8]uint8) [8]bool {
line := [8]bool{}
for i, p := range prob {
line[i] = rand.Intn(101) < int(p)
}
return line
}
func (m *Music) GenLeadLine(prob [8]uint8) [8]uint {
line := [8]uint{}
pattern := [8]int{}
offset := rand.Intn(6)
switch rand.Intn(4) {
case 0:
pattern = [8]int{0, 1, 2, 3, 0, 1, 2, 3}
case 1:
pattern = [8]int{3, 2, 1, 0, 3, 2, 1, 0}
case 2:
pattern = [8]int{7, 5, 6, 4, 5, 3, 4, 2}
case 3:
pattern = [8]int{0, 2, 1, 3, 2, 4, 3, 5}
case 4:
pattern = [8]int{0, 2, 4, 1, 3, 5, 2, 4}
case 5:
pattern = [8]int{4, 2, 5, 3, 1, 4, 2, 0}
}
for i, p := range prob {
if rand.Intn(101) < int(p) {
line[i] = m.PentaNote(pattern[i]+offset) / 2
continue
}
line[i] = 0
}
return line
}