feat: add ability to import 4klang patches and instruments

This commit is contained in:
5684185+vsariola@users.noreply.github.com
2023-07-06 23:47:55 +03:00
parent c06ac6ea5e
commit 248ba483c6
87 changed files with 643 additions and 55 deletions

View File

@ -33,7 +33,7 @@ type SampleOffset struct {
LoopLength uint16
}
func Encode(patch sointu.Patch, featureSet FeatureSet) (*BytePatch, error) {
func Encode(patch sointu.Patch, featureSet FeatureSet, bpm int) (*BytePatch, error) {
c := BytePatch{PolyphonyBitmask: polyphonyBitmask(patch), NumVoices: uint32(patch.NumVoices())}
if c.NumVoices > 32 {
return nil, fmt.Errorf("Sointu does not support more than 32 concurrent voices; patch uses %v", c.NumVoices)
@ -42,7 +42,7 @@ func Encode(patch sointu.Patch, featureSet FeatureSet) (*BytePatch, error) {
globalAddrs := map[int]uint16{}
globalFixups := map[int]([]int){}
voiceNo := 0
delayTable, delayIndices := constructDelayTimeTable(patch)
delayTable, delayIndices := constructDelayTimeTable(patch, bpm)
c.DelayTimes = make([]uint16, len(delayTable))
for i := range delayTable {
c.DelayTimes[i] = uint16(delayTable[i])
@ -171,7 +171,7 @@ func Encode(patch sointu.Patch, featureSet FeatureSet) (*BytePatch, error) {
if count == 0 {
continue // skip encoding delays without any delay lines
}
countTrack := count*2 - 1 + unit.Parameters["notetracking"] // 1 means no note tracking and 1 delay, 2 means notetracking with 1 delay, 3 means no note tracking and 2 delays etc.
countTrack := count*2 - 1 + (unit.Parameters["notetracking"] & 1) // 1 means no note tracking and 1 delay, 2 means notetracking with 1 delay, 3 means no note tracking and 2 delays etc.
values = append(values, byte(delayIndices[instrIndex][unitIndex]), byte(countTrack))
}
c.Commands = append(c.Commands, byte(opcode+unit.Parameters["stereo"]))

View File

@ -16,17 +16,17 @@ import (
type BridgeService struct {
}
func (s BridgeService) Compile(patch sointu.Patch) (sointu.Synth, error) {
synth, err := Synth(patch)
func (s BridgeService) Compile(patch sointu.Patch, bpm int) (sointu.Synth, error) {
synth, err := Synth(patch, bpm)
return synth, err
}
func Synth(patch sointu.Patch) (*C.Synth, error) {
func Synth(patch sointu.Patch, bpm int) (*C.Synth, error) {
s := new(C.Synth)
if n := patch.NumDelayLines(); n > 64 {
return nil, fmt.Errorf("native bridge has currently a hard limit of 64 delaylines; patch uses %v", n)
}
comPatch, err := vm.Encode(patch, vm.AllFeatures{})
comPatch, err := vm.Encode(patch, vm.AllFeatures{}, bpm)
if err != nil {
return nil, fmt.Errorf("error compiling patch: %v", err)
}
@ -109,11 +109,11 @@ func (s *C.Synth) Release(voice int) {
}
// Update
func (s *C.Synth) Update(patch sointu.Patch) error {
func (s *C.Synth) Update(patch sointu.Patch, bpm int) error {
if n := patch.NumDelayLines(); n > 64 {
return fmt.Errorf("native bridge has currently a hard limit of 64 delaylines; patch uses %v", n)
}
comPatch, err := vm.Encode(patch, vm.AllFeatures{})
comPatch, err := vm.Encode(patch, vm.AllFeatures{}, bpm)
if err != nil {
return fmt.Errorf("error compiling patch: %v", err)
}

View File

@ -54,7 +54,7 @@ func TestRenderSamples(t *testing.T) {
sointu.Unit{Type: "out", Parameters: map[string]int{"stereo": 1, "gain": 128}},
}}}
synth, err := bridge.Synth(patch)
synth, err := bridge.Synth(patch, 120)
if err != nil {
t.Fatalf("bridge compile error: %v", err)
}
@ -130,7 +130,7 @@ func TestStackUnderflow(t *testing.T) {
patch := sointu.Patch{sointu.Instrument{NumVoices: 1, Units: []sointu.Unit{
sointu.Unit{Type: "pop", Parameters: map[string]int{}},
}}}
synth, err := bridge.Synth(patch)
synth, err := bridge.Synth(patch, 120)
if err != nil {
t.Fatalf("bridge compile error: %v", err)
}
@ -146,7 +146,7 @@ func TestStackBalancing(t *testing.T) {
sointu.Instrument{NumVoices: 1, Units: []sointu.Unit{
sointu.Unit{Type: "push", Parameters: map[string]int{}},
}}}
synth, err := bridge.Synth(patch)
synth, err := bridge.Synth(patch, 120)
if err != nil {
t.Fatalf("bridge compile error: %v", err)
}
@ -179,7 +179,7 @@ func TestStackOverflow(t *testing.T) {
sointu.Unit{Type: "pop", Parameters: map[string]int{}},
sointu.Unit{Type: "pop", Parameters: map[string]int{}},
}}}
synth, err := bridge.Synth(patch)
synth, err := bridge.Synth(patch, 120)
if err != nil {
t.Fatalf("bridge compile error: %v", err)
}
@ -196,7 +196,7 @@ func TestDivideByZero(t *testing.T) {
sointu.Unit{Type: "invgain", Parameters: map[string]int{"invgain": 0}},
sointu.Unit{Type: "pop", Parameters: map[string]int{}},
}}}
synth, err := bridge.Synth(patch)
synth, err := bridge.Synth(patch, 120)
if err != nil {
t.Fatalf("bridge compile error: %v", err)
}

View File

@ -84,7 +84,7 @@ func (com *Compiler) Song(song *sointu.Song) (map[string]string, error) {
}
features := vm.NecessaryFeaturesFor(song.Patch)
retmap := map[string]string{}
encodedPatch, err := vm.Encode(song.Patch, features)
encodedPatch, err := vm.Encode(song.Patch, features, song.BPM)
if err != nil {
return nil, fmt.Errorf(`could not encode patch: %v`, err)
}

View File

@ -107,7 +107,7 @@ func findSuperIntArray(arrays [][]int) ([]int, []int) {
// Returns the delay time table and two dimensional array of integers where
// element [i][u] is the index for instrument i / unit u in the delay table if
// the unit was a delay unit. For non-delay untis, the element is just 0.
func constructDelayTimeTable(patch sointu.Patch) ([]int, [][]int) {
func constructDelayTimeTable(patch sointu.Patch, bpm int) ([]int, [][]int) {
ind := make([][]int, len(patch))
var subarrays [][]int
// flatten the delay times into one array of arrays
@ -119,11 +119,18 @@ func constructDelayTimeTable(patch sointu.Patch) ([]int, [][]int) {
// should use delay times
if unit.Type == "delay" {
ind[i][j] = len(subarrays)
end := unit.Parameters["count"]
if unit.Parameters["stereo"] > 0 {
end *= 2
converted := make([]int, len(unit.VarArgs))
copy(converted, unit.VarArgs)
if unit.Parameters["notetracking"] == 2 {
for i, t := range converted {
delay := 44100 * 60 * t / 48 / bpm
if delay > 65535 {
delay = 65535
}
converted[i] = delay
}
}
subarrays = append(subarrays, unit.VarArgs)
subarrays = append(subarrays, converted)
}
}
}

View File

@ -63,8 +63,8 @@ const (
envStateRelease
)
func Synth(patch sointu.Patch) (sointu.Synth, error) {
bytePatch, err := Encode(patch, AllFeatures{})
func Synth(patch sointu.Patch, bpm int) (sointu.Synth, error) {
bytePatch, err := Encode(patch, AllFeatures{}, bpm)
if err != nil {
return nil, fmt.Errorf("error compiling %v", err)
}
@ -73,8 +73,8 @@ func Synth(patch sointu.Patch) (sointu.Synth, error) {
return ret, nil
}
func (s SynthService) Compile(patch sointu.Patch) (sointu.Synth, error) {
synth, err := Synth(patch)
func (s SynthService) Compile(patch sointu.Patch, bpm int) (sointu.Synth, error) {
synth, err := Synth(patch, bpm)
return synth, err
}
@ -87,8 +87,8 @@ func (s *Interpreter) Release(voiceIndex int) {
s.synth.voices[voiceIndex].release = true
}
func (s *Interpreter) Update(patch sointu.Patch) error {
bytePatch, err := Encode(patch, AllFeatures{})
func (s *Interpreter) Update(patch sointu.Patch, bpm int) error {
bytePatch, err := Encode(patch, AllFeatures{}, bpm)
if err != nil {
return fmt.Errorf("error compiling %v", err)
}
@ -140,9 +140,11 @@ func (s *Interpreter) Render(buffer []float32, maxtime int) (samples int, time i
stereo := channels == 2
opNoStereo := (op & 0xFE) >> 1
if opNoStereo == 0 {
voices = voices[1:]
units = voices[0].units[:]
voicesRemaining--
if voicesRemaining > 0 {
voices = voices[1:]
units = voices[0].units[:]
}
if mask := uint32(1) << uint32(voicesRemaining); s.bytePatch.PolyphonyBitmask&mask == mask {
commands, values = commandInstr, valuesInstr
} else {

View File

@ -76,7 +76,7 @@ func TestStackUnderflow(t *testing.T) {
patch := sointu.Patch{sointu.Instrument{NumVoices: 1, Units: []sointu.Unit{
sointu.Unit{Type: "pop", Parameters: map[string]int{}},
}}}
synth, err := vm.Synth(patch)
synth, err := vm.Synth(patch, 120)
if err != nil {
t.Fatalf("bridge compile error: %v", err)
}
@ -92,7 +92,7 @@ func TestStackBalancing(t *testing.T) {
sointu.Instrument{NumVoices: 1, Units: []sointu.Unit{
sointu.Unit{Type: "push", Parameters: map[string]int{}},
}}}
synth, err := vm.Synth(patch)
synth, err := vm.Synth(patch, 120)
if err != nil {
t.Fatalf("bridge compile error: %v", err)
}