File: modules.asm

package info (click to toggle)
komposter 0%2Bgit20201216%2Bds-2
  • links: PTS, VCS
  • area: main
  • in suites: bookworm
  • size: 4,716 kB
  • sloc: ansic: 15,586; sh: 4,176; asm: 642; makefile: 72
file content (489 lines) | stat: -rw-r--r-- 12,201 bytes parent folder | download | duplicates (2)
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
;;
;; Komposter player (c) 2010 Noora Halme et al. (see AUTHORS)
;;
;; This code is licensed under the MIT license:                             
;; http://www.opensource.org/licenses/mit-license.php
;;
;; This file contains all the implementations for the modules
;; used for sound synthesis. Note that some of the modules are
;; not fully optimized for size yet.
;;

;;
;; TODO: implement modules for supersaw and bitcrush
;;

;
; the synthesizer modules are always called with the registers set up as
; follows:
;
; flags      = cl
; voice	     = edx
; *data      = esi
; mod	     = eax (as integer)
;            = st4 (as single float)
; ms[0]      = st0
; ms[1]	     = st1	
; ms[2]      = st2
; ms[3]      = st3
;
; the module returns its output in st0.
;




; vco waveforms
%define         VCO_PULSE               0
%define         VCO_SAW                 1
%define         VCO_TRIANGLE            2
%define         VCO_SINE                3



; this is used to hard restart the accumulators on ADSR, VCO and LFO
; old flags in env are also zeroed on hard restart
hardrestart:
	test	cl, 1 		; module shifts its restart flag to lsb
	jz	.restart_out
	xor	ebx, ebx
	mov	[esi], ebx  	; clear accumulator
	mov	[esi+4], ebx	; clear subosc accumulator for vco
.restart_out:
	ret



; MODULE_FUNC(kbd)
module_func_kbd:
	fld	dword SONGBSS(pitch+edx*4) ; only place where channel nbr in edx is used
	ret



; MODULE_FUNC(env)
module_func_env:
	; st0=attack, st1=decay, st2=sustain, st3=release, st4=0
	; [esi] = accumulator, byte [esi+4]=old flags w/ trig
	push	cx
	shr	cl, 4
	call 	hardrestart
	pop	ax
.env_process:
	fld	dword [esi]	;accumulator to st0
	; st0=acc, st1=att, st2=dec, st3=sus, st4=rel, st5=0
	test	al, FLAG_GATE
	jz	.env_release
	; gate is up, was it down previously?
	mov 	cl, [esi+4]
	test 	cl, FLAG_GATE
	jnz 	.env_notrig;
	or 	al, FLAG_TRIG
.env_notrig:
	and 	cl, FLAG_TRIG
	or 	al, cl
	test	al, FLAG_TRIG
	jz	.env_decay_sustain
	fadd	st0, st1	;add attack rate to acc
	fld1
	fcomip	st1
	jnc	.env_done 	; return current acc
	fld1			; return 1.0 in st0
	and	al, 0xff-FLAG_TRIG	; trig off
	jmp	.env_done
.env_decay_sustain:
	fsub	st0, st2
	fcomi	st3		; compare to sustain
	jnc	.env_done
	fxch	st3		; return sustain level
	jmp	.env_done
.env_release:
	fsub	st0, st4	; dec by release
	fldz
	fcomip	st1
	jc	.env_done	; return current acc
	fldz			; return zero as acc was < 0
.env_done:
	fst	dword [esi]	;store new acc back and return as output
	mov	[esi+4], al ; store old flags and trig
	ret



; MODULE_FUNC(vco) {
module_func_vco: ; phase-accumulating oscillator w/ suboscillator
	shr	cl, 5
	call	hardrestart

	fld1				; 1.0 cv  pwm sub nse jnk
	fld	st1			; cv  1.0 cv  pwm sub nse jnk
	fadd	dword [esi]		; acc 1.0 cv  pwm sub nse jnk
	fprem				; acc 1.0 cv  pwm sub nse jnk
	fst	dword [esi]		;;; store main oscillator accumulator
	fstp	st6			; 1.0 cv  pwm sub nse acc
	fxch	st0, st1		; cv  1.0 pwm sub nse acc
	fmul	dword SONGDATA(half)    ; scv 1.0 pwm sub nse acc
	fadd	dword [esi+4]		; sac 1.0 pwm sub nse acc
	fprem				; sac 1.0 pwm sub nse acc
	fstp	dword [esi+4]		;;; store subosc accumulator
	fcomp	st0, st1		; pwm sub nse acc

	; modulator is already in eax
        ; fpu: pwm, sub, noise, acc
	cmp	al, VCO_PULSE
	jnz	.vco_saw
.vco_pulse:
	fcomi	st0, st3		; compare pwm vs accumulator
	fld1
	jc	.vco_oscdone
	fchs
.vco_saw:
	cmp	al, VCO_SAW
	jnz	.vco_triangle
	fld1
	fld	st4
	fadd	st0, st0
	fsubrp	st1, st0
.vco_triangle:
	cmp	al, VCO_TRIANGLE
	jnz	.vco_sine
	fld	st3
	fld	dword SONGDATA(threefourths)
	fcomip	st0, st1
	fmul	dword SONGDATA(four)	;4*acc
	jc	.vco_trilastq
	fld1
	jmp	.vco_tricalc
.vco_trilastq:
	fld	dword SONGDATA(five)
.vco_tricalc:
	fsubp	st1, st0	;acc*4 - [1 or 5]
	fabs
	fld1
	fsubrp	st1, st0	;1-fabs(acc*4 - [1 or 5])
.vco_sine:
	cmp	al, VCO_SINE
	jnz	.vco_oscdone
	; none of the others matched, so it has to be sine
	fldpi
	fadd	st0, st0
	fmul	st4
	fsin
.vco_oscdone:
	; osc pwm sub nse acc

.vco_sub:
	fld	dword [esi+4]	; sac osc pwm sub nse acc
	fcomip	st0, st2	; osc pwm sub nse acc
	fxch	st0, st2	; sub pwm osc nse acc
	jc	.vco_suboscdone	; jump depending on the fcomip
	fchs
.vco_suboscdone:
	; sub  pwm  osc  noise  acc
	faddp	st2, st0	; pwm out noise acc

	; noise
	;out=noise*(noise_x2*(2.0f/0xffffffff));
	fxch	st0, st2 ; noise  osc+sub  pwm  acc
	fadd	st0, st0 ; 2*noise  osc+sub  pwm  acc
	fild	dword SONGBSS(noise_x2)
	fmulp	st1, st0 ; 2*noise*x2   osc+sub  pwm acc
	fdiv 	dword SONGDATA(noise_div)
	faddp	st1, st0

	; done, st0 has output
	ret



; MODULE_FUNC(lfo) { // low-frequency oscillator
module_func_lfo:
	; st0=freq, st1=ampl, st2=bias, st3=0, st4=junk
	shr	cl, 6
	call	hardrestart
.lfo_osc:
	fadd	dword [esi]	; accu ampl bias 0.0  junk
	fld1			; 1.0  accu ampl bias 0.0  junk
	fxch 	st0, st1	; accu 1.0  ampl bias 0.0  junk
	fprem			; accu 1.0 ...
	fst	dword [esi]	
				;acc,  1.0,  ampl,  bias,  0,  junk
.lfo_triangle:
	and	al, al
	jz	.lfo_sine
	fadd	st0, st0	;2*acc	
	fcomi	st0, st1	;2acc  1.0  ampl  bias  0.0  junk
	jc	.lfo_done
	fchs			;-2acc ..
	fadd	st0, st1	;-2acc+1
	fadd	st0, st1	;-2acc+2
	jmp	.lfo_done
.lfo_sine:
 	fldpi
	fadd	st0, st0
	fmulp	st1, st0
	fcos
	fsub	st0, st1
	fmul 	dword SONGDATA(half)
	fchs
.lfo_done:
	; fpu should now be:
	; out,  1.0,  ampl,  bias,  0,  junk
	fmul	st0, st2
	fadd	st0, st3
	ret



; MODULE_FUNC(accent) { return accent[v] ? *mod : 0.0; }
; MODULE_FUNC(cv) { return *mod; }
; MODULE_FUNC(amp) { return ms[0]*ms[1]; }
module_func_accent:
	; st0 to st3 are zero
	test	cl, FLAG_ACCENT
	jnz	module_func_cv
module_func_amp:
	fmul	st1	; return 0*0 on accent, amp*input on amp
	ret
module_func_cv:
	fxch	st4	; return modulator in st0
	ret



; MODULE_FUNC(output) { return ms[0]*(*mod); }
; MODULE_FUNC(att) { return ms[0]*(*mod); }
module_func_output:
module_func_att:
	fmul	st4		; ms0 * mod
	ret



; MODULE_FUNC(mixer) { return ms[0]+ms[1]+ms[2]+ms[3]; }
module_func_mixer:
        ; s0 s1 s2 s3 mod
	faddp	st1  ; s0+s1  s2  s3  mod
	faddp	st1  ; s0+s1+s2  s3  mod
	faddp	st1  ; s0+s1+s2+s3  mod
	ret



; MODULE_FUNC(vcf) // 12db/oct resonant state variable low-/high-/bandpass filter
module_func_vcf:
	; esi = lp, esi+8 = bp, esi+4 = hp
	and	al, 3
	jz	.vcf_out	; filter is off, return unmodified input
	dec	al 		; 0=lp, 1=hp, 2=bp
	shl	al, 2

	; q = 1.0 - q  -- no resonance when res input is not connected
	fld1
	fsub	st0, st3
	fstp	st3
				; s      fc     q      0      0
	fstp	st3		; fc     q      s      0 
	fldpi            	; pi     fc     q      s      0
	fmulp 	st1        	; pi*fc  q      s      0
	fsin             	; sin(pi*fc) q  s      0
	fadd 	st0, st0    	; f      q      s      0
	fld	st0		; f      f      q      s      0
	fmul	dword [esi+8]	; f*bp   f      q      s      0
	fadd	dword [esi]	; lp+f*bp  f    q      s      0
	fst	dword [esi]	; lp     f      q      s      0
	fld	st2		; q      lp     f      q      s      0
	fsqrt			; r      lp     f      q      s      0
	fmul	st0, st4	; r*s    lp     f      q      s      0
	fsubrp	st1, st0	; r*s-lp f      q      s      0
	fld	st2		; q      r*s-lp f      q      s      0
	fmul	dword [esi+8]	; q*bp   r*s-lp f      q      s      0
	fsubp	st1, st0	; r*s-lp-q*bp, f, q    s      0
	fst	dword [esi+4]	; hp     f      q      s      0
	fmulp	st1, st0	; f*hp   q      s      0
	fadd	dword [esi+8]	; bp+f*hp q     s      0
	fstp	dword [esi+8]	; q      s      0
	fld 	dword [esi+eax] ; out    q      s      0
.vcf_out:
	ret



;MODULE_FUNC(lpf24) { // 24db/oct four-pole low pass
module_func_lpf24:
        ;   in      fc     q      0      0
        fxch    st0, st1
        fmul    dword [three_point_foureight] ; f=fc*3.48
        fld     st0  ;  f,   f, in, q, 0, 0
        fmul    st1  ;  f^2, f, ..
        fld     st0  ;  f^2, f^2, f, ...
        fmul    st1  ;  f^4, f^2, f, in, q, 0, 0
        fmul    dword [lpf_feedback_coef]
        fxch    st0, st1        ; f^2, 0.35013*f^4, f, in, q, 0, 0
        fmul    dword [minus_point_onefive]
        fld1
        faddp   st1, st0 ; 1.0-0.15*f^2, 0.3504*f^4, f, in, q, 0, 0
        fld     st4 ; q, ...
        fadd    st0 ; q*2, ...
        fadd    st0 ; q*4, 1.0-...
        fmulp   st1, st0 ; fb, 0.3504*f^4, f, in, q, 0, 0
        fmul    qword [esi+4*8]
        fchs
        fadd    st3
        fmulp   st1, st0
        fstp    qword [esi] ; feed pole 1
        ;f, in, q, 0, 0

        ; calculate all four poles
        ; p_inputs from esi+0, p_outputs from esi+8, p_states from esi+40
        xor     ecx, ecx
        mov     cl, 4
.lpf24_pole:
        ; st0 = f, esi = base offset for pole input
        fld1
        fsub    st0, st1       ; 1-f, f, in, q, 0, 0
        fmul    qword [esi+8]  ; (1-f)*p_out, f, in, q, 0, 0
        fld     dword [point_three]
        fmul    qword [esi+40] ; 0.3*p_state, (1-f)*p_out, f, in, q, 0, 0
        faddp   st1, st0       ; 0.3*p_state + (1-f)*p_out, f, in, q, 0, 0
        fld     qword [esi]    ; p_input, 0.3*p_state + (1-f)*p_out, f, in,q, 0, 0
        fst     qword [esi+40]
        faddp   st1, st0       ; p_input + 0.3*p_state + (1-f)*p_out, f, in, q, 0, 0
        fstp    qword [esi+8]
        add     esi, 8
        loop    .lpf24_pole
        fld     qword [esi] ; return pole 4 output
        ret




;MODULE_FUNC(delay) // interpolated comb/allpass filter delay
module_func_delay:
	; st0=in, st1=time, st2=loop, st3=fb
	push	eax
	mov	edi, [esi]
	cmp	edi, 0	; do we have a buffer already
	jnz	.delay_bufok
	movzx	eax, byte SONGBSS(delaycount)
	shl	eax, 18 ; = imul eax, eax, DELAYBUFFERSIZE
	lea	edi, SONGBSS(delaybuffer+eax*4)
	inc	byte SONGBSS(delaycount)
	mov	[esi], edi
.delay_bufok:
	mov	ebx, [esi+4]
	; edi is ptr to delay buffer
	; ebx is write offset

	mov	ecx, DELAYBUFFERSIZE	
;; ignoring the loop input for now
;	sub	esp, 4
;	fld	st2
;	fistp	dword [esp] ; loopend in samples
;	pop	ecx

;ret

	;  ptrdelta=(long)(ms[1]); // truncate fractional part
	;  spfrac=ms[1]-(float)(ptrdelta);
	sub	esp, 4
	fld	st1		; time  in   time  loop  fb
	fistp	dword [esp]
	fild	dword [esp]	; trunc(time)   in   time   loop  fb
	pop	edx ; ptrdelta
	fsubr	st0, st2	; spfrac  in  time  loop  fb

	;  readptr=(writeptr - ptrdelta);
	;  while (readptr<0) readptr+=loopend;
	mov	eax, ebx
	sub	eax, edx	; eax = readptr
	jns	.delay_inrange
	add	eax, ecx
.delay_inrange:

	; interpolation
	;  out=buffer[readptr]*spfrac;
	;  readptr++;
	;  readptr%=loopend;
	;  out+= buffer[readptr]*(1-spfrac);
	fld	dword [edi+eax*4]
	fmul	st0, st1	; out  spfrac  in  time  loop  fb
	inc	eax
	xor	edx, edx
	idiv	ecx		; edx = remainder from readptr/loopend
	fld1			;  1  out1  spfrac  in  time  loop  fb
	fsub	st0, st2	; 1-spfrac  out1  spfrac  in  time  loop  fb
	fmul	dword [edi+edx*4] ; out2  out1  spfrac  in  time  loop  fb
	faddp	st1, st0	; out  spfrac  in  time  loop  fb

	pop	eax
	and	al, al
	jz	.delay_comb 
	; allpass mode, do a feedforward
	; out+=ms[0]*(-ms[3]);
	fld	st2		; in  out  spfrac  in  time  loop  fb
	fmul	st6             ;in*fb ...
	fchs
	faddp	st1, st0
.delay_comb:

	;  buffer[writeptr]=ms[0] + out*ms[3]; 
	fld	st0		; out  out  spfrac  in  time  loop fb
	fmul	st0, st6	; out*fb  out  spfrac  in  time  loop  fb
	fadd	st0, st3	; in+out*fb  out  spfrac  in  time  loop  fb
	fstp	dword [edi+ebx*4]

	;  mod_ldata[1]=(writeptr+1)%loopend;
	inc	ebx
	mov	eax, ebx
	xor	edx, edx
	idiv	ecx
	mov	[esi+4], edx

	ret





; MODULD_FUNC(supersaw)  {  // jp8000-like detunable 7-sawtooth VCO
module_func_supersaw:
	; not yet implemented. requires some setup prior to playback.
	ret



module_func_resample:
	; st0=in, st1=rate, st2=0. st3=0, st4=0
	fxch	st0, st1	;rate, in, 0, 0, 0
	fld	dword [esi]	;acc, rate, in, 0, 0, 0
	fsubrp	st1, st0	;acc-rate, in, 0, 0, 0
	fst	dword [esi]
	fldz
	fcomip	st0, st1
	jc	.snh_out
	fxch	st0, st1	; in, acc-rate, 0, 0, 0
	fstp	dword [esi+4]	; sample the input
	fld1
	fstp	dword [esi]	; reset acc back to 1.0
.snh_out:
	fld	dword [esi+4]	; return held sample
	ret



; MODULE_FUNC(dist)  { // simple clipping distort, input 1 is amplification
module_func_dist:
	; in  amp  0.0  0.0  0.0
	fmul	st0, st1 ; in*amp  amp
	fld	st0	; out  out  amp
	fabs		; abs(out)  out  amp
	fxch	st0, st1
	fld1		; 1.0  out  abs(out)
	fcomip	st0, st2	;out  abs(out)
	jnc	.dist_noclip
	fdiv	st0, st1
.dist_noclip:
	ret