Browse code

Implemented sample-based synth for APU

Andrew Alderwick authored on 25/04/2021 14:12:45
Showing 5 changed files
... ...
@@ -21,7 +21,7 @@
21 21
 @knob [ &x $2 &y $2 &value $1 ]
22 22
 @head [ &pos $1 ]
23 23
 @track [ &active $1 &ch1 $20 &ch2 $20 &ch3 $20 &ch4 $20 ]
24
-@adsr [ &ch1a $1 &ch1d $1 &ch1s $1 &ch1r $1 &ch2a $1 &ch2d $1 &ch2s $1 &ch2r $1 &ch3a $1 &ch3d $1 &ch3s $1 &ch3r $1 &ch4a $1 &ch4d $1 &ch4s $1 &ch4r $1 ]
24
+@adsr [ &ch1 $2 &ch2 $2 &ch3 $2 &ch4 $2 ]
25 25
 @volume [ &ch1 $1 &ch2 $1 &ch3 $1 &ch4 $1 ]
26 26
 
27 27
 ( devices )
... ...
@@ -29,7 +29,7 @@
29 29
 |00 @System [ &vector $2 &pad $6 &r $2 &g $2 &b $2 ]
30 30
 |10 @Console [ &vector $2 &pad $6 &char $1 &byte $1 &short $2 &string $2 ]
31 31
 |20 @Screen [ &vector $2 &width $2 &height $2 &pad $2 &x $2 &y $2 &addr $2 &color $1 ]
32
-|30 @Audio [ &wave $2 &envelope $2 &pad $4 &volume $1 &pitch $1 &play $1 &value $2 &delay $2 &finish $1 ]
32
+|30 @Audio [ &pad $8 &adsr $2 &len $2 &addr $2 &volume $1 &pitch $1 ]
33 33
 |40 @Controller [ &vector $2 &button $1 &key $1 ]
34 34
 |60 @Mouse [ &vector $2 &x $2 &y $2 &state $1 &chord $1 ]
35 35
 |70 @File [ &vector $2 &success $2 &offset $2 &pad $2 &name $2 &length $2 &load $2 &save $2 ]
... ...
@@ -37,6 +37,7 @@
37 37
 ( vectors )
38 38
 
39 39
 |0100
40
+	@noise-wave
40 41
 
41 42
 	( theme ) #e0fa .System/r DEO2 #30fa .System/g DEO2 #30fa .System/b DEO2
42 43
 	( vectors ) ;on-screen .Screen/vector DEO2
... ...
@@ -55,10 +56,10 @@
55 56
 	.trkframe/x2 PEK2 .ctlframe/x2 POK2 .chnframe/y2 PEK2 .ctlframe/y2 POK2
56 57
 
57 58
 	( default settings )
58
-	;adsr-envelope .Audio/envelope DEO2
59
-	#00 .adsr/ch1a POK #40 .adsr/ch1d POK #80 .adsr/ch1s POK #c0 .adsr/ch1r POK #88 .volume/ch1 POK
60
-	#10 .adsr/ch2a POK #50 .adsr/ch2d POK #90 .adsr/ch2s POK #d0 .adsr/ch2r POK #88 .volume/ch2 POK
61
-	#20 .adsr/ch3a POK #60 .adsr/ch3d POK #a0 .adsr/ch3s POK #e0 .adsr/ch3r POK #88 .volume/ch3 POK
59
+	#048c .adsr/ch1 POK2 #88 .volume/ch1 POK
60
+	#159d .adsr/ch2 POK2 #88 .volume/ch2 POK
61
+	#26ae .adsr/ch3 POK2 #88 .volume/ch3 POK
62
+	#260e .adsr/ch4 POK2 #88 .volume/ch4 POK
62 63
 
63 64
 	.volume/ch3 PEK .Audio/volume DEO
64 65
 
... ...
@@ -120,9 +121,9 @@ BRK
120 121
 
121 122
 @play ( pitch -- )
122 123
 
123
-	#80 ORA .Audio/pitch DEO
124
-	;triangle-wave .Audio/wave DEO2
125
-	.track/active PEK .Audio/play DEO
124
+	;triangle-wave .Audio/addr DEO2
125
+	;triangle-wave/end ;triangle-wave SUB2 #0001 SFT2 .Audio/len DEO2
126
+	.Audio/pitch DEO
126 127
 
127 128
 RTN
128 129
 
... ...
@@ -154,21 +155,21 @@ BRK
154 155
 	
155 156
 	.Mouse/x DEI2 .ctlframe/x1 PEK2 SUB2 8- 8/ SWP POP #02 DIV
156 157
 	DUP #00 NEQ ,&no-a JNZ
157
-		;adsr #00 .track/active PEK #04 MUL ADD2 GET
158
+		.adsr .track/active PEK #02 MUL ADD PEK
158 159
 		#10 .Mouse/state DEI #10 EQU #e0 MUL ADD ADD
159
-		;adsr #00 .track/active PEK #04 MUL ADD2 PUT &no-a
160
+		.adsr .track/active PEK #02 MUL ADD POK &no-a
160 161
 	DUP #01 NEQ ,&no-d JNZ
161
-		;adsr #00 .track/active PEK #04 MUL ADD2 #0001 ADD2 GET
162
-		#10 .Mouse/state DEI #10 EQU #e0 MUL ADD ADD
163
-		;adsr #00 .track/active PEK #04 MUL ADD2 #0001 ADD2 PUT &no-d
162
+		.adsr .track/active PEK #02 MUL ADD PEK
163
+		DUP #f0 AND STH #01 .Mouse/state DEI #10 EQU #0e MUL ADD ADD #0f AND STHr ADD
164
+		.adsr .track/active PEK #02 MUL ADD POK &no-d
164 165
 	DUP #02 NEQ ,&no-s JNZ
165
-		;adsr #00 .track/active PEK #04 MUL ADD2 #0002 ADD2 GET
166
+		.adsr .track/active PEK #02 MUL ADD #01 ADD PEK
166 167
 		#10 .Mouse/state DEI #10 EQU #e0 MUL ADD ADD
167
-		;adsr #00 .track/active PEK #04 MUL ADD2 #0002 ADD2 PUT &no-s
168
+		.adsr .track/active PEK #02 MUL ADD #01 ADD POK &no-s
168 169
 	DUP #03 NEQ ,&no-r JNZ
169
-		;adsr #00 .track/active PEK #04 MUL ADD2 #0003 ADD2 GET
170
-		#10 .Mouse/state DEI #10 EQU #e0 MUL ADD ADD
171
-		;adsr #00 .track/active PEK #04 MUL ADD2 #0003 ADD2 PUT &no-r
170
+		.adsr .track/active PEK #02 MUL ADD #01 ADD PEK
171
+		DUP #f0 AND STH #01 .Mouse/state DEI #10 EQU #0e MUL ADD ADD #0f AND STHr ADD
172
+		.adsr .track/active PEK #02 MUL ADD #01 ADD POK &no-r
172 173
 	DUP #05 NEQ ,&no-left JNZ
173 174
 		;volume #00 .track/active PEK ADD2 GET
174 175
 		#10 .Mouse/state DEI #10 EQU #e0 MUL ADD ADD
... ...
@@ -190,30 +191,44 @@ BRK
190 191
 	DUP #ff NEQ ,&skip1 JNZ
191 192
 		POP ,&listen2 JMP
192 193
 	&skip1
193
-	#00 SWP ;notes ADD2 GET #80 ORA .Audio/pitch DEO
194
+	.adsr/ch1 PEK2 .Audio/adsr DEO2
194 195
 	.volume/ch1 PEK .Audio/volume DEO
195
-	;square-wave .Audio/wave DEO2
196
-	#00 .Audio/play DEO
196
+	;square-wave .Audio/addr DEO2
197
+	;square-wave/end ;square-wave SUB2 #0001 SFT2 .Audio/len DEO2
198
+	#00 SWP ;notes ADD2 GET .Audio/pitch DEO
197 199
 	&listen2
198 200
 	;track/ch2 #00 .head/pos PEK #08 DIV ADD2 GET
199 201
 	#01 SUB
200 202
 	DUP #ff NEQ ,&skip2 JNZ
201 203
 		POP ,&listen3 JMP
202 204
 	&skip2
203
-	#00 SWP ;notes ADD2 GET #80 ORA .Audio/pitch DEO
205
+	.adsr/ch2 PEK2 .Audio/adsr DEO2
204 206
 	.volume/ch2 PEK .Audio/volume DEO
205
-	;square-wave .Audio/wave DEO2
206
-	#01 .Audio/play DEO
207
+	;triangle-wave .Audio/addr DEO2
208
+	;triangle-wave/end ;triangle-wave SUB2 #0001 SFT2 .Audio/len DEO2
209
+	#00 SWP ;notes ADD2 GET .Audio/pitch DEO
207 210
 	&listen3
208 211
 	;track/ch3 #00 .head/pos PEK #08 DIV ADD2 GET
209 212
 	#01 SUB
210 213
 	DUP #ff NEQ ,&skip3 JNZ
211
-		POP ,&end JMP
214
+		POP ,&listen4 JMP
212 215
 	&skip3
213
-	#00 SWP ;notes ADD2 GET #80 ORA .Audio/pitch DEO
216
+	.adsr/ch3 PEK2 .Audio/adsr DEO2
214 217
 	.volume/ch3 PEK .Audio/volume DEO
215
-	;triangle-wave .Audio/wave DEO2
216
-	#02 .Audio/play DEO
218
+	;sine-wave .Audio/addr DEO2
219
+	;sine-wave/end ;sine-wave SUB2 #0001 SFT2 .Audio/len DEO2
220
+	#00 SWP ;notes ADD2 GET .Audio/pitch DEO
221
+	&listen4
222
+	;track/ch4 #00 .head/pos PEK #08 DIV ADD2 GET
223
+	#01 SUB
224
+	DUP #ff NEQ ,&skip4 JNZ
225
+		POP ,&end JMP
226
+	&skip4
227
+	#0000 .Audio/adsr DEO2
228
+	.volume/ch4 PEK .Audio/volume DEO
229
+	;noise-wave .Audio/addr DEO2
230
+	;noise-wave-end ;noise-wave SUB2 #0001 SFT2 .Audio/len DEO2
231
+	#00 SWP ;notes ADD2 GET #80 ORA .Audio/pitch DEO
217 232
 	&end
218 233
 
219 234
 RTN
... ...
@@ -319,14 +334,14 @@ RTN
319 334
 	.trkframe/y1 PEK2 #0038 ADD2 ;draw-octave JSR2
320 335
 	.trkframe/x1 PEK2 #0028 SUB2 .Screen/x DEO2
321 336
 	.trkframe/y1 PEK2 #0030 ADD2 .Screen/y DEO2
322
-	;font_hex #0028 ADD2 .Screen/addr DEO2
337
+	;font_hex #0020 ADD2 .Screen/addr DEO2
323 338
 	#23 .Screen/color DEO
324 339
 	.trkframe/x1 PEK2 #0030 SUB2 .Screen/x DEO2
325 340
 	;font_hex #0060 ADD2 .Screen/addr DEO2
326 341
 	#23 .Screen/color DEO
327 342
 	.trkframe/x1 PEK2 #0028 SUB2 .Screen/x DEO2
328 343
 	.trkframe/y1 PEK2 #0068 ADD2 .Screen/y DEO2
329
-	;font_hex #0020 ADD2 .Screen/addr DEO2
344
+	;font_hex #0018 ADD2 .Screen/addr DEO2
330 345
 	#23 .Screen/color DEO
331 346
 	.trkframe/x1 PEK2 #0030 SUB2 .Screen/x DEO2
332 347
 	;font_hex #0060 ADD2 .Screen/addr DEO2
... ...
@@ -342,8 +357,11 @@ RTN
342 357
 RTN
343 358
 
344 359
 @draw-knob ( x* y* value -- )
345
-	
360
+	.track/active PEK #03 EQU ;&blank JNZ2
361
+
362
+	&force
346 363
 	( load ) .knob/value POK .knob/y POK2 .knob/x POK2
364
+
347 365
 	.knob/x PEK2 .Screen/x DEO2
348 366
 	.knob/y PEK2 .Screen/y DEO2 ;knob_icns .Screen/addr DEO2 #21 .Screen/color DEO
349 367
 	.knob/x PEK2 8+ .Screen/x DEO2 ;knob_icns 8+ .Screen/addr DEO2 #21 .Screen/color DEO
... ...
@@ -360,31 +378,45 @@ RTN
360 378
 
361 379
 RTN
362 380
 
381
+	&blank
382
+
383
+	( load ) .knob/value POK .knob/y POK2 .knob/x POK2
384
+
385
+	.knob/x PEK2 .Screen/x DEO2
386
+	.knob/y PEK2 .Screen/y DEO2 #20 .Screen/color DEO
387
+	.knob/x PEK2 8+ .Screen/x DEO2 #20 .Screen/color DEO
388
+	.knob/y PEK2 8+ .Screen/y DEO2 #20 .Screen/color DEO
389
+	.knob/x PEK2 .Screen/x DEO2 #20 .Screen/color DEO
390
+	.knob/x PEK2 #0004 ADD2 .Screen/x DEO2
391
+	.knob/y PEK2 #0010 ADD2 .Screen/y DEO2
392
+	#20 .Screen/color DEO
393
+RTN
394
+
363 395
 @draw-controls ( -- )
364 396
 	
365 397
 	.ctlframe/x1 PEK2 .ctlframe/y1 PEK2 .ctlframe/x2 PEK2 .ctlframe/y2 PEK2 #01 ;line-rect JSR2
366 398
 	( env )
367 399
 	.ctlframe/x1 PEK2 8+ .ctlframe/y1 PEK2 8+ #22 ;env_txt ;draw-label JSR2
368 400
 	.ctlframe/x1 PEK2 8+ .ctlframe/y1 PEK2 #0010 ADD2
369
-		;adsr #00 .track/active PEK #04 MUL ADD2 GET #04 SFT
401
+		.adsr .track/active PEK #02 MUL ADD PEK #04 SFT
370 402
 		;draw-knob JSR2
371 403
 	.ctlframe/x1 PEK2 #0018 ADD2 .ctlframe/y1 PEK2 #0010 ADD2
372
-		;adsr #00 .track/active PEK #04 MUL ADD2 #0001 ADD2 GET #04 SFT
404
+		.adsr .track/active PEK #02 MUL ADD PEK #0f AND
373 405
 		;draw-knob JSR2
374 406
 	.ctlframe/x1 PEK2 #0028 ADD2 .ctlframe/y1 PEK2 #0010 ADD2
375
-		;adsr #00 .track/active PEK #04 MUL ADD2 #0002 ADD2 GET #04 SFT
407
+		.adsr .track/active PEK #02 MUL ADD #01 ADD PEK #04 SFT
376 408
 		;draw-knob JSR2
377 409
 	.ctlframe/x1 PEK2 #0038 ADD2 .ctlframe/y1 PEK2 #0010 ADD2
378
-		;adsr #00 .track/active PEK #04 MUL ADD2 #0003 ADD2 GET #04 SFT
410
+		.adsr .track/active PEK #02 MUL ADD #01 ADD PEK #0f AND
379 411
 		;draw-knob JSR2
380 412
 	( vol )
381 413
 	.ctlframe/x1 PEK2 #0058 ADD2 .ctlframe/y1 PEK2 8+ #22 ;vol_txt ;draw-label JSR2
382 414
 	.ctlframe/x1 PEK2 #0058 ADD2 .ctlframe/y1 PEK2 #0010 ADD2
383 415
 		;volume #00 .track/active PEK ADD2 GET #04 SFT
384
-	;draw-knob JSR2
416
+	;draw-knob/force JSR2
385 417
 	.ctlframe/x1 PEK2 #0068 ADD2 .ctlframe/y1 PEK2 #0010 ADD2
386 418
 		;volume #00 .track/active PEK ADD2 GET #0f AND
387
-	;draw-knob JSR2
419
+	;draw-knob/force JSR2
388 420
 
389 421
 RTN
390 422
 
... ...
@@ -469,31 +501,10 @@ RTN
469 501
 
470 502
 RTN
471 503
 
472
-@adsr-envelope ( -- )
473
-	#ff ;adsr #00 .Audio/play DEI #04 MUL ADD2 GET SOUND
474
-	#80 ;adsr #00 .Audio/play DEI #04 MUL ADD2 #0001 ADD2 GET SOUND
475
-	#80 ;adsr #00 .Audio/play DEI #04 MUL ADD2 #0002 ADD2 GET SOUND
476
-	#00 ;adsr #00 .Audio/play DEI #04 MUL ADD2 #0003 ADD2 GET SOUND
477
-	SOUND_FINISH
478
-	BRK
479
-
480
-@square-wave ( -- )
481
-	#a800 SOUND
482
-	#a880 SOUND
483
-	#5800 SOUND
484
-	#5880 SOUND
485
-	BRK
486
-
487
-@triangle-wave ( -- )
488
-	#ff40 SOUND
489
-	#0080 SOUND
490
-	#8040 SOUND
491
-	BRK
492
-
493
-@ch1_txt [ "CHN0 00 ]
494
-@ch2_txt [ "CHN1 00 ]
495
-@ch3_txt [ "CHN2 00 ]
496
-@ch4_txt [ "---- 00 ]
504
+@ch1_txt [ "SQR  00 ]
505
+@ch2_txt [ "TRI  00 ]
506
+@ch3_txt [ "SINE 00 ]
507
+@ch4_txt [ "DRUM 00 ]
497 508
 @env_txt [ "Envelope 00 ]
498 509
 @vol_txt [ "Volume 00 ]
499 510
 
... ...
@@ -588,4 +599,62 @@ RTN
588 599
 	0010 103c 1010 0c00 0000 4444 4444 3800 0000 4444 2828 1000 0000 4454 5454 2800
589 600
 	0000 4428 1028 4400 0000 4444 443c 0438 0000 7c08 1020 7c00 000c 0810 1008 0c00
590 601
 	0008 0808 0808 0800 0030 1008 0810 3000 0000 0032 4c00 0000 3c42 99a1 a199 423c
591
-]
592 602
\ No newline at end of file
603
+]
604
+
605
+@square-wave
606
+	a800 5800
607
+	&end
608
+
609
+@triangle-wave
610
+	8000 81ff 83ff 85ff 87ff 89ff 8bff 8dff 8fff 91ff 93ff 95ff 97ff
611
+	99ff 9bff 9dff 9fff a1ff a3ff a5ff a7ff a9ff abff adff afff b1ff
612
+	b3ff b5ff b7ff b9ff bbff bdff bfff c1ff c3ff c5ff c7ff c9ff cbff
613
+	cdff cfff d1ff d3ff d5ff d7ff d9ff dbff ddff dfff e1ff e3ff e5ff
614
+	e7ff e9ff ebff edff efff f1ff f3ff f5ff f7ff f9ff fbff fdff ffff
615
+	fdff fbff f9ff f7ff f5ff f3ff f1ff efff edff ebff e9ff e7ff e5ff
616
+	e3ff e1ff dfff ddff dbff d9ff d7ff d5ff d3ff d1ff cfff cdff cbff
617
+	c9ff c7ff c5ff c3ff c1ff bfff bdff bbff b9ff b7ff b5ff b3ff b1ff
618
+	afff adff abff a9ff a7ff a5ff a3ff a1ff 9fff 9dff 9bff 99ff 97ff
619
+	95ff 93ff 91ff 8fff 8dff 8bff 89ff 87ff 85ff 83ff 81ff 8000 7e00
620
+	7c00 7a00 7800 7600 7400 7200 7000 6e00 6c00 6a00 6800 6600 6400
621
+	6200 6000 5e00 5c00 5a00 5800 5600 5400 5200 5000 4e00 4c00 4a00
622
+	4800 4600 4400 4200 4000 3e00 3c00 3a00 3800 3600 3400 3200 3000
623
+	2e00 2c00 2a00 2800 2600 2400 2200 2000 1e00 1c00 1a00 1800 1600
624
+	1400 1200 1000 0e00 0c00 0a00 0800 0600 0400 0200 0001 0200 0400
625
+	0600 0800 0a00 0c00 0e00 1000 1200 1400 1600 1800 1a00 1c00 1e00
626
+	2000 2200 2400 2600 2800 2a00 2c00 2e00 3000 3200 3400 3600 3800
627
+	3a00 3c00 3e00 4000 4200 4400 4600 4800 4a00 4c00 4e00 5000 5200
628
+	5400 5600 5800 5a00 5c00 5e00 6000 6200 6400 6600 6800 6a00 6c00
629
+	6e00 7000 7200 7400 7600 7800 7a00 7c00 7e00
630
+	&end
631
+
632
+@sine-wave
633
+	8000 84c5 8989 8e49 9305 97b9 9c66 a108 a59e aa27 aea1 b30a b761
634
+	bba5 bfd3 c3ea c7ea cbcf cf9a d349 d6d9 da4b dd9d e0cd e3db e6c6
635
+	e98b ec2c eea6 f0f8 f322 f523 f6fb f8a8 fa2a fb81 fcac fdaa fe7c
636
+	ff20 ff98 ffe2 fffe ffed ffae ff42 fea9 fde3 fcef fbd0 fa84 f90c
637
+	f76a f59d f3a6 f186 ef3e ecce ea37 e77b e499 e194 de6c db23 d7b9
638
+	d430 d088 ccc5 c8e6 c4ed c0db bcb2 b874 b422 afbd ab47 a6c1 a22e
639
+	9d8f 98e5 9433 8f79 8ab9 85f6 8131 7c6b 77a7 72e5 6e28 6972 64c4
640
+	601f 5b85 56f9 527b 4e0e 49b2 4569 4136 3d18 3913 3527 3155 2d9f
641
+	2a07 268d 2333 1ffb 1ce4 19f1 1721 1478 11f4 0f98 0d64 0b58 0976
642
+	07be 0631 04d0 039a 0290 01b4 0104 0081 002c 0004 0009 003d 009d
643
+	012b 01e7 02cf 03e3 0524 0691 0828 09eb 0bd7 0ded 102b 1292 151f
644
+	17d2 1aaa 1da6 20c6 2407 2769 2aea 2e8a 3247 361f 3a12 3e1d 4240
645
+	4679 4ac7 4f27 5399 581b 5cab 6147 65ee 6a9f 6f57 7415 78d8 7d9d
646
+	8262 8727 8bea 90a8 9560 9a11 9eb8 a354 a7e4 ac66 b0d8 b538 b986
647
+	bdbf c1e2 c5ed c9e0 cdb8 d175 d515 d896 dbf8 df39 e259 e555 e82d
648
+	eae0 ed6d efd4 f212 f428 f614 f7d7 f96e fadb fc1c fd30 fe18 fed4
649
+	ff62 ffc2 fff6 fffb ffd3 ff7e fefb fe4b fd6f fc65 fb2f f9ce f841
650
+	f689 f4a7 f29b f067 ee0b eb87 e8de e60e e31b e004 dccc d972 d5f8
651
+	d260 ceaa cad8 c6ec c2e7 bec9 ba96 b64d b1f1 ad84 a906 a47a 9fe0
652
+	9b3b 968d 91d7 8d1a 8858 8394 7ece 7a09 7546 7086 6bcc 671a 6270
653
+	5dd1 593e 54b8 5042 4bdd 478b 434d 3f24 3b12 3719 333a 2f77 2bcf
654
+	2846 24dc 2193 1e6b 1b66 1884 15c8 1331 10c1 0e79 0c59 0a62 0895
655
+	06f3 057b 042f 0310 021c 0156 00bd 0051 0012 0001 001d 0067 00df
656
+	0183 0255 0353 047e 05d5 0757 0904 0adc 0cdd 0f07 1159 13d3 1674
657
+	1939 1c24 1f32 2262 25b4 2926 2cb6 3065 3430 3815 3c15 402c 445a
658
+	489e 4cf5 515e 55d8 5a61 5ef7 6399 6846 6cfa 71b6 7676 7b3a
659
+	&end
660
+
661
+@noise-wave-end
... ...
@@ -9,7 +9,7 @@
9 9
 
10 10
 |00 @System     [ &vector $2 &pad    $6 &r      $2 &g     $2 &b      $2 ]
11 11
 |20 @Screen     [ &vector $2 &width  $2 &height $2 &pad   $2 &x      $2 &y    $2 &addr  $2 &color $1 ]
12
-|30 @Audio     	[ &wave $2   &env    $2 &pad    $4 &vol   $1 &pitch  $1 &play $1 &value $2 &delay $2 &finish $1 ]
12
+|30 @Audio      [ &pad $8 &adsr $2 &len $2 &addr $2 &volume $1 &pitch $1 ]
13 13
 
14 14
 |0100 ( -> )
15 15
 	
... ...
@@ -19,9 +19,10 @@
19 19
 	#0ff0 .System/b DEO2 
20 20
 
21 21
 	;on-frame .Screen/vector DEO2 ( run on-frame every 1/60th of a second )
22
-	#ff .Audio/vol DEO            ( set volume to max )
23
-	;saw .Audio/wave DEO2         ( set waveform to saw for audio engine )
24
-	;env .Audio/env DEO2          ( set envelope for audio engine )
22
+	#ff .Audio/volume DEO         ( set volume to max )
23
+	;saw .Audio/addr DEO2         ( set waveform to saw for audio engine )
24
+	;saw/end ;saw SUB2 #0002 SFT2 .Audio/len DEO2
25
+	#1202 .Audio/adsr DEO2        ( set envelope for audio engine )
25 26
 
26 27
 BRK
27 28
 
... ...
@@ -34,8 +35,7 @@ BRK
34 35
 	;melody #00 .progress PEK ADD2 GET 
35 36
 
36 37
 	( play note )
37
-	DUP #80 ORA .Audio/pitch DEO
38
-	#01 .Audio/play DEO
38
+	DUP .Audio/pitch DEO
39 39
 
40 40
 	( erase last note )
41 41
 	#20  .Screen/color DEO
... ...
@@ -59,23 +59,39 @@ BRK
59 59
 ( defines a sawtooth wave. )
60 60
 
61 61
 @saw ( -> )
62
-
63
-	#6000 .Audio/value DEO2
64
-	#0000 .Audio/delay DEO2 ( move to volume #600 after 0 delay )
65
-	#0000 .Audio/value DEO2
66
-	#ffff .Audio/delay DEO2 ( reach volume 0 after the whole note. Interpolated linearly )
67
-
68
-BRK
69
-
70
-( defines an envelope )
71
-
72
-@env ( -> )
73
-
74
-	#ffff .Audio/value DEO2
75
-	#1000 .Audio/delay DEO2 ( move pretty quickly to volume #ffff (maximum) ) 
76
-	#0000 .Audio/value DEO2
77
-	#4000 .Audio/delay DEO2 ( interpolating linearly, move to #0000 after a delay of #4000 where #8000 is half a second )
78
-	#00   .Audio/finish DEO ( end the envelope )
62
+	dfa0 df40 dee0 de80 de20 ddc0 dd60 dd00
63
+	dca0 dc40 dbe0 db80 db20 dac0 da60 da00
64
+	d9a0 d940 d8e0 d880 d820 d7c0 d760 d700
65
+	d6a0 d640 d5e0 d580 d520 d4c0 d460 d400
66
+	d3a0 d340 d2e0 d280 d220 d1c0 d160 d100
67
+	d0a0 d040 cfe0 cf80 cf20 cec0 ce60 ce00
68
+	cda0 cd40 cce0 cc80 cc20 cbc0 cb60 cb00
69
+	caa0 ca40 c9e0 c980 c920 c8c0 c860 c800
70
+	c7a0 c740 c6e0 c680 c620 c5c0 c560 c500
71
+	c4a0 c440 c3e0 c380 c320 c2c0 c260 c200
72
+	c1a0 c140 c0e0 c080 c020 bfc0 bf60 bf00
73
+	bea0 be40 bde0 bd80 bd20 bcc0 bc60 bc00
74
+	bba0 bb40 bae0 ba80 ba20 b9c0 b960 b900
75
+	b8a0 b840 b7e0 b780 b720 b6c0 b660 b600
76
+	b5a0 b540 b4e0 b480 b420 b3c0 b360 b300
77
+	b2a0 b240 b1e0 b180 b120 b0c0 b060 b000
78
+	afa0 af40 aee0 ae80 ae20 adc0 ad60 ad00
79
+	aca0 ac40 abe0 ab80 ab20 aac0 aa60 aa00
80
+	a9a0 a940 a8e0 a880 a820 a7c0 a760 a700
81
+	a6a0 a640 a5e0 a580 a520 a4c0 a460 a400
82
+	a3a0 a340 a2e0 a280 a220 a1c0 a160 a100
83
+	a0a0 a040 9fe0 9f80 9f20 9ec0 9e60 9e00
84
+	9da0 9d40 9ce0 9c80 9c20 9bc0 9b60 9b00
85
+	9aa0 9a40 99e0 9980 9920 98c0 9860 9800
86
+	97a0 9740 96e0 9680 9620 95c0 9560 9500
87
+	94a0 9440 93e0 9380 9320 92c0 9260 9200
88
+	91a0 9140 90e0 9080 9020 8fc0 8f60 8f00
89
+	8ea0 8e40 8de0 8d80 8d20 8cc0 8c60 8c00
90
+	8ba0 8b40 8ae0 8a80 8a20 89c0 8960 8900
91
+	88a0 8840 87e0 8780 8720 86c0 8660 8600
92
+	85a0 8540 84e0 8480 8420 83c0 8360 8300
93
+	82a0 8240 81e0 8180 8120 80c0 8060 8000
94
+	&end
79 95
 
80 96
 BRK
81 97
 
... ...
@@ -13,87 +13,69 @@ WITH REGARD TO THIS SOFTWARE.
13 13
 #include "uxn.h"
14 14
 #include "apu.h"
15 15
 
16
-static Uint32 note_advances[12] = {
17
-	0x82d01286 / (SAMPLE_FREQUENCY / 30), /* C7 */
18
-	0x8a976073 / (SAMPLE_FREQUENCY / 30),
19
-	0x92d5171d / (SAMPLE_FREQUENCY / 30), /* D7 */
20
-	0x9b904100 / (SAMPLE_FREQUENCY / 30),
21
-	0xa4d053c8 / (SAMPLE_FREQUENCY / 30), /* E7 */
22
-	0xae9d36b0 / (SAMPLE_FREQUENCY / 30), /* F7 */
23
-	0xb8ff493e / (SAMPLE_FREQUENCY / 30),
24
-	0xc3ff6a72 / (SAMPLE_FREQUENCY / 30), /* G7 */
25
-	0xcfa70054 / (SAMPLE_FREQUENCY / 30),
26
-	0xdc000000 / (SAMPLE_FREQUENCY / 30), /* A7 */
27
-	0xe914f623 / (SAMPLE_FREQUENCY / 30),
28
-	0xf6f11003 / (SAMPLE_FREQUENCY / 30) /* B7 */
16
+#define NOTE_PERIOD 0x10000
17
+#define ADSR_STEP (SAMPLE_FREQUENCY / 0xf)
18
+
19
+/* clang-format off */
20
+
21
+static Uint32 advances[12] = {
22
+	0x80000, 0x879c8, 0x8facd, 0x9837f, 0xa1451, 0xaadc1,
23
+	0xb504f, 0xbfc88, 0xcb2ff, 0xd7450, 0xe411f, 0xf1a1c
29 24
 };
30 25
 
31
-static void
32
-render_note(Apu *apu, Uxn *u, int note_i, Sint16 *samples, int n_samples)
26
+/* clang-format on */
27
+
28
+static Sint32
29
+envelope(Apu *c, Uint32 age)
33 30
 {
34
-	int i;
35
-	Note *note = &apu->notes[note_i];
36
-	while(n_samples--) {
37
-		Sint32 sample = 1;
38
-		for(i = 0; i < 2; ++i) {
39
-			WaveformGenerator *wv = &note->wv[i];
40
-			apu->queue = &wv->queue;
41
-			wv->count += wv->advance;
42
-			while(wv->count > wv->period) {
43
-				wv->count -= wv->period;
44
-				wv->start_value = wv->end_value;
45
-				if(apu->queue->i == apu->queue->n) {
46
-					apu->queue->i = apu->queue->n = 0;
47
-					if(!apu->queue->finishes) {
48
-						*apu->channel_ptr = note_i;
49
-						evaluxn(u, wv->vector);
50
-					}
51
-				}
52
-				if(!apu->queue->n) {
53
-					note->playing = 0;
54
-					return;
55
-				}
56
-				wv->end_value = (Sint16)apu->queue->dat[apu->queue->i++];
57
-				wv->period = (30 << 4) * apu->queue->dat[apu->queue->i++];
58
-			}
59
-			if(wv->period >> 9)
60
-				sample *= wv->start_value + (Sint32)(wv->end_value - wv->start_value) * (Sint32)(wv->count >> 10) / (Sint32)(wv->period >> 10);
61
-			else
62
-				sample *= wv->end_value;
63
-		}
64
-		for(i = 0; i < 2; ++i)
65
-			*(samples++) += sample / 0xf * note->volume[i] / 0x10000;
66
-	}
31
+	if(!c->r) return 0x0888;
32
+	if(age < c->a) return 0x0888 * age / c->a;
33
+	if(age < c->d) return 0x0444 * (2 * c->d - c->a - age) / (c->d - c->a);
34
+	if(age < c->s) return 0x0444;
35
+	if(age < c->r) return 0x0444 * (c->r - age) / (c->r - c->s);
36
+	c->advance = 0;
37
+	return 0x0000;
67 38
 }
68 39
 
69 40
 void
70
-apu_render(Apu *apu, Uxn *u, Sint16 *samples, int n_samples)
41
+apu_render(Apu *c, Sint16 *sample, Sint16 *end)
71 42
 {
72
-	int i;
73
-	for(i = 0; i < n_samples * 2; ++i)
74
-		samples[i] = 0;
75
-	for(i = 0; i < apu->n_notes; ++i)
76
-		if(apu->notes[i].playing) render_note(apu, u, i, samples, n_samples);
77
-	apu->queue = NULL;
43
+	Sint32 s;
44
+	if(!c->advance || !c->period) return;
45
+	while(sample < end) {
46
+		c->count += c->advance;
47
+		c->i += c->count / c->period;
48
+		c->count %= c->period;
49
+		if(c->i >= c->len) {
50
+			if(!c->repeat) {
51
+				c->advance = 0;
52
+				return;
53
+			}
54
+			c->i %= c->len;
55
+		}
56
+		s = (Sint16)(mempeek16(c->addr, c->i * 2) + 0x8000) * envelope(c, c->age++);
57
+		*sample++ += s * c->volume_l / 0x8000;
58
+		*sample++ += s * c->volume_r / 0x8000;
59
+	}
78 60
 }
79 61
 
80 62
 void
81
-apu_play_note(Note *note, Uint16 wave_vector, Uint16 envelope_vector, Uint8 volume, Uint8 pitch, Uint8 impl)
63
+apu_start(Apu *c, Uint16 adsr, Uint8 pitch)
82 64
 {
83
-	int i;
84
-	if(pitch >= 108 || impl == 0) return;
85
-	note->playing = 1;
86
-	note->impl = impl;
87
-	for(i = 0; i < 2; ++i) {
88
-		note->volume[i] = 0xf & (volume >> 4 * (1 - i));
89
-		note->wv[i].count = note->wv[i].period = 0;
90
-		note->wv[i].end_value = 0x8000 * (1 - i);
91
-		note->wv[i].queue.n = note->wv[i].queue.i = 0;
92
-		note->wv[i].queue.finishes = 0;
93
-		note->wv[i].queue.is_envelope = i;
65
+	if(pitch < 108 && c->len)
66
+		c->advance = advances[pitch % 12] >> (8 - pitch / 12);
67
+	else {
68
+		c->advance = 0;
69
+		return;
94 70
 	}
95
-	note->wv[0].vector = wave_vector;
96
-	note->wv[0].advance = note_advances[pitch % 12] >> (8 - pitch / 12);
97
-	note->wv[1].vector = envelope_vector;
98
-	note->wv[1].advance = (30 << 20) / SAMPLE_FREQUENCY;
71
+	c->a = ADSR_STEP * (adsr >> 12);
72
+	c->d = ADSR_STEP * (adsr >> 8 & 0xf) + c->a;
73
+	c->s = ADSR_STEP * (adsr >> 4 & 0xf) + c->d;
74
+	c->r = ADSR_STEP * (adsr >> 0 & 0xf) + c->s;
75
+	c->age = 0;
76
+	c->i = 0;
77
+	if(c->len <= 0x100) /* single cycle mode */
78
+		c->period = NOTE_PERIOD * 337 / 2 / c->len;
79
+	else /* sample repeat mode */
80
+		c->period = NOTE_PERIOD;
99 81
 }
... ...
@@ -14,31 +14,14 @@ typedef unsigned int Uint32;
14 14
 typedef signed int Sint32;
15 15
 
16 16
 #define SAMPLE_FREQUENCY 44100
17
+#define POLYPHONY 4
17 18
 
18 19
 typedef struct {
19
-	Uint16 *dat;
20
-	Uint8 i, n, sz, finishes, is_envelope;
21
-} Queue;
22
-
23
-typedef struct {
24
-	Uint32 count, advance, period;
25
-	Uint16 vector;
26
-	Sint16 start_value, end_value;
27
-	Queue queue;
28
-} WaveformGenerator;
29
-
30
-typedef struct {
31
-	WaveformGenerator wv[2];
32
-	Sint8 volume[2];
33
-	Uint8 playing, impl;
34
-} Note;
35
-
36
-typedef struct {
37
-	Queue *queue;
38
-	Note *notes;
39
-	Uint8 *channel_ptr;
40
-	int n_notes;
20
+	Uint8 *addr;
21
+	Uint32 count, advance, period, age, a, d, s, r;
22
+	Uint16 i, len;
23
+	Uint8 volume_l, volume_r, pitch, repeat;
41 24
 } Apu;
42 25
 
43
-void apu_render(Apu *apu, Uxn *u, Sint16 *samples, int n_samples);
44
-void apu_play_note(Note *note, Uint16 wave_vector, Uint16 envelope_vector, Uint8 volume, Uint8 pitch, Uint8 impl);
26
+void apu_render(Apu *c, Sint16 *sample, Sint16 *end);
27
+void apu_start(Apu *c, Uint16 adsr, Uint8 pitch);
... ...
@@ -22,8 +22,8 @@ static SDL_Window *gWindow;
22 22
 static SDL_Renderer *gRenderer;
23 23
 static SDL_Texture *gTexture;
24 24
 static Ppu ppu;
25
-static Apu apu;
26
-static Device *devscreen, *devmouse, *devctrl, *devapu;
25
+static Apu apu[POLYPHONY];
26
+static Device *devscreen, *devmouse, *devctrl;
27 27
 
28 28
 Uint8 zoom = 0, debug = 0, reqdraw = 0;
29 29
 
... ...
@@ -43,7 +43,12 @@ error(char *msg, const char *err)
43 43
 static void
44 44
 audio_callback(void *u, Uint8 *stream, int len)
45 45
 {
46
-	apu_render(&apu, (Uxn *)u, (Sint16 *)stream, len >> 2);
46
+	int i;
47
+	Sint16 *samples = (Sint16 *)stream;
48
+	SDL_memset(stream, 0, len);
49
+	for(i = 0; i < POLYPHONY; ++i)
50
+		apu_render(&apu[i], samples, samples + len / 2);
51
+	(void)u;
47 52
 }
48 53
 
49 54
 void
... ...
@@ -92,7 +97,7 @@ quit(void)
92 97
 }
93 98
 
94 99
 int
95
-init(Uxn *u)
100
+init(void)
96 101
 {
97 102
 	SDL_AudioSpec as;
98 103
 	if(!initppu(&ppu, 48, 32, 16))
... ...
@@ -116,7 +121,7 @@ init(Uxn *u)
116 121
 	as.channels = 2;
117 122
 	as.callback = audio_callback;
118 123
 	as.samples = 512;
119
-	as.userdata = u;
124
+	as.userdata = NULL;
120 125
 	audio_id = SDL_OpenAudioDevice(NULL, 0, &as, NULL, 0);
121 126
 	if(!audio_id)
122 127
 		return error("Audio", SDL_GetError());
... ...
@@ -242,22 +247,23 @@ file_talk(Device *d, Uint8 b0, Uint8 w)
242 247
 static void
243 248
 audio_talk(Device *d, Uint8 b0, Uint8 w)
244 249
 {
245
-	if(w && b0 == 0xa) {
246
-		if(d->dat[0xa] >= apu.n_notes) apu.notes = SDL_realloc(apu.notes, (d->dat[0xa] + 1) * sizeof(Note));
247
-		while(d->dat[0xa] >= apu.n_notes) SDL_zero(apu.notes[apu.n_notes++]);
248
-		apu_play_note(&apu.notes[d->dat[0xa]], mempeek16(d->dat, 0x0), mempeek16(d->dat, 0x2), d->dat[0x8], d->dat[0x9] & 0x7f, d->dat[0x9] > 0x7f);
249
-	} else if(w && b0 == 0xe && apu.queue != NULL) {
250
-		if(apu.queue->n == apu.queue->sz) {
251
-			apu.queue->sz = apu.queue->sz < 4 ? 4 : apu.queue->sz * 2;
252
-			apu.queue->dat = SDL_realloc(apu.queue->dat, apu.queue->sz * sizeof(*apu.queue->dat));
253
-		}
254
-		if(apu.queue->is_envelope)
255
-			apu.queue->dat[apu.queue->n++] = mempeek16(d->dat, 0xb) >> 1;
256
-		else
257
-			apu.queue->dat[apu.queue->n++] = mempeek16(d->dat, 0xb) + 0x8000;
258
-		apu.queue->dat[apu.queue->n++] = mempeek16(d->dat, 0xd);
259
-	} else if(w && b0 == 0xf && apu.queue != NULL)
260
-		apu.queue->finishes = 1;
250
+	Apu *c;
251
+	if(!w) return;
252
+	c = &apu[d->dat[0x7] % POLYPHONY];
253
+	SDL_LockAudioDevice(audio_id);
254
+	if(b0 == 0x1) c->period -= (Sint16)mempeek16(d->dat, 0x0);
255
+	if(b0 == 0x3 || b0 == 0xf) c->len = mempeek16(d->dat, (b0 & 0x8) | 0x2);
256
+	if(b0 == 0x5 || b0 == 0xf) c->addr = &d->mem[mempeek16(d->dat, (b0 & 0x8) | 0x4)];
257
+	if(b0 == 0x6 || b0 == 0xf) {
258
+		c->volume_l = d->dat[(b0 & 0x8) | 0x6] >> 4;
259
+		c->volume_r = d->dat[(b0 & 0x8) | 0x6] & 0xf;
260
+	}
261
+	if(b0 == 0xf) {
262
+		c->repeat = !(d->dat[0xf] & 0x80);
263
+		apu_start(c, mempeek16(d->dat, 0x8), d->dat[0xf] & 0x7f);
264
+		d->dat[0x7]++;
265
+	}
266
+	SDL_UnlockAudioDevice(audio_id);
261 267
 }
262 268
 
263 269
 void
... ...
@@ -297,7 +303,6 @@ start(Uxn *u)
297 303
 	while(1) {
298 304
 		SDL_Event event;
299 305
 		double elapsed, start = SDL_GetPerformanceCounter();
300
-		SDL_LockAudioDevice(audio_id);
301 306
 		while(SDL_PollEvent(&event) != 0) {
302 307
 			switch(event.type) {
303 308
 			case SDL_QUIT:
... ...
@@ -326,7 +331,6 @@ start(Uxn *u)
326 331
 			}
327 332
 		}
328 333
 		evaluxn(u, mempeek16(devscreen->dat, 0));
329
-		SDL_UnlockAudioDevice(audio_id);
330 334
 		if(reqdraw)
331 335
 			redraw(ppu.output, u);
332 336
 		elapsed = (SDL_GetPerformanceCounter() - start) / (double)SDL_GetPerformanceFrequency() * 1000.0f;
... ...
@@ -347,13 +351,13 @@ main(int argc, char **argv)
347 351
 		return error("Boot", "Failed");
348 352
 	if(!loaduxn(&u, argv[1]))
349 353
 		return error("Load", "Failed");
350
-	if(!init(&u))
354
+	if(!init())
351 355
 		return error("Init", "Failed");
352 356
 
353 357
 	portuxn(&u, 0x0, "system", system_talk);
354 358
 	portuxn(&u, 0x1, "console", console_talk);
355 359
 	devscreen = portuxn(&u, 0x2, "screen", screen_talk);
356
-	devapu = portuxn(&u, 0x3, "audio", audio_talk);
360
+	portuxn(&u, 0x3, "audio", audio_talk);
357 361
 	devctrl = portuxn(&u, 0x4, "controller", nil_talk);
358 362
 	portuxn(&u, 0x5, "---", nil_talk);
359 363
 	devmouse = portuxn(&u, 0x6, "mouse", nil_talk);
... ...
@@ -367,8 +371,6 @@ main(int argc, char **argv)
367 371
 	portuxn(&u, 0xe, "---", nil_talk);
368 372
 	portuxn(&u, 0xf, "---", nil_talk);
369 373
 
370
-	apu.channel_ptr = &devapu->dat[0xa];
371
-
372 374
 	/* Write screen size to dev/screen */
373 375
 	mempoke16(devscreen->dat, 2, ppu.hor * 8);
374 376
 	mempoke16(devscreen->dat, 4, ppu.ver * 8);