| ... | ... |
@@ -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); |