From bcbb5aaf19e0816001ff6c2de54612dddc77a55b Mon Sep 17 00:00:00 2001 From: Veikko Sariola Date: Sun, 8 Nov 2020 16:03:10 +0200 Subject: [PATCH] feat: Delays and samples are now working through the bridge. One should call bridge.Init() once during the initialization of the program to load the static sample table. On linux, bridge.Init() does nothing. --- go4k/asmformat.go | 42 +++++++++++++++++++++++++++++++++++++ go4k/asmformat_test.go | 4 +--- go4k/bridge/bridge.go | 36 ++++++++++++++++++++++++------- go4k/bridge/bridge_test.go | 6 +++--- go4k/bridge/init_nonwin.go | 6 ++++++ go4k/bridge/init_win.go | 12 +++++++++++ go4k/go4k.go | 1 + go4k/song_json_test.go | 19 +++++++++-------- go4k/song_test.go | 16 +++++++------- go4k/tracker/defaultsong.go | 14 ++++++------- include/sointu/sointu.h | 14 +++++++++---- render.asm | 35 ++++++++++++++++++++++++------- 12 files changed, 155 insertions(+), 50 deletions(-) create mode 100644 go4k/bridge/init_nonwin.go create mode 100644 go4k/bridge/init_win.go diff --git a/go4k/asmformat.go b/go4k/asmformat.go index ebbb886..4145e1a 100644 --- a/go4k/asmformat.go +++ b/go4k/asmformat.go @@ -17,6 +17,8 @@ func ParseAsm(reader io.Reader) (*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 @@ -158,6 +160,20 @@ func ParseAsm(reader io.Reader) (*Song, error) { instr = Instrument{NumVoices: ints[0], Units: []Unit{}} case "END_INSTRUMENT": patch = append(patch, instr) + case "DELTIME": + ints, err := parseNumbers(rest) + if err != nil { + return nil, err + } + for _, v := range ints { + delayTimes = append(delayTimes, v) + } + case "SAMPLE_OFFSET": + ints, err := parseNumbers(rest) + if err != nil { + return nil, err + } + sampleOffsets = append(sampleOffsets, ints) } if unittype, ok := unitNameMap[word]; ok { instrMatch := wordReg.FindStringSubmatch(rest) @@ -214,6 +230,12 @@ func ParseAsm(reader io.Reader) (*Song, error) { } else { parameters["pop"] = 0 } + } else if unittype == "delay" { + if flags["NOTETRACKING"] { + parameters["notetracking"] = 1 + } else { + parameters["notetracking"] = 0 + } } unit := Unit{Type: unittype, Stereo: stereo, Parameters: parameters} instr.Units = append(instr.Units, unit) @@ -221,6 +243,26 @@ func ParseAsm(reader io.Reader) (*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].Stereo { + 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 } diff --git a/go4k/asmformat_test.go b/go4k/asmformat_test.go index 550b6bf..ce1dbaf 100644 --- a/go4k/asmformat_test.go +++ b/go4k/asmformat_test.go @@ -18,6 +18,7 @@ import ( ) func TestAllAsmFiles(t *testing.T) { + bridge.Init() _, myname, _, _ := runtime.Caller(0) files, err := filepath.Glob(path.Join(path.Dir(myname), "..", "tests", "*.asm")) if err != nil { @@ -27,9 +28,6 @@ func TestAllAsmFiles(t *testing.T) { basename := filepath.Base(filename) testname := strings.TrimSuffix(basename, path.Ext(basename)) t.Run(testname, func(t *testing.T) { - if strings.Contains(testname, "delay") || strings.Contains(testname, "sample") { - return // delays and samples are not implemented yet in the bridge, so skip them for now - } file, err := os.Open(filename) if err != nil { t.Fatalf("cannot read the .asm file: %v", filename) diff --git a/go4k/bridge/bridge.go b/go4k/bridge/bridge.go index 93b5bfc..74f85ca 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", "damp", "delay", "count"}}, + "delay": opTableEntry{C.su_delay_id, []string{"pregain", "dry", "feedback", "damp", "delaycount"}}, "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"}}, @@ -80,6 +80,9 @@ 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 + delaytimeno := 0 totalVoices := 0 commands := make([]byte, 0) values := make([]byte, 0) @@ -99,13 +102,31 @@ func Synth(patch go4k.Patch) (*C.Synth, error) { } commands = append(commands, byte(opCode)) for _, paramname := range val.parameterList { - if pval, ok := unit.Parameters[paramname]; ok { - if unit.Type == "delay" && paramname == "count" { - pval = pval*2 - 1 - if val, ok := unit.Parameters["notetracking"]; ok && val == 1 { - pval++ - } + if unit.Type == "delay" && paramname == "delaycount" { + if unit.Stereo && len(unit.DelayTimes)%2 != 0 { + return nil, errors.New("Stereo delays should have even number of delaytimes") } + values = append(values, byte(delaytimeno)) + for _, v := range unit.DelayTimes { + s.DelayTimes[delaytimeno] = C.ushort(v) + delaytimeno++ + } + count := len(unit.DelayTimes) + if unit.Stereo { + count /= 2 + } + count = 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 { return nil, fmt.Errorf("Unit parameter undefined: %v (at instrument %v, unit %v)", paramname, insid, unitid) @@ -178,7 +199,6 @@ func Synth(patch go4k.Patch) (*C.Synth, error) { if len(values) > 16384 { // TODO: 16384 could probably be pulled automatically from cgo return nil, errors.New("The patch would result in more than 16384 values") } - s := new(C.Synth) for i := range commands { s.Commands[i] = (C.uchar)(commands[i]) } diff --git a/go4k/bridge/bridge_test.go b/go4k/bridge/bridge_test.go index c5a8618..a2ae725 100644 --- a/go4k/bridge/bridge_test.go +++ b/go4k/bridge/bridge_test.go @@ -24,9 +24,9 @@ const su_max_samples = SAMPLES_PER_ROW * TOTAL_ROWS func TestBridge(t *testing.T) { patch := []go4k.Instrument{ go4k.Instrument{1, []go4k.Unit{ - go4k.Unit{"envelope", false, map[string]int{"attack": 64, "decay": 64, "sustain": 64, "release": 80, "gain": 128}}, - go4k.Unit{"envelope", false, map[string]int{"attack": 95, "decay": 64, "sustain": 64, "release": 80, "gain": 128}}, - go4k.Unit{"out", true, map[string]int{"gain": 128}}, + go4k.Unit{"envelope", false, map[string]int{"attack": 64, "decay": 64, "sustain": 64, "release": 80, "gain": 128}, []int{}}, + go4k.Unit{"envelope", false, map[string]int{"attack": 95, "decay": 64, "sustain": 64, "release": 80, "gain": 128}, []int{}}, + go4k.Unit{"out", true, map[string]int{"gain": 128}, []int{}}, }}} synth, err := bridge.Synth(patch) if err != nil { diff --git a/go4k/bridge/init_nonwin.go b/go4k/bridge/init_nonwin.go new file mode 100644 index 0000000..f23fcd9 --- /dev/null +++ b/go4k/bridge/init_nonwin.go @@ -0,0 +1,6 @@ +// +build !windows + +package bridge + +func Init() { +} diff --git a/go4k/bridge/init_win.go b/go4k/bridge/init_win.go new file mode 100644 index 0000000..b49ee7e --- /dev/null +++ b/go4k/bridge/init_win.go @@ -0,0 +1,12 @@ +// +build windows + +package bridge + +// #cgo CFLAGS: -I"${SRCDIR}/../../include/sointu" +// #cgo LDFLAGS: "${SRCDIR}/../../build/libsointu.a" +// #include +import "C" + +func Init() { + C.su_load_gmdls() // GM.DLS is an windows specific sound bank so samples work currently only on windows +} diff --git a/go4k/go4k.go b/go4k/go4k.go index d8edf19..9e603b5 100644 --- a/go4k/go4k.go +++ b/go4k/go4k.go @@ -10,6 +10,7 @@ type Unit struct { Type string Stereo bool Parameters map[string]int + DelayTimes []int } const ( diff --git a/go4k/song_json_test.go b/go4k/song_json_test.go index 80217d3..3c7b7ee 100644 --- a/go4k/song_json_test.go +++ b/go4k/song_json_test.go @@ -2,12 +2,13 @@ package go4k_test import ( "encoding/json" - "github.com/vsariola/sointu/go4k" "reflect" "testing" + + "github.com/vsariola/sointu/go4k" ) -const expectedMarshaled = "{\"BPM\":100,\"Patterns\":[\"QABEACAAAABLAE4AAAAAAA==\"],\"Tracks\":[{\"NumVoices\":1,\"Sequence\":\"AA==\"}],\"SongLength\":0,\"Patch\":[{\"NumVoices\":1,\"Units\":[{\"Type\":\"envelope\",\"Stereo\":false,\"Parameters\":{\"attack\":32,\"decay\":32,\"gain\":128,\"release\":64,\"sustain\":64}},{\"Type\":\"oscillator\",\"Stereo\":false,\"Parameters\":{\"color\":96,\"detune\":64,\"flags\":64,\"gain\":128,\"phase\":0,\"shape\":64,\"transpose\":64}},{\"Type\":\"mulp\",\"Stereo\":false,\"Parameters\":{}},{\"Type\":\"envelope\",\"Stereo\":false,\"Parameters\":{\"attack\":32,\"decay\":32,\"gain\":128,\"release\":64,\"sustain\":64}},{\"Type\":\"oscillator\",\"Stereo\":false,\"Parameters\":{\"color\":64,\"detune\":64,\"flags\":64,\"gain\":128,\"phase\":64,\"shape\":96,\"transpose\":72}},{\"Type\":\"mulp\",\"Stereo\":false,\"Parameters\":{}},{\"Type\":\"out\",\"Stereo\":true,\"Parameters\":{\"gain\":128}}]}]}" +const expectedMarshaled = "{\"BPM\":100,\"Patterns\":[\"QABEACAAAABLAE4AAAAAAA==\"],\"Tracks\":[{\"NumVoices\":1,\"Sequence\":\"AA==\"}],\"SongLength\":0,\"Patch\":[{\"NumVoices\":1,\"Units\":[{\"Type\":\"envelope\",\"Stereo\":false,\"Parameters\":{\"attack\":32,\"decay\":32,\"gain\":128,\"release\":64,\"sustain\":64},\"DelayTimes\":[]},{\"Type\":\"oscillator\",\"Stereo\":false,\"Parameters\":{\"color\":96,\"detune\":64,\"flags\":64,\"gain\":128,\"phase\":0,\"shape\":64,\"transpose\":64},\"DelayTimes\":[]},{\"Type\":\"mulp\",\"Stereo\":false,\"Parameters\":{},\"DelayTimes\":[]},{\"Type\":\"envelope\",\"Stereo\":false,\"Parameters\":{\"attack\":32,\"decay\":32,\"gain\":128,\"release\":64,\"sustain\":64},\"DelayTimes\":[]},{\"Type\":\"oscillator\",\"Stereo\":false,\"Parameters\":{\"color\":64,\"detune\":64,\"flags\":64,\"gain\":128,\"phase\":64,\"shape\":96,\"transpose\":72},\"DelayTimes\":[]},{\"Type\":\"mulp\",\"Stereo\":false,\"Parameters\":{},\"DelayTimes\":[]},{\"Type\":\"out\",\"Stereo\":true,\"Parameters\":{\"gain\":128},\"DelayTimes\":[]}]}]}" var testSong = go4k.Song{ BPM: 100, @@ -18,13 +19,13 @@ var testSong = go4k.Song{ SongLength: 0, Patch: go4k.Patch{ go4k.Instrument{NumVoices: 1, Units: []go4k.Unit{ - {"envelope", false, map[string]int{"attack": 32, "decay": 32, "sustain": 64, "release": 64, "gain": 128}}, - {"oscillator", false, map[string]int{"transpose": 64, "detune": 64, "phase": 0, "color": 96, "shape": 64, "gain": 128, "flags": 0x40}}, - {"mulp", false, map[string]int{}}, - {"envelope", false, map[string]int{"attack": 32, "decay": 32, "sustain": 64, "release": 64, "gain": 128}}, - {"oscillator", false, map[string]int{"transpose": 72, "detune": 64, "phase": 64, "color": 64, "shape": 96, "gain": 128, "flags": 0x40}}, - {"mulp", false, map[string]int{}}, - {"out", true, map[string]int{"gain": 128}}, + {"envelope", false, map[string]int{"attack": 32, "decay": 32, "sustain": 64, "release": 64, "gain": 128}, []int{}}, + {"oscillator", false, map[string]int{"transpose": 64, "detune": 64, "phase": 0, "color": 96, "shape": 64, "gain": 128, "flags": 0x40}, []int{}}, + {"mulp", false, map[string]int{}, []int{}}, + {"envelope", false, map[string]int{"attack": 32, "decay": 32, "sustain": 64, "release": 64, "gain": 128}, []int{}}, + {"oscillator", false, map[string]int{"transpose": 72, "detune": 64, "phase": 64, "color": 64, "shape": 96, "gain": 128, "flags": 0x40}, []int{}}, + {"mulp", false, map[string]int{}, []int{}}, + {"out", true, map[string]int{"gain": 128}, []int{}}, }}, }, } diff --git a/go4k/song_test.go b/go4k/song_test.go index b59c7f3..d174b7a 100644 --- a/go4k/song_test.go +++ b/go4k/song_test.go @@ -23,14 +23,14 @@ const su_max_samples = SAMPLES_PER_ROW * TOTAL_ROWS // const bufsize = su_max_samples * 2 func TestPlayer(t *testing.T) { - patch := go4k.Patch{go4k.Instrument{1, []go4k.Unit{ - go4k.Unit{"envelope", false, map[string]int{"attack": 32, "decay": 32, "sustain": 64, "release": 64, "gain": 128}}, - go4k.Unit{"oscillator", false, map[string]int{"transpose": 64, "detune": 64, "phase": 0, "color": 96, "shape": 64, "gain": 128, "type": go4k.Sine, "lfo": 0, "unison": 0}}, - go4k.Unit{"mulp", false, map[string]int{}}, - go4k.Unit{"envelope", false, map[string]int{"attack": 32, "decay": 32, "sustain": 64, "release": 64, "gain": 128}}, - go4k.Unit{"oscillator", false, map[string]int{"transpose": 72, "detune": 64, "phase": 64, "color": 64, "shape": 96, "gain": 128, "type": go4k.Sine, "lfo": 0, "unison": 0}}, - go4k.Unit{"mulp", false, map[string]int{}}, - go4k.Unit{"out", true, map[string]int{"gain": 128}}, + patch := []go4k.Instrument{go4k.Instrument{1, []go4k.Unit{ + go4k.Unit{"envelope", false, map[string]int{"attack": 32, "decay": 32, "sustain": 64, "release": 64, "gain": 128}, []int{}}, + go4k.Unit{"oscillator", false, map[string]int{"transpose": 64, "detune": 64, "phase": 0, "color": 96, "shape": 64, "gain": 128, "type": go4k.Sine, "lfo": 0, "unison": 0}, []int{}}, + go4k.Unit{"mulp", false, map[string]int{}, []int{}}, + go4k.Unit{"envelope", false, map[string]int{"attack": 32, "decay": 32, "sustain": 64, "release": 64, "gain": 128}, []int{}}, + go4k.Unit{"oscillator", false, map[string]int{"transpose": 72, "detune": 64, "phase": 64, "color": 64, "shape": 96, "gain": 128, "type": go4k.Sine, "lfo": 0, "unison": 0}, []int{}}, + go4k.Unit{"mulp", false, map[string]int{}, []int{}}, + go4k.Unit{"out", true, map[string]int{"gain": 128}, []int{}}, }}} 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}}} diff --git a/go4k/tracker/defaultsong.go b/go4k/tracker/defaultsong.go index 9fe338a..0622be4 100644 --- a/go4k/tracker/defaultsong.go +++ b/go4k/tracker/defaultsong.go @@ -15,13 +15,13 @@ var defaultSong = go4k.Song{ SongLength: 0, Patch: go4k.Patch{ go4k.Instrument{NumVoices: 2, Units: []go4k.Unit{ - {"envelope", false, map[string]int{"attack": 32, "decay": 32, "sustain": 64, "release": 64, "gain": 128}}, - {"oscillator", false, map[string]int{"transpose": 64, "detune": 64, "phase": 0, "color": 96, "shape": 64, "gain": 128, "type": go4k.Sine}}, - {"mulp", false, map[string]int{}}, - {"envelope", false, map[string]int{"attack": 32, "decay": 32, "sustain": 64, "release": 64, "gain": 128}}, - {"oscillator", false, map[string]int{"transpose": 72, "detune": 64, "phase": 64, "color": 64, "shape": 96, "gain": 128, "type": go4k.Sine}}, - {"mulp", false, map[string]int{}}, - {"out", true, map[string]int{"gain": 128}}, + {"envelope", false, map[string]int{"attack": 32, "decay": 32, "sustain": 64, "release": 64, "gain": 128}, []int{}}, + {"oscillator", false, map[string]int{"transpose": 64, "detune": 64, "phase": 0, "color": 96, "shape": 64, "gain": 128, "type": go4k.Sine}, []int{}}, + {"mulp", false, map[string]int{}, []int{}}, + {"envelope", false, map[string]int{"attack": 32, "decay": 32, "sustain": 64, "release": 64, "gain": 128}, []int{}}, + {"oscillator", false, map[string]int{"transpose": 72, "detune": 64, "phase": 64, "color": 64, "shape": 96, "gain": 128, "type": go4k.Sine}, []int{}}, + {"mulp", false, map[string]int{}, []int{}}, + {"out", true, map[string]int{"gain": 128}, []int{}}, }}, }, } diff --git a/include/sointu/sointu.h b/include/sointu/sointu.h index 584d21a..4ef5867 100644 --- a/include/sointu/sointu.h +++ b/include/sointu/sointu.h @@ -1,7 +1,7 @@ #ifndef _SOINTU_H #define _SOINTU_H -#pragma pack(push,4) // this should be fine for both Go and assembly +#pragma pack(push,1) // this should be fine for both Go and assembly typedef struct Unit { float State[8]; float Ports[8]; @@ -30,9 +30,17 @@ typedef struct SynthWorkspace { struct Voice Voices[32]; } SynthWorkspace; +typedef struct SampleOffset { + unsigned int Start; + unsigned short LoopStart; + unsigned short LoopLength; +} SampleOffset; + typedef struct Synth { struct SynthWorkspace SynthWrk; struct DelayWorkspace DelayWrks[64]; // let's keep this as 64 for now, so the delays take 16 meg. If that's too little or too much, we can change this in future. + unsigned short DelayTimes[768]; + struct SampleOffset SampleOffsets[256]; unsigned int RandSeed; unsigned int GlobalTick; unsigned char Commands[32 * 64]; @@ -52,9 +60,7 @@ typedef struct Synth { #define CALLCONV // the asm will use honor honor correct x64 ABI on all 64-bit platforms #endif -#ifdef INCLUDE_GMDLS -extern void CALLCONV su_load_gmdls(void); -#endif +void CALLCONV su_load_gmdls(void); // int su_render(Synth* synth, float* buffer, int* samples, int* time): // Renders samples until 'samples' number of samples are reached or 'time' number of diff --git a/render.asm b/render.asm index 87b422c..9bf53de 100644 --- a/render.asm +++ b/render.asm @@ -70,6 +70,7 @@ USE_IN %define INCLUDE_SINE %define INCLUDE_PULSE %define INCLUDE_GATE +%define INCLUDE_SAMPLES %define INCLUDE_UNISONS %define INCLUDE_POLYPHONY %define INCLUDE_MULTIVOICE_TRACKS @@ -80,14 +81,26 @@ USE_IN %define INCLUDE_NEGBANDPASS %define INCLUDE_NEGHIGHPASS %define INCLUDE_GLOBAL_SEND +%define INCLUDE_DELAY_NOTETRACKING +%define INCLUDE_DELAY_FLOAT_TIME +%define INCLUDE_GMDLS %include "sointu/footer.inc" section .text +struc su_sampleoff + .start resd 1 + .loopstart resw 1 + .looplength resw 1 + .size: +endstruc + struc su_synth .synthwrk resb su_synthworkspace.size .delaywrks resb su_delayline_wrk.size * 64 + .delaytimes resw 768 + .sampleoffs resb su_sampleoff.size * 256 .randseed resd 1 .globaltime resd 1 .commands resb 32 * 64 @@ -125,6 +138,10 @@ EXPORT MANGLE_FUNC(su_render,16) push _SI ; push bufsize push _DX ; push bufptr push _CX ; this takes place of the voicetrack + lea _AX, [_CX + su_synth.sampleoffs] + push _AX + lea _AX, [_CX + su_synth.delaytimes] + push _AX mov eax, [_CX + su_synth.randseed] push _AX ; randseed mov eax, [_CX + su_synth.globaltime] @@ -135,12 +152,12 @@ EXPORT MANGLE_FUNC(su_render,16) su_render_samples_loop: cmp eax, [_SP] ; if rowtick >= maxtime jge su_render_samples_time_finish ; goto finish - mov ecx, [_SP + PTRSIZE*5] ; ecx = buffer length in samples - cmp [_SP + PTRSIZE*6], ecx ; if samples >= maxsamples + mov ecx, [_SP + PTRSIZE*7] ; ecx = buffer length in samples + cmp [_SP + PTRSIZE*8], ecx ; if samples >= maxsamples jge su_render_samples_time_finish ; goto finish inc eax ; time++ - inc dword [_SP + PTRSIZE*6] ; samples++ - mov _CX, [_SP + PTRSIZE*3] + inc dword [_SP + PTRSIZE*8] ; samples++ + mov _CX, [_SP + PTRSIZE*5] push _AX ; push rowtick mov eax, [_CX + su_synth.polyphony] push _AX ;polyphony @@ -154,12 +171,12 @@ su_render_samples_loop: call MANGLE_FUNC(su_run_vm,0) pop _AX pop _AX - mov _DI, [_SP + PTRSIZE*5] ; edi containts buffer ptr - mov _CX, [_SP + PTRSIZE*4] + mov _DI, [_SP + PTRSIZE*7] ; edi containts buffer ptr + mov _CX, [_SP + PTRSIZE*6] lea _SI, [_CX + su_synth.synthwrk + su_synthworkspace.left] movsd ; copy left channel to output buffer movsd ; copy right channel to output buffer - mov [_SP + PTRSIZE*5], _DI ; save back the updated ptr + mov [_SP + PTRSIZE*7], _DI ; save back the updated ptr lea _DI, [_SI-8] xor eax, eax stosd ; clear left channel so the VM is ready to write them again @@ -170,7 +187,9 @@ su_render_samples_loop: su_render_samples_time_finish: pop _CX pop _BX - pop _DX + pop _DX + pop _CX ; discard delaytimes ptr + pop _CX ; discard samplesoffs ptr pop _CX mov [_CX + su_synth.randseed], edx mov [_CX + su_synth.globaltime], ebx