From 95c8c9c2b7b6fc1a5c439d4a6739545ed5efc021 Mon Sep 17 00:00:00 2001 From: Veikko Sariola Date: Fri, 20 Nov 2020 22:21:21 +0200 Subject: [PATCH] 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. --- go4k/algorithms.go | 18 ++++++----- go4k/asmformat.go | 63 ++++++++++--------------------------- go4k/asmformat_test.go | 10 ++++-- go4k/bridge/bridge.go | 35 +++++++-------------- go4k/bridge/bridge_test.go | 16 ++++++---- go4k/go4k.go | 29 ++++++++--------- go4k/song_json_test.go | 22 +++++++------ go4k/song_test.go | 21 +++++++------ go4k/tracker/defaultsong.go | 20 ++++++------ 9 files changed, 106 insertions(+), 128 deletions(-) diff --git a/go4k/algorithms.go b/go4k/algorithms.go index 666b371..ddf871f 100644 --- a/go4k/algorithms.go +++ b/go4k/algorithms.go @@ -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 { diff --git a/go4k/asmformat.go b/go4k/asmformat.go index 9ba3fa1..ddb30ce 100644 --- a/go4k/asmformat.go +++ b/go4k/asmformat.go @@ -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), diff --git a/go4k/asmformat_test.go b/go4k/asmformat_test.go index f986266..ce9a9b8 100644 --- a/go4k/asmformat_test.go +++ b/go4k/asmformat_test.go @@ -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) } diff --git a/go4k/bridge/bridge.go b/go4k/bridge/bridge.go index b40362e..6e127c4 100644 --- a/go4k/bridge/bridge.go +++ b/go4k/bridge/bridge.go @@ -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 diff --git a/go4k/bridge/bridge_test.go b/go4k/bridge/bridge_test.go index 382681b..eafa673 100644 --- a/go4k/bridge/bridge_test.go +++ b/go4k/bridge/bridge_test.go @@ -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) diff --git a/go4k/go4k.go b/go4k/go4k.go index 3f0f93a..2dea9a5 100644 --- a/go4k/go4k.go +++ b/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", diff --git a/go4k/song_json_test.go b/go4k/song_json_test.go index 7b7c140..415b893 100644 --- a/go4k/song_json_test.go +++ b/go4k/song_json_test.go @@ -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{}, }, } diff --git a/go4k/song_test.go b/go4k/song_test.go index 3e985d9..4e90b69 100644 --- a/go4k/song_test.go +++ b/go4k/song_test.go @@ -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} diff --git a/go4k/tracker/defaultsong.go b/go4k/tracker/defaultsong.go index ec61465..1dab304 100644 --- a/go4k/tracker/defaultsong.go +++ b/go4k/tracker/defaultsong.go @@ -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{}, }, }