mirror of
https://github.com/vsariola/sointu.git
synced 2025-06-03 17:18:20 -04:00
refactor(go4k): Remove all special treatment from samples and map Song 1-1 to what's in the .asm file.
Whoever uses it, probably wants their own Patch format, as now it is pretty cumbersome to work with sampleoffsets and delays, as the user needs to construct the delaytimes tables and sampleoffset tables.
This commit is contained in:
parent
f076409eb1
commit
95c8c9c2b7
@ -102,26 +102,30 @@ func FindSuperIntArray(arrays [][]int) ([]int, []int) {
|
||||
// as possible. Especially: if two delay units use exactly the same
|
||||
// delay times, they appear in the table only once.
|
||||
func ConstructDelayTimeTable(patch Patch) ([]int, [][]int) {
|
||||
ind := make([][]int, len(patch))
|
||||
ind := make([][]int, len(patch.Instruments))
|
||||
var subarrays [][]int
|
||||
// flatten the delay times into one array of arrays
|
||||
// saving the indices where they were placed
|
||||
for i, instr := range patch {
|
||||
for i, instr := range patch.Instruments {
|
||||
ind[i] = make([]int, len(instr.Units))
|
||||
for j, unit := range instr.Units {
|
||||
// only include delay times for delays. Only delays
|
||||
// should use delay times
|
||||
if unit.Type == "delay" {
|
||||
ind[i][j] = len(subarrays)
|
||||
subarrays = append(subarrays, unit.DelayTimes)
|
||||
end := unit.Parameters["count"]
|
||||
if unit.Parameters["stereo"] > 0 {
|
||||
end *= 2
|
||||
}
|
||||
subarrays = append(subarrays, patch.DelayTimes[unit.Parameters["delay"]:end])
|
||||
}
|
||||
}
|
||||
}
|
||||
delayTable, indices := FindSuperIntArray(subarrays)
|
||||
// cancel the flattening, so unitindices can be used to
|
||||
// to find the index of each delay in the delay table
|
||||
unitindices := make([][]int, len(patch))
|
||||
for i, instr := range patch {
|
||||
unitindices := make([][]int, len(patch.Instruments))
|
||||
for i, instr := range patch.Instruments {
|
||||
unitindices[i] = make([]int, len(instr.Units))
|
||||
for j, unit := range instr.Units {
|
||||
if unit.Type == "delay" {
|
||||
@ -139,10 +143,10 @@ func ConstructDelayTimeTable(patch Patch) ([]int, [][]int) {
|
||||
// table by instrument i / unit j (units other than sample oscillators
|
||||
// have the value 0)
|
||||
func ConstructSampleOffsetTable(patch Patch) ([]SampleOffset, [][]int) {
|
||||
unitindices := make([][]int, len(patch))
|
||||
unitindices := make([][]int, len(patch.Instruments))
|
||||
var offsetTable []SampleOffset
|
||||
offsetMap := map[SampleOffset]int{}
|
||||
for i, instr := range patch {
|
||||
for i, instr := range patch.Instruments {
|
||||
unitindices[i] = make([]int, len(instr.Units))
|
||||
for j, unit := range instr.Units {
|
||||
if unit.Type == "oscillator" && unit.Parameters["type"] == Sample {
|
||||
|
@ -16,8 +16,6 @@ func DeserializeAsm(asmcode string) (*Song, error) {
|
||||
tracks := make([]Track, 0)
|
||||
var patch Patch
|
||||
var instr Instrument
|
||||
var delayTimes []int
|
||||
var sampleOffsets [][]int
|
||||
paramReg, err := regexp.Compile(`([a-zA-Z]\w*)\s*\(\s*([0-9]+)\s*\)`) // matches FOO(42), groups "FOO" and "42"
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -111,7 +109,7 @@ func DeserializeAsm(asmcode string) (*Song, error) {
|
||||
instr = Instrument{NumVoices: ints[0], Units: []Unit{}}
|
||||
inInstrument = true
|
||||
case "END_INSTRUMENT":
|
||||
patch = append(patch, instr)
|
||||
patch.Instruments = append(patch.Instruments, instr)
|
||||
inInstrument = false
|
||||
case "DELTIME":
|
||||
ints, err := parseNumbers(rest)
|
||||
@ -119,14 +117,17 @@ func DeserializeAsm(asmcode string) (*Song, error) {
|
||||
return nil, err
|
||||
}
|
||||
for _, v := range ints {
|
||||
delayTimes = append(delayTimes, v)
|
||||
patch.DelayTimes = append(patch.DelayTimes, v)
|
||||
}
|
||||
case "SAMPLE_OFFSET":
|
||||
ints, err := parseNumbers(rest)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
sampleOffsets = append(sampleOffsets, ints)
|
||||
patch.SampleOffsets = append(patch.SampleOffsets, SampleOffset{
|
||||
Start: ints[0],
|
||||
LoopStart: ints[1],
|
||||
LoopLength: ints[2]})
|
||||
}
|
||||
if inInstrument && strings.HasPrefix(word, "SU_") {
|
||||
unittype := strings.ToLower(word[3:])
|
||||
@ -157,26 +158,6 @@ func DeserializeAsm(asmcode string) (*Song, error) {
|
||||
}
|
||||
}
|
||||
}
|
||||
for i := range patch {
|
||||
for u := range patch[i].Units {
|
||||
if patch[i].Units[u].Type == "delay" {
|
||||
s := patch[i].Units[u].Parameters["delay"]
|
||||
e := patch[i].Units[u].Parameters["count"]
|
||||
if patch[i].Units[u].Parameters["stereo"] == 1 {
|
||||
e *= 2 // stereo delays use 'count' number of delaytimes, but for both channels
|
||||
}
|
||||
patch[i].Units[u].DelayTimes = append(patch[i].Units[u].DelayTimes, delayTimes[s:e]...)
|
||||
delete(patch[i].Units[u].Parameters, "delay")
|
||||
delete(patch[i].Units[u].Parameters, "count")
|
||||
} else if patch[i].Units[u].Type == "oscillator" && patch[i].Units[u].Parameters["type"] == Sample {
|
||||
sampleno := patch[i].Units[u].Parameters["color"]
|
||||
patch[i].Units[u].Parameters["start"] = sampleOffsets[sampleno][0]
|
||||
patch[i].Units[u].Parameters["loopstart"] = sampleOffsets[sampleno][1]
|
||||
patch[i].Units[u].Parameters["looplength"] = sampleOffsets[sampleno][2]
|
||||
delete(patch[i].Units[u].Parameters, "color")
|
||||
}
|
||||
}
|
||||
}
|
||||
s := Song{BPM: bpm, Patterns: patterns, Tracks: tracks, Patch: patch, SongLength: -1}
|
||||
return &s, nil
|
||||
}
|
||||
@ -269,15 +250,13 @@ func SerializeAsm(song *Song) (string, error) {
|
||||
}
|
||||
indentation--
|
||||
}
|
||||
delayTable, delayIndices := ConstructDelayTimeTable(song.Patch)
|
||||
sampleTable, sampleIndices := ConstructSampleOffsetTable(song.Patch)
|
||||
// The actual printing starts here
|
||||
println("%%define BPM %d", song.BPM)
|
||||
// delay modulation is pretty much the only %define that the asm preprocessor cannot figure out
|
||||
// as the preprocessor has no clue if a SEND modulates a delay unit. So, unfortunately, for the
|
||||
// time being, we need to figure during export if INCLUDE_DELAY_MODULATION needs to be defined.
|
||||
delaymod := false
|
||||
for i, instrument := range song.Patch {
|
||||
for i, instrument := range song.Patch.Instruments {
|
||||
for j, unit := range instrument.Units {
|
||||
if unit.Type == "send" {
|
||||
targetInstrument := i
|
||||
@ -288,10 +267,10 @@ func SerializeAsm(song *Song) (string, error) {
|
||||
}
|
||||
targetInstrument = v
|
||||
}
|
||||
if unit.Parameters["unit"] < 0 || unit.Parameters["unit"] >= len(song.Patch[targetInstrument].Units) {
|
||||
if unit.Parameters["unit"] < 0 || unit.Parameters["unit"] >= len(song.Patch.Instruments[targetInstrument].Units) {
|
||||
return "", fmt.Errorf("INSTRUMENT #%v / SEND #%v target unit %v out of range", i, j, unit.Parameters["unit"])
|
||||
}
|
||||
if song.Patch[targetInstrument].Units[unit.Parameters["unit"]].Type == "delay" && unit.Parameters["port"] == 5 {
|
||||
if song.Patch.Instruments[targetInstrument].Units[unit.Parameters["unit"]].Type == "delay" && unit.Parameters["port"] == 5 {
|
||||
delaymod = true
|
||||
}
|
||||
}
|
||||
@ -330,22 +309,12 @@ func SerializeAsm(song *Song) (string, error) {
|
||||
println("END_TRACKS\n")
|
||||
println("BEGIN_PATCH")
|
||||
indentation++
|
||||
for i, instrument := range song.Patch {
|
||||
for _, instrument := range song.Patch.Instruments {
|
||||
var instrTable [][]string
|
||||
for j, unit := range instrument.Units {
|
||||
for _, unit := range instrument.Units {
|
||||
row := []string{fmt.Sprintf("SU_%v", strings.ToUpper(unit.Type))}
|
||||
for _, parname := range paramorder[unit.Type] {
|
||||
if unit.Type == "oscillator" && unit.Parameters["type"] == Sample && parname == "color" {
|
||||
row = append(row, fmt.Sprintf("COLOR(%v)", strconv.Itoa(sampleIndices[i][j])))
|
||||
} else if unit.Type == "delay" && parname == "count" {
|
||||
count := len(unit.DelayTimes)
|
||||
if unit.Parameters["stereo"] == 1 {
|
||||
count /= 2
|
||||
}
|
||||
row = append(row, fmt.Sprintf("COUNT(%v)", strconv.Itoa(count)))
|
||||
} else if unit.Type == "delay" && parname == "delay" {
|
||||
row = append(row, fmt.Sprintf("DELAY(%v)", strconv.Itoa(delayIndices[i][j])))
|
||||
} else if unit.Type == "oscillator" && parname == "type" {
|
||||
if unit.Type == "oscillator" && parname == "type" {
|
||||
switch unit.Parameters["type"] {
|
||||
case Sine:
|
||||
row = append(row, "TYPE(SINE)")
|
||||
@ -372,9 +341,9 @@ func SerializeAsm(song *Song) (string, error) {
|
||||
}
|
||||
indentation--
|
||||
println("END_PATCH\n")
|
||||
if len(delayTable) > 0 {
|
||||
if len(song.Patch.DelayTimes) > 0 {
|
||||
var delStrTable [][]string
|
||||
for _, v := range delayTable {
|
||||
for _, v := range song.Patch.DelayTimes {
|
||||
row := []string{"DELTIME", strconv.Itoa(int(v))}
|
||||
delStrTable = append(delStrTable, row)
|
||||
}
|
||||
@ -382,9 +351,9 @@ func SerializeAsm(song *Song) (string, error) {
|
||||
printTable(align(delStrTable, "lr"))
|
||||
println("END_DELTIMES\n")
|
||||
}
|
||||
if len(sampleTable) > 0 {
|
||||
if len(song.Patch.SampleOffsets) > 0 {
|
||||
var samStrTable [][]string
|
||||
for _, v := range sampleTable {
|
||||
for _, v := range song.Patch.SampleOffsets {
|
||||
samStrTable = append(samStrTable, []string{
|
||||
"SAMPLE_OFFSET",
|
||||
fmt.Sprintf("START(%d)", v.Start),
|
||||
|
@ -9,6 +9,7 @@ import (
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"runtime"
|
||||
"strings"
|
||||
"testing"
|
||||
@ -101,15 +102,18 @@ func TestSerializingAllAsmFiles(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatalf("Could not serialize asm file: %v", err)
|
||||
}
|
||||
song, err = go4k.DeserializeAsm(str) // deserialize again. The rendered song should still give same results.
|
||||
song2, err := go4k.DeserializeAsm(str) // deserialize again. The rendered song should still give same results.
|
||||
if err != nil {
|
||||
t.Fatalf("could not parse the serialized asm code: %v", err)
|
||||
}
|
||||
synth, err := bridge.Synth(song.Patch)
|
||||
if !reflect.DeepEqual(song, song2) {
|
||||
t.Fatalf("serialize/deserialize does not result equal songs, before: %v, after %v", song, song2)
|
||||
}
|
||||
synth, err := bridge.Synth(song2.Patch)
|
||||
if err != nil {
|
||||
t.Fatalf("Compiling patch failed: %v", err)
|
||||
}
|
||||
buffer, err := go4k.Play(synth, *song)
|
||||
buffer, err := go4k.Play(synth, *song2)
|
||||
if err != nil {
|
||||
t.Fatalf("Play failed: %v", err)
|
||||
}
|
||||
|
@ -34,7 +34,7 @@ var opcodeTable = map[string]opTableEntry{
|
||||
"filter": opTableEntry{C.su_filter_id, []string{"frequency", "resonance"}},
|
||||
"clip": opTableEntry{C.su_clip_id, []string{}},
|
||||
"pan": opTableEntry{C.su_pan_id, []string{"panning"}},
|
||||
"delay": opTableEntry{C.su_delay_id, []string{"pregain", "dry", "feedback", "damp", "delaycount"}},
|
||||
"delay": opTableEntry{C.su_delay_id, []string{"pregain", "dry", "feedback", "damp", "delay", "count"}},
|
||||
"compressor": opTableEntry{C.su_compres_id, []string{"attack", "release", "invgain", "threshold", "ratio"}},
|
||||
"speed": opTableEntry{C.su_speed_id, []string{}},
|
||||
"out": opTableEntry{C.su_out_id, []string{"gain"}},
|
||||
@ -81,16 +81,11 @@ func (synth *C.Synth) Render(buffer []float32, maxtime int) (int, int, error) {
|
||||
|
||||
func Synth(patch go4k.Patch) (*C.Synth, error) {
|
||||
s := new(C.Synth)
|
||||
sampleno := 0
|
||||
totalVoices := 0
|
||||
commands := make([]byte, 0)
|
||||
values := make([]byte, 0)
|
||||
polyphonyBitmask := 0
|
||||
delayTable, delayIndices := go4k.ConstructDelayTimeTable(patch)
|
||||
for i, v := range delayTable {
|
||||
s.DelayTimes[i] = C.ushort(v)
|
||||
}
|
||||
for insid, instr := range patch {
|
||||
for insid, instr := range patch.Instruments {
|
||||
if len(instr.Units) > 63 {
|
||||
return nil, errors.New("An instrument can have a maximum of 63 units")
|
||||
}
|
||||
@ -105,26 +100,12 @@ func Synth(patch go4k.Patch) (*C.Synth, error) {
|
||||
}
|
||||
commands = append(commands, byte(opCode))
|
||||
for _, paramname := range val.parameterList {
|
||||
if unit.Type == "delay" && paramname == "delaycount" {
|
||||
if unit.Parameters["stereo"] == 1 && len(unit.DelayTimes)%2 != 0 {
|
||||
return nil, errors.New("Stereo delays should have even number of delaytimes")
|
||||
}
|
||||
values = append(values, byte(delayIndices[insid][unitid]))
|
||||
count := len(unit.DelayTimes)
|
||||
if unit.Parameters["stereo"] == 1 {
|
||||
count /= 2
|
||||
}
|
||||
count = count*2 - 1
|
||||
if unit.Type == "delay" && paramname == "count" {
|
||||
count := unit.Parameters["count"]*2 - 1
|
||||
if unit.Parameters["notetracking"] == 1 {
|
||||
count++
|
||||
}
|
||||
values = append(values, byte(count))
|
||||
} else if unit.Type == "oscillator" && unit.Parameters["type"] == go4k.Sample && paramname == "color" {
|
||||
values = append(values, byte(sampleno))
|
||||
s.SampleOffsets[sampleno].Start = (C.uint)(unit.Parameters["start"])
|
||||
s.SampleOffsets[sampleno].LoopStart = (C.ushort)(unit.Parameters["loopstart"])
|
||||
s.SampleOffsets[sampleno].LoopLength = (C.ushort)(unit.Parameters["looplength"])
|
||||
sampleno++
|
||||
} else if pval, ok := unit.Parameters[paramname]; ok {
|
||||
values = append(values, byte(pval))
|
||||
} else {
|
||||
@ -204,6 +185,14 @@ func Synth(patch go4k.Patch) (*C.Synth, error) {
|
||||
for i := range values {
|
||||
s.Values[i] = (C.uchar)(values[i])
|
||||
}
|
||||
for i, deltime := range patch.DelayTimes {
|
||||
s.DelayTimes[i] = (C.ushort)(deltime)
|
||||
}
|
||||
for i, samoff := range patch.SampleOffsets {
|
||||
s.SampleOffsets[i].Start = (C.uint)(samoff.Start)
|
||||
s.SampleOffsets[i].LoopStart = (C.ushort)(samoff.LoopStart)
|
||||
s.SampleOffsets[i].LoopLength = (C.ushort)(samoff.LoopLength)
|
||||
}
|
||||
s.NumVoices = C.uint(totalVoices)
|
||||
s.Polyphony = C.uint(polyphonyBitmask)
|
||||
s.RandSeed = 1
|
||||
|
@ -22,12 +22,16 @@ const su_max_samples = SAMPLES_PER_ROW * TOTAL_ROWS
|
||||
// const bufsize = su_max_samples * 2
|
||||
|
||||
func TestBridge(t *testing.T) {
|
||||
patch := []go4k.Instrument{
|
||||
go4k.Instrument{1, []go4k.Unit{
|
||||
go4k.Unit{"envelope", map[string]int{"stereo": 0, "attack": 64, "decay": 64, "sustain": 64, "release": 80, "gain": 128}, []int{}},
|
||||
go4k.Unit{"envelope", map[string]int{"stereo": 0, "attack": 95, "decay": 64, "sustain": 64, "release": 80, "gain": 128}, []int{}},
|
||||
go4k.Unit{"out", map[string]int{"stereo": 1, "gain": 128}, []int{}},
|
||||
}}}
|
||||
patch := go4k.Patch{
|
||||
Instruments: []go4k.Instrument{
|
||||
go4k.Instrument{1, []go4k.Unit{
|
||||
go4k.Unit{"envelope", map[string]int{"stereo": 0, "attack": 64, "decay": 64, "sustain": 64, "release": 80, "gain": 128}},
|
||||
go4k.Unit{"envelope", map[string]int{"stereo": 0, "attack": 95, "decay": 64, "sustain": 64, "release": 80, "gain": 128}},
|
||||
go4k.Unit{"out", map[string]int{"stereo": 1, "gain": 128}},
|
||||
}}},
|
||||
SampleOffsets: []go4k.SampleOffset{},
|
||||
DelayTimes: []int{}}
|
||||
|
||||
synth, err := bridge.Synth(patch)
|
||||
if err != nil {
|
||||
t.Fatalf("bridge compile error: %v", err)
|
||||
|
29
go4k/go4k.go
29
go4k/go4k.go
@ -9,7 +9,6 @@ import (
|
||||
type Unit struct {
|
||||
Type string
|
||||
Parameters map[string]int
|
||||
DelayTimes []int
|
||||
}
|
||||
|
||||
const (
|
||||
@ -26,12 +25,22 @@ type Instrument struct {
|
||||
Units []Unit
|
||||
}
|
||||
|
||||
type SampleOffset struct {
|
||||
Start int
|
||||
LoopStart int
|
||||
LoopLength int
|
||||
}
|
||||
|
||||
// Patch is simply a list of instruments used in a song
|
||||
type Patch []Instrument
|
||||
type Patch struct {
|
||||
Instruments []Instrument
|
||||
DelayTimes []int
|
||||
SampleOffsets []SampleOffset
|
||||
}
|
||||
|
||||
func (p Patch) TotalVoices() int {
|
||||
ret := 0
|
||||
for _, i := range p {
|
||||
for _, i := range p.Instruments {
|
||||
ret += i.NumVoices
|
||||
}
|
||||
return ret
|
||||
@ -41,7 +50,7 @@ func (patch Patch) InstrumentForVoice(voice int) (int, error) {
|
||||
if voice < 0 {
|
||||
return 0, errors.New("voice cannot be negative")
|
||||
}
|
||||
for i, instr := range patch {
|
||||
for i, instr := range patch.Instruments {
|
||||
if voice < instr.NumVoices {
|
||||
return i, nil
|
||||
} else {
|
||||
@ -70,12 +79,6 @@ func Render(synth Synth, buffer []float32) error {
|
||||
return err
|
||||
}
|
||||
|
||||
type SampleOffset struct {
|
||||
Start int
|
||||
LoopStart int
|
||||
LoopLength int
|
||||
}
|
||||
|
||||
// UnitParameter documents one parameter that an unit takes
|
||||
type UnitParameter struct {
|
||||
Name string // thould be found with this name in the Unit.Parameters map
|
||||
@ -171,7 +174,8 @@ var UnitTypes = []UnitType{
|
||||
{Name: "feedback", MinValue: 0, MaxValue: 128, CanSet: true, CanModulate: true},
|
||||
{Name: "damp", MinValue: 0, MaxValue: 128, CanSet: true, CanModulate: true},
|
||||
{Name: "notetracking", MinValue: 0, MaxValue: 1, CanSet: true, CanModulate: false},
|
||||
{Name: "delay", MinValue: 0, MaxValue: -1, CanSet: false, CanModulate: true},
|
||||
{Name: "delay", MinValue: 0, MaxValue: 255, CanSet: true, CanModulate: true},
|
||||
{Name: "count", MinValue: 0, MaxValue: 255, CanSet: true, CanModulate: false},
|
||||
}},
|
||||
{
|
||||
Name: "compressor",
|
||||
@ -245,9 +249,6 @@ var UnitTypes = []UnitType{
|
||||
{Name: "type", MinValue: int(Sine), MaxValue: int(Sample), CanSet: true, CanModulate: false},
|
||||
{Name: "lfo", MinValue: 0, MaxValue: 1, CanSet: true, CanModulate: false},
|
||||
{Name: "unison", MinValue: 0, MaxValue: 3, CanSet: true, CanModulate: false},
|
||||
{Name: "start", MinValue: 0, MaxValue: 3440659, CanSet: true, CanModulate: false}, // if type is "sample", then the waveform starts at this position
|
||||
{Name: "loopstart", MinValue: 0, MaxValue: 65535, CanSet: true, CanModulate: false}, // if type is "sample", then the loop starts at this position, relative to "start"
|
||||
{Name: "looplength", MinValue: 0, MaxValue: 65535, CanSet: true, CanModulate: false}, // if type is "sample", then the loop length is this i.e. loop ends at "start" + "loopstart" + "looplength"
|
||||
}},
|
||||
{
|
||||
Name: "loadval",
|
||||
|
@ -8,7 +8,7 @@ import (
|
||||
"github.com/vsariola/sointu/go4k"
|
||||
)
|
||||
|
||||
const expectedMarshaled = `{"BPM":100,"Patterns":["QABEACAAAABLAE4AAAAAAA=="],"Tracks":[{"NumVoices":1,"Sequence":"AA=="}],"SongLength":0,"Patch":[{"NumVoices":1,"Units":[{"Type":"envelope","Parameters":{"attack":32,"decay":32,"gain":128,"release":64,"stereo":0,"sustain":64},"DelayTimes":[]},{"Type":"oscillator","Parameters":{"color":96,"detune":64,"flags":64,"gain":128,"phase":0,"shape":64,"stereo":0,"transpose":64},"DelayTimes":[]},{"Type":"mulp","Parameters":{"stereo":0},"DelayTimes":[]},{"Type":"envelope","Parameters":{"attack":32,"decay":32,"gain":128,"release":64,"stereo":0,"sustain":64},"DelayTimes":[]},{"Type":"oscillator","Parameters":{"color":64,"detune":64,"flags":64,"gain":128,"phase":64,"shape":96,"stereo":0,"transpose":72},"DelayTimes":[]},{"Type":"mulp","Parameters":{"stereo":0},"DelayTimes":[]},{"Type":"out","Parameters":{"gain":128,"stereo":1},"DelayTimes":[]}]}]}`
|
||||
const expectedMarshaled = `{"BPM":100,"Patterns":["QABEACAAAABLAE4AAAAAAA=="],"Tracks":[{"NumVoices":1,"Sequence":"AA=="}],"SongLength":0,"Patch":{"Instruments":[{"NumVoices":1,"Units":[{"Type":"envelope","Parameters":{"attack":32,"decay":32,"gain":128,"release":64,"stereo":0,"sustain":64}},{"Type":"oscillator","Parameters":{"color":96,"detune":64,"flags":64,"gain":128,"phase":0,"shape":64,"stereo":0,"transpose":64}},{"Type":"mulp","Parameters":{"stereo":0}},{"Type":"envelope","Parameters":{"attack":32,"decay":32,"gain":128,"release":64,"stereo":0,"sustain":64}},{"Type":"oscillator","Parameters":{"color":64,"detune":64,"flags":64,"gain":128,"phase":64,"shape":96,"stereo":0,"transpose":72}},{"Type":"mulp","Parameters":{"stereo":0}},{"Type":"out","Parameters":{"gain":128,"stereo":1}}]}],"DelayTimes":[],"SampleOffsets":[]}}`
|
||||
|
||||
var testSong = go4k.Song{
|
||||
BPM: 100,
|
||||
@ -18,15 +18,17 @@ var testSong = go4k.Song{
|
||||
},
|
||||
SongLength: 0,
|
||||
Patch: go4k.Patch{
|
||||
go4k.Instrument{NumVoices: 1, Units: []go4k.Unit{
|
||||
{"envelope", map[string]int{"stereo": 0, "attack": 32, "decay": 32, "sustain": 64, "release": 64, "gain": 128}, []int{}},
|
||||
{"oscillator", map[string]int{"stereo": 0, "transpose": 64, "detune": 64, "phase": 0, "color": 96, "shape": 64, "gain": 128, "flags": 0x40}, []int{}},
|
||||
{"mulp", map[string]int{"stereo": 0}, []int{}},
|
||||
{"envelope", map[string]int{"stereo": 0, "attack": 32, "decay": 32, "sustain": 64, "release": 64, "gain": 128}, []int{}},
|
||||
{"oscillator", map[string]int{"stereo": 0, "transpose": 72, "detune": 64, "phase": 64, "color": 64, "shape": 96, "gain": 128, "flags": 0x40}, []int{}},
|
||||
{"mulp", map[string]int{"stereo": 0}, []int{}},
|
||||
{"out", map[string]int{"stereo": 1, "gain": 128}, []int{}},
|
||||
}},
|
||||
Instruments: []go4k.Instrument{{NumVoices: 1, Units: []go4k.Unit{
|
||||
{"envelope", map[string]int{"stereo": 0, "attack": 32, "decay": 32, "sustain": 64, "release": 64, "gain": 128}},
|
||||
{"oscillator", map[string]int{"stereo": 0, "transpose": 64, "detune": 64, "phase": 0, "color": 96, "shape": 64, "gain": 128, "flags": 0x40}},
|
||||
{"mulp", map[string]int{"stereo": 0}},
|
||||
{"envelope", map[string]int{"stereo": 0, "attack": 32, "decay": 32, "sustain": 64, "release": 64, "gain": 128}},
|
||||
{"oscillator", map[string]int{"stereo": 0, "transpose": 72, "detune": 64, "phase": 64, "color": 64, "shape": 96, "gain": 128, "flags": 0x40}},
|
||||
{"mulp", map[string]int{"stereo": 0}},
|
||||
{"out", map[string]int{"stereo": 1, "gain": 128}},
|
||||
}}},
|
||||
DelayTimes: []int{},
|
||||
SampleOffsets: []go4k.SampleOffset{},
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -23,15 +23,18 @@ const su_max_samples = SAMPLES_PER_ROW * TOTAL_ROWS
|
||||
// const bufsize = su_max_samples * 2
|
||||
|
||||
func TestPlayer(t *testing.T) {
|
||||
patch := []go4k.Instrument{go4k.Instrument{1, []go4k.Unit{
|
||||
go4k.Unit{"envelope", map[string]int{"stereo": 0, "attack": 32, "decay": 32, "sustain": 64, "release": 64, "gain": 128}, []int{}},
|
||||
go4k.Unit{"oscillator", map[string]int{"stereo": 0, "transpose": 64, "detune": 64, "phase": 0, "color": 96, "shape": 64, "gain": 128, "type": go4k.Sine, "lfo": 0, "unison": 0}, []int{}},
|
||||
go4k.Unit{"mulp", map[string]int{"stereo": 0}, []int{}},
|
||||
go4k.Unit{"envelope", map[string]int{"stereo": 0, "attack": 32, "decay": 32, "sustain": 64, "release": 64, "gain": 128}, []int{}},
|
||||
go4k.Unit{"oscillator", map[string]int{"stereo": 0, "transpose": 72, "detune": 64, "phase": 64, "color": 64, "shape": 96, "gain": 128, "type": go4k.Sine, "lfo": 0, "unison": 0}, []int{}},
|
||||
go4k.Unit{"mulp", map[string]int{"stereo": 0}, []int{}},
|
||||
go4k.Unit{"out", map[string]int{"stereo": 1, "gain": 128}, []int{}},
|
||||
}}}
|
||||
patch := go4k.Patch{
|
||||
Instruments: []go4k.Instrument{go4k.Instrument{1, []go4k.Unit{
|
||||
go4k.Unit{"envelope", map[string]int{"stereo": 0, "attack": 32, "decay": 32, "sustain": 64, "release": 64, "gain": 128}},
|
||||
go4k.Unit{"oscillator", map[string]int{"stereo": 0, "transpose": 64, "detune": 64, "phase": 0, "color": 96, "shape": 64, "gain": 128, "type": go4k.Sine, "lfo": 0, "unison": 0}},
|
||||
go4k.Unit{"mulp", map[string]int{"stereo": 0}},
|
||||
go4k.Unit{"envelope", map[string]int{"stereo": 0, "attack": 32, "decay": 32, "sustain": 64, "release": 64, "gain": 128}},
|
||||
go4k.Unit{"oscillator", map[string]int{"stereo": 0, "transpose": 72, "detune": 64, "phase": 64, "color": 64, "shape": 96, "gain": 128, "type": go4k.Sine, "lfo": 0, "unison": 0}},
|
||||
go4k.Unit{"mulp", map[string]int{"stereo": 0}},
|
||||
go4k.Unit{"out", map[string]int{"stereo": 1, "gain": 128}},
|
||||
}}},
|
||||
DelayTimes: []int{},
|
||||
SampleOffsets: []go4k.SampleOffset{}}
|
||||
patterns := [][]byte{{64, 0, 68, 0, 32, 0, 0, 0, 75, 0, 78, 0, 0, 0, 0, 0}}
|
||||
tracks := []go4k.Track{go4k.Track{1, []byte{0}}}
|
||||
song := go4k.Song{100, patterns, tracks, 0, patch}
|
||||
|
@ -14,14 +14,16 @@ var defaultSong = go4k.Song{
|
||||
},
|
||||
SongLength: 0,
|
||||
Patch: go4k.Patch{
|
||||
go4k.Instrument{NumVoices: 2, Units: []go4k.Unit{
|
||||
{"envelope", map[string]int{"stereo": 0, "attack": 32, "decay": 32, "sustain": 64, "release": 64, "gain": 128}, []int{}},
|
||||
{"oscillator", map[string]int{"stereo": 0, "transpose": 64, "detune": 64, "phase": 0, "color": 96, "shape": 64, "gain": 128, "type": go4k.Sine}, []int{}},
|
||||
{"mulp", map[string]int{"stereo": 0}, []int{}},
|
||||
{"envelope", map[string]int{"stereo": 0, "attack": 32, "decay": 32, "sustain": 64, "release": 64, "gain": 128}, []int{}},
|
||||
{"oscillator", map[string]int{"stereo": 0, "transpose": 72, "detune": 64, "phase": 64, "color": 64, "shape": 96, "gain": 128, "type": go4k.Sine}, []int{}},
|
||||
{"mulp", map[string]int{"stereo": 0}, []int{}},
|
||||
{"out", map[string]int{"stereo": 1, "gain": 128}, []int{}},
|
||||
}},
|
||||
Instruments: []go4k.Instrument{{NumVoices: 2, Units: []go4k.Unit{
|
||||
{"envelope", map[string]int{"stereo": 0, "attack": 32, "decay": 32, "sustain": 64, "release": 64, "gain": 128}},
|
||||
{"oscillator", map[string]int{"stereo": 0, "transpose": 64, "detune": 64, "phase": 0, "color": 96, "shape": 64, "gain": 128, "type": go4k.Sine}},
|
||||
{"mulp", map[string]int{"stereo": 0}},
|
||||
{"envelope", map[string]int{"stereo": 0, "attack": 32, "decay": 32, "sustain": 64, "release": 64, "gain": 128}},
|
||||
{"oscillator", map[string]int{"stereo": 0, "transpose": 72, "detune": 64, "phase": 64, "color": 64, "shape": 96, "gain": 128, "type": go4k.Sine}},
|
||||
{"mulp", map[string]int{"stereo": 0}},
|
||||
{"out", map[string]int{"stereo": 1, "gain": 128}},
|
||||
}}},
|
||||
DelayTimes: []int{},
|
||||
SampleOffsets: []go4k.SampleOffset{},
|
||||
},
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user