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{}, }, }