-
Notifications
You must be signed in to change notification settings - Fork 1
/
4klang.asm
1629 lines (1565 loc) · 44.2 KB
/
4klang.asm
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
bits 32
%define WRK ebp ; // alias for unit workspace
%define VAL esi ; // alias for unit values (transformed/untransformed)
%define COM ebx ; // alias for instrument opcodes
%include "4klang.inc"
;// conditional defines
%ifdef GO4K_USE_VCO_SHAPE
%define INCLUDE_WAVESHAPER
%endif
%ifdef GO4K_USE_DST
%define INCLUDE_WAVESHAPER
%endif
%ifdef GO4K_USE_VCO_MOD_PM
%define PHASE_RENORMALIZE
%endif
%ifdef GO4K_USE_VCO_PHASE_OFFSET
%define PHASE_RENORMALIZE
%endif
%ifdef GO4K_USE_ENVELOPE_RECORDINGS
%define GO4K_USE_BUFFER_RECORDINGS
%endif
%ifdef GO4K_USE_NOTE_RECORDINGS
%define GO4K_USE_BUFFER_RECORDINGS
%endif
; //========================================================================================
; // .bss section
; //========================================================================================
%ifdef USE_SECTIONS
section .g4kbss1 bss align=1
%else
section .bss
%endif
; // the one and only synth object
%if MAX_VOICES > 1
go4k_voiceindex resd 16
%endif
go4k_transformed_values resd 16
go4k_synth_wrk resb go4k_synth.size
global _go4k_delay_buffer_ofs
_go4k_delay_buffer_ofs resd 1
global _go4k_delay_buffer
_go4k_delay_buffer resd 16*16*go4kDLL_wrk.size
%ifdef AUTHORING
global __4klang_current_tick
__4klang_current_tick resd 0
%endif
%ifdef GO4K_USE_ENVELOPE_RECORDINGS
global __4klang_envelope_buffer
__4klang_envelope_buffer resd ((MAX_SAMPLES)/8) ; // samples every 256 samples and stores 16*2 = 32 values
%endif
%ifdef GO4K_USE_NOTE_RECORDINGS
global __4klang_note_buffer
__4klang_note_buffer resd ((MAX_SAMPLES)/8) ; // samples every 256 samples and stores 16*2 = 32 values
%endif
; //========================================================================================
; // .g4kdat section (initialized data for go4k)
; //========================================================================================
%ifdef USE_SECTIONS
section .g4kdat1 data align=1
%else
section .data
%endif
; // some synth constants
go4k_synth_commands dd 0
dd _go4kENV_func@0
dd _go4kVCO_func@0
dd _go4kVCF_func@0
dd _go4kDST_func@0
dd _go4kDLL_func@0
dd _go4kFOP_func@0
dd _go4kFST_func@0
dd _go4kPAN_func@0
dd _go4kOUT_func@0
dd _go4kACC_func@0
dd _go4kFLD_func@0
%ifdef GO4K_USE_FSTG
dd _go4kFSTG_func@0
%endif
%ifdef USE_SECTIONS
section .g4kdat2 data align=1
%else
section .data
%endif
%ifdef GO4K_USE_16BIT_OUTPUT
c_32767 dd 32767.0
%endif
c_i128 dd 0.0078125
c_RandDiv dd 65536*32768
c_0_5 dd 0.5
%ifdef GO4K_USE_VCO_GATE
c_16 dd 16.0
%endif
%ifdef GO4K_USE_DLL_CHORUS
DLL_DEPTH dd 1024.0
%endif
%ifdef GO4K_USE_DLL_DC_FILTER
c_dc_const dd 0.99609375 ; R = 1 - (pi*2 * frequency /samplerate)
%else
%ifdef GO4K_USE_VCO_GATE
c_dc_const dd 0.99609375 ; R = 1 - (pi*2 * frequency /samplerate)
%endif
%endif
global _RandSeed
_RandSeed dd 1
c_24 dd 24
c_i12 dd 0x3DAAAAAA
FREQ_NORMALIZE dd 0.000092696138 ; // 220.0/(2^(69/12)) / 44100.0
global _LFO_NORMALIZE
_LFO_NORMALIZE dd DEF_LFO_NORMALIZE
%ifdef GO4K_USE_GROOVE_PATTERN
go4k_groove_pattern dw 0011100111001110b
%endif
; //========================================================================================
; // .crtemui section (emulates crt functions)
; //========================================================================================
%ifdef USE_SECTIONS
section .crtemui code align=1
%else
section .text
%endif
export_func FloatRandomNumber@0
push eax
imul eax,dword [_RandSeed],16007
mov dword [_RandSeed], eax
fild dword [_RandSeed]
fidiv dword [c_RandDiv]
pop eax
ret
; //========================================================================================
; // .g4kcod* sections (code for go4k)
; //========================================================================================
%ifdef INCLUDE_WAVESHAPER
%ifdef USE_SECTIONS
section .g4kcod2 code align=1
%else
section .text
%endif
; //----------------------------------------------------------------------------------------
; // Waveshaper function
; //----------------------------------------------------------------------------------------
; // Input : st0 : shaping coeff
; // st1 : input
; // Output: st0 : result
; //----------------------------------------------------------------------------------------
go4kWaveshaper:
%ifdef GO4K_USE_WAVESHAPER_CLIP
fxch
fld1 ; // 1 val
fucomi st1 ; // 1 val
jbe short go4kWaveshaper_clip
fchs ; // -1 val
fucomi st1 ; // -1 val
fcmovb st0, st1 ; // val -1 (if val > -1)
go4kWaveshaper_clip:
fstp st1 ; // newval
fxch
%endif
fsub dword [c_0_5]
fadd st0
fst dword [esp-4] ; // amnt in
fadd st0 ; // 2*amnt in
fld1 ; // 1 2*amnt in
fsub dword [esp-4] ; // 1-amnt 2*amnt in
fdivp st1, st0 ; // k in
fld st1 ; // sin k in
fabs ; // a(in) k in
fmul st1 ; // k*a(in) k in
fld1
faddp st1, st0 ; // 1+k*a(in) k in
fxch st1 ; // k 1+k*a(in) in
fld1
faddp st1, st0 ; // 1+k 1+k*a(in) in
fmulp st2 ; // 1+k*a(in) (1+k)*in
fdivp st1, st0 ; // out
ret
%endif
%ifdef USE_SECTIONS
section .g4kcod3 code align=1
%else
section .text
%endif
; //----------------------------------------------------------------------------------------
; // unit values preparation/transform
; //----------------------------------------------------------------------------------------
go4kTransformValues:
push ecx
xor ecx, ecx
xor eax, eax
mov edx, go4k_transformed_values
go4kTransformValues_loop:
lodsb
push eax
fild dword [esp]
fmul dword [c_i128]
fstp dword [edx+ecx*4]
pop eax
inc ecx
cmp ecx, dword [esp+8]
jl go4kTransformValues_loop
pop ecx
ret 4
%ifdef USE_SECTIONS
section .g4kcod4 code align=1
%else
section .text
%endif
; //----------------------------------------------------------------------------------------
; // Envelope param mapping
; //----------------------------------------------------------------------------------------
go4kENVMap:
fld dword [edx+eax*4]
%ifdef GO4K_USE_ENV_MOD_ADR
fadd dword [WRK+go4kENV_wrk.am+eax*4]
%endif
fimul dword [c_24]
fchs
; //----------------------------------------------------------------------------------------
; // Power function (2^x)
; //----------------------------------------------------------------------------------------
; // Input : st0 : base
; // st1 : exponent
; // Output: st0 : result
; //----------------------------------------------------------------------------------------
export_func Power@0 ; // base exp
fld1
fadd st0
fyl2x ; // log2_base
fld1 ; // 1 log2_base
fld st1 ; // log2_base 1 log2_base
fprem ; // (frac)log2_base 1 log2_base
f2xm1 ; // 2 ^ '' - 1 1 log2_base
faddp st1, st0 ; // 2 ^ '' (int)log2_base
fscale
fstp st1
ret
%ifdef USE_SECTIONS
section .g4kcoda code align=1
%else
section .text
%endif
; //----------------------------------------------------------------------------------------
; // ENV Tick
; //----------------------------------------------------------------------------------------
; // IN : WRK = unit workspace
; // IN : VAL = unit values
; // IN : ecx = global workspace
; // OUT :
; // DIRTY : eax
; //----------------------------------------------------------------------------------------
export_func go4kENV_func@0
push 5
call go4kTransformValues
%ifdef GO4K_USE_ENV_CHECK
; check if current note still active
mov eax, dword [ecx-4]
test eax, eax
jne go4kENV_func_do
fldz
ret
%endif
go4kENV_func_do:
mov eax, dword [ecx-8] ; // is the instrument in release mode (note off)?
test eax, eax
je go4kENV_func_process
mov dword [WRK+go4kENV_wrk.state], ENV_STATE_RELEASE
go4kENV_func_process:
mov eax, dword [WRK+go4kENV_wrk.state]
fld dword [WRK+go4kENV_wrk.level] ; // val -
; // check for sustain state
cmp al, ENV_STATE_SUSTAIN
je short go4kENV_func_leave2
go4kENV_func_attac:
cmp al, ENV_STATE_ATTAC
jne short go4kENV_func_decay
call go4kENVMap ; // newval
faddp st1, st0
; // check for end of attac
fld1 ; // 1 newval
fucomi st1 ; // 1 newval
fcmovnb st0, st1 ; // newval 1 (if newval < 1)
jbe short go4kENV_func_statechange
go4kENV_func_decay:
cmp al, ENV_STATE_DECAY
jne short go4kENV_func_release
call go4kENVMap ; // newval
fsubp st1, st0
; // check for end of decay
fld dword [edx+go4kENV_val.sustain] ; // sustain newval
fucomi st1 ; // sustain newval
fcmovb st0, st1 ; // newval sustain (if newval > sustain)
jnc short go4kENV_func_statechange
go4kENV_func_release:
; // release state?
cmp al, ENV_STATE_RELEASE
jne short go4kENV_func_leave
call go4kENVMap ; // newval
fsubp st1, st0
; // check for end of release
fldz ; // 0 newval
fucomi st1 ; // 0 newval
fcmovb st0, st1 ; // newval 0 (if newval > 0)
jc short go4kENV_func_leave
go4kENV_func_statechange: ; // newval
inc dword [WRK+go4kENV_wrk.state]
go4kENV_func_leave: ; // newval bla
fstp st1
; // store new env value
fst dword [WRK+go4kENV_wrk.level]
go4kENV_func_leave2:
; // mul by gain
%ifdef GO4K_USE_ENV_MOD_GM
fld dword [edx+go4kENV_val.gain]
fadd dword [WRK+go4kENV_wrk.gm]
fmulp st1, st0
%else
fmul dword [edx+go4kENV_val.gain]
%endif
ret
; //----------------------------------------------------------------------------------------
; // VCO Tick
; //----------------------------------------------------------------------------------------
; // IN : WRK = unit workspace
; // IN : VAL = unit values
; // IN : ecx = global workspace
; // OUT :
; // DIRTY : eax
; //----------------------------------------------------------------------------------------
%ifdef USE_SECTIONS
section .g4kcodp code align=1
%else
section .text
%endif
go4kVCO_pulse:
fucomi st1 ; // c p
fld1
jnc short go4kVCO_func_pulse_up ; // +1 c p
fchs ; // -1 c p
go4kVCO_func_pulse_up:
fstp st1 ; // +-1 p
fstp st1 ; // +-1
ret
%ifdef USE_SECTIONS
section .g4kcodt code align=1
%else
section .text
%endif
go4kVCO_trisaw:
fucomi st1 ; // c p
jnc short go4kVCO_func_trisaw_up
fld1 ; // 1 c p
fsubr st2, st0 ; // 1 c 1-p
fsubrp st1, st0 ; // 1-c 1-p
go4kVCO_func_trisaw_up:
fdivp st1, st0 ; // tp'/tc
fadd st0 ; // 2*''
fld1 ; // 1 2*''
fsubp st1, st0 ; // 2*''-1
ret
%ifdef USE_SECTIONS
section .g4kcods code align=1
%else
section .text
%endif
go4kVCO_sine:
fucomi st1 ; // c p
jnc short go4kVCO_func_sine_do
fstp st1
fsub st0, st0 ; // 0
ret
go4kVCO_func_sine_do
fdivp st1, st0 ; // p/c
fldpi ; // pi p
fadd st0 ; // 2*pi p
fmulp st1, st0 ; // 2*pi*p
fsin ; // sin(2*pi*p)
ret
%ifdef GO4K_USE_VCO_GATE
%ifdef USE_SECTIONS
section .g4kcodq code align=1
%else
section .text
%endif
go4kVCO_gate:
fxch ; // p c
fstp st1 ; // p
fmul dword [c_16] ; // p'
push eax
push eax
fistp dword [esp] ; // -
fld1 ; // 1
pop eax
and al, 0xf
bt word [VAL-5],ax
jc go4kVCO_gate_bit
fsub st0, st0 ; // 0
go4kVCO_gate_bit:
fld dword [WRK+go4kVCO_wrk.cm] ; // f x
fsub st1 ; // f-x x
fmul dword [c_dc_const] ; // c(f-x) x
faddp st1, st0 ; // x'
fst dword [WRK+go4kVCO_wrk.cm]
pop eax
ret
%endif
%ifdef USE_SECTIONS
section .g4kcodb code align=1
%else
section .text
%endif
export_func go4kVCO_func@0
%ifdef GO4K_USE_VCO_PHASE_OFFSET
%ifdef GO4K_USE_VCO_SHAPE
%ifdef GO4K_USE_VCO_GATE
push 8
%else
push 7
%endif
%else
%ifdef GO4K_USE_VCO_GATE
push 7
%else
push 6
%endif
%endif
%else
%ifdef GO4K_USE_VCO_SHAPE
%ifdef GO4K_USE_VCO_GATE
push 7
%else
push 6
%endif
%else
%ifdef GO4K_USE_VCO_GATE
push 6
%else
push 5
%endif
%endif
%endif
call go4kTransformValues
%ifdef GO4K_USE_VCO_CHECK
; check if current note still active
mov eax, dword [ecx-4]
test eax, eax
jne go4kVCO_func_do
%ifdef GO4K_USE_VCO_STEREO
movzx eax, byte [VAL-1] ; // get flags and check for stereo
test al, byte VCO_STEREO
jz short go4kVCO_func_nostereoout
fldz
go4kVCO_func_nostereoout:
%endif
fldz
ret
go4kVCO_func_do:
%endif
movzx eax, byte [VAL-1] ; // get flags
%ifdef GO4K_USE_VCO_STEREO
test al, byte VCO_STEREO
jz short go4kVCO_func_nopswap
fld dword [WRK+go4kVCO_wrk.phase] ;// swap left/right phase values for first stereo run
fld dword [WRK+go4kVCO_wrk.phase2]
fstp dword [WRK+go4kVCO_wrk.phase]
fstp dword [WRK+go4kVCO_wrk.phase2]
go4kVCO_func_nopswap:
%endif
go4kVCO_func_process:
fld dword [edx+go4kVCO_val.transpose]
fsub dword [c_0_5]
%ifdef GO4K_USE_VCO_MOD_TM
fadd dword [WRK+go4kVCO_wrk.tm]
%endif
fdiv dword [c_i128]
fld dword [edx+go4kVCO_val.detune]
fsub dword [c_0_5]
fadd st0
%ifdef GO4K_USE_VCO_STEREO
test al, byte VCO_STEREO
jz short go4kVCO_func_nodswap
fchs ;// negate detune for stereo
go4kVCO_func_nodswap:
%endif
faddp st1
%ifdef GO4K_USE_VCO_MOD_DM
fadd dword [WRK+go4kVCO_wrk.dm]
%endif
; // st0 now contains the transpose+detune offset
test al, byte LFO
jnz go4kVCO_func_skipnote
fiadd dword [ecx-4] ; // st0 is note, st1 is t+d offset
go4kVCO_func_skipnote:
fmul dword [c_i12]
call _Power@0
test al, byte LFO
jz short go4kVCO_func_normalize_note
fmul dword [_LFO_NORMALIZE] ; // st0 is now frequency for lfo
jmp short go4kVCO_func_normalized
go4kVCO_func_normalize_note:
fmul dword [FREQ_NORMALIZE] ; // st0 is now frequency
go4kVCO_func_normalized:
fadd dword [WRK+go4kVCO_wrk.phase]
%ifdef GO4K_USE_VCO_MOD_FM
fadd dword [WRK+go4kVCO_wrk.fm]
%endif
fld1
fadd st1, st0
fxch
fprem
fstp st1
fst dword [WRK+go4kVCO_wrk.phase]
%ifdef GO4K_USE_VCO_MOD_PM
fadd dword [WRK+go4kVCO_wrk.pm]
%endif
%ifdef GO4K_USE_VCO_PHASE_OFFSET
fadd dword [edx+go4kVCO_val.phaseofs]
%endif
%ifdef PHASE_RENORMALIZE
fld1
fadd st1, st0
fxch
fprem
fstp st1 ; // p
%endif
fld dword [edx+go4kVCO_val.color] ; // c p
%ifdef GO4K_USE_VCO_MOD_CM
fadd dword [WRK+go4kVCO_wrk.cm] ; // c p
%endif
go4kVCO_func_sine:
test al, byte SINE
jz short go4kVCO_func_trisaw
call go4kVCO_sine
go4kVCO_func_trisaw:
test al, byte TRISAW
jz short go4kVCO_func_pulse
call go4kVCO_trisaw
go4kVCO_func_pulse:
test al, byte PULSE
%ifdef GO4K_USE_VCO_GATE
jz short go4kVCO_func_gate
%else
jz short go4kVCO_func_noise
%endif
call go4kVCO_pulse
%ifdef GO4K_USE_VCO_GATE
go4kVCO_func_gate:
test al, byte GATE
jz short go4kVCO_func_noise
call go4kVCO_gate
%endif
go4kVCO_func_noise:
test al, byte NOISE
jz short go4kVCO_func_end
call _FloatRandomNumber@0
fstp st1
fstp st1
go4kVCO_func_end:
%ifdef GO4K_USE_VCO_SHAPE
fld dword [edx+go4kVCO_val.shape]
%ifdef GO4K_USE_VCO_MOD_SM
fadd dword [WRK+go4kVCO_wrk.sm]
%endif
call go4kWaveshaper
%endif
fld dword [edx+go4kVCO_val.gain]
%ifdef GO4K_USE_VCO_MOD_GM
fadd dword [WRK+go4kVCO_wrk.gm]
%endif
fmulp st1, st0
%ifdef GO4K_USE_VCO_STEREO
test al, byte VCO_STEREO
jz short go4kVCO_func_stereodone
sub al, byte VCO_STEREO
fld dword [WRK+go4kVCO_wrk.phase] ;// swap left/right phase values again for second stereo run
fld dword [WRK+go4kVCO_wrk.phase2]
fstp dword [WRK+go4kVCO_wrk.phase]
fstp dword [WRK+go4kVCO_wrk.phase2]
jmp go4kVCO_func_process
go4kVCO_func_stereodone:
%endif
ret
%ifdef USE_SECTIONS
section .g4kcodc code align=1
%else
section .text
%endif
; //----------------------------------------------------------------------------------------
; // VCF Tick
; //----------------------------------------------------------------------------------------
; // IN : WRK = unit workspace
; // IN : VAL = unit values
; // IN : ecx = global workspace
; // OUT :
; // DIRTY : eax
; //----------------------------------------------------------------------------------------
export_func go4kVCF_func@0
push 3
call go4kTransformValues
%ifdef GO4K_USE_VCF_CHECK
; check if current note still active
mov eax, dword [ecx-4]
test eax, eax
jne go4kVCF_func_do
ret
go4kVCF_func_do:
%endif
movzx eax, byte [VAL-1] ; // get type flag
fld dword [edx+go4kVCF_val.res] ; // r in
%ifdef GO4K_USE_VCF_MOD_RM
fadd dword [WRK+go4kVCF_wrk.rm]
%endif
fstp dword [esp-8]
fld dword [edx+go4kVCF_val.freq] ; // f in
%ifdef GO4K_USE_VCF_MOD_FM
fadd dword [WRK+go4kVCF_wrk.fm]
%endif
fmul st0, st0 ; // square the input so we never get negative and also have a smoother behaviour in the lower frequencies
fstp dword [esp-4] ; // in
%ifdef GO4K_USE_VCF_STEREO
test al, byte STEREO
jz short go4kVCF_func_process
add WRK, go4kVCF_wrk.low2
go4kVCF_func_stereoloop: ; // switch channels
fxch st1 ; // inr inl
%endif
go4kVCF_func_process:
fld dword [esp-8]
fld dword [esp-4]
fmul dword [WRK+go4kVCF_wrk.band] ; // f*b r in
fadd dword [WRK+go4kVCF_wrk.low] ; // l' r in
fst dword [WRK+go4kVCF_wrk.low] ; // l' r in
fsubp st2, st0 ; // r in-l'
fmul dword [WRK+go4kVCF_wrk.band] ; // r*b in-l'
fsubp st1, st0 ; // h'
fst dword [WRK+go4kVCF_wrk.high] ; // h'
fmul dword [esp-4] ; // h'*f
fadd dword [WRK+go4kVCF_wrk.band] ; // b'
fstp dword [WRK+go4kVCF_wrk.band]
fldz
go4kVCF_func_low:
test al, byte LOWPASS
jz short go4kVCF_func_high
fadd dword [WRK+go4kVCF_wrk.low]
go4kVCF_func_high:
%ifdef GO4K_USE_VCF_HIGH
test al, byte HIGHPASS
jz short go4kVCF_func_band
fadd dword [WRK+go4kVCF_wrk.high]
%endif
go4kVCF_func_band:
%ifdef GO4K_USE_VCF_BAND
test al, byte BANDPASS
jz short go4kVCF_func_peak
fadd dword [WRK+go4kVCF_wrk.band]
%endif
go4kVCF_func_peak:
%ifdef GO4K_USE_VCF_PEAK
test al, byte PEAK
jz short go4kVCF_func_processdone
fadd dword [WRK+go4kVCF_wrk.low]
fsub dword [WRK+go4kVCF_wrk.high]
%endif
go4kVCF_func_processdone:
%ifdef GO4K_USE_VCF_STEREO
test al, byte STEREO ; // outr inl
jz short go4kVCF_func_end
sub al, byte STEREO
sub WRK, go4kVCF_wrk.low2
jmp go4kVCF_func_stereoloop
%endif
go4kVCF_func_end: ; // value - - - -
ret
%ifdef USE_SECTIONS
section .g4kcodd code align=1
%else
section .text
%endif
; //----------------------------------------------------------------------------------------
; // DST Tick
; //----------------------------------------------------------------------------------------
; // IN : WRK = unit workspace
; // IN : VAL = unit values
; // IN : ecx = global workspace
; // OUT :
; // DIRTY : eax
; //----------------------------------------------------------------------------------------
export_func go4kDST_func@0
%ifdef GO4K_USE_DST
%ifdef GO4K_USE_DST_SH
%ifdef GO4K_USE_DST_STEREO
push 3
%else
push 2
%endif
%else
%ifdef GO4K_USE_DST_STEREO
push 2
%else
push 1
%endif
%endif
call go4kTransformValues
%ifdef GO4K_USE_DST_CHECK
; check if current note still active
mov eax, dword [ecx-4]
test eax, eax
jne go4kDST_func_do
ret
go4kDST_func_do:
%endif
movzx eax, byte [VAL-1] ; // get type flag
%ifdef GO4K_USE_DST_SH
fld dword [edx+go4kDST_val.snhfreq] ; // snh in
%ifdef GO4K_USE_DST_MOD_SH
fadd dword [WRK+go4kDST_wrk.sm] ; // snh' in
%endif
fmul st0, st0 ; // square the input so we never get negative and also have a smoother behaviour in the lower frequencies
fchs
fadd dword [WRK+go4kDST_wrk.snhphase]; // snh' in
fst dword [WRK+go4kDST_wrk.snhphase]
fldz ; // 0 snh' in
fucomip st1 ; // 0 snh' in
jc short go4kDST_func_hold
fld1 ; // 1 snh' in
faddp st1, st0 ; // 1+snh' in
fstp dword [WRK+go4kDST_wrk.snhphase]; // in
%endif
; // calc pregain and postgain
%ifdef GO4K_USE_DST_STEREO
test al, byte STEREO ; // outr inl
jz short go4kDST_func_mono
fxch st1 ; // inr inl
fld dword [edx+go4kDST_val.drive] ; // drive inr inl
%ifdef GO4K_USE_DST_MOD_DM
fadd dword [WRK+go4kDST_wrk.dm]
%endif
call go4kWaveshaper ; // outr inl
%ifdef GO4K_USE_DST_SH
fst dword [WRK+go4kDST_wrk.out2] ; // outr inl
%endif
fxch st1 ; // inl outr
go4kDST_func_mono:
%endif
fld dword [edx+go4kDST_val.drive] ; // drive in
%ifdef GO4K_USE_DST_MOD_DM
fadd dword [WRK+go4kDST_wrk.dm]
%endif
call go4kWaveshaper ; // out
%ifdef GO4K_USE_DST_SH
fst dword [WRK+go4kDST_wrk.out] ; // out'
%endif
ret ; // out'
%ifdef GO4K_USE_DST_SH
go4kDST_func_hold:
fstp st0 ; // in
fstp st0
%ifdef GO4K_USE_DST_STEREO
test al, byte STEREO ; // outr inl
jz short go4kDST_func_monohold
fld dword [WRK+go4kDST_wrk.out2] ; // out2
go4kDST_func_monohold:
%endif
fld dword [WRK+go4kDST_wrk.out] ; // out
ret
%endif
%endif
%ifdef USE_SECTIONS
section .g4kcodf code align=1
%else
section .text
%endif
; //----------------------------------------------------------------------------------------
; // DLL Tick
; //----------------------------------------------------------------------------------------
; // IN : WRK = unit workspace
; // IN : VAL = unit values
; // IN : ecx = global workspace
; // OUT :
; // DIRTY : eax
; //----------------------------------------------------------------------------------------
export_func go4kDLL_func@0
%ifdef GO4K_USE_DLL
%ifdef GO4K_USE_DLL_CHORUS
%ifdef GO4K_USE_DLL_DAMP
push 8
%else
push 7
%endif
%else
%ifdef GO4K_USE_DLL_DAMP
push 6
%else
push 5
%endif
%endif
call go4kTransformValues
pushad
movzx ebx, byte [VAL-(go4kDLL_val.size-go4kDLL_val.delay)/4] ;// delay length index
%ifdef GO4K_USE_DLL_NOTE_SYNC
test ebx, ebx
jne go4kDLL_func_process
fld1
fild dword [ecx-4] ; // load note freq
fmul dword [c_i12]
call _Power@0
fmul dword [FREQ_NORMALIZE] ; // normalize
fdivp st1, st0 ; // invert to get numer of samples
fistp word [_go4k_delay_times+ebx*2] ; store current comb size
%endif
go4kDLL_func_process:
mov ecx, eax ;// ecx is delay counter
%ifdef GO4K_USE_DLL_MOD
mov edi, WRK ;// edi is modulation workspace
%endif
mov WRK, dword [_go4k_delay_buffer_ofs] ;// ebp is current delay
fld st0 ;// in in
%ifdef GO4K_USE_DLL_MOD_IM
fld dword [edx+go4kDLL_val.dry] ;// dry in in
fadd dword [edi+go4kDLL_wrk2.im] ;// dry' in in
fmulp st1, st0 ;// out in
%else
fmul dword [edx+go4kDLL_val.dry] ;// out in
%endif
fxch ;// in out
%ifdef GO4K_USE_DLL_MOD_PM
fld dword [edx+go4kDLL_val.pregain] ;// pg in out
fadd dword [edi+go4kDLL_wrk2.pm] ;// pg' in out
fmul st0, st0 ;// pg'' in out
fmulp st1, st0 ;// in' out
%else
fmul dword [edx+go4kDLL_val.pregain] ;// in' out
fmul dword [edx+go4kDLL_val.pregain] ;// in' out
%endif
%ifdef GO4K_USE_DLL_CHORUS
;// update saw lfo for chorus/flanger
fld dword [edx+go4kDLL_val.freq] ;// f in' out
%ifdef GO4K_USE_DLL_MOD_SM
fadd dword [edi+go4kDLL_wrk2.sm] ;// f' in' out
%endif
fmul st0, st0
fmul st0, st0
fdiv dword [DLL_DEPTH]
fadd dword [WRK+go4kDLL_wrk.phase] ;// p' in' out
;// clamp phase to 0,1 (only in editor, since delay can be active quite long)
%ifdef GO4K_USE_DLL_CHORUS_CLAMP
fld1 ;// 1 p' in' out
fadd st1, st0 ;// 1 1+p' in' out
fxch ;// 1+p' 1 in' out
fprem ;// p'' 1 in' out
fstp st1 ;// p'' in' out
%endif
fst dword [WRK+go4kDLL_wrk.phase]
;// get current sine value
fldpi ; // pi p in' out
fadd st0 ; // 2*pi p in' out
fmulp st1, st0 ; // 2*pi*p in' out
fsin ; // sin in' out
fld1 ; // 1 sin in' out
faddp st1, st0 ; // 1+sin in' out
;// mul with depth and convert to samples
fld dword [edx+go4kDLL_val.depth] ; // d 1+sin in' out
%ifdef GO4K_USE_DLL_MOD_AM
fadd dword [edi+go4kDLL_wrk2.am] ; // d' 1+sin in' out
%endif
fmul st0, st0
fmul st0, st0
fmul dword [DLL_DEPTH]
fmulp st1, st0
fistp dword [esp-4] ; // in' out
%endif
go4kDLL_func_loop:
movzx esi, word [_go4k_delay_times+ebx*2] ; fetch comb size
mov eax, dword [WRK+go4kDLL_wrk.index] ;// eax is current comb index
%ifdef GO4K_USE_DLL_CHORUS
;// add lfo offset and wrap buffer
add eax, dword [esp-4]
cmp eax, esi
jl short go4kDLL_func_buffer_nowrap1
sub eax, esi
go4kDLL_func_buffer_nowrap1:
%endif
fld dword [WRK+eax*4+go4kDLL_wrk.buffer];// cout in' out
%ifdef GO4K_USE_DLL_CHORUS
mov eax, dword [WRK+go4kDLL_wrk.index]
%endif
;// add comb output to current output
fadd st2, st0 ;// cout in' out'
%ifdef GO4K_USE_DLL_DAMP
fld1 ;// 1 cout in' out'
fsub dword [edx+go4kDLL_val.damp] ;// 1-damp cout in' out'
%ifdef GO4K_USE_DLL_MOD_DM
fsub dword [edi+go4kDLL_wrk2.dm] ;// 1-damp' cout in' out'
%endif
fmulp st1, st0 ;// cout*d2 in' out'
fld dword [edx+go4kDLL_val.damp] ;// d1 cout*d2 in' out'
%ifdef GO4K_USE_DLL_MOD_DM
fadd dword [edi+go4kDLL_wrk2.dm] ;// d1' cout*d2 in' out'
%endif
fmul dword [WRK+go4kDLL_wrk.store] ;// store*d1 cout*d2 in' out'
faddp st1, st0 ;// store' in' out'
fst dword [WRK+go4kDLL_wrk.store] ;// store' in' out'
%endif
%ifdef GO4K_USE_DLL_MOD_FM
fld dword [edx+go4kDLL_val.feedback] ;// fb cout in' out'
fadd dword [edi+go4kDLL_wrk2.fm] ;// fb' cout in' out'
fmulp st1, st0 ;// cout*fb' in' out'
%else
fmul dword [edx+go4kDLL_val.feedback] ;// cout*fb in' out'
%endif
%ifdef GO4K_USE_DLL_DC_FILTER
fadd st0, st1 ;// store in' out'
fstp dword [WRK+eax*4+go4kDLL_wrk.buffer];// in' out'
%else
fsub st0, st1 ;// store in' out'
fstp dword [WRK+eax*4+go4kDLL_wrk.buffer];// in' out'
fneg
%endif
;// wrap comb buffer pos
inc eax
cmp eax, esi
jl short go4kDLL_func_buffer_nowrap2
%ifdef GO4K_USE_DLL_CHORUS
sub eax, esi
%else
xor eax, eax
%endif
go4kDLL_func_buffer_nowrap2:
mov dword [WRK+go4kDLL_wrk.index], eax
;// increment buffer pointer to next buffer
inc ebx ;// go to next delay length index
add WRK, go4kDLL_wrk.size ;// go to next delay
mov dword [_go4k_delay_buffer_ofs], WRK ;// store next delay offset
loopne go4kDLL_func_loop
fstp st0 ;// out'
;// process a dc filter to prevent heavy offsets in reverb
;// we're using the dc filter variables from the next delay line here, but doesnt hurt anyway
%ifdef GO4K_USE_DLL_DC_FILTER
; y(n) = x(n) - x(n-1) + R * y(n-1)
fld dword [WRK+go4kDLL_wrk.dcout] ;// dco out'
fmul dword [c_dc_const] ;// dcc*dco out'
fsub dword [WRK+go4kDLL_wrk.dcin] ;// dcc*dco-dci out'
fxch ;// out' dcc*dco-dci
fst dword [WRK+go4kDLL_wrk.dcin] ;// out' dcc*dco-dci
faddp st1 ;// out'
%ifdef GO4K_USE_UNDENORMALIZE
fadd dword [c_0_5] ;// add and sub small offset to prevent denormalization
fsub dword [c_0_5]
%endif
fst dword [WRK+go4kDLL_wrk.dcout]
%endif
popad
ret
%endif