refactor(vm): rename Commands/Values to Opcodes/Operands

The commands and values were not very good names to what the
byte sequences actually are: opcodes and their operands. In
many other places, we were already calling the byte in the Command
stream as Opcode, so a logical name for a sequence of these is
Opcodes. Values is such a generic name that it's not immediately
clear that this sequence is related to the opcodes. Operands is not
perfect but clearly suggests that this sequence is related to
the Opcodes.
This commit is contained in:
5684185+vsariola@users.noreply.github.com 2023-10-18 19:51:56 +03:00
parent 87604dd92e
commit 01bf409929
14 changed files with 158 additions and 156 deletions

View File

@ -7,11 +7,11 @@
void SU_CALLCONV su_render_song(float *buffer) void SU_CALLCONV su_render_song(float *buffer)
{ {
Synth *synth; Synth *synth;
const unsigned char commands[] = {SU_ENVELOPE_ID, // MONO const unsigned char opcodes[] = {SU_ENVELOPE_ID, // MONO
SU_ENVELOPE_ID, // MONO SU_ENVELOPE_ID, // MONO
SU_OUT_ID + 1, // STEREO SU_OUT_ID + 1, // STEREO
SU_ADVANCE_ID}; // MONO SU_ADVANCE_ID}; // MONO
const unsigned char values[] = {64, 64, 64, 80, 128, // envelope 1 const unsigned char operands[] = {64, 64, 64, 80, 128, // envelope 1
95, 64, 64, 80, 128, // envelope 2 95, 64, 64, 80, 128, // envelope 2
128}; 128};
int retval; int retval;
@ -20,8 +20,8 @@ void SU_CALLCONV su_render_song(float *buffer)
// initialize Synth // initialize Synth
synth = (Synth *)malloc(sizeof(Synth)); synth = (Synth *)malloc(sizeof(Synth));
memset(synth, 0, sizeof(Synth)); memset(synth, 0, sizeof(Synth));
memcpy(synth->Commands, commands, sizeof(commands)); memcpy(synth->Opcodes, opcodes, sizeof(opcodes));
memcpy(synth->Values, values, sizeof(values)); memcpy(synth->Operands, operands, sizeof(operands));
synth->NumVoices = 1; synth->NumVoices = 1;
synth->Polyphony = 0; synth->Polyphony = 0;
synth->RandSeed = 1; synth->RandSeed = 1;

View File

@ -14,11 +14,11 @@ int main(int argc, char *argv[])
{ {
Synth *synth; Synth *synth;
float *buffer; float *buffer;
const unsigned char commands[] = {SU_ENVELOPE_ID, // MONO const unsigned char opcodes[] = {SU_ENVELOPE_ID, // MONO
SU_ENVELOPE_ID, // MONO SU_ENVELOPE_ID, // MONO
SU_OUT_ID + 1, // STEREO SU_OUT_ID + 1, // STEREO
SU_ADVANCE_ID}; // MONO SU_ADVANCE_ID}; // MONO
const unsigned char values[] = {64, 64, 64, 80, 128, // envelope 1 const unsigned char operands[] = {64, 64, 64, 80, 128, // envelope 1
95, 64, 64, 80, 128, // envelope 2 95, 64, 64, 80, 128, // envelope 2
128}; 128};
int errcode; int errcode;
@ -29,8 +29,8 @@ int main(int argc, char *argv[])
// initialize Synth // initialize Synth
synth = (Synth *)malloc(sizeof(Synth)); synth = (Synth *)malloc(sizeof(Synth));
memset(synth, 0, sizeof(Synth)); memset(synth, 0, sizeof(Synth));
memcpy(synth->Commands, commands, sizeof(commands)); memcpy(synth->Opcodes, opcodes, sizeof(opcodes));
memcpy(synth->Values, values, sizeof(values)); memcpy(synth->Operands, operands, sizeof(operands));
synth->NumVoices = 1; synth->NumVoices = 1;
synth->Polyphony = 0; synth->Polyphony = 0;
synth->RandSeed = 1; synth->RandSeed = 1;

View File

@ -11,15 +11,16 @@ type (
// Bytecode is the Sointu VM bytecode & data (delay times, sample offsets) // Bytecode is the Sointu VM bytecode & data (delay times, sample offsets)
// which is executed by the synthesizer. It is generated from a Sointu patch. // which is executed by the synthesizer. It is generated from a Sointu patch.
Bytecode struct { Bytecode struct {
// Commands is the bytecode, which is a sequence of opcode bytes, one // Opcodes is the bytecode, which is a sequence of opcode bytes, one
// per unit in the patch. A byte of 0 denotes the end of an instrument, // per unit in the patch. A byte of 0 denotes the end of an instrument,
// at which point if that instrument has more than one voice, the // at which point if that instrument has more than one voice, the
// commands are repeated for each voice. // opcodes are repeated for each voice.
Commands []byte Opcodes []byte
// Values are the operands of the opcodes. Every opcode reads 0 or more // Operands are the operands of the opcodes. When executing the
// values from the value sequence. // bytecodes, every opcode reads 0 or more operands from it and advances
Values []byte // in the sequence.
Operands []byte
// DelayTimes is a table of delay times in samples. The delay times are // DelayTimes is a table of delay times in samples. The delay times are
// used by the delay units in the patch. The delay unit only stores // used by the delay units in the patch. The delay unit only stores
@ -114,8 +115,8 @@ func Encode(patch sointu.Patch, featureSet FeatureSet, bpm int) (*Bytecode, erro
flags += 0x08 flags += 0x08
} }
flags += p["unison"] flags += p["unison"]
b.cmd(opcode + p["stereo"]) b.op(opcode + p["stereo"])
b.vals(p["transpose"], p["detune"], p["phase"], color, p["shape"], p["gain"], flags) b.operand(p["transpose"], p["detune"], p["phase"], color, p["shape"], p["gain"], flags)
case "delay": case "delay":
count := len(unit.VarArgs) count := len(unit.VarArgs)
if unit.Parameters["stereo"] == 1 { if unit.Parameters["stereo"] == 1 {
@ -125,13 +126,13 @@ func Encode(patch sointu.Patch, featureSet FeatureSet, bpm int) (*Bytecode, erro
continue // skip encoding delays without any delay lines continue // skip encoding delays without any delay lines
} }
countTrack := count*2 - 1 + (unit.Parameters["notetracking"] & 1) // 1 means no note tracking and 1 delay, 2 means notetracking with 1 delay, 3 means no note tracking and 2 delays etc. countTrack := count*2 - 1 + (unit.Parameters["notetracking"] & 1) // 1 means no note tracking and 1 delay, 2 means notetracking with 1 delay, 3 means no note tracking and 2 delays etc.
b.cmd(opcode + p["stereo"]) b.op(opcode + p["stereo"])
b.defaultVals(unit) b.defOperands(unit)
b.vals(b.delayIndices[instrIndex][unitIndex], countTrack) b.operand(b.delayIndices[instrIndex][unitIndex], countTrack)
case "aux", "in": case "aux", "in":
b.cmd(opcode + p["stereo"]) b.op(opcode + p["stereo"])
b.defaultVals(unit) b.defOperands(unit)
b.vals(unit.Parameters["channel"]) b.operand(unit.Parameters["channel"])
case "filter": case "filter":
flags := 0 flags := 0
if unit.Parameters["lowpass"] == 1 { if unit.Parameters["lowpass"] == 1 {
@ -149,9 +150,9 @@ func Encode(patch sointu.Patch, featureSet FeatureSet, bpm int) (*Bytecode, erro
if unit.Parameters["neghighpass"] == 1 { if unit.Parameters["neghighpass"] == 1 {
flags += 0x04 flags += 0x04
} }
b.cmd(opcode + p["stereo"]) b.op(opcode + p["stereo"])
b.defaultVals(unit) b.defOperands(unit)
b.vals(flags) b.operand(flags)
case "send": case "send":
targetID := unit.Parameters["target"] targetID := unit.Parameters["target"]
targetInstrIndex, _, err := patch.FindSendTarget(targetID) targetInstrIndex, _, err := patch.FindSendTarget(targetID)
@ -164,8 +165,8 @@ func Encode(patch sointu.Patch, featureSet FeatureSet, bpm int) (*Bytecode, erro
if unit.Parameters["sendpop"] == 1 { if unit.Parameters["sendpop"] == 1 {
addr += 0x8 addr += 0x8
} }
b.cmd(opcode + p["stereo"]) b.op(opcode + p["stereo"])
b.defaultVals(unit) b.defOperands(unit)
b.localIDRef(targetID, addr) b.localIDRef(targetID, addr)
} else { } else {
addr += 0x8000 addr += 0x8000
@ -177,8 +178,8 @@ func Encode(patch sointu.Patch, featureSet FeatureSet, bpm int) (*Bytecode, erro
} }
addr += voiceStart * 0x400 addr += voiceStart * 0x400
for i := voiceStart; i < voiceEnd; i++ { for i := voiceStart; i < voiceEnd; i++ {
b.cmd(opcode + p["stereo"]) b.op(opcode + p["stereo"])
b.defaultVals(unit) b.defOperands(unit)
if i == voiceEnd-1 && unit.Parameters["sendpop"] == 1 { if i == voiceEnd-1 && unit.Parameters["sendpop"] == 1 {
addr += 0x8 // when making multi unit send, only the last one should have POP bit set if popping addr += 0x8 // when making multi unit send, only the last one should have POP bit set if popping
} }
@ -194,19 +195,19 @@ func Encode(patch sointu.Patch, featureSet FeatureSet, bpm int) (*Bytecode, erro
if unit.Parameters["sendpop"] == 1 { if unit.Parameters["sendpop"] == 1 {
addr |= 0x8 addr |= 0x8
} }
b.cmd(opcode + p["stereo"]) b.op(opcode + p["stereo"])
b.defaultVals(unit) b.defOperands(unit)
b.Values = append(b.Values, byte(addr&255), byte(addr>>8)) b.Operands = append(b.Operands, byte(addr&255), byte(addr>>8))
} }
default: default:
b.cmd(opcode + p["stereo"]) b.op(opcode + p["stereo"])
b.defaultVals(unit) b.defOperands(unit)
} }
if b.unitNo > 63 { if b.unitNo > 63 {
return nil, fmt.Errorf(`Instrument %v has over 63 units`, instrIndex) return nil, fmt.Errorf(`Instrument %v has over 63 units`, instrIndex)
} }
} }
b.cmdFinish(instr) b.opFinish(instr)
} }
return &b.Bytecode, nil return &b.Bytecode, nil
} }
@ -235,34 +236,35 @@ func newBytecodeBuilder(patch sointu.Patch, bpm int) *bytecodeBuilder {
return &c return &c
} }
// cmd adds a command to the bytecode, and increments the unit number // op adds a command to the bytecode, and increments the unit number
func (b *bytecodeBuilder) cmd(opcode int) { func (b *bytecodeBuilder) op(opcode int) {
b.Commands = append(b.Commands, byte(opcode)) b.Opcodes = append(b.Opcodes, byte(opcode))
b.unitNo++ b.unitNo++
} }
// cmdFinish adds a command to the bytecode that marks the end of an instrument, resets the unit number and increments the voice number // opFinish adds a command to the bytecode that marks the end of an instrument, resets the unit number and increments the voice number
// local addresses are forgotten when instrument ends // local addresses are forgotten when instrument ends
func (b *bytecodeBuilder) cmdFinish(instr sointu.Instrument) { func (b *bytecodeBuilder) opFinish(instr sointu.Instrument) {
b.Commands = append(b.Commands, 0) b.Opcodes = append(b.Opcodes, 0)
b.unitNo = 0 b.unitNo = 0
b.voiceNo += instr.NumVoices b.voiceNo += instr.NumVoices
b.localAddrs = map[int]uint16{} b.localAddrs = map[int]uint16{}
b.localFixups = map[int]([]int){} b.localFixups = map[int]([]int){}
} }
// vals appends values to the value stream // operand appends operands to the operand stream
func (b *bytecodeBuilder) vals(values ...int) { func (b *bytecodeBuilder) operand(operands ...int) {
for _, v := range values { for _, v := range operands {
b.Values = append(b.Values, byte(v)) b.Operands = append(b.Operands, byte(v))
} }
} }
// defaultVals appends the values to the value stream for all parameters that can be modulated and set // defOperands appends the operands to the stream for all parameters that can be
func (b *bytecodeBuilder) defaultVals(unit sointu.Unit) { // modulated and set
func (b *bytecodeBuilder) defOperands(unit sointu.Unit) {
for _, v := range sointu.UnitTypes[unit.Type] { for _, v := range sointu.UnitTypes[unit.Type] {
if v.CanModulate && v.CanSet { if v.CanModulate && v.CanSet {
b.Values = append(b.Values, byte(unit.Parameters[v.Name])) b.Operands = append(b.Operands, byte(unit.Parameters[v.Name]))
} }
} }
} }
@ -272,9 +274,9 @@ func (b *bytecodeBuilder) localIDRef(id int, addr int) {
if v, ok := b.localAddrs[id]; ok { if v, ok := b.localAddrs[id]; ok {
addr += int(v) addr += int(v)
} else { } else {
b.localFixups[id] = append(b.localFixups[id], len(b.Values)) b.localFixups[id] = append(b.localFixups[id], len(b.Operands))
} }
b.Values = append(b.Values, byte(addr&255), byte(addr>>8)) b.Operands = append(b.Operands, byte(addr&255), byte(addr>>8))
} }
// globalIDRef adds a reference to a global id label to the value stream; if the targeted ID has not been seen yet, it is added to the fixup list // globalIDRef adds a reference to a global id label to the value stream; if the targeted ID has not been seen yet, it is added to the fixup list
@ -282,9 +284,9 @@ func (b *bytecodeBuilder) globalIDRef(id int, addr int) {
if v, ok := b.globalAddrs[id]; ok { if v, ok := b.globalAddrs[id]; ok {
addr += int(v) addr += int(v)
} else { } else {
b.globalFixups[id] = append(b.globalFixups[id], len(b.Values)) b.globalFixups[id] = append(b.globalFixups[id], len(b.Operands))
} }
b.Values = append(b.Values, byte(addr&255), byte(addr>>8)) b.Operands = append(b.Operands, byte(addr&255), byte(addr>>8))
} }
// idLabel adds a label to the value stream for the given id; all earlier references to the id are fixed up // idLabel adds a label to the value stream for the given id; all earlier references to the id are fixed up
@ -302,10 +304,10 @@ func (b *bytecodeBuilder) idLabel(id int) {
// fixUp fixes up the references to the given id with the given delta // fixUp fixes up the references to the given id with the given delta
func (b *bytecodeBuilder) fixUp(positions []int, delta uint16) { func (b *bytecodeBuilder) fixUp(positions []int, delta uint16) {
for _, pos := range positions { for _, pos := range positions {
orig := (uint16(b.Values[pos+1]) << 8) + uint16(b.Values[pos]) orig := (uint16(b.Operands[pos+1]) << 8) + uint16(b.Operands[pos])
new := orig + delta new := orig + delta
b.Values[pos] = byte(new & 255) b.Operands[pos] = byte(new & 255)
b.Values[pos+1] = byte(new >> 8) b.Operands[pos+1] = byte(new >> 8)
} }
} }

View File

@ -32,17 +32,17 @@ func Synth(patch sointu.Patch, bpm int) (*NativeSynth, error) {
if err != nil { if err != nil {
return nil, fmt.Errorf("error compiling patch: %v", err) return nil, fmt.Errorf("error compiling patch: %v", err)
} }
if len(comPatch.Commands) > 2048 { // TODO: 2048 could probably be pulled automatically from cgo if len(comPatch.Opcodes) > 2048 { // TODO: 2048 could probably be pulled automatically from cgo
return nil, errors.New("bridge supports at most 2048 commands; the compiled patch has more") return nil, errors.New("bridge supports at most 2048 opcodes; the compiled patch has more")
} }
if len(comPatch.Values) > 16384 { // TODO: 16384 could probably be pulled automatically from cgo if len(comPatch.Operands) > 16384 { // TODO: 16384 could probably be pulled automatically from cgo
return nil, errors.New("bridge supports at most 16384 values; the compiled patch has more") return nil, errors.New("bridge supports at most 16384 operands; the compiled patch has more")
} }
for i, v := range comPatch.Commands { for i, v := range comPatch.Opcodes {
s.Commands[i] = (C.uchar)(v) s.Opcodes[i] = (C.uchar)(v)
} }
for i, v := range comPatch.Values { for i, v := range comPatch.Operands {
s.Values[i] = (C.uchar)(v) s.Operands[i] = (C.uchar)(v)
} }
for i, v := range comPatch.DelayTimes { for i, v := range comPatch.DelayTimes {
s.DelayTimes[i] = (C.ushort)(v) s.DelayTimes[i] = (C.ushort)(v)
@ -124,21 +124,21 @@ func (bridgesynth *NativeSynth) Update(patch sointu.Patch, bpm int) error {
if err != nil { if err != nil {
return fmt.Errorf("error compiling patch: %v", err) return fmt.Errorf("error compiling patch: %v", err)
} }
if len(comPatch.Commands) > 2048 { // TODO: 2048 could probably be pulled automatically from cgo if len(comPatch.Opcodes) > 2048 { // TODO: 2048 could probably be pulled automatically from cgo
return errors.New("bridge supports at most 2048 commands; the compiled patch has more") return errors.New("bridge supports at most 2048 opcodes; the compiled patch has more")
} }
if len(comPatch.Values) > 16384 { // TODO: 16384 could probably be pulled automatically from cgo if len(comPatch.Operands) > 16384 { // TODO: 16384 could probably be pulled automatically from cgo
return errors.New("bridge supports at most 16384 values; the compiled patch has more") return errors.New("bridge supports at most 16384 operands; the compiled patch has more")
} }
needsRefresh := false needsRefresh := false
for i, v := range comPatch.Commands { for i, v := range comPatch.Opcodes {
if cmdChar := (C.uchar)(v); s.Commands[i] != cmdChar { if cmdChar := (C.uchar)(v); s.Opcodes[i] != cmdChar {
s.Commands[i] = cmdChar s.Opcodes[i] = cmdChar
needsRefresh = true // if any of the commands change, we retrigger all units needsRefresh = true // if any of the opcodes change, we retrigger all units
} }
} }
for i, v := range comPatch.Values { for i, v := range comPatch.Operands {
s.Values[i] = (C.uchar)(v) s.Operands[i] = (C.uchar)(v)
} }
for i, v := range comPatch.DelayTimes { for i, v := range comPatch.DelayTimes {
s.DelayTimes[i] = (C.ushort)(v) s.DelayTimes[i] = (C.ushort)(v)
@ -152,7 +152,7 @@ func (bridgesynth *NativeSynth) Update(patch sointu.Patch, bpm int) error {
s.Polyphony = C.uint(comPatch.PolyphonyBitmask) s.Polyphony = C.uint(comPatch.PolyphonyBitmask)
if needsRefresh { if needsRefresh {
for i := range s.SynthWrk.Voices { for i := range s.SynthWrk.Voices {
// if any of the commands change, we retrigger all units // if any of the opcodes change, we retrigger all units
// note that we don't change the notes or release states, just the units // note that we don't change the notes or release states, just the units
for j := range s.SynthWrk.Voices[i].Units { for j := range s.SynthWrk.Voices[i].Units {
s.SynthWrk.Voices[i].Units[j] = C.Unit{} s.SynthWrk.Voices[i].Units[j] = C.Unit{}

View File

@ -7,8 +7,8 @@ struc su_synth
.sampleoffs resb su_sample_offset.size * 256 .sampleoffs resb su_sample_offset.size * 256
.randseed resd 1 .randseed resd 1
.globaltime resd 1 .globaltime resd 1
.commands resb 32 * 64 .opcodes resb 32 * 64
.values resb 32 * 64 * 8 .operands resb 32 * 64 * 8
.polyphony resd 1 .polyphony resd 1
.numvoices resd 1 .numvoices resd 1
endstruc endstruc
@ -73,8 +73,8 @@ su_render_samples_loop:
mov eax, [{{.CX}} + su_synth.numvoices] mov eax, [{{.CX}} + su_synth.numvoices]
{{.Push .AX "VoicesRemain"}} {{.Push .AX "VoicesRemain"}}
lea {{.DX}}, [{{.CX}}+ su_synth.synth_wrk] lea {{.DX}}, [{{.CX}}+ su_synth.synth_wrk]
lea {{.COM}}, [{{.CX}}+ su_synth.commands] lea {{.COM}}, [{{.CX}}+ su_synth.opcodes]
lea {{.VAL}}, [{{.CX}}+ su_synth.values] lea {{.VAL}}, [{{.CX}}+ su_synth.operands]
lea {{.WRK}}, [{{.DX}} + su_synthworkspace.voices] lea {{.WRK}}, [{{.DX}} + su_synthworkspace.voices]
lea {{.CX}}, [{{.CX}}+ su_synth.delay_wrks - su_delayline_wrk.filtstate] lea {{.CX}}, [{{.CX}}+ su_synth.delay_wrks - su_delayline_wrk.filtstate]
{{.Call "su_run_vm"}} {{.Call "su_run_vm"}}

View File

@ -43,8 +43,8 @@ typedef struct Synth {
struct SampleOffset SampleOffsets[256]; struct SampleOffset SampleOffsets[256];
unsigned int RandSeed; unsigned int RandSeed;
unsigned int GlobalTick; unsigned int GlobalTick;
unsigned char Commands[32 * 64]; unsigned char Opcodes[32 * 64];
unsigned char Values[32 * 64 * 8]; unsigned char Operands[32 * 64 * 8];
unsigned int Polyphony; unsigned int Polyphony;
unsigned int NumVoices; unsigned int NumVoices;
} Synth; } Synth;

View File

@ -5,15 +5,15 @@
; su_synth_obj.right : Set to 0 before calling ; su_synth_obj.right : Set to 0 before calling
; _CX : Pointer to delay workspace (if needed) ; _CX : Pointer to delay workspace (if needed)
; _DX : Pointer to synth object ; _DX : Pointer to synth object
; COM : Pointer to command stream ; COM : Pointer to opcode stream
; VAL : Pointer to value stream ; VAL : Pointer to operand stream
; WRK : Pointer to the last workspace processed ; WRK : Pointer to the last workspace processed
; Output: su_synth_obj.left : left sample ; Output: su_synth_obj.left : left sample
; su_synth_obj.right : right sample ; su_synth_obj.right : right sample
; Dirty: everything ; Dirty: everything
;------------------------------------------------------------------------------- ;-------------------------------------------------------------------------------
{{.Func "su_run_vm"}} {{.Func "su_run_vm"}}
{{- .PushRegs .CX "DelayWorkSpace" .DX "Synth" .COM "CommandStream" .WRK "Voice" .VAL "ValueStream" | indent 4}} {{- .PushRegs .CX "DelayWorkSpace" .DX "Synth" .COM "OpcodeStream" .WRK "Voice" .VAL "OperandStream" | indent 4}}
{{- if .RowSync}} {{- if .RowSync}}
fild dword [{{.Stack "Sample"}}] fild dword [{{.Stack "Sample"}}]
{{.Int .Song.SamplesPerRow | .Prepare | indent 8}} {{.Int .Song.SamplesPerRow | .Prepare | indent 8}}
@ -33,13 +33,13 @@ su_run_vm_loop: ; loop until all voices done
add {{.INP}}, su_voice.inputs add {{.INP}}, su_voice.inputs
xor ecx, ecx ; counter = 0 xor ecx, ecx ; counter = 0
xor eax, eax ; clear out high bits of eax, as lodsb only sets al xor eax, eax ; clear out high bits of eax, as lodsb only sets al
su_transform_values_loop: su_transform_operands_loop:
{{- .Prepare "su_vm_transformcounts-1" | indent 4}} {{- .Prepare "su_vm_transformcounts-1" | indent 4}}
cmp cl, byte [{{.Use "su_vm_transformcounts-1"}}+{{.DI}}] ; compare the counter to the value in the param count table cmp cl, byte [{{.Use "su_vm_transformcounts-1"}}+{{.DI}}] ; compare the counter to the value in the param count table
je su_transform_values_out je su_transform_operands_out
lodsb ; load the byte value from VAL stream lodsb ; load the operand from VAL stream
push {{.AX}} ; push it to memory so FPU can read it push {{.AX}} ; push it to memory so FPU can read it
fild dword [{{.SP}}] ; load the value to FPU stack fild dword [{{.SP}}] ; load the operand value to FPU stack
{{- .Prepare (.Float 0.0078125) | indent 4}} {{- .Prepare (.Float 0.0078125) | indent 4}}
fmul dword [{{.Use (.Float 0.0078125)}}] ; divide it by 128 (0 => 0, 128 => 1.0) fmul dword [{{.Use (.Float 0.0078125)}}] ; divide it by 128 (0 => 0, 128 => 1.0)
fadd dword [{{.WRK}}+su_unit.ports+{{.CX}}*4] ; add the modulations in the current workspace fadd dword [{{.WRK}}+su_unit.ports+{{.CX}}*4] ; add the modulations in the current workspace
@ -48,8 +48,8 @@ su_transform_values_loop:
mov dword [{{.WRK}}+su_unit.ports+{{.CX}}*4], eax ; clear out the modulation ports mov dword [{{.WRK}}+su_unit.ports+{{.CX}}*4], eax ; clear out the modulation ports
pop {{.AX}} pop {{.AX}}
inc ecx inc ecx
jmp su_transform_values_loop jmp su_transform_operands_loop
su_transform_values_out: su_transform_operands_out:
popf ; pop flags for the carry bit = stereo bit popf ; pop flags for the carry bit = stereo bit
{{- .SaveStack "Opcode"}} {{- .SaveStack "Opcode"}}
{{- $x := printf "su_vm_jumptable-%v" .PTRSIZE}} {{- $x := printf "su_vm_jumptable-%v" .PTRSIZE}}
@ -65,11 +65,11 @@ su_run_vm_advance:
dec ecx ; decrement number of voices to process dec ecx ; decrement number of voices to process
bt dword [{{.Stack "PolyphonyBitmask"}}], ecx ; if voice bit of su_polyphonism not set bt dword [{{.Stack "PolyphonyBitmask"}}], ecx ; if voice bit of su_polyphonism not set
jnc su_op_advance_next_instrument ; goto next_instrument jnc su_op_advance_next_instrument ; goto next_instrument
mov {{.VAL}}, [{{.Stack "ValueStream"}}] ; if it was set, then repeat the opcodes for the current voice mov {{.VAL}}, [{{.Stack "OperandStream"}}] ; if it was set, then repeat the opcodes for the current voice
mov {{.COM}}, [{{.Stack "CommandStream"}}] mov {{.COM}}, [{{.Stack "OpcodeStream"}}]
su_op_advance_next_instrument: su_op_advance_next_instrument:
mov [{{.Stack "ValueStream"}}], {{.VAL}} ; save current VAL as a checkpoint mov [{{.Stack "OperandStream"}}], {{.VAL}} ; save current VAL as a checkpoint
mov [{{.Stack "CommandStream"}}], {{.COM}} ; save current COM as a checkpoint mov [{{.Stack "OpcodeStream"}}], {{.COM}} ; save current COM as a checkpoint
su_op_advance_finish: su_op_advance_finish:
mov [{{.Stack "VoicesRemain"}}], ecx mov [{{.Stack "VoicesRemain"}}], ecx
jne su_run_vm_loop ; ZF was set by dec ecx jne su_run_vm_loop ; ZF was set by dec ecx
@ -95,7 +95,7 @@ su_op_advance_finish:
; su_nonlinear_map function: returns 2^(-24*x) of parameter number _AX ; su_nonlinear_map function: returns 2^(-24*x) of parameter number _AX
;------------------------------------------------------------------------------- ;-------------------------------------------------------------------------------
; Input: _AX : parameter number (e.g. for envelope: 0 = attac, 1 = decay...) ; Input: _AX : parameter number (e.g. for envelope: 0 = attac, 1 = decay...)
; INP : pointer to transformed values ; INP : pointer to transformed operands
; Output: st0 : 2^(-24*x), where x is the parameter in the range 0-1 ; Output: st0 : 2^(-24*x), where x is the parameter in the range 0-1
;------------------------------------------------------------------------------- ;-------------------------------------------------------------------------------
{{.Func "su_nonlinear_map"}} {{.Func "su_nonlinear_map"}}

View File

@ -60,8 +60,8 @@ su_render_sampleloop: ; loop through every sample in the row
{{- end}} {{- end}}
{{.Push (.Song.Patch.NumVoices | printf "%v") "VoicesRemain"}} {{.Push (.Song.Patch.NumVoices | printf "%v") "VoicesRemain"}}
mov {{.DX}}, {{.PTRWORD}} su_synth_obj ; {{.DX}} points to the synth object mov {{.DX}}, {{.PTRWORD}} su_synth_obj ; {{.DX}} points to the synth object
mov {{.COM}}, {{.PTRWORD}} su_patch_code ; COM points to vm code mov {{.COM}}, {{.PTRWORD}} su_patch_opcodes ; COM points to vm code
mov {{.VAL}}, {{.PTRWORD}} su_patch_parameters ; VAL points to unit params mov {{.VAL}}, {{.PTRWORD}} su_patch_operands ; VAL points to unit params
{{- if .HasOp "delay"}} {{- if .HasOp "delay"}}
mov {{.CX}}, {{.PTRWORD}} su_synth_obj + su_synthworkspace.size - su_delayline_wrk.filtstate mov {{.CX}}, {{.PTRWORD}} su_synth_obj + su_synthworkspace.size - su_delayline_wrk.filtstate
{{- end}} {{- end}}
@ -240,14 +240,14 @@ su_update_voices_skipadd:
;------------------------------------------------------------------------------- ;-------------------------------------------------------------------------------
; The code for this patch, basically indices to vm jump table ; The code for this patch, basically indices to vm jump table
;------------------------------------------------------------------------------- ;-------------------------------------------------------------------------------
{{.Data "su_patch_code"}} {{.Data "su_patch_opcodes"}}
db {{.Commands | toStrings | join ","}} db {{.Opcodes | toStrings | join ","}}
;------------------------------------------------------------------------------- ;-------------------------------------------------------------------------------
; The parameters / inputs to each opcode ; The parameters / inputs to each opcode
;------------------------------------------------------------------------------- ;-------------------------------------------------------------------------------
{{.Data "su_patch_parameters"}} {{.Data "su_patch_operands"}}
db {{.Values | toStrings | join ","}} db {{.Operands | toStrings | join ","}}
;------------------------------------------------------------------------------- ;-------------------------------------------------------------------------------
; Constants ; Constants

View File

@ -115,7 +115,7 @@
(global.set $VAL (i32.sub (global.get $VAL) (i32.const 1))) (global.set $VAL (i32.sub (global.get $VAL) (i32.const 1)))
)) ))
{{- end}} {{- end}}
(local.set $flags (call $scanValueByte)) (local.set $flags (call $scanOperand))
(local.set $freq (f32.mul (local.set $freq (f32.mul
(call $input (i32.const {{.InputNumber "filter" "frequency"}})) (call $input (i32.const {{.InputNumber "filter" "frequency"}}))
(call $input (i32.const {{.InputNumber "filter" "frequency"}})) (call $input (i32.const {{.InputNumber "filter" "frequency"}}))
@ -252,16 +252,16 @@
(func $su_op_delay (param $stereo i32) (local $delayIndex i32) (local $delayCount i32) (local $output f32) (local $s f32) (local $filtstate f32) (func $su_op_delay (param $stereo i32) (local $delayIndex i32) (local $delayCount i32) (local $output f32) (local $s f32) (local $filtstate f32)
{{- if .Stereo "delay"}} (local $delayCountStash i32) {{- end}} {{- if .Stereo "delay"}} (local $delayCountStash i32) {{- end}}
{{- if or (.SupportsModulation "delay" "delaytime") (.SupportsParamValue "delay" "notetracking" 1)}} (local $delayTime f32) {{- end}} {{- if or (.SupportsModulation "delay" "delaytime") (.SupportsParamValue "delay" "notetracking" 1)}} (local $delayTime f32) {{- end}}
(local.set $delayIndex (i32.mul (call $scanValueByte) (i32.const 2))) (local.set $delayIndex (i32.mul (call $scanOperand) (i32.const 2)))
{{- if .Stereo "delay"}} {{- if .Stereo "delay"}}
(local.set $delayCountStash (call $scanValueByte)) (local.set $delayCountStash (call $scanOperand))
(if (local.get $stereo)(then (if (local.get $stereo)(then
(call $su_op_xch (i32.const 0)) (call $su_op_xch (i32.const 0))
)) ))
loop $stereoLoop loop $stereoLoop
(local.set $delayCount (local.get $delayCountStash)) (local.set $delayCount (local.get $delayCountStash))
{{- else}} {{- else}}
(local.set $delayCount (call $scanValueByte)) (local.set $delayCount (call $scanOperand))
{{- end}} {{- end}}
(local.set $output (f32.mul (local.set $output (f32.mul
(call $input (i32.const {{.InputNumber "delay" "dry"}})) (call $input (i32.const {{.InputNumber "delay" "dry"}}))

View File

@ -9,15 +9,15 @@
(if (local.tee $opcode (i32.shr_u (local.get $opcodeWithStereo) (i32.const 1)))(then ;; if $opcode = $opcodeStereo >> 1; $opcode != 0 { (if (local.tee $opcode (i32.shr_u (local.get $opcodeWithStereo) (i32.const 1)))(then ;; if $opcode = $opcodeStereo >> 1; $opcode != 0 {
(local.set $paramNum (i32.const 0)) (local.set $paramNum (i32.const 0))
(local.set $paramX4 (i32.const 0)) (local.set $paramX4 (i32.const 0))
loop $transform_values_loop loop $transform_operands_loop
{{- $addr := sub (index .Labels "su_vm_transformcounts") 1}} {{- $addr := sub (index .Labels "su_vm_transformcounts") 1}}
(if (i32.lt_u (local.get $paramNum) (i32.load8_u offset={{$addr}} (local.get $opcode)))(then ;;(i32.ge (local.get $paramNum) (i32.load8_u (local.get $opcode))) /*TODO: offset to transformvalues (if (i32.lt_u (local.get $paramNum) (i32.load8_u offset={{$addr}} (local.get $opcode)))(then ;;(i32.ge (local.get $paramNum) (i32.load8_u (local.get $opcode))) /*TODO: offset to transformvalues
(local.set $WRKplusparam (i32.add (global.get $WRK) (local.get $paramX4))) (local.set $WRKplusparam (i32.add (global.get $WRK) (local.get $paramX4)))
(f32.store offset={{index .Labels "su_transformedvalues"}} (f32.store offset={{index .Labels "su_transformedoperands"}}
(local.get $paramX4) (local.get $paramX4)
(f32.add (f32.add
(f32.mul (f32.mul
(f32.convert_i32_u (call $scanValueByte)) (f32.convert_i32_u (call $scanOperand))
(f32.const 0.0078125) ;; scale from 0-128 to 0.0 - 1.0 (f32.const 0.0078125) ;; scale from 0-128 to 0.0 - 1.0
) )
(f32.load offset=32 (local.get $WRKplusparam)) ;; add modulation (f32.load offset=32 (local.get $WRKplusparam)) ;; add modulation
@ -26,7 +26,7 @@
(f32.store offset=32 (local.get $WRKplusparam) (f32.const 0.0)) ;; clear modulations (f32.store offset=32 (local.get $WRKplusparam) (f32.const 0.0)) ;; clear modulations
(local.set $paramNum (i32.add (local.get $paramNum) (i32.const 1))) ;; $paramNum++ (local.set $paramNum (i32.add (local.get $paramNum) (i32.const 1))) ;; $paramNum++
(local.set $paramX4 (i32.add (local.get $paramX4) (i32.const 4))) (local.set $paramX4 (i32.add (local.get $paramX4) (i32.const 4)))
br $transform_values_loop ;; continue looping br $transform_operands_loop ;; continue looping
)) ))
;; paramNum was >= the number of parameters to transform, exiting loop ;; paramNum was >= the number of parameters to transform, exiting loop
end end
@ -59,7 +59,7 @@
;; The transformed values start at 512 (TODO: change magic constants somehow) ;; The transformed values start at 512 (TODO: change magic constants somehow)
;;------------------------------------------------------------------------------- ;;-------------------------------------------------------------------------------
(func $input (param $inputNumber i32) (result f32) (func $input (param $inputNumber i32) (result f32)
(f32.load offset={{index .Labels "su_transformedvalues"}} (i32.mul (local.get $inputNumber) (i32.const 4))) (f32.load offset={{index .Labels "su_transformedoperands"}} (i32.mul (local.get $inputNumber) (i32.const 4)))
) )
;;------------------------------------------------------------------------------- ;;-------------------------------------------------------------------------------

View File

@ -31,8 +31,8 @@
; The code for this patch, basically indices to vm jump table ; The code for this patch, basically indices to vm jump table
;------------------------------------------------------------------------------- ;-------------------------------------------------------------------------------
*/}} */}}
{{- .SetDataLabel "su_patch_code"}} {{- .SetDataLabel "su_patch_opcodes"}}
{{- range .Commands}} {{- range .Opcodes}}
{{- $.DataB .}} {{- $.DataB .}}
{{- end}} {{- end}}
@ -41,8 +41,8 @@
; The parameters / inputs to each opcode ; The parameters / inputs to each opcode
;------------------------------------------------------------------------------- ;-------------------------------------------------------------------------------
*/}} */}}
{{- .SetDataLabel "su_patch_parameters"}} {{- .SetDataLabel "su_patch_operands"}}
{{- range .Values}} {{- range .Operands}}
{{- $.DataB .}} {{- $.DataB .}}
{{- end}} {{- end}}
@ -79,11 +79,11 @@
{{- /* {{- /*
;------------------------------------------------------------------------------- ;-------------------------------------------------------------------------------
; Allocate memory for transformed values. ; Allocate memory for transformed operands.
;------------------------------------------------------------------------------- ;-------------------------------------------------------------------------------
*/}} */}}
{{- .Align}} {{- .Align}}
{{- .SetBlockLabel "su_transformedvalues"}} {{- .SetBlockLabel "su_transformedoperands"}}
{{- .Block 32}} {{- .Block 32}}
{{- /* {{- /*
@ -193,7 +193,7 @@
local.get 0 local.get 0
) )
(func $scanValueByte (result i32) ;; scans positions $VAL for a byte, incrementing $VAL afterwards (func $scanOperand (result i32) ;; scans positions $VAL for a byte, incrementing $VAL afterwards
(i32.load8_u (global.get $VAL)) ;; in other words: returns byte [$VAL++] (i32.load8_u (global.get $VAL)) ;; in other words: returns byte [$VAL++]
(global.set $VAL (i32.add (global.get $VAL) (i32.const 1))) ;; $VAL++ (global.set $VAL (i32.add (global.get $VAL) (i32.const 1))) ;; $VAL++
) )
@ -211,8 +211,8 @@
(call $su_update_voices) (call $su_update_voices)
(global.set $sample (i32.const 0)) (global.set $sample (i32.const 0))
loop $sample_loop loop $sample_loop
(global.set $COM (i32.const {{index .Labels "su_patch_code"}})) (global.set $COM (i32.const {{index .Labels "su_patch_opcodes"}}))
(global.set $VAL (i32.const {{index .Labels "su_patch_parameters"}})) (global.set $VAL (i32.const {{index .Labels "su_patch_operands"}}))
{{- if .SupportsPolyphony}} {{- if .SupportsPolyphony}}
(global.set $COM_instr_start (global.get $COM)) (global.set $COM_instr_start (global.get $COM))
(global.set $VAL_instr_start (global.get $VAL)) (global.set $VAL_instr_start (global.get $VAL))

View File

@ -47,7 +47,7 @@
;; Stereo: also add gain*ST1 to right port ;; Stereo: also add gain*ST1 to right port
;;------------------------------------------------------------------------------- ;;-------------------------------------------------------------------------------
(func $su_op_aux (param $stereo i32) (local $addr i32) (func $su_op_aux (param $stereo i32) (local $addr i32)
(local.set $addr (i32.add (i32.mul (call $scanValueByte) (i32.const 4)) (i32.const {{index .Labels "su_globalports"}}))) (local.set $addr (i32.add (i32.mul (call $scanOperand) (i32.const 4)) (i32.const {{index .Labels "su_globalports"}})))
{{- if .Stereo "aux"}} {{- if .Stereo "aux"}}
loop $stereoLoop loop $stereoLoop
{{- end}} {{- end}}
@ -78,7 +78,7 @@
;; Stereo: also add right signal to the following address ;; Stereo: also add right signal to the following address
;;------------------------------------------------------------------------------- ;;-------------------------------------------------------------------------------
(func $su_op_send (param $stereo i32) (local $address i32) (local $scaledAddress i32) (func $su_op_send (param $stereo i32) (local $address i32) (local $scaledAddress i32)
(local.set $address (i32.add (call $scanValueByte) (i32.shl (call $scanValueByte) (i32.const 8)))) (local.set $address (i32.add (call $scanOperand) (i32.shl (call $scanOperand) (i32.const 8))))
(if (i32.eqz (i32.and (local.get $address) (i32.const 8)))(then (if (i32.eqz (i32.and (local.get $address) (i32.const 8)))(then
{{- if .Stereo "send"}} {{- if .Stereo "send"}}
(if (local.get $stereo)(then (if (local.get $stereo)(then

View File

@ -118,7 +118,7 @@
(local.set $freqMod (f32.load offset={{.InputNumber "oscillator" "frequency" | mul 4 | add 32}} (global.get $WRK))) (local.set $freqMod (f32.load offset={{.InputNumber "oscillator" "frequency" | mul 4 | add 32}} (global.get $WRK)))
(f32.store offset={{.InputNumber "oscillator" "frequency" | mul 4 | add 32}} (global.get $WRK) (f32.const 0)) (f32.store offset={{.InputNumber "oscillator" "frequency" | mul 4 | add 32}} (global.get $WRK) (f32.const 0))
{{- end}} {{- end}}
(local.set $flags (call $scanValueByte)) (local.set $flags (call $scanOperand))
(local.set $detune (call $inputSigned (i32.const {{.InputNumber "oscillator" "detune"}}))) (local.set $detune (call $inputSigned (i32.const {{.InputNumber "oscillator" "detune"}})))
{{- if .Stereo "oscillator"}} {{- if .Stereo "oscillator"}}
loop $stereoLoop loop $stereoLoop
@ -197,7 +197,7 @@
{{- if .SupportsParamValueOtherThan "oscillator" "unison" 0}} {{- if .SupportsParamValueOtherThan "oscillator" "unison" 0}}
(call $push (f32.add (call $pop) (call $pop))) (call $push (f32.add (call $pop) (call $pop)))
(if (local.tee $unison (i32.sub (local.get $unison) (i32.const 1)))(then (if (local.tee $unison (i32.sub (local.get $unison) (i32.const 1)))(then
(f32.store offset={{.InputNumber "oscillator" "phase" | mul 4 | add (index .Labels "su_transformedvalues")}} (i32.const 0) (f32.store offset={{.InputNumber "oscillator" "phase" | mul 4 | add (index .Labels "su_transformedoperands")}} (i32.const 0)
(f32.add (f32.add
(call $input (i32.const {{.InputNumber "oscillator" "phase"}})) (call $input (i32.const {{.InputNumber "oscillator" "phase"}}))
(f32.const 0.08333333) ;; 1/12, add small phase shift so all oscillators don't start in phase (f32.const 0.08333333) ;; 1/12, add small phase shift so all oscillators don't start in phase
@ -341,7 +341,7 @@
;; Stereo: also push the right channel (stack in l r order) ;; Stereo: also push the right channel (stack in l r order)
;;------------------------------------------------------------------------------- ;;-------------------------------------------------------------------------------
(func $su_op_in (param $stereo i32) (local $addr i32) (func $su_op_in (param $stereo i32) (local $addr i32)
call $scanValueByte call $scanOperand
{{- if .Stereo "in"}} {{- if .Stereo "in"}}
(i32.add (local.get $stereo)) ;; start from right channel if stereo (i32.add (local.get $stereo)) ;; start from right channel if stereo
{{- end}} {{- end}}

View File

@ -117,10 +117,10 @@ func (s *GoSynth) Update(patch sointu.Patch, bpm int) error {
if err != nil { if err != nil {
return fmt.Errorf("error compiling %v", err) return fmt.Errorf("error compiling %v", err)
} }
needsRefresh := len(bytecode.Commands) != len(s.bytecode.Commands) needsRefresh := len(bytecode.Opcodes) != len(s.bytecode.Opcodes)
if !needsRefresh { if !needsRefresh {
for i, c := range bytecode.Commands { for i, c := range bytecode.Opcodes {
if s.bytecode.Commands[i] != c { if s.bytecode.Opcodes[i] != c {
needsRefresh = true needsRefresh = true
break break
} }
@ -151,16 +151,16 @@ func (s *GoSynth) Render(buffer sointu.AudioBuffer, maxtime int) (samples int, t
stack = append(stack, []float32{0, 0, 0, 0}...) stack = append(stack, []float32{0, 0, 0, 0}...)
synth := &s.synth synth := &s.synth
for time < maxtime && len(buffer) > 0 { for time < maxtime && len(buffer) > 0 {
commandInstr := s.bytecode.Commands opcodesInstr := s.bytecode.Opcodes
valuesInstr := s.bytecode.Values operandsInstr := s.bytecode.Operands
commands, values := commandInstr, valuesInstr opcodes, operands := opcodesInstr, operandsInstr
delaylines := s.delaylines delaylines := s.delaylines
voicesRemaining := s.bytecode.NumVoices voicesRemaining := s.bytecode.NumVoices
voices := s.synth.voices[:] voices := s.synth.voices[:]
units := voices[0].units[:] units := voices[0].units[:]
for voicesRemaining > 0 { for voicesRemaining > 0 {
op := commands[0] op := opcodes[0]
commands = commands[1:] opcodes = opcodes[1:]
channels := int((op & 1) + 1) channels := int((op & 1) + 1)
stereo := channels == 2 stereo := channels == 2
opNoStereo := (op & 0xFE) >> 1 opNoStereo := (op & 0xFE) >> 1
@ -171,23 +171,23 @@ func (s *GoSynth) Render(buffer sointu.AudioBuffer, maxtime int) (samples int, t
units = voices[0].units[:] units = voices[0].units[:]
} }
if mask := uint32(1) << uint32(voicesRemaining); s.bytecode.PolyphonyBitmask&mask == mask { if mask := uint32(1) << uint32(voicesRemaining); s.bytecode.PolyphonyBitmask&mask == mask {
commands, values = commandInstr, valuesInstr opcodes, operands = opcodesInstr, operandsInstr
} else { } else {
commandInstr, valuesInstr = commands, values opcodesInstr, operandsInstr = opcodes, operands
} }
continue continue
} }
tcount := transformCounts[opNoStereo-1] tcount := transformCounts[opNoStereo-1]
if len(values) < tcount { if len(operands) < tcount {
return samples, time, errors.New("value stream ended prematurely") return samples, time, errors.New("operand stream ended prematurely")
} }
voice := &voices[0] voice := &voices[0]
unit := &units[0] unit := &units[0]
valuesAtTransform := values operandsAtTransform := operands
for i := 0; i < tcount; i++ { for i := 0; i < tcount; i++ {
params[i] = float32(values[0])/128.0 + unit.ports[i] params[i] = float32(operands[0])/128.0 + unit.ports[i]
unit.ports[i] = 0 unit.ports[i] = 0
values = values[1:] operands = operands[1:]
} }
l := len(stack) l := len(stack)
switch opNoStereo { switch opNoStereo {
@ -276,7 +276,7 @@ func (s *GoSynth) Render(buffer sointu.AudioBuffer, maxtime int) (samples int, t
} }
case opAux: case opAux:
var channel byte var channel byte
channel, values = values[0], values[1:] channel, operands = operands[0], operands[1:]
if stereo { if stereo {
synth.outputs[channel+1] += params[0] * stack[l-2] synth.outputs[channel+1] += params[0] * stack[l-2]
} }
@ -290,7 +290,7 @@ func (s *GoSynth) Render(buffer sointu.AudioBuffer, maxtime int) (samples int, t
stack = stack[:l-1] stack = stack[:l-1]
case opIn: case opIn:
var channel byte var channel byte
channel, values = values[0], values[1:] channel, operands = operands[0], operands[1:]
if stereo { if stereo {
stack = append(stack, synth.outputs[channel+1]) stack = append(stack, synth.outputs[channel+1])
synth.outputs[channel+1] = 0 synth.outputs[channel+1] = 0
@ -368,7 +368,7 @@ func (s *GoSynth) Render(buffer sointu.AudioBuffer, maxtime int) (samples int, t
} }
case opSend: case opSend:
var addrLow, addrHigh byte var addrLow, addrHigh byte
addrLow, addrHigh, values = values[0], values[1], values[2:] addrLow, addrHigh, operands = operands[0], operands[1], operands[2:]
addr := (uint16(addrHigh) << 8) + uint16(addrLow) addr := (uint16(addrHigh) << 8) + uint16(addrLow)
targetVoice := voice targetVoice := voice
if addr&0x8000 == 0x8000 { if addr&0x8000 == 0x8000 {
@ -408,7 +408,7 @@ func (s *GoSynth) Render(buffer sointu.AudioBuffer, maxtime int) (samples int, t
freq2 := params[0] * params[0] freq2 := params[0] * params[0]
res := params[1] res := params[1]
var flags byte var flags byte
flags, values = values[0], values[1:] flags, operands = operands[0], operands[1:]
for i := 0; i < channels; i++ { for i := 0; i < channels; i++ {
low, band := unit.state[0+i], unit.state[2+i] low, band := unit.state[0+i], unit.state[2+i]
low += freq2 * band low += freq2 * band
@ -435,7 +435,7 @@ func (s *GoSynth) Render(buffer sointu.AudioBuffer, maxtime int) (samples int, t
} }
case opOscillator: case opOscillator:
var flags byte var flags byte
flags, values = values[0], values[1:] flags, operands = operands[0], operands[1:]
detuneStereo := params[1]*2 - 1 detuneStereo := params[1]*2 - 1
unison := flags & 3 unison := flags & 3
for i := 0; i < channels; i++ { for i := 0; i < channels; i++ {
@ -460,7 +460,7 @@ func (s *GoSynth) Render(buffer sointu.AudioBuffer, maxtime int) (samples int, t
if flags&0x80 == 0x80 { // if this is a sample oscillator if flags&0x80 == 0x80 { // if this is a sample oscillator
phase := *statevar phase := *statevar
phase += params[2] phase += params[2]
sampleno := valuesAtTransform[3] // reuse color as the sample number sampleno := operandsAtTransform[3] // reuse color as the sample number
sampleoffset := s.bytecode.SampleOffsets[sampleno] sampleoffset := s.bytecode.SampleOffsets[sampleno]
sampleindex := int(phase*84.28074964676522 + 0.5) sampleindex := int(phase*84.28074964676522 + 0.5)
loopstart := int(sampleoffset.LoopStart) loopstart := int(sampleoffset.LoopStart)
@ -495,7 +495,7 @@ func (s *GoSynth) Render(buffer sointu.AudioBuffer, maxtime int) (samples int, t
amplitude = 1 amplitude = 1
} }
case flags&0x4 == 0x4: // Gate case flags&0x4 == 0x4: // Gate
maskLow, maskHigh := valuesAtTransform[3], valuesAtTransform[4] maskLow, maskHigh := operandsAtTransform[3], operandsAtTransform[4]
gateBits := (int(maskHigh) << 8) + int(maskLow) gateBits := (int(maskHigh) << 8) + int(maskLow)
amplitude = float32((gateBits >> (int(phase*16+.5) & 15)) & 1) amplitude = float32((gateBits >> (int(phase*16+.5) & 15)) & 1)
g := unit.state[4+i] // warning: still fucks up with unison = 3 g := unit.state[4+i] // warning: still fucks up with unison = 3
@ -522,7 +522,7 @@ func (s *GoSynth) Render(buffer sointu.AudioBuffer, maxtime int) (samples int, t
damp := params[3] damp := params[3]
feedback := params[2] feedback := params[2]
var index, count byte var index, count byte
index, count, values = values[0], values[1], values[2:] index, count, operands = operands[0], operands[1], operands[2:]
t := uint16(s.synth.globalTime) t := uint16(s.synth.globalTime)
stackIndex := l - channels stackIndex := l - channels
for i := 0; i < channels; i++ { for i := 0; i < channels; i++ {