mirror of
https://github.com/vsariola/sointu.git
synced 2025-07-22 15:04:36 -04:00
feat(tracker): rework the MIDI input and note event handling
This commit is contained in:
parent
7ef868a434
commit
283fbc1171
@ -8,42 +8,77 @@ import (
|
||||
"github.com/vsariola/sointu"
|
||||
)
|
||||
|
||||
type Recording struct {
|
||||
BPM float64 // vsts allow bpms as floats so for accurate reconstruction, keep it as float for recording
|
||||
Events []MIDINoteEvent
|
||||
TotalFrames int
|
||||
}
|
||||
type (
|
||||
Recording struct {
|
||||
BPM float64 // vsts allow bpms as floats so for accurate reconstruction, keep it as float for recording
|
||||
Events NoteEventList
|
||||
StartFrame, EndFrame int64
|
||||
State RecordingState
|
||||
}
|
||||
|
||||
type recordingNote struct {
|
||||
note byte
|
||||
startRow int
|
||||
endRow int
|
||||
}
|
||||
RecordingState int
|
||||
)
|
||||
|
||||
const (
|
||||
RecordingNone RecordingState = iota
|
||||
RecordingWaitingForNote
|
||||
RecordingStarted // StartFrame is set, but EndFrame is not
|
||||
RecordingFinished // StartFrame and EndFrame are both set, recording is finished
|
||||
)
|
||||
|
||||
var ErrInvalidRows = errors.New("rows per beat and rows per pattern must be greater than 1")
|
||||
var ErrNotFinished = errors.New("the recording was not finished")
|
||||
|
||||
func (r *Recording) Record(ev NoteEvent, frame int64) {
|
||||
if r.State == RecordingNone || r.State == RecordingFinished {
|
||||
return
|
||||
}
|
||||
if r.State == RecordingWaitingForNote {
|
||||
r.StartFrame = frame
|
||||
r.State = RecordingStarted
|
||||
}
|
||||
r.Events = append(r.Events, ev)
|
||||
}
|
||||
|
||||
func (r *Recording) Finish(frame int64, frameDeltas map[any]int64) {
|
||||
if r.State != RecordingStarted {
|
||||
return
|
||||
}
|
||||
r.State = RecordingFinished
|
||||
r.EndFrame = frame
|
||||
r.Events.adjustTimes(frameDeltas, r.StartFrame, r.EndFrame)
|
||||
}
|
||||
|
||||
func (recording *Recording) Score(patch sointu.Patch, rowsPerBeat, rowsPerPattern int) (sointu.Score, error) {
|
||||
if rowsPerBeat <= 1 || rowsPerPattern <= 1 {
|
||||
return sointu.Score{}, ErrInvalidRows
|
||||
}
|
||||
if recording.State != RecordingFinished {
|
||||
return sointu.Score{}, ErrNotFinished
|
||||
}
|
||||
type recordingNote struct {
|
||||
note byte
|
||||
startRow int
|
||||
endRow int
|
||||
}
|
||||
channelNotes := make([][]recordingNote, 0)
|
||||
// find the length of each note and assign it to its respective channel
|
||||
for i, m := range recording.Events {
|
||||
if !m.On || m.Channel >= len(patch) {
|
||||
if !m.On || m.Channel >= len(patch) || m.IsTrack {
|
||||
continue
|
||||
}
|
||||
endFrame := math.MaxInt
|
||||
var endFrame int64 = math.MaxInt64
|
||||
for j := i + 1; j < len(recording.Events); j++ {
|
||||
if recording.Events[j].Channel == m.Channel && recording.Events[j].Note == m.Note {
|
||||
endFrame = recording.Events[j].Frame
|
||||
endFrame = recording.Events[j].playerTimestamp
|
||||
break
|
||||
}
|
||||
}
|
||||
for len(channelNotes) <= m.Channel {
|
||||
channelNotes = append(channelNotes, make([]recordingNote, 0))
|
||||
}
|
||||
startRow := frameToRow(recording.BPM, rowsPerBeat, m.Frame)
|
||||
endRow := frameToRow(recording.BPM, rowsPerBeat, endFrame)
|
||||
startRow := frameToRow(recording.BPM, rowsPerBeat, m.playerTimestamp-recording.StartFrame)
|
||||
endRow := frameToRow(recording.BPM, rowsPerBeat, endFrame-recording.StartFrame)
|
||||
channelNotes[m.Channel] = append(channelNotes[m.Channel], recordingNote{m.Note, startRow, endRow})
|
||||
}
|
||||
//assign notes to tracks, assigning it to left most track that is released
|
||||
@ -83,7 +118,7 @@ func (recording *Recording) Score(patch sointu.Patch, rowsPerBeat, rowsPerPatter
|
||||
tracks[i] = append(tracks[i], []recordingNote{})
|
||||
}
|
||||
}
|
||||
songLengthPatterns := (frameToRow(recording.BPM, rowsPerBeat, recording.TotalFrames) + rowsPerPattern - 1) / rowsPerPattern
|
||||
songLengthPatterns := (frameToRow(recording.BPM, rowsPerBeat, recording.EndFrame-recording.StartFrame) + rowsPerPattern - 1) / rowsPerPattern
|
||||
songLengthRows := songLengthPatterns * rowsPerPattern
|
||||
songTracks := make([]sointu.Track, 0)
|
||||
for i, tg := range tracks {
|
||||
@ -148,6 +183,6 @@ func (recording *Recording) Score(patch sointu.Patch, rowsPerBeat, rowsPerPatter
|
||||
return score, nil
|
||||
}
|
||||
|
||||
func frameToRow(BPM float64, rowsPerBeat, frame int) int {
|
||||
func frameToRow(BPM float64, rowsPerBeat int, frame int64) int {
|
||||
return int(float64(frame)/44100/60*BPM*float64(rowsPerBeat) + 0.5)
|
||||
}
|
||||
|
Reference in New Issue
Block a user