... | ... |
@@ -6,6 +6,7 @@ clang-format -i src/uxn.h |
6 | 6 |
clang-format -i src/uxn.c |
7 | 7 |
clang-format -i src/emulator.c |
8 | 8 |
clang-format -i src/debugger.c |
9 |
+clang-format -i src/apu.c |
|
9 | 10 |
|
10 | 11 |
echo "Cleaning.." |
11 | 12 |
rm -f ./bin/assembler |
... | ... |
@@ -19,12 +20,12 @@ if [ "${1}" = '--debug' ]; |
19 | 20 |
then |
20 | 21 |
echo "[debug]" |
21 | 22 |
cc -std=c89 -DDEBUG -Wall -Wno-unknown-pragmas -Wpedantic -Wshadow -Wextra -Werror=implicit-int -Werror=incompatible-pointer-types -Werror=int-conversion -Wvla -g -Og -fsanitize=address -fsanitize=undefined src/assembler.c -o bin/assembler |
22 |
- cc -std=c89 -DDEBUG -Wall -Wno-unknown-pragmas -Wpedantic -Wshadow -Wextra -Werror=implicit-int -Werror=incompatible-pointer-types -Werror=int-conversion -Wvla -g -Og -fsanitize=address -fsanitize=undefined src/uxn.c src/emulator.c -L/usr/local/lib -lSDL2 -o bin/emulator |
|
23 |
+ cc -std=c89 -DDEBUG -Wall -Wno-unknown-pragmas -Wpedantic -Wshadow -Wextra -Werror=implicit-int -Werror=incompatible-pointer-types -Werror=int-conversion -Wvla -g -Og -fsanitize=address -fsanitize=undefined src/uxn.c src/emulator.c src/apu.c -L/usr/local/lib -lSDL2 -o bin/emulator |
|
23 | 24 |
cc -std=c89 -DDEBUG -Wall -Wno-unknown-pragmas -Wpedantic -Wshadow -Wextra -Werror=implicit-int -Werror=incompatible-pointer-types -Werror=int-conversion -Wvla -g -Og -fsanitize=address -fsanitize=undefined src/uxn.c src/debugger.c -o bin/debugger |
24 | 25 |
else |
25 | 26 |
cc src/assembler.c -std=c89 -Os -DNDEBUG -g0 -s -Wall -Wno-unknown-pragmas -o bin/assembler |
26 | 27 |
cc src/uxn.c src/debugger.c -std=c89 -Os -DNDEBUG -g0 -s -Wall -Wno-unknown-pragmas -o bin/debugger |
27 |
- cc src/uxn.c src/emulator.c -std=c89 -Os -DNDEBUG -g0 -s -Wall -Wno-unknown-pragmas -L/usr/local/lib -lSDL2 -o bin/emulator |
|
28 |
+ cc src/uxn.c src/emulator.c src/apu.c -std=c89 -Os -DNDEBUG -g0 -s -Wall -Wno-unknown-pragmas -L/usr/local/lib -lSDL2 -o bin/emulator |
|
28 | 29 |
fi |
29 | 30 |
|
30 | 31 |
echo "Assembling.." |
... | ... |
@@ -11,7 +11,7 @@ |
11 | 11 |
|0140 ;Keys { key 1 } |
12 | 12 |
|0150 ;Mouse { x 2 y 2 state 1 chord 1 } |
13 | 13 |
|0160 ;File { pad 8 name 2 length 2 load 2 save 2 } |
14 |
-|0170 ;Audio { ch1asdr 2 ch2asdr 2 ch3asdr 2 ch4asdr 2 ch1pitch 1 ch1vol 1 ch2pitch 1 ch2vol 1 ch3pitch 1 ch3vol 1 ch4pitch 1 ch4vol 1 } |
|
14 |
+|0180 ;Audio { wave 2 envelope 2 pad 4 volume 1 pitch 1 play 1 value 2 delay 2 finish 1 } |
|
15 | 15 |
|01F0 ;System { pad 8 r 2 g 2 b 2 } |
16 | 16 |
|
17 | 17 |
( vectors ) |
... | ... |
@@ -6,6 +6,9 @@ |
6 | 6 |
%++ { #0001 ADD2 } |
7 | 7 |
%MOD { DUP2 DIV MUL SUB } |
8 | 8 |
%TRACK { ,track.ch1 #00 ~track.active #0020 MUL2 ADD2 } |
9 |
+%SOUND { STH #00 =Audio.value STHr #00 =Audio.delay } |
|
10 |
+%SOUND2 { =Audio.value =Audio.delay } |
|
11 |
+%SOUND_FINISH { #00 =Audio.finish } |
|
9 | 12 |
|
10 | 13 |
( variables ) |
11 | 14 |
|
... | ... |
@@ -19,6 +22,8 @@ |
19 | 22 |
;knob { x 2 y 2 value 1 } |
20 | 23 |
;head { pos 1 } |
21 | 24 |
;track { active 1 ch1 20 ch2 20 ch3 20 ch4 20 } |
25 |
+;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 } |
|
26 |
+;volume { ch1 1 ch2 1 ch3 1 ch4 1 } |
|
22 | 27 |
|
23 | 28 |
( devices ) |
24 | 29 |
|
... | ... |
@@ -30,7 +35,7 @@ |
30 | 35 |
|0150 ;Keys { key 1 } |
31 | 36 |
|0160 ;Mouse { vector 2 x 2 y 2 state 1 chord 1 } |
32 | 37 |
|0170 ;File { pad 8 name 2 length 2 load 2 save 2 } |
33 |
-|0180 ;Audio { ch1adsr 2 ch2adsr 2 ch3adsr 2 ch4adsr 2 ch1vol 1 ch1pitch 1 ch2vol 1 ch2pitch 1 ch3vol 1 ch3pitch 1 ch4vol 1 ch4pitch 1 } |
|
38 |
+|0180 ;Audio { wave 2 envelope 2 pad 4 volume 1 pitch 1 play 1 value 2 delay 2 finish 1 } |
|
34 | 39 |
|
35 | 40 |
( vectors ) |
36 | 41 |
|
... | ... |
@@ -51,9 +56,10 @@ |
51 | 56 |
~trkframe.x2 =ctlframe.x2 ~chnframe.y2 =ctlframe.y2 |
52 | 57 |
|
53 | 58 |
( default settings ) |
54 |
- #048c =Audio.ch1adsr #88 =Audio.ch1vol |
|
55 |
- #159d =Audio.ch2adsr #88 =Audio.ch2vol |
|
56 |
- #26ae =Audio.ch3adsr #88 =Audio.ch3vol |
|
59 |
+ ,adsr-envelope =Audio.envelope |
|
60 |
+ #00 =adsr.ch1a #40 =adsr.ch1d #80 =adsr.ch1s #c0 =adsr.ch1r #88 =volume.ch1 |
|
61 |
+ #10 =adsr.ch2a #50 =adsr.ch2d #90 =adsr.ch2s #d0 =adsr.ch2r #88 =volume.ch2 |
|
62 |
+ #20 =adsr.ch3a #60 =adsr.ch3d #a0 =adsr.ch3s #e0 =adsr.ch3r #88 =volume.ch3 |
|
57 | 63 |
|
58 | 64 |
,draw-timeline JSR2 |
59 | 65 |
,draw-controls JSR2 |
... | ... |
@@ -110,29 +116,29 @@ BRK |
110 | 116 |
|
111 | 117 |
~Mouse.x ~ctlframe.x1 SUB2 8- 8/ SWP POP #02 DIV |
112 | 118 |
DUP #00 NEQ ^$no-a JNZ |
113 |
- ,Audio #00 ~track.active #02 MUL ADD2 PEK2 |
|
114 |
- #10 ~Mouse.state #10 EQU #e0 MUL ADD ADD |
|
115 |
- ,Audio #00 ~track.active #02 MUL ADD2 POK2 $no-a |
|
119 |
+ ,adsr #00 ~track.active #04 MUL ADD2 PEK2 |
|
120 |
+ #10 ~Mouse.state #10 EQU #e0 MUL ADD ADD |
|
121 |
+ ,adsr #00 ~track.active #04 MUL ADD2 POK2 $no-a |
|
116 | 122 |
DUP #01 NEQ ^$no-d JNZ |
117 |
- ,Audio #00 ~track.active #02 MUL ADD2 PEK2 |
|
118 |
- DUP #f0 AND STH #01 ~Mouse.state #10 EQU #0e MUL ADD ADD #0f AND STHr ADD |
|
119 |
- ,Audio #00 ~track.active #02 MUL ADD2 POK2 $no-d |
|
123 |
+ ,adsr #00 ~track.active #04 MUL ADD2 #0001 ADD2 PEK2 |
|
124 |
+ #10 ~Mouse.state #10 EQU #e0 MUL ADD ADD |
|
125 |
+ ,adsr #00 ~track.active #04 MUL ADD2 #0001 ADD2 POK2 $no-d |
|
120 | 126 |
DUP #02 NEQ ^$no-s JNZ |
121 |
- ,Audio #00 ~track.active #02 MUL ADD2 ++ PEK2 |
|
122 |
- #10 ~Mouse.state #10 EQU #e0 MUL ADD ADD |
|
123 |
- ,Audio #00 ~track.active #02 MUL ADD2 ++ POK2 $no-s |
|
127 |
+ ,adsr #00 ~track.active #04 MUL ADD2 #0002 ADD2 PEK2 |
|
128 |
+ #10 ~Mouse.state #10 EQU #e0 MUL ADD ADD |
|
129 |
+ ,adsr #00 ~track.active #04 MUL ADD2 #0002 ADD2 POK2 $no-s |
|
124 | 130 |
DUP #03 NEQ ^$no-r JNZ |
125 |
- ,Audio #00 ~track.active #02 MUL ADD2 ++ PEK2 |
|
126 |
- DUP #f0 AND STH #01 ~Mouse.state #10 EQU #0e MUL ADD ADD #0f AND STHr ADD |
|
127 |
- ,Audio #00 ~track.active #02 MUL ADD2 ++ POK2 $no-r |
|
131 |
+ ,adsr #00 ~track.active #04 MUL ADD2 #0003 ADD2 PEK2 |
|
132 |
+ #10 ~Mouse.state #10 EQU #e0 MUL ADD ADD |
|
133 |
+ ,adsr #00 ~track.active #04 MUL ADD2 #0003 ADD2 POK2 $no-r |
|
128 | 134 |
DUP #05 NEQ ^$no-left JNZ |
129 |
- ,Audio 8+ #00 ~track.active #02 MUL ADD2 PEK2 |
|
130 |
- #10 ~Mouse.state #10 EQU #e0 MUL ADD ADD |
|
131 |
- ,Audio 8+ #00 ~track.active #02 MUL ADD2 POK2 $no-left |
|
135 |
+ ,volume #00 ~track.active ADD2 PEK2 |
|
136 |
+ #10 ~Mouse.state #10 EQU #e0 MUL ADD ADD |
|
137 |
+ ,volume #00 ~track.active ADD2 POK2 $no-left |
|
132 | 138 |
DUP #06 NEQ ^$no-right JNZ |
133 |
- ,Audio 8+ #00 ~track.active #02 MUL ADD2 PEK2 |
|
139 |
+ ,volume #00 ~track.active ADD2 PEK2 |
|
134 | 140 |
DUP #f0 AND STH #01 ~Mouse.state #10 EQU #0e MUL ADD ADD #0f AND STHr ADD |
135 |
- ,Audio 8+ #00 ~track.active #02 MUL ADD2 POK2 $no-right |
|
141 |
+ ,volume #00 ~track.active ADD2 POK2 $no-right |
|
136 | 142 |
POP |
137 | 143 |
( release ) #00 =Mouse.state |
138 | 144 |
,draw-controls JSR2 |
... | ... |
@@ -146,21 +152,30 @@ BRK |
146 | 152 |
DUP #ff NEQ ^$skip1 JNZ |
147 | 153 |
POP ^$listen2 JMP |
148 | 154 |
$skip1 |
149 |
- #00 SWP ,notes ADD2 PEK2 =Audio.ch1pitch |
|
155 |
+ #00 SWP ,notes ADD2 PEK2 =Audio.pitch |
|
156 |
+ ~volume.ch1 =Audio.volume |
|
157 |
+ ,square-wave =Audio.wave |
|
158 |
+ #00 =Audio.play |
|
150 | 159 |
$listen2 |
151 | 160 |
,track.ch2 #00 ~head.pos #08 DIV ADD2 PEK2 |
152 | 161 |
#01 SUB |
153 | 162 |
DUP #ff NEQ ^$skip2 JNZ |
154 | 163 |
POP ^$listen3 JMP |
155 | 164 |
$skip2 |
156 |
- #00 SWP ,notes ADD2 PEK2 =Audio.ch2pitch |
|
165 |
+ #00 SWP ,notes ADD2 PEK2 =Audio.pitch |
|
166 |
+ ~volume.ch2 =Audio.volume |
|
167 |
+ ,square-wave =Audio.wave |
|
168 |
+ #01 =Audio.play |
|
157 | 169 |
$listen3 |
158 | 170 |
,track.ch3 #00 ~head.pos #08 DIV ADD2 PEK2 |
159 | 171 |
#01 SUB |
160 | 172 |
DUP #ff NEQ ^$skip3 JNZ |
161 | 173 |
POP ^$end JMP |
162 | 174 |
$skip3 |
163 |
- #00 SWP ,notes ADD2 PEK2 =Audio.ch3pitch |
|
175 |
+ #00 SWP ,notes ADD2 PEK2 =Audio.pitch |
|
176 |
+ ~volume.ch3 =Audio.volume |
|
177 |
+ ,triangle-wave =Audio.wave |
|
178 |
+ #02 =Audio.play |
|
164 | 179 |
$end |
165 | 180 |
|
166 | 181 |
RTN |
... | ... |
@@ -264,18 +279,18 @@ RTN |
264 | 279 |
~trkframe.x1 #0018 SUB2 DUP2 ~trkframe.y1 ,draw-octave JSR2 |
265 | 280 |
~trkframe.y1 #0038 ADD2 ,draw-octave JSR2 |
266 | 281 |
~trkframe.x1 #0028 SUB2 =Sprite.x |
267 |
- ~trkframe.y1 =Sprite.y |
|
268 |
- ,font_hex #0060 ADD2 =Sprite.addr |
|
282 |
+ ~trkframe.y1 #0030 ADD2 =Sprite.y |
|
283 |
+ ,font_hex #0028 ADD2 =Sprite.addr |
|
269 | 284 |
#03 =Sprite.color |
270 | 285 |
~trkframe.x1 #0030 SUB2 =Sprite.x |
271 |
- ,font_hex #0020 ADD2 =Sprite.addr |
|
286 |
+ ,font_hex #0060 ADD2 =Sprite.addr |
|
272 | 287 |
#03 =Sprite.color |
273 | 288 |
~trkframe.x1 #0028 SUB2 =Sprite.x |
274 |
- ~trkframe.y1 #0038 ADD2 =Sprite.y |
|
275 |
- ,font_hex #0060 ADD2 =Sprite.addr |
|
289 |
+ ~trkframe.y1 #0068 ADD2 =Sprite.y |
|
290 |
+ ,font_hex #0020 ADD2 =Sprite.addr |
|
276 | 291 |
#03 =Sprite.color |
277 | 292 |
~trkframe.x1 #0030 SUB2 =Sprite.x |
278 |
- ,font_hex #0018 ADD2 =Sprite.addr |
|
293 |
+ ,font_hex #0060 ADD2 =Sprite.addr |
|
279 | 294 |
#03 =Sprite.color |
280 | 295 |
|
281 | 296 |
RTN |
... | ... |
@@ -312,24 +327,24 @@ RTN |
312 | 327 |
( env ) |
313 | 328 |
~ctlframe.x1 8+ ~ctlframe.y1 8+ #02 ,env_txt ,draw-label JSR2 |
314 | 329 |
~ctlframe.x1 8+ ~ctlframe.y1 #0010 ADD2 |
315 |
- ,Audio #00 ~track.active #02 MUL ADD2 PEK2 #04 SFT |
|
330 |
+ ,adsr #00 ~track.active #04 MUL ADD2 PEK2 #04 SFT |
|
316 | 331 |
,draw-knob JSR2 |
317 | 332 |
~ctlframe.x1 #0018 ADD2 ~ctlframe.y1 #0010 ADD2 |
318 |
- ,Audio #00 ~track.active #02 MUL ADD2 PEK2 #0f AND |
|
333 |
+ ,adsr #00 ~track.active #04 MUL ADD2 #0001 ADD2 PEK2 #04 SFT |
|
319 | 334 |
,draw-knob JSR2 |
320 | 335 |
~ctlframe.x1 #0028 ADD2 ~ctlframe.y1 #0010 ADD2 |
321 |
- ,Audio #00 ~track.active #02 MUL ADD2 ++ PEK2 #04 SFT |
|
336 |
+ ,adsr #00 ~track.active #04 MUL ADD2 #0002 ADD2 PEK2 #04 SFT |
|
322 | 337 |
,draw-knob JSR2 |
323 | 338 |
~ctlframe.x1 #0038 ADD2 ~ctlframe.y1 #0010 ADD2 |
324 |
- ,Audio #00 ~track.active #02 MUL ADD2 ++ PEK2 #0f AND |
|
339 |
+ ,adsr #00 ~track.active #04 MUL ADD2 #0003 ADD2 PEK2 #04 SFT |
|
325 | 340 |
,draw-knob JSR2 |
326 | 341 |
( vol ) |
327 | 342 |
~ctlframe.x1 #0058 ADD2 ~ctlframe.y1 8+ #02 ,vol_txt ,draw-label JSR2 |
328 | 343 |
~ctlframe.x1 #0058 ADD2 ~ctlframe.y1 #0010 ADD2 |
329 |
- ,Audio 8+ #00 ~track.active #02 MUL ADD2 PEK2 #04 SFT |
|
344 |
+ ,volume #00 ~track.active ADD2 PEK2 #04 SFT |
|
330 | 345 |
,draw-knob JSR2 |
331 | 346 |
~ctlframe.x1 #0068 ADD2 ~ctlframe.y1 #0010 ADD2 |
332 |
- ,Audio 8+ #00 ~track.active #02 MUL ADD2 PEK2 #0f AND |
|
347 |
+ ,volume #00 ~track.active ADD2 PEK2 #0f AND |
|
333 | 348 |
,draw-knob JSR2 |
334 | 349 |
|
335 | 350 |
RTN |
... | ... |
@@ -415,6 +430,27 @@ RTN |
415 | 430 |
|
416 | 431 |
RTN |
417 | 432 |
|
433 |
+@adsr-envelope ( -- ) |
|
434 |
+ #7f ,adsr #00 ~Audio.play #04 MUL ADD2 PEK2 SOUND |
|
435 |
+ #40 ,adsr #00 ~Audio.play #04 MUL ADD2 #0001 ADD2 PEK2 SOUND |
|
436 |
+ #40 ,adsr #00 ~Audio.play #04 MUL ADD2 #0002 ADD2 PEK2 SOUND |
|
437 |
+ #00 ,adsr #00 ~Audio.play #04 MUL ADD2 #0003 ADD2 PEK2 SOUND |
|
438 |
+ SOUND_FINISH |
|
439 |
+ BRK |
|
440 |
+ |
|
441 |
+@square-wave ( -- ) |
|
442 |
+ #5800 SOUND |
|
443 |
+ #5880 SOUND |
|
444 |
+ #a800 SOUND |
|
445 |
+ #a880 SOUND |
|
446 |
+ BRK |
|
447 |
+ |
|
448 |
+@triangle-wave ( -- ) |
|
449 |
+ #7f40 SOUND |
|
450 |
+ #8180 SOUND |
|
451 |
+ #0040 SOUND |
|
452 |
+ BRK |
|
453 |
+ |
|
418 | 454 |
@ch1_txt [ CHN0 00 ] |
419 | 455 |
@ch2_txt [ CHN1 00 ] |
420 | 456 |
@ch3_txt [ CHN2 00 ] |
... | ... |
@@ -458,13 +494,13 @@ RTN |
458 | 494 |
] |
459 | 495 |
|
460 | 496 |
@knob_offsetx [ |
461 |
- 04 05 06 07 08 07 06 05 |
|
462 |
- 04 04 03 02 01 00 01 02 |
|
497 |
+ 01 00 00 00 00 01 02 03 |
|
498 |
+ 05 06 07 08 08 08 08 07 |
|
463 | 499 |
] |
464 | 500 |
|
465 | 501 |
@knob_offsety [ |
466 |
- 00 01 02 03 04 05 06 07 |
|
467 |
- 08 07 06 05 04 04 03 02 |
|
502 |
+ 07 06 05 03 02 01 00 00 |
|
503 |
+ 00 00 01 02 03 05 06 07 |
|
468 | 504 |
] |
469 | 505 |
|
470 | 506 |
@font_hex ( 0-F ) |
... | ... |
@@ -17,7 +17,6 @@ |
17 | 17 |
|0100 ;System { vector 2 pad 6 r 2 g 2 b 2 } |
18 | 18 |
|0120 ;Screen { vector 2 width 2 height 2 pad 2 x 2 y 2 color 1 } |
19 | 19 |
|0130 ;Sprite { vector 2 pad 6 x 2 y 2 addr 2 color 1 } |
20 |
-|0180 ;Audio { ch1adsr 2 ch2adsr 2 ch3adsr 2 ch4adsr 2 ch1vol 1 ch1pitch 1 ch2vol 1 ch2pitch 1 ch3vol 1 ch3pitch 1 ch4vol 1 ch4pitch 1 } |
|
21 | 20 |
|01a0 ;Time { year 2 month 1 day 1 hour 1 minute 1 second 1 dow 1 doy 2 isdst 1 get 1 } |
22 | 21 |
|
23 | 22 |
( program ) |
... | ... |
@@ -27,12 +26,6 @@ |
27 | 26 |
( theme ) #0ff8 =System.r #0f08 =System.g #0f08 =System.b |
28 | 27 |
( vectors ) ,FRAME =Screen.vector |
29 | 28 |
|
30 |
- #1000 =Audio.ch1adsr |
|
31 |
- #66 =Audio.ch1vol |
|
32 |
- |
|
33 |
- #0003 =Audio.ch2adsr |
|
34 |
- #66 =Audio.ch2vol |
|
35 |
- |
|
36 | 29 |
BRK |
37 | 30 |
|
38 | 31 |
@FRAME |
... | ... |
@@ -43,13 +36,6 @@ BRK |
43 | 36 |
~Time.second ~current.second NEQ #01 JNZ BRK |
44 | 37 |
~Time.second =current.second |
45 | 38 |
|
46 |
- ( play sounds ) |
|
47 |
- #0d =Audio.ch1pitch |
|
48 |
- |
|
49 |
- ~Time.second #0f MOD #00 NEQ ^$no-tone JNZ |
|
50 |
- #0d #02 MUL =Audio.ch2pitch |
|
51 |
- $no-tone |
|
52 |
- |
|
53 | 39 |
( clear ) |
54 | 40 |
#0080 SCALEX #0080 SCALEY ~needles.sx ~needles.sy #00 ,draw-line JSR2 |
55 | 41 |
#0080 SCALEX #0080 SCALEY ~needles.mx ~needles.my #00 ,draw-line JSR2 |
56 | 42 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,164 @@ |
1 |
+#include <SDL2/SDL.h> |
|
2 |
+#include <stdlib.h> |
|
3 |
+ |
|
4 |
+/* |
|
5 |
+Copyright (c) 2021 Devine Lu Linvega |
|
6 |
+Copyright (c) 2021 Andrew Alderwick |
|
7 |
+ |
|
8 |
+Permission to use, copy, modify, and distribute this software for any |
|
9 |
+purpose with or without fee is hereby granted, provided that the above |
|
10 |
+copyright notice and this permission notice appear in all copies. |
|
11 |
+ |
|
12 |
+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
|
13 |
+WITH REGARD TO THIS SOFTWARE. |
|
14 |
+*/ |
|
15 |
+ |
|
16 |
+#include "uxn.h" |
|
17 |
+ |
|
18 |
+#define SAMPLE_FREQUENCY 48000 |
|
19 |
+ |
|
20 |
+extern SDL_AudioDeviceID audio_id; |
|
21 |
+int error(char *msg, const char *err); |
|
22 |
+ |
|
23 |
+static Uint32 note_advances[12] = { |
|
24 |
+ 0x82d01286 / (SAMPLE_FREQUENCY / 30), /* C7 */ |
|
25 |
+ 0x8a976073 / (SAMPLE_FREQUENCY / 30), |
|
26 |
+ 0x92d5171d / (SAMPLE_FREQUENCY / 30), /* D7 */ |
|
27 |
+ 0x9b904100 / (SAMPLE_FREQUENCY / 30), |
|
28 |
+ 0xa4d053c8 / (SAMPLE_FREQUENCY / 30), /* E7 */ |
|
29 |
+ 0xae9d36b0 / (SAMPLE_FREQUENCY / 30), /* F7 */ |
|
30 |
+ 0xb8ff493e / (SAMPLE_FREQUENCY / 30), |
|
31 |
+ 0xc3ff6a72 / (SAMPLE_FREQUENCY / 30), /* G7 */ |
|
32 |
+ 0xcfa70054 / (SAMPLE_FREQUENCY / 30), |
|
33 |
+ 0xdc000000 / (SAMPLE_FREQUENCY / 30), /* A7 */ |
|
34 |
+ 0xe914f623 / (SAMPLE_FREQUENCY / 30), |
|
35 |
+ 0xf6f11003 / (SAMPLE_FREQUENCY / 30) /* B7 */ |
|
36 |
+}; |
|
37 |
+ |
|
38 |
+typedef struct { |
|
39 |
+ Uint16 *dat; |
|
40 |
+ Uint8 i, n, sz, ends; |
|
41 |
+} Queue; |
|
42 |
+ |
|
43 |
+typedef struct { |
|
44 |
+ Uint32 count, advance, period; |
|
45 |
+ Uint16 vector; |
|
46 |
+ Sint16 start_value, end_value; |
|
47 |
+ Queue queue; |
|
48 |
+} WaveformGenerator; |
|
49 |
+ |
|
50 |
+typedef struct { |
|
51 |
+ WaveformGenerator wv[2]; |
|
52 |
+ Sint8 volume[2], playing; |
|
53 |
+} Note; |
|
54 |
+ |
|
55 |
+static Note *notes = NULL; |
|
56 |
+static int n_notes = 0; |
|
57 |
+static Queue *q; |
|
58 |
+static Uint16 id_addr; |
|
59 |
+ |
|
60 |
+static void |
|
61 |
+play_note(Uxn *u, int note_i, Sint16 *samples, int n_samples) |
|
62 |
+{ |
|
63 |
+ int i; |
|
64 |
+ Note *note = ¬es[note_i]; |
|
65 |
+ while(n_samples--) { |
|
66 |
+ Sint32 sample = 1; |
|
67 |
+ for(i = 0; i < 2; ++i) { |
|
68 |
+ WaveformGenerator *wv = ¬e->wv[i]; |
|
69 |
+ q = &wv->queue; |
|
70 |
+ wv->count += wv->advance; |
|
71 |
+ while(wv->count > wv->period) { |
|
72 |
+ wv->count -= wv->period; |
|
73 |
+ wv->start_value = wv->end_value; |
|
74 |
+ if(q->i == q->n) { |
|
75 |
+ q->i = q->n = 0; |
|
76 |
+ if(!q->ends) { |
|
77 |
+ u->ram.dat[id_addr] = note_i; |
|
78 |
+ evaluxn(u, wv->vector); |
|
79 |
+ } |
|
80 |
+ } |
|
81 |
+ if(!q->n) { |
|
82 |
+ note->playing = 0; |
|
83 |
+ return; |
|
84 |
+ } |
|
85 |
+ wv->end_value = (Sint16)q->dat[q->i++]; |
|
86 |
+ wv->period = (30 << 4) * q->dat[q->i++]; |
|
87 |
+ } |
|
88 |
+ if(wv->period >> 9) |
|
89 |
+ sample *= wv->start_value + (Sint32)(wv->end_value - wv->start_value) * (Sint32)(wv->count >> 10) / (Sint32)(wv->period >> 10); |
|
90 |
+ else |
|
91 |
+ sample *= wv->end_value; |
|
92 |
+ } |
|
93 |
+ for(i = 0; i < 2; ++i) |
|
94 |
+ *(samples++) += sample / 0xf * note->volume[i] / 0x10000; |
|
95 |
+ } |
|
96 |
+} |
|
97 |
+ |
|
98 |
+static void |
|
99 |
+play_all_notes(void *u, Uint8 *stream, int len) |
|
100 |
+{ |
|
101 |
+ int i; |
|
102 |
+ SDL_memset(stream, 0, len); |
|
103 |
+ for(i = 0; i < n_notes; ++i) |
|
104 |
+ if(notes[i].playing) play_note(u, i, (Sint16 *)stream, len >> 2); |
|
105 |
+ q = NULL; |
|
106 |
+} |
|
107 |
+ |
|
108 |
+static Note * |
|
109 |
+get_note(Uint8 i) |
|
110 |
+{ |
|
111 |
+ if(i >= n_notes) notes = SDL_realloc(notes, (i + 1) * sizeof(Note)); |
|
112 |
+ while(i >= n_notes) SDL_zero(notes[n_notes++]); |
|
113 |
+ return ¬es[i]; |
|
114 |
+} |
|
115 |
+ |
|
116 |
+static Uint8 |
|
117 |
+audio_poke(Uxn *u, Uint16 ptr, Uint8 b0, Uint8 b1) |
|
118 |
+{ |
|
119 |
+ Uint8 *m = u->ram.dat + ptr; |
|
120 |
+ int i; |
|
121 |
+ if(b0 == 0xa) { |
|
122 |
+ Note *note = get_note(b1); |
|
123 |
+ note->playing = 1; |
|
124 |
+ for(i = 0; i < 2; ++i) { |
|
125 |
+ note->volume[i] = 0xf & (m[0x8] >> 4 * (1 - i)); |
|
126 |
+ note->wv[i].vector = (m[0x0 + i * 2] << 8) + m[0x1 + i * 2]; |
|
127 |
+ note->wv[i].count = note->wv[i].period = 0; |
|
128 |
+ note->wv[i].end_value = 0; |
|
129 |
+ note->wv[i].queue.n = note->wv[i].queue.i = 0; |
|
130 |
+ note->wv[i].queue.ends = 0; |
|
131 |
+ } |
|
132 |
+ note->wv[0].advance = note_advances[m[0x9] % 12] >> (8 - m[0x9] / 12); |
|
133 |
+ note->wv[1].advance = (30 << 20) / SAMPLE_FREQUENCY; |
|
134 |
+ } else if(b0 == 0xe && q != NULL) { |
|
135 |
+ if(q->n == q->sz) { |
|
136 |
+ q->sz = q->sz < 4 ? 4 : q->sz * 2; |
|
137 |
+ q->dat = SDL_realloc(q->dat, q->sz * sizeof(*q->dat)); |
|
138 |
+ } |
|
139 |
+ q->dat[q->n++] = (m[0xb] << 8) + m[0xc]; |
|
140 |
+ q->dat[q->n++] = (m[0xd] << 8) + b1; |
|
141 |
+ } else if(b0 == 0xf && q != NULL) { |
|
142 |
+ q->ends = 1; |
|
143 |
+ return b1; |
|
144 |
+ } |
|
145 |
+} |
|
146 |
+ |
|
147 |
+int |
|
148 |
+initapu(Uxn *u, Uint8 id) |
|
149 |
+{ |
|
150 |
+ SDL_AudioSpec as; |
|
151 |
+ SDL_zero(as); |
|
152 |
+ as.freq = SAMPLE_FREQUENCY; |
|
153 |
+ as.format = AUDIO_S16; |
|
154 |
+ as.channels = 2; |
|
155 |
+ as.callback = play_all_notes; |
|
156 |
+ as.samples = 2048; |
|
157 |
+ as.userdata = u; |
|
158 |
+ audio_id = SDL_OpenAudioDevice(NULL, 0, &as, NULL, 0); |
|
159 |
+ if(!audio_id) |
|
160 |
+ return error("Audio", SDL_GetError()); |
|
161 |
+ id_addr = portuxn(u, id, "audio", audio_poke)->addr + 0xa; |
|
162 |
+ SDL_PauseAudioDevice(audio_id, 0); |
|
163 |
+ return 1; |
|
164 |
+} |
... | ... |
@@ -15,6 +15,9 @@ WITH REGARD TO THIS SOFTWARE. |
15 | 15 |
|
16 | 16 |
#include "uxn.h" |
17 | 17 |
|
18 |
+int initapu(Uxn *u, Uint8 id); |
|
19 |
+void stepapu(Uxn *u); |
|
20 |
+ |
|
18 | 21 |
#define HOR 48 |
19 | 22 |
#define VER 32 |
20 | 23 |
#define PAD 2 |
... | ... |
@@ -49,38 +52,12 @@ Uint8 font[][8] = { |
49 | 52 |
{0x00, 0x7e, 0x40, 0x7c, 0x40, 0x40, 0x7e, 0x00}, |
50 | 53 |
{0x00, 0x7e, 0x40, 0x40, 0x7c, 0x40, 0x40, 0x00}}; |
51 | 54 |
|
52 |
-#define SAMPLE_FREQUENCY 48000 |
|
53 |
- |
|
54 |
-static Uint32 note_periods[12] = { |
|
55 |
- /* middle C (C4) is note 60 */ |
|
56 |
- (Uint32)0xfa7e * SAMPLE_FREQUENCY, /* C-1 */ |
|
57 |
- (Uint32)0xec6f * SAMPLE_FREQUENCY, |
|
58 |
- (Uint32)0xdf2a * SAMPLE_FREQUENCY, /* D-1 */ |
|
59 |
- (Uint32)0xd2a4 * SAMPLE_FREQUENCY, |
|
60 |
- (Uint32)0xc6d1 * SAMPLE_FREQUENCY, /* E-1 */ |
|
61 |
- (Uint32)0xbba8 * SAMPLE_FREQUENCY, /* F-1 */ |
|
62 |
- (Uint32)0xb120 * SAMPLE_FREQUENCY, |
|
63 |
- (Uint32)0xa72f * SAMPLE_FREQUENCY, /* G-1 */ |
|
64 |
- (Uint32)0x9dcd * SAMPLE_FREQUENCY, |
|
65 |
- (Uint32)0x94f2 * SAMPLE_FREQUENCY, /* A-1 */ |
|
66 |
- (Uint32)0x8c95 * SAMPLE_FREQUENCY, |
|
67 |
- (Uint32)0x84b2 * SAMPLE_FREQUENCY /* B-1 */ |
|
68 |
-}; |
|
69 |
- |
|
70 |
-typedef struct audio_channel { |
|
71 |
- Uint32 period, count; |
|
72 |
- Sint32 age, a, d, s, r; |
|
73 |
- Sint16 value[2]; |
|
74 |
- Sint8 volume[2], phase; |
|
75 |
-} Channel; |
|
76 |
-Channel channels[4]; |
|
77 |
- |
|
78 | 55 |
static SDL_Window *gWindow; |
79 | 56 |
static SDL_Renderer *gRenderer; |
80 | 57 |
static SDL_Texture *gTexture; |
81 |
-static SDL_AudioDeviceID audio_id; |
|
58 |
+SDL_AudioDeviceID audio_id; |
|
82 | 59 |
static Screen screen; |
83 |
-static Device *devsystem, *devscreen, *devmouse, *devkey, *devctrl, *devaudio; |
|
60 |
+static Device *devsystem, *devscreen, *devmouse, *devkey, *devctrl; |
|
84 | 61 |
|
85 | 62 |
#pragma mark - Helpers |
86 | 63 |
|
... | ... |
@@ -241,61 +218,6 @@ togglezoom(Uxn *u) |
241 | 218 |
redraw(pixels, u); |
242 | 219 |
} |
243 | 220 |
|
244 |
-Sint16 |
|
245 |
-audio_envelope(Channel *c) |
|
246 |
-{ |
|
247 |
- if(c->age < c->a) |
|
248 |
- return 0x0888 * c->age / c->a; |
|
249 |
- else if(c->age < c->d) |
|
250 |
- return 0x0444 * (2 * c->d - c->a - c->age) / (c->d - c->a); |
|
251 |
- else if(c->age < c->s) |
|
252 |
- return 0x0444; |
|
253 |
- else if(c->age < c->r) |
|
254 |
- return 0x0444 * (c->r - c->age) / (c->r - c->s); |
|
255 |
- else |
|
256 |
- return 0x0000; |
|
257 |
-} |
|
258 |
- |
|
259 |
-void |
|
260 |
-audio_callback(void *userdata, Uint8 *stream, int len) |
|
261 |
-{ |
|
262 |
- Sint16 *samples = (Sint16 *)stream; |
|
263 |
- int i, j; |
|
264 |
- len >>= 2; /* use len for number of samples, not bytes */ |
|
265 |
- for(j = len * 2 - 1; j >= 0; --j) samples[j] = 0; |
|
266 |
- for(i = 0; i < 4; ++i) { |
|
267 |
- Channel *c = &channels[i]; |
|
268 |
- if(c->period < (1 << 20)) continue; |
|
269 |
- for(j = 0; j < len; ++j) { |
|
270 |
- c->age += 1; |
|
271 |
- c->count += 1 << 20; |
|
272 |
- while(c->count > c->period) { |
|
273 |
- Sint16 mul; |
|
274 |
- c->count -= c->period; |
|
275 |
- c->phase = !c->phase; |
|
276 |
- mul = (c->phase * 2 - 1) * audio_envelope(c); |
|
277 |
- c->value[0] = mul * c->volume[0]; |
|
278 |
- c->value[1] = mul * c->volume[1]; |
|
279 |
- } |
|
280 |
- samples[j * 2] += c->value[0]; |
|
281 |
- samples[j * 2 + 1] += c->value[1]; |
|
282 |
- } |
|
283 |
- } |
|
284 |
- (void)userdata; |
|
285 |
-} |
|
286 |
- |
|
287 |
-void |
|
288 |
-silence(void) |
|
289 |
-{ |
|
290 |
- int i; |
|
291 |
- for(i = 0; i < 4; ++i) { |
|
292 |
- Channel *c = &channels[i]; |
|
293 |
- c->volume[0] = 0; |
|
294 |
- c->volume[1] = 0; |
|
295 |
- c->period = 0; |
|
296 |
- } |
|
297 |
-} |
|
298 |
- |
|
299 | 221 |
void |
300 | 222 |
quit(void) |
301 | 223 |
{ |
... | ... |
@@ -313,7 +235,6 @@ quit(void) |
313 | 235 |
int |
314 | 236 |
init(void) |
315 | 237 |
{ |
316 |
- SDL_AudioSpec as; |
|
317 | 238 |
if(SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO) < 0) |
318 | 239 |
return error("Init", SDL_GetError()); |
319 | 240 |
gWindow = SDL_CreateWindow("Uxn", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, WIDTH * ZOOM, HEIGHT * ZOOM, SDL_WINDOW_SHOWN); |
... | ... |
@@ -328,18 +249,8 @@ init(void) |
328 | 249 |
if(!(pixels = (Uint32 *)malloc(WIDTH * HEIGHT * sizeof(Uint32)))) |
329 | 250 |
return error("Pixels", "Failed to allocate memory"); |
330 | 251 |
clear(pixels); |
331 |
- silence(); |
|
332 | 252 |
SDL_StartTextInput(); |
333 | 253 |
SDL_ShowCursor(SDL_DISABLE); |
334 |
- as.freq = SAMPLE_FREQUENCY; |
|
335 |
- as.format = AUDIO_S16; |
|
336 |
- as.channels = 2; |
|
337 |
- as.callback = audio_callback; |
|
338 |
- as.samples = 2048; |
|
339 |
- audio_id = SDL_OpenAudioDevice(NULL, 0, &as, NULL, 0); |
|
340 |
- if(!audio_id) |
|
341 |
- return error("Audio", SDL_GetError()); |
|
342 |
- SDL_PauseAudioDevice(audio_id, 0); |
|
343 | 254 |
screen.x1 = PAD * 8; |
344 | 255 |
screen.x2 = WIDTH - PAD * 8 - 1; |
345 | 256 |
screen.y1 = PAD * 8; |
... | ... |
@@ -504,29 +415,6 @@ file_poke(Uxn *u, Uint16 ptr, Uint8 b0, Uint8 b1) |
504 | 415 |
return b1; |
505 | 416 |
} |
506 | 417 |
|
507 |
-Uint8 |
|
508 |
-audio_poke(Uxn *u, Uint16 ptr, Uint8 b0, Uint8 b1) |
|
509 |
-{ |
|
510 |
- Uint8 *m = u->ram.dat; |
|
511 |
- m[PAGE_DEVICE + 0x0070 + b0] = b1; |
|
512 |
- if(b0 > 0x08 && b0 & 1) { |
|
513 |
- Uint16 addr = ptr + (b0 & 0x6); |
|
514 |
- Channel *c = &channels[(b0 & 0x6) >> 1]; |
|
515 |
- SDL_LockAudioDevice(audio_id); |
|
516 |
- c->period = note_periods[m[addr + 9] % 12] >> (m[addr + 9] / 12); |
|
517 |
- c->count %= c->period; |
|
518 |
- c->volume[0] = (m[addr + 8] >> 4) & 0xf; |
|
519 |
- c->volume[1] = m[addr + 8] & 0xf; |
|
520 |
- c->age = 0; |
|
521 |
- c->a = (SAMPLE_FREQUENCY >> 4) * ((m[addr] >> 4) & 0xf); |
|
522 |
- c->d = c->a + (SAMPLE_FREQUENCY >> 4) * (m[addr] & 0xf); |
|
523 |
- c->s = c->d + (SAMPLE_FREQUENCY >> 4) * ((m[addr + 1] >> 4) & 0xf); |
|
524 |
- c->r = c->s + (SAMPLE_FREQUENCY >> 4) * (m[addr + 1] & 0xf); |
|
525 |
- SDL_UnlockAudioDevice(audio_id); |
|
526 |
- } |
|
527 |
- return b1; |
|
528 |
-} |
|
529 |
- |
|
530 | 418 |
Uint8 |
531 | 419 |
midi_poke(Uxn *u, Uint16 ptr, Uint8 b0, Uint8 b1) |
532 | 420 |
{ |
... | ... |
@@ -586,9 +474,13 @@ start(Uxn *u) |
586 | 474 |
while(1) { |
587 | 475 |
SDL_Event event; |
588 | 476 |
double elapsed, start = SDL_GetPerformanceCounter(); |
477 |
+ SDL_LockAudioDevice(audio_id); |
|
589 | 478 |
while(SDL_PollEvent(&event) != 0) { |
590 | 479 |
switch(event.type) { |
591 |
- case SDL_QUIT: quit(); break; |
|
480 |
+ case SDL_QUIT: |
|
481 |
+ SDL_UnlockAudioDevice(audio_id); |
|
482 |
+ quit(); |
|
483 |
+ break; |
|
592 | 484 |
case SDL_MOUSEBUTTONUP: |
593 | 485 |
case SDL_MOUSEBUTTONDOWN: |
594 | 486 |
case SDL_MOUSEMOTION: |
... | ... |
@@ -611,6 +503,7 @@ start(Uxn *u) |
611 | 503 |
} |
612 | 504 |
} |
613 | 505 |
evaluxn(u, devscreen->vector); |
506 |
+ SDL_UnlockAudioDevice(audio_id); |
|
614 | 507 |
if(screen.reqdraw) |
615 | 508 |
redraw(pixels, u); |
616 | 509 |
elapsed = (SDL_GetPerformanceCounter() - start) / (double)SDL_GetPerformanceFrequency() * 1000.0f; |
... | ... |
@@ -641,7 +534,8 @@ main(int argc, char **argv) |
641 | 534 |
devkey = portuxn(&u, 0x05, "key", ppnil); |
642 | 535 |
devmouse = portuxn(&u, 0x06, "mouse", ppnil); |
643 | 536 |
portuxn(&u, 0x07, "file", file_poke); |
644 |
- devaudio = portuxn(&u, 0x08, "audio", audio_poke); |
|
537 |
+ if(!initapu(&u, 0x08)) |
|
538 |
+ return 1; |
|
645 | 539 |
portuxn(&u, 0x09, "midi", ppnil); |
646 | 540 |
portuxn(&u, 0x0a, "datetime", datetime_poke); |
647 | 541 |
portuxn(&u, 0x0b, "---", ppnil); |