mirror of
https://github.com/vsariola/sointu.git
synced 2025-05-27 10:50:23 -04:00
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:
parent
87604dd92e
commit
01bf409929
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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{}
|
||||
|
@ -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"}}
|
||||
|
@ -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;
|
||||
|
@ -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"}}
|
||||
|
@ -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
|
||||
|
@ -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"}}))
|
||||
|
@ -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)))
|
||||
)
|
||||
|
||||
;;-------------------------------------------------------------------------------
|
||||
|
@ -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))
|
||||
|
@ -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
|
||||
|
@ -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}}
|
||||
|
@ -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++ {
|
||||
|
Loading…
Reference in New Issue
Block a user