mirror of
https://github.com/vsariola/sointu.git
synced 2026-04-12 17:14:43 -04:00
drafting increasing maximum voice numbers to 256
This commit is contained in:
parent
ac218e7e54
commit
44ee37882b
@ -42,7 +42,7 @@ type (
|
||||
// example, if first instrument has 3 voices, second instrument has 2
|
||||
// voices, and third instrument four voices, the PolyphonyBitmask is: (MSB)
|
||||
// 110101110 (LSB)
|
||||
PolyphonyBitmask uint32
|
||||
PolyphonyBitmask []byte
|
||||
|
||||
// NumVoices is the total number of voices in the patch
|
||||
NumVoices uint32
|
||||
@ -69,8 +69,8 @@ type bytecodeBuilder struct {
|
||||
}
|
||||
|
||||
func NewBytecode(patch sointu.Patch, featureSet FeatureSet, bpm int) (*Bytecode, error) {
|
||||
if patch.NumVoices() > 32 {
|
||||
return nil, fmt.Errorf("Sointu does not support more than 32 concurrent voices; patch uses %v", patch.NumVoices())
|
||||
if patch.NumVoices() > MAX_VOICES {
|
||||
return nil, fmt.Errorf("Sointu does not support more than %d concurrent voices; patch uses %v", MAX_VOICES, patch.NumVoices())
|
||||
}
|
||||
b := newBytecodeBuilder(patch, bpm)
|
||||
for instrIndex, instr := range patch {
|
||||
@ -213,12 +213,13 @@ func NewBytecode(patch sointu.Patch, featureSet FeatureSet, bpm int) (*Bytecode,
|
||||
}
|
||||
|
||||
func newBytecodeBuilder(patch sointu.Patch, bpm int) *bytecodeBuilder {
|
||||
var polyphonyBitmask uint32 = 0
|
||||
var polyphonyBitmask []byte = make([]byte, (patch.NumVoices()+7)/8) // 1 bit per voice, rounded up to full bytes
|
||||
for _, instr := range patch {
|
||||
for j := 0; j < instr.NumVoices-1; j++ {
|
||||
polyphonyBitmask = (polyphonyBitmask << 1) + 1 // for each instrument, NumVoices - 1 bits are ones
|
||||
shiftLeftMask(polyphonyBitmask)
|
||||
polyphonyBitmask[0] += 1 // for each instrument, NumVoices - 1 bits are ones
|
||||
}
|
||||
polyphonyBitmask <<= 1 // ...and the last bit is zero, to denote "change instrument"
|
||||
shiftLeftMask(polyphonyBitmask) // ...and the last bit is zero, to denote "change instrument"
|
||||
}
|
||||
delayTimesInt, delayIndices := constructDelayTimeTable(patch, bpm)
|
||||
delayTimesU16 := make([]uint16, len(delayTimesInt))
|
||||
@ -236,6 +237,13 @@ func newBytecodeBuilder(patch sointu.Patch, bpm int) *bytecodeBuilder {
|
||||
return &c
|
||||
}
|
||||
|
||||
func shiftLeftMask(b []byte) {
|
||||
var carry byte = 0
|
||||
for i := range b {
|
||||
carry, b[i] = b[i]&128, (b[i]<<1)+(carry>>7)
|
||||
}
|
||||
}
|
||||
|
||||
// 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))
|
||||
|
||||
@ -49,7 +49,6 @@ func Synth(patch sointu.Patch, bpm int) (*NativeSynth, error) {
|
||||
if len(comPatch.Opcodes) == 0 {
|
||||
s.Opcodes[0] = 0
|
||||
s.NumVoices = 1
|
||||
s.Polyphony = 0
|
||||
return &NativeSynth{csynth: *s}, nil
|
||||
}
|
||||
for i, v := range comPatch.Opcodes {
|
||||
@ -67,7 +66,9 @@ func Synth(patch sointu.Patch, bpm int) (*NativeSynth, error) {
|
||||
s.SampleOffsets[i].LoopLength = (C.ushort)(v.LoopLength)
|
||||
}
|
||||
s.NumVoices = C.uint(comPatch.NumVoices)
|
||||
s.Polyphony = C.uint(comPatch.PolyphonyBitmask)
|
||||
for i, v := range comPatch.PolyphonyBitmask {
|
||||
s.Polyphony[i] = (C.uchar)(v)
|
||||
}
|
||||
s.RandSeed = 1
|
||||
return &NativeSynth{csynth: *s}, nil
|
||||
}
|
||||
@ -160,7 +161,6 @@ func (bridgesynth *NativeSynth) Update(patch sointu.Patch, bpm int) error {
|
||||
if len(comPatch.Opcodes) == 0 {
|
||||
s.Opcodes[0] = 0
|
||||
s.NumVoices = 1
|
||||
s.Polyphony = 0
|
||||
return nil
|
||||
}
|
||||
needsRefresh := false
|
||||
@ -182,7 +182,9 @@ func (bridgesynth *NativeSynth) Update(patch sointu.Patch, bpm int) error {
|
||||
s.SampleOffsets[i].LoopLength = (C.ushort)(v.LoopLength)
|
||||
}
|
||||
s.NumVoices = C.uint(comPatch.NumVoices)
|
||||
s.Polyphony = C.uint(comPatch.PolyphonyBitmask)
|
||||
for i, v := range comPatch.PolyphonyBitmask {
|
||||
s.Polyphony[i] = (C.uchar)(v)
|
||||
}
|
||||
if needsRefresh {
|
||||
for i := range s.SynthWrk.Voices {
|
||||
// if any of the opcodes change, we retrigger all units
|
||||
|
||||
@ -6,7 +6,8 @@ import (
|
||||
|
||||
type SongMacros struct {
|
||||
Song *sointu.Song
|
||||
VoiceTrackBitmask int
|
||||
VoiceTrackBitmask []byte
|
||||
HasVoiceTrack bool
|
||||
MaxSamples int
|
||||
}
|
||||
|
||||
@ -14,9 +15,13 @@ func NewSongMacros(s *sointu.Song) *SongMacros {
|
||||
maxSamples := s.SamplesPerRow() * s.Score.LengthInRows()
|
||||
p := SongMacros{Song: s, MaxSamples: maxSamples}
|
||||
trackVoiceNumber := 0
|
||||
p.VoiceTrackBitmask = make([]byte, (s.Score.NumVoices()+7)/8)
|
||||
for _, t := range s.Score.Tracks {
|
||||
if t.NumVoices > 0 {
|
||||
p.HasVoiceTrack = true
|
||||
}
|
||||
for b := 0; b < t.NumVoices-1; b++ {
|
||||
p.VoiceTrackBitmask += 1 << trackVoiceNumber
|
||||
p.VoiceTrackBitmask[trackVoiceNumber/8] += 1 << (trackVoiceNumber % 8)
|
||||
trackVoiceNumber++
|
||||
}
|
||||
trackVoiceNumber++ // set all bits except last one
|
||||
|
||||
@ -7,9 +7,9 @@ struc su_synth
|
||||
.sampleoffs resb su_sample_offset.size * 256
|
||||
.randseed resd 1
|
||||
.globaltime resd 1
|
||||
.opcodes resb 32 * 64
|
||||
.operands resb 32 * 64 * 8
|
||||
.polyphony resd 1
|
||||
.opcodes resb 256 * 64
|
||||
.operands resb 256 * 64 * 8
|
||||
.polyphony resb 32
|
||||
.numvoices resd 1
|
||||
endstruc
|
||||
|
||||
@ -68,8 +68,8 @@ su_render_samples_loop:
|
||||
inc dword [{{.Stack "BufSample"}}] ; samples++
|
||||
mov {{.CX}}, [{{.Stack "SynthState"}}]
|
||||
{{.Push .AX "Sample"}}
|
||||
mov eax, [{{.CX}} + su_synth.polyphony]
|
||||
{{.Push .AX "PolyphonyBitmask"}}
|
||||
lea {{.AX}}, [{{.CX}} + su_synth.polyphony]
|
||||
{{.Push .AX "PolyphonyBitmaskAddr"}}
|
||||
mov eax, [{{.CX}} + su_synth.numvoices]
|
||||
{{.Push .AX "VoicesRemain"}}
|
||||
lea {{.DX}}, [{{.CX}}+ su_synth.synth_wrk]
|
||||
|
||||
@ -23,11 +23,11 @@ typedef struct DelayWorkspace {
|
||||
} DelayWorkspace;
|
||||
|
||||
typedef struct SynthWorkspace {
|
||||
unsigned char Curvoices[32];
|
||||
unsigned char Curvoices[256];
|
||||
float Left;
|
||||
float Right;
|
||||
float Aux[6];
|
||||
struct Voice Voices[32];
|
||||
struct Voice Voices[256];
|
||||
} SynthWorkspace;
|
||||
|
||||
typedef struct SampleOffset {
|
||||
@ -43,9 +43,9 @@ typedef struct Synth {
|
||||
struct SampleOffset SampleOffsets[256];
|
||||
unsigned int RandSeed;
|
||||
unsigned int GlobalTick;
|
||||
unsigned char Opcodes[32 * 64];
|
||||
unsigned char Operands[32 * 64 * 8];
|
||||
unsigned int Polyphony;
|
||||
unsigned char Opcodes[256 * 64];
|
||||
unsigned char Operands[256 * 64 * 8];
|
||||
unsigned char Polyphony[32];
|
||||
unsigned int NumVoices;
|
||||
} Synth;
|
||||
#pragma pack(pop)
|
||||
|
||||
@ -63,7 +63,13 @@ su_run_vm_advance:
|
||||
mov [{{.Stack "Voice"}}], {{.WRK}} ; update the pointer in the stack to point to the new voice
|
||||
mov ecx, [{{.Stack "VoicesRemain"}}] ; ecx = how many voices remain to process
|
||||
dec ecx ; decrement number of voices to process
|
||||
bt dword [{{.Stack "PolyphonyBitmask"}}], ecx ; if voice bit of su_polyphonism not set
|
||||
{{- if .Library}}
|
||||
mov {{.VAL}}, [{{.Stack "PolyphonyBitmaskAddr"}}]
|
||||
bt dword [{{.VAL}}], ecx ; if voice bit of su_polyphonism not set
|
||||
{{- else }}
|
||||
{{- .Prepare "su_polyphony_bitmask" | indent 4}}
|
||||
bt dword [{{.Use "su_polyphony_bitmask"}}], ecx ; if voice bit of su_polyphonism not set
|
||||
{{- end}}
|
||||
jnc su_op_advance_next_instrument ; goto next_instrument
|
||||
mov {{.VAL}}, [{{.Stack "OperandStream"}}] ; if it was set, then repeat the opcodes for the current voice
|
||||
mov {{.COM}}, [{{.Stack "OpcodeStream"}}]
|
||||
|
||||
@ -44,9 +44,6 @@ extern syncBuf
|
||||
{{- end}}
|
||||
{{- end}}
|
||||
xor eax, eax
|
||||
{{- if ne .VoiceTrackBitmask 0}}
|
||||
{{.Push (.VoiceTrackBitmask | printf "%v") "VoiceTrackBitmask"}}
|
||||
{{- end}}
|
||||
{{.Push "1" "RandSeed"}}
|
||||
{{.Push .AX "GlobalTick"}}
|
||||
su_render_rowloop: ; loop through every row in the song
|
||||
@ -55,9 +52,6 @@ su_render_rowloop: ; loop through every row in the song
|
||||
xor eax, eax ; ecx is the current sample within row
|
||||
su_render_sampleloop: ; loop through every sample in the row
|
||||
{{.Push .AX "Sample"}}
|
||||
{{- if .SupportsPolyphony}}
|
||||
{{.Push (.PolyphonyBitmask | printf "%v") "PolyphonyBitmask"}} ; does the next voice reuse the current opcodes?
|
||||
{{- 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_opcodes ; COM points to vm code
|
||||
@ -68,9 +62,6 @@ su_render_sampleloop: ; loop through every sample in the row
|
||||
lea {{.WRK}}, [{{.DX}} + su_synthworkspace.voices] ; WRK points to the first voice
|
||||
{{.Call "su_run_vm"}} ; run through the VM code
|
||||
{{.Pop .AX}}
|
||||
{{- if .SupportsPolyphony}}
|
||||
{{.Pop .AX}}
|
||||
{{- end}}
|
||||
{{- template "output_sound.asm" .}} ; *ptr++ = left, *ptr++ = right
|
||||
{{.Pop .AX}}
|
||||
inc dword [{{.Stack "GlobalTick"}}] ; increment global time, used by delays
|
||||
@ -106,7 +97,7 @@ su_render_sampleloop: ; loop through every sample in the row
|
||||
; Dirty: pretty much everything
|
||||
;-------------------------------------------------------------------------------
|
||||
{{.Func "su_update_voices"}}
|
||||
{{- if ne .VoiceTrackBitmask 0}}
|
||||
{{- if .HasVoiceTrack}}
|
||||
; The more complicated implementation: one track can trigger multiple voices
|
||||
xor edx, edx
|
||||
mov ebx, {{.PatternLength}} ; we could do xor ebx,ebx; mov bl,PATTERN_SIZE, but that would limit patternsize to 256...
|
||||
@ -125,7 +116,8 @@ su_update_voices_trackloop:
|
||||
xor edx, edx ; edx=0
|
||||
mov ecx, ebx ; ecx=first voice of the track to be done
|
||||
su_calculate_voices_loop: ; do {
|
||||
bt dword [{{.Stack "VoiceTrackBitmask"}} + {{.PTRSIZE}}],ecx ; test voicetrack_bitmask// notice that the incs don't set carry
|
||||
{{- .Prepare "su_voicetrack_bitmask" | indent 4}}
|
||||
bt dword [{{.Use "su_voicetrack_bitmask"}}], ecx ; if voice bit of su_polyphonism not set
|
||||
inc edx ; edx++ // edx=numvoices
|
||||
inc ecx ; ecx++ // ecx=the first voice of next track
|
||||
jc su_calculate_voices_loop ; } while bit ecx-1 of bitmask is on
|
||||
@ -249,6 +241,24 @@ su_update_voices_skipadd:
|
||||
{{.Data "su_patch_operands"}}
|
||||
db {{.Operands | toStrings | join ","}}
|
||||
|
||||
{{- if not .Library}}
|
||||
{{- if .SupportsPolyphony}}
|
||||
;-------------------------------------------------------------------------------
|
||||
; PolyphonyBitmask
|
||||
;-------------------------------------------------------------------------------
|
||||
{{.Data "su_polyphony_bitmask"}}
|
||||
db {{.PolyphonyBitmask | toStrings | join ","}}
|
||||
{{- end}}
|
||||
{{- end}}
|
||||
|
||||
{{- if .HasVoiceTrack}}
|
||||
;-------------------------------------------------------------------------------
|
||||
; VoiceTrackBitmask
|
||||
;-------------------------------------------------------------------------------
|
||||
{{.Data "su_voicetrack_bitmask"}}
|
||||
db {{.VoiceTrackBitmask | toStrings | join ","}}
|
||||
{{- end}}
|
||||
|
||||
;-------------------------------------------------------------------------------
|
||||
; Constants
|
||||
;-------------------------------------------------------------------------------
|
||||
|
||||
@ -94,6 +94,7 @@ su_op_aux_mono:
|
||||
test ah, 0x80
|
||||
jz su_op_send_skipglobal
|
||||
mov {{.CX}}, [{{.Stack "Synth"}} + {{.PTRSIZE}}]
|
||||
lea {{.CX}}, [{{.CX}} + su_synthworkspace.voices - su_unit.size]
|
||||
su_op_send_skipglobal:
|
||||
popf
|
||||
{{- end}}
|
||||
|
||||
@ -23,11 +23,11 @@ endstruc
|
||||
; synthworkspace struct
|
||||
;-------------------------------------------------------------------------------
|
||||
struc su_synthworkspace
|
||||
.curvoices resb 32 ; these are used by the multitrack player to store which voice is playing on which track
|
||||
.curvoices resb 256 ; these are used by the multitrack player to store which voice is playing on which track
|
||||
.left resd 1
|
||||
.right resd 1
|
||||
.aux resd 6 ; 3 auxiliary signals
|
||||
.voices resb 32 * su_voice.size
|
||||
.voices resb 256 * su_voice.size
|
||||
.size:
|
||||
endstruc
|
||||
|
||||
|
||||
@ -37,7 +37,7 @@ type (
|
||||
}
|
||||
)
|
||||
|
||||
const MAX_VOICES = 32
|
||||
const MAX_VOICES = 256
|
||||
const MAX_UNITS = 63
|
||||
|
||||
type (
|
||||
@ -189,7 +189,7 @@ func (s *GoSynth) Render(buffer sointu.AudioBuffer, maxtime int) (samples int, r
|
||||
voices = voices[1:]
|
||||
units = voices[0].units[:]
|
||||
}
|
||||
if mask := uint32(1) << uint32(voicesRemaining); s.bytecode.PolyphonyBitmask&mask == mask {
|
||||
if mask := byte(1) << uint32(voicesRemaining&7); s.bytecode.PolyphonyBitmask[voicesRemaining>>3]&mask == mask {
|
||||
opcodes, operands = opcodesInstr, operandsInstr
|
||||
} else {
|
||||
opcodesInstr, operandsInstr = opcodes, operands
|
||||
|
||||
Reference in New Issue
Block a user