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

View File

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

View File

@ -11,15 +11,16 @@ type (
// Bytecode is the Sointu VM bytecode & data (delay times, sample offsets)
// which is executed by the synthesizer. It is generated from a Sointu patch.
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,
// at which point if that instrument has more than one voice, the
// commands are repeated for each voice.
Commands []byte
// opcodes are repeated for each voice.
Opcodes []byte
// Values are the operands of the opcodes. Every opcode reads 0 or more
// values from the value sequence.
Values []byte
// Operands are the operands of the opcodes. When executing the
// bytecodes, every opcode reads 0 or more operands from it and advances
// in the sequence.
Operands []byte
// 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
@ -114,8 +115,8 @@ func Encode(patch sointu.Patch, featureSet FeatureSet, bpm int) (*Bytecode, erro
flags += 0x08
}
flags += p["unison"]
b.cmd(opcode + p["stereo"])
b.vals(p["transpose"], p["detune"], p["phase"], color, p["shape"], p["gain"], flags)
b.op(opcode + p["stereo"])
b.operand(p["transpose"], p["detune"], p["phase"], color, p["shape"], p["gain"], flags)
case "delay":
count := len(unit.VarArgs)
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
}
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.defaultVals(unit)
b.vals(b.delayIndices[instrIndex][unitIndex], countTrack)
b.op(opcode + p["stereo"])
b.defOperands(unit)
b.operand(b.delayIndices[instrIndex][unitIndex], countTrack)
case "aux", "in":
b.cmd(opcode + p["stereo"])
b.defaultVals(unit)
b.vals(unit.Parameters["channel"])
b.op(opcode + p["stereo"])
b.defOperands(unit)
b.operand(unit.Parameters["channel"])
case "filter":
flags := 0
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 {
flags += 0x04
}
b.cmd(opcode + p["stereo"])
b.defaultVals(unit)
b.vals(flags)
b.op(opcode + p["stereo"])
b.defOperands(unit)
b.operand(flags)
case "send":
targetID := unit.Parameters["target"]
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 {
addr += 0x8
}
b.cmd(opcode + p["stereo"])
b.defaultVals(unit)
b.op(opcode + p["stereo"])
b.defOperands(unit)
b.localIDRef(targetID, addr)
} else {
addr += 0x8000
@ -177,8 +178,8 @@ func Encode(patch sointu.Patch, featureSet FeatureSet, bpm int) (*Bytecode, erro
}
addr += voiceStart * 0x400
for i := voiceStart; i < voiceEnd; i++ {
b.cmd(opcode + p["stereo"])
b.defaultVals(unit)
b.op(opcode + p["stereo"])
b.defOperands(unit)
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
}
@ -194,19 +195,19 @@ func Encode(patch sointu.Patch, featureSet FeatureSet, bpm int) (*Bytecode, erro
if unit.Parameters["sendpop"] == 1 {
addr |= 0x8
}
b.cmd(opcode + p["stereo"])
b.defaultVals(unit)
b.Values = append(b.Values, byte(addr&255), byte(addr>>8))
b.op(opcode + p["stereo"])
b.defOperands(unit)
b.Operands = append(b.Operands, byte(addr&255), byte(addr>>8))
}
default:
b.cmd(opcode + p["stereo"])
b.defaultVals(unit)
b.op(opcode + p["stereo"])
b.defOperands(unit)
}
if b.unitNo > 63 {
return nil, fmt.Errorf(`Instrument %v has over 63 units`, instrIndex)
}
}
b.cmdFinish(instr)
b.opFinish(instr)
}
return &b.Bytecode, nil
}
@ -235,34 +236,35 @@ func newBytecodeBuilder(patch sointu.Patch, bpm int) *bytecodeBuilder {
return &c
}
// cmd adds a command to the bytecode, and increments the unit number
func (b *bytecodeBuilder) cmd(opcode int) {
b.Commands = append(b.Commands, byte(opcode))
// op adds a command to the bytecode, and increments the unit number
func (b *bytecodeBuilder) op(opcode int) {
b.Opcodes = append(b.Opcodes, byte(opcode))
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
func (b *bytecodeBuilder) cmdFinish(instr sointu.Instrument) {
b.Commands = append(b.Commands, 0)
func (b *bytecodeBuilder) opFinish(instr sointu.Instrument) {
b.Opcodes = append(b.Opcodes, 0)
b.unitNo = 0
b.voiceNo += instr.NumVoices
b.localAddrs = map[int]uint16{}
b.localFixups = map[int]([]int){}
}
// vals appends values to the value stream
func (b *bytecodeBuilder) vals(values ...int) {
for _, v := range values {
b.Values = append(b.Values, byte(v))
// operand appends operands to the operand stream
func (b *bytecodeBuilder) operand(operands ...int) {
for _, v := range operands {
b.Operands = append(b.Operands, byte(v))
}
}
// defaultVals appends the values to the value stream for all parameters that can be modulated and set
func (b *bytecodeBuilder) defaultVals(unit sointu.Unit) {
// defOperands appends the operands to the stream for all parameters that can be
// modulated and set
func (b *bytecodeBuilder) defOperands(unit sointu.Unit) {
for _, v := range sointu.UnitTypes[unit.Type] {
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 {
addr += int(v)
} 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
@ -282,9 +284,9 @@ func (b *bytecodeBuilder) globalIDRef(id int, addr int) {
if v, ok := b.globalAddrs[id]; ok {
addr += int(v)
} 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
@ -302,10 +304,10 @@ func (b *bytecodeBuilder) idLabel(id int) {
// fixUp fixes up the references to the given id with the given delta
func (b *bytecodeBuilder) fixUp(positions []int, delta uint16) {
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
b.Values[pos] = byte(new & 255)
b.Values[pos+1] = byte(new >> 8)
b.Operands[pos] = byte(new & 255)
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 {
return nil, fmt.Errorf("error compiling patch: %v", err)
}
if len(comPatch.Commands) > 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")
if len(comPatch.Opcodes) > 2048 { // TODO: 2048 could probably be pulled automatically from cgo
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
return nil, errors.New("bridge supports at most 16384 values; the compiled patch has more")
if len(comPatch.Operands) > 16384 { // TODO: 16384 could probably be pulled automatically from cgo
return nil, errors.New("bridge supports at most 16384 operands; the compiled patch has more")
}
for i, v := range comPatch.Commands {
s.Commands[i] = (C.uchar)(v)
for i, v := range comPatch.Opcodes {
s.Opcodes[i] = (C.uchar)(v)
}
for i, v := range comPatch.Values {
s.Values[i] = (C.uchar)(v)
for i, v := range comPatch.Operands {
s.Operands[i] = (C.uchar)(v)
}
for i, v := range comPatch.DelayTimes {
s.DelayTimes[i] = (C.ushort)(v)
@ -124,21 +124,21 @@ func (bridgesynth *NativeSynth) Update(patch sointu.Patch, bpm int) error {
if err != nil {
return fmt.Errorf("error compiling patch: %v", err)
}
if len(comPatch.Commands) > 2048 { // TODO: 2048 could probably be pulled automatically from cgo
return errors.New("bridge supports at most 2048 commands; the compiled patch has more")
if len(comPatch.Opcodes) > 2048 { // TODO: 2048 could probably be pulled automatically from cgo
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
return errors.New("bridge supports at most 16384 values; the compiled patch has more")
if len(comPatch.Operands) > 16384 { // TODO: 16384 could probably be pulled automatically from cgo
return errors.New("bridge supports at most 16384 operands; the compiled patch has more")
}
needsRefresh := false
for i, v := range comPatch.Commands {
if cmdChar := (C.uchar)(v); s.Commands[i] != cmdChar {
s.Commands[i] = cmdChar
needsRefresh = true // if any of the commands change, we retrigger all units
for i, v := range comPatch.Opcodes {
if cmdChar := (C.uchar)(v); s.Opcodes[i] != cmdChar {
s.Opcodes[i] = cmdChar
needsRefresh = true // if any of the opcodes change, we retrigger all units
}
}
for i, v := range comPatch.Values {
s.Values[i] = (C.uchar)(v)
for i, v := range comPatch.Operands {
s.Operands[i] = (C.uchar)(v)
}
for i, v := range comPatch.DelayTimes {
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)
if needsRefresh {
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
for j := range s.SynthWrk.Voices[i].Units {
s.SynthWrk.Voices[i].Units[j] = C.Unit{}

View File

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

View File

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

View File

@ -5,15 +5,15 @@
; su_synth_obj.right : Set to 0 before calling
; _CX : Pointer to delay workspace (if needed)
; _DX : Pointer to synth object
; COM : Pointer to command stream
; VAL : Pointer to value stream
; COM : Pointer to opcode stream
; VAL : Pointer to operand stream
; WRK : Pointer to the last workspace processed
; Output: su_synth_obj.left : left sample
; su_synth_obj.right : right sample
; Dirty: everything
;-------------------------------------------------------------------------------
{{.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}}
fild dword [{{.Stack "Sample"}}]
{{.Int .Song.SamplesPerRow | .Prepare | indent 8}}
@ -33,13 +33,13 @@ su_run_vm_loop: ; loop until all voices done
add {{.INP}}, su_voice.inputs
xor ecx, ecx ; counter = 0
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}}
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
lodsb ; load the byte value from VAL stream
je su_transform_operands_out
lodsb ; load the operand from VAL stream
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}}
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
@ -48,8 +48,8 @@ su_transform_values_loop:
mov dword [{{.WRK}}+su_unit.ports+{{.CX}}*4], eax ; clear out the modulation ports
pop {{.AX}}
inc ecx
jmp su_transform_values_loop
su_transform_values_out:
jmp su_transform_operands_loop
su_transform_operands_out:
popf ; pop flags for the carry bit = stereo bit
{{- .SaveStack "Opcode"}}
{{- $x := printf "su_vm_jumptable-%v" .PTRSIZE}}
@ -65,11 +65,11 @@ su_run_vm_advance:
dec ecx ; decrement number of voices to process
bt dword [{{.Stack "PolyphonyBitmask"}}], ecx ; if voice bit of su_polyphonism not set
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 {{.COM}}, [{{.Stack "CommandStream"}}]
mov {{.VAL}}, [{{.Stack "OperandStream"}}] ; if it was set, then repeat the opcodes for the current voice
mov {{.COM}}, [{{.Stack "OpcodeStream"}}]
su_op_advance_next_instrument:
mov [{{.Stack "ValueStream"}}], {{.VAL}} ; save current VAL as a checkpoint
mov [{{.Stack "CommandStream"}}], {{.COM}} ; save current COM as a checkpoint
mov [{{.Stack "OperandStream"}}], {{.VAL}} ; save current VAL as a checkpoint
mov [{{.Stack "OpcodeStream"}}], {{.COM}} ; save current COM as a checkpoint
su_op_advance_finish:
mov [{{.Stack "VoicesRemain"}}], 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
;-------------------------------------------------------------------------------
; 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
;-------------------------------------------------------------------------------
{{.Func "su_nonlinear_map"}}

View File

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

View File

@ -115,7 +115,7 @@
(global.set $VAL (i32.sub (global.get $VAL) (i32.const 1)))
))
{{- end}}
(local.set $flags (call $scanValueByte))
(local.set $flags (call $scanOperand))
(local.set $freq (f32.mul
(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)
{{- if .Stereo "delay"}} (local $delayCountStash i32) {{- 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"}}
(local.set $delayCountStash (call $scanValueByte))
(local.set $delayCountStash (call $scanOperand))
(if (local.get $stereo)(then
(call $su_op_xch (i32.const 0))
))
loop $stereoLoop
(local.set $delayCount (local.get $delayCountStash))
{{- else}}
(local.set $delayCount (call $scanValueByte))
(local.set $delayCount (call $scanOperand))
{{- end}}
(local.set $output (f32.mul
(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 {
(local.set $paramNum (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}}
(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)))
(f32.store offset={{index .Labels "su_transformedvalues"}}
(f32.store offset={{index .Labels "su_transformedoperands"}}
(local.get $paramX4)
(f32.add
(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.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
(local.set $paramNum (i32.add (local.get $paramNum) (i32.const 1))) ;; $paramNum++
(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
end
@ -59,7 +59,7 @@
;; The transformed values start at 512 (TODO: change magic constants somehow)
;;-------------------------------------------------------------------------------
(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
;-------------------------------------------------------------------------------
*/}}
{{- .SetDataLabel "su_patch_code"}}
{{- range .Commands}}
{{- .SetDataLabel "su_patch_opcodes"}}
{{- range .Opcodes}}
{{- $.DataB .}}
{{- end}}
@ -41,8 +41,8 @@
; The parameters / inputs to each opcode
;-------------------------------------------------------------------------------
*/}}
{{- .SetDataLabel "su_patch_parameters"}}
{{- range .Values}}
{{- .SetDataLabel "su_patch_operands"}}
{{- range .Operands}}
{{- $.DataB .}}
{{- end}}
@ -79,11 +79,11 @@
{{- /*
;-------------------------------------------------------------------------------
; Allocate memory for transformed values.
; Allocate memory for transformed operands.
;-------------------------------------------------------------------------------
*/}}
{{- .Align}}
{{- .SetBlockLabel "su_transformedvalues"}}
{{- .SetBlockLabel "su_transformedoperands"}}
{{- .Block 32}}
{{- /*
@ -193,7 +193,7 @@
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++]
(global.set $VAL (i32.add (global.get $VAL) (i32.const 1))) ;; $VAL++
)
@ -211,8 +211,8 @@
(call $su_update_voices)
(global.set $sample (i32.const 0))
loop $sample_loop
(global.set $COM (i32.const {{index .Labels "su_patch_code"}}))
(global.set $VAL (i32.const {{index .Labels "su_patch_parameters"}}))
(global.set $COM (i32.const {{index .Labels "su_patch_opcodes"}}))
(global.set $VAL (i32.const {{index .Labels "su_patch_operands"}}))
{{- if .SupportsPolyphony}}
(global.set $COM_instr_start (global.get $COM))
(global.set $VAL_instr_start (global.get $VAL))

View File

@ -47,7 +47,7 @@
;; Stereo: also add gain*ST1 to right port
;;-------------------------------------------------------------------------------
(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"}}
loop $stereoLoop
{{- end}}
@ -78,7 +78,7 @@
;; Stereo: also add right signal to the following address
;;-------------------------------------------------------------------------------
(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 .Stereo "send"}}
(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)))
(f32.store offset={{.InputNumber "oscillator" "frequency" | mul 4 | add 32}} (global.get $WRK) (f32.const 0))
{{- end}}
(local.set $flags (call $scanValueByte))
(local.set $flags (call $scanOperand))
(local.set $detune (call $inputSigned (i32.const {{.InputNumber "oscillator" "detune"}})))
{{- if .Stereo "oscillator"}}
loop $stereoLoop
@ -197,7 +197,7 @@
{{- if .SupportsParamValueOtherThan "oscillator" "unison" 0}}
(call $push (f32.add (call $pop) (call $pop)))
(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
(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
@ -341,7 +341,7 @@
;; Stereo: also push the right channel (stack in l r order)
;;-------------------------------------------------------------------------------
(func $su_op_in (param $stereo i32) (local $addr i32)
call $scanValueByte
call $scanOperand
{{- if .Stereo "in"}}
(i32.add (local.get $stereo)) ;; start from right channel if stereo
{{- end}}

View File

@ -117,10 +117,10 @@ func (s *GoSynth) Update(patch sointu.Patch, bpm int) error {
if err != nil {
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 {
for i, c := range bytecode.Commands {
if s.bytecode.Commands[i] != c {
for i, c := range bytecode.Opcodes {
if s.bytecode.Opcodes[i] != c {
needsRefresh = true
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}...)
synth := &s.synth
for time < maxtime && len(buffer) > 0 {
commandInstr := s.bytecode.Commands
valuesInstr := s.bytecode.Values
commands, values := commandInstr, valuesInstr
opcodesInstr := s.bytecode.Opcodes
operandsInstr := s.bytecode.Operands
opcodes, operands := opcodesInstr, operandsInstr
delaylines := s.delaylines
voicesRemaining := s.bytecode.NumVoices
voices := s.synth.voices[:]
units := voices[0].units[:]
for voicesRemaining > 0 {
op := commands[0]
commands = commands[1:]
op := opcodes[0]
opcodes = opcodes[1:]
channels := int((op & 1) + 1)
stereo := channels == 2
opNoStereo := (op & 0xFE) >> 1
@ -171,23 +171,23 @@ func (s *GoSynth) Render(buffer sointu.AudioBuffer, maxtime int) (samples int, t
units = voices[0].units[:]
}
if mask := uint32(1) << uint32(voicesRemaining); s.bytecode.PolyphonyBitmask&mask == mask {
commands, values = commandInstr, valuesInstr
opcodes, operands = opcodesInstr, operandsInstr
} else {
commandInstr, valuesInstr = commands, values
opcodesInstr, operandsInstr = opcodes, operands
}
continue
}
tcount := transformCounts[opNoStereo-1]
if len(values) < tcount {
return samples, time, errors.New("value stream ended prematurely")
if len(operands) < tcount {
return samples, time, errors.New("operand stream ended prematurely")
}
voice := &voices[0]
unit := &units[0]
valuesAtTransform := values
operandsAtTransform := operands
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
values = values[1:]
operands = operands[1:]
}
l := len(stack)
switch opNoStereo {
@ -276,7 +276,7 @@ func (s *GoSynth) Render(buffer sointu.AudioBuffer, maxtime int) (samples int, t
}
case opAux:
var channel byte
channel, values = values[0], values[1:]
channel, operands = operands[0], operands[1:]
if stereo {
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]
case opIn:
var channel byte
channel, values = values[0], values[1:]
channel, operands = operands[0], operands[1:]
if stereo {
stack = append(stack, synth.outputs[channel+1])
synth.outputs[channel+1] = 0
@ -368,7 +368,7 @@ func (s *GoSynth) Render(buffer sointu.AudioBuffer, maxtime int) (samples int, t
}
case opSend:
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)
targetVoice := voice
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]
res := params[1]
var flags byte
flags, values = values[0], values[1:]
flags, operands = operands[0], operands[1:]
for i := 0; i < channels; i++ {
low, band := unit.state[0+i], unit.state[2+i]
low += freq2 * band
@ -435,7 +435,7 @@ func (s *GoSynth) Render(buffer sointu.AudioBuffer, maxtime int) (samples int, t
}
case opOscillator:
var flags byte
flags, values = values[0], values[1:]
flags, operands = operands[0], operands[1:]
detuneStereo := params[1]*2 - 1
unison := flags & 3
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
phase := *statevar
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]
sampleindex := int(phase*84.28074964676522 + 0.5)
loopstart := int(sampleoffset.LoopStart)
@ -495,7 +495,7 @@ func (s *GoSynth) Render(buffer sointu.AudioBuffer, maxtime int) (samples int, t
amplitude = 1
}
case flags&0x4 == 0x4: // Gate
maskLow, maskHigh := valuesAtTransform[3], valuesAtTransform[4]
maskLow, maskHigh := operandsAtTransform[3], operandsAtTransform[4]
gateBits := (int(maskHigh) << 8) + int(maskLow)
amplitude = float32((gateBits >> (int(phase*16+.5) & 15)) & 1)
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]
feedback := params[2]
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)
stackIndex := l - channels
for i := 0; i < channels; i++ {