Implement unison oscillators: multiple versions of slightly detuned oscillators that are added up to make a signal.

This commit is contained in:
Veikko Sariola 2020-05-19 21:12:49 +03:00 committed by Veikko Sariola
parent adc4a6e45f
commit dc99157fbb
8 changed files with 95 additions and 14 deletions

View File

@ -73,6 +73,10 @@ New features since fork
spare. See [this example](tests/test_oscillat_sample.asm), and this Python
[script](scripts/parse_gmdls.py) parses the gm.dls file and dumps the
sample offsets from it.
- **Unison oscillators**. Multiple copies of the oscillator running sligthly
detuned and added up to together. Great for trance leads (supersaw). Unison
of up to 4, or 8 if you make stereo unison oscillator and add up both left
and right channels. See [this example](tests/test_oscillat_unison.asm).
Future goals
------------

View File

@ -101,24 +101,46 @@ su_op_noise_mono:
SECT_TEXT(suoscill)
EXPORT MANGLE_FUNC(su_op_oscillat,0)
lodsb ; load the flags
lodsb ; load the flags
fld dword [edx+su_osc_ports.detune] ; e, where e is the detune [0,1]
fsub dword [c_0_5] ; e-.5
fadd st0, st0 ; d=2*e-.5, where d is the detune [-1,1]
%ifdef INCLUDE_STEREO_OSCILLAT
jnc su_op_oscillat_mono
add WRK, 4
call su_op_oscillat_mono
fld1 ; invert the detune for second run for some stereo separation
fld dword [edx+su_osc_ports.detune]
fsubp st1
fstp dword [edx+su_osc_ports.detune]
sub WRK, 4
su_op_oscillat_mono:
fld st0 ; d d
call su_op_oscillat_mono ; r d
add WRK, 4 ; state vars: r1 l1 r2 l2 r3 l3 r4 l4, for the unison osc phases
fxch ; d r
fchs ; -d r, negate the detune for second round
su_op_oscillat_mono:
%endif
%ifdef INCLUDE_UNISONS
pushad ; push eax, WRK, WRK would suffice but this is shorter
fldz ; 0 d
fxch ; d a=0, "accumulated signal"
su_op_oscillat_unison_loop:
fst dword [esp] ; save the current detune, d. We could keep it in fpu stack but it was getting big.
call su_op_oscillat_single ; s a
faddp st1, st0 ; a+=s
test al, UNISON4
je su_op_oscillat_unison_out
add WRK, 8
fld dword [edx+su_osc_ports.phaseofs] ; p s
fadd dword [c_i12] ; p s, add some little phase offset to unison oscillators so they don't start in sync
fstp dword [edx+su_osc_ports.phaseofs] ; s note that this changes the phase for second, possible stereo run. That's probably ok
fld dword [esp] ; d s
fmul dword [c_0_5] ; .5*d s // negate and halve the detune of each oscillator
fchs ; -.5*d s // negate and halve the detune of each oscillator
dec eax
jmp short su_op_oscillat_unison_loop
su_op_oscillat_unison_out:
popad ; similarly, pop WRK, WRK, eax would suffice
ret
su_op_oscillat_single:
%endif
fld dword [edx+su_osc_ports.transpose]
fsub dword [c_0_5]
fdiv dword [c_i128]
fld dword [edx+su_osc_ports.detune]
fsub dword [c_0_5]
fadd st0
faddp st1
test al, byte LFO
jnz su_op_oscillat_skipnote

View File

@ -64,12 +64,15 @@ endstruc
%endif
%endmacro
%define SAMPLE 0x80 ; in this case, all the rest of the bits is the sample index
%define SAMPLE 0x80
%define SINE 0x40
%define TRISAW 0x20
%define PULSE 0x10
%define LFO 0x08
%define GATE 0x04
%define UNISON2 0x01
%define UNISON3 0x02 ; Warning, UNISON3 and UNISON4 do not work with gate at the moment, as they use the same state variable
%define UNISON4 0x03
%macro SU_OSCILLAT 8
db %2
@ -99,6 +102,9 @@ endstruc
%if (%8) & SAMPLE == SAMPLE
%define INCLUDE_SAMPLES
%endif
%if (%8) & UNISON4 > 0
%define INCLUDE_UNISONS
%endif
%endmacro
struc su_osc_ports
@ -113,7 +119,7 @@ endstruc
struc su_osc_wrk
.phase resd 1
.gatestate resd 1
.gatestate equ 16 ; we put is late so only UNISON3 and UNISON4 are unusable with gate
.size
endstruc

View File

@ -80,6 +80,8 @@ regression_test(test_oscillat_gate ENVELOPE)
regression_test(test_oscillat_stereo ENVELOPE)
regression_test(test_oscillat_sample ENVELOPE)
regression_test(test_oscillat_sample_stereo ENVELOPE)
regression_test(test_oscillat_unison ENVELOPE)
regression_test(test_oscillat_unison_stereo ENVELOPE)
regression_test(test_oscillat_lfo "ENVELOPE;VCO_SINE;VCO_PULSE;FOP_MULP2")
regression_test(test_oscillat_transposemod "VCO_SINE;ENVELOPE;FOP_MULP;FOP_PUSH;SEND")
regression_test(test_oscillat_detunemod "VCO_SINE;ENVELOPE;FOP_MULP;FOP_PUSH;SEND")

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,24 @@
%define BPM 100
%define USE_SECTIONS
%include "../src/sointu.inc"
SU_BEGIN_PATTERNS
PATTERN 64, 0, 68, 0, 32, 0, 0, 0, 75, 0, 78, 0, 0, 0, 0, 0,
SU_END_PATTERNS
SU_BEGIN_TRACKS
TRACK VOICES(1),0
SU_END_TRACKS
SU_BEGIN_PATCH
SU_BEGIN_INSTRUMENT VOICES(1) ; Instrument0
SU_ENVELOPE MONO, ATTAC(32),DECAY(32),SUSTAIN(64),RELEASE(64),GAIN(128)
SU_OSCILLAT MONO, TRANSPOSE(64),DETUNE(0),PHASE(64),COLOR(128),SHAPE(64),GAIN(32), FLAGS(TRISAW + UNISON4)
SU_MULP MONO
SU_PUSH MONO
SU_OUT STEREO, GAIN(128)
SU_END_INSTRUMENT
SU_END_PATCH
%include "../src/sointu.asm"

View File

@ -0,0 +1,23 @@
%define BPM 100
%define USE_SECTIONS
%include "../src/sointu.inc"
SU_BEGIN_PATTERNS
PATTERN 64, 0, 68, 0, 32, 0, 0, 0, 75, 0, 78, 0, 0, 0, 0, 0,
SU_END_PATTERNS
SU_BEGIN_TRACKS
TRACK VOICES(1),0
SU_END_TRACKS
SU_BEGIN_PATCH
SU_BEGIN_INSTRUMENT VOICES(1) ; Instrument0
SU_ENVELOPE STEREO, ATTAC(32),DECAY(32),SUSTAIN(64),RELEASE(64),GAIN(128)
SU_OSCILLAT STEREO, TRANSPOSE(64),DETUNE(0),PHASE(64),COLOR(128),SHAPE(64),GAIN(32), FLAGS(TRISAW + UNISON4)
SU_MULP STEREO
SU_OUT STEREO, GAIN(128)
SU_END_INSTRUMENT
SU_END_PATCH
%include "../src/sointu.asm"