uxn

Varvara Ordinator, written in ANSI C(SDL2)
git clone https://git.eamoncaddigan.net/uxn.git
Log | Files | Refs | README | LICENSE

commit d892f29ea1590d34e24e941b0c5c0965128e17c7
parent 38a9be86c2ab093abdcc600ff992448ab8e440cf
Author: Andrew Alderwick <andrew@alderwick.co.uk>
Date:   Sun, 25 Apr 2021 15:12:45 +0100

Implemented sample-based synth for APU

Diffstat:
Mprojects/demos/musictracker.usm | 198+++++++++++++++++++++++++++++++++++++++++++++++++++++--------------------------
Mprojects/examples/devices/audio.usm | 62+++++++++++++++++++++++++++++++++++++++-----------------------
Msrc/apu.c | 124++++++++++++++++++++++++++++++++++---------------------------------------------
Msrc/apu.h | 31+++++++------------------------
Msrc/emulator.c | 56+++++++++++++++++++++++++++++---------------------------
5 files changed, 261 insertions(+), 210 deletions(-)

diff --git a/projects/demos/musictracker.usm b/projects/demos/musictracker.usm @@ -21,7 +21,7 @@ @knob [ &x $2 &y $2 &value $1 ] @head [ &pos $1 ] @track [ &active $1 &ch1 $20 &ch2 $20 &ch3 $20 &ch4 $20 ] -@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 ] +@adsr [ &ch1 $2 &ch2 $2 &ch3 $2 &ch4 $2 ] @volume [ &ch1 $1 &ch2 $1 &ch3 $1 &ch4 $1 ] ( devices ) @@ -29,7 +29,7 @@ |00 @System [ &vector $2 &pad $6 &r $2 &g $2 &b $2 ] |10 @Console [ &vector $2 &pad $6 &char $1 &byte $1 &short $2 &string $2 ] |20 @Screen [ &vector $2 &width $2 &height $2 &pad $2 &x $2 &y $2 &addr $2 &color $1 ] -|30 @Audio [ &wave $2 &envelope $2 &pad $4 &volume $1 &pitch $1 &play $1 &value $2 &delay $2 &finish $1 ] +|30 @Audio [ &pad $8 &adsr $2 &len $2 &addr $2 &volume $1 &pitch $1 ] |40 @Controller [ &vector $2 &button $1 &key $1 ] |60 @Mouse [ &vector $2 &x $2 &y $2 &state $1 &chord $1 ] |70 @File [ &vector $2 &success $2 &offset $2 &pad $2 &name $2 &length $2 &load $2 &save $2 ] @@ -37,6 +37,7 @@ ( vectors ) |0100 + @noise-wave ( theme ) #e0fa .System/r DEO2 #30fa .System/g DEO2 #30fa .System/b DEO2 ( vectors ) ;on-screen .Screen/vector DEO2 @@ -55,10 +56,10 @@ .trkframe/x2 PEK2 .ctlframe/x2 POK2 .chnframe/y2 PEK2 .ctlframe/y2 POK2 ( default settings ) - ;adsr-envelope .Audio/envelope DEO2 - #00 .adsr/ch1a POK #40 .adsr/ch1d POK #80 .adsr/ch1s POK #c0 .adsr/ch1r POK #88 .volume/ch1 POK - #10 .adsr/ch2a POK #50 .adsr/ch2d POK #90 .adsr/ch2s POK #d0 .adsr/ch2r POK #88 .volume/ch2 POK - #20 .adsr/ch3a POK #60 .adsr/ch3d POK #a0 .adsr/ch3s POK #e0 .adsr/ch3r POK #88 .volume/ch3 POK + #048c .adsr/ch1 POK2 #88 .volume/ch1 POK + #159d .adsr/ch2 POK2 #88 .volume/ch2 POK + #26ae .adsr/ch3 POK2 #88 .volume/ch3 POK + #260e .adsr/ch4 POK2 #88 .volume/ch4 POK .volume/ch3 PEK .Audio/volume DEO @@ -120,9 +121,9 @@ BRK @play ( pitch -- ) - #80 ORA .Audio/pitch DEO - ;triangle-wave .Audio/wave DEO2 - .track/active PEK .Audio/play DEO + ;triangle-wave .Audio/addr DEO2 + ;triangle-wave/end ;triangle-wave SUB2 #0001 SFT2 .Audio/len DEO2 + .Audio/pitch DEO RTN @@ -154,21 +155,21 @@ BRK .Mouse/x DEI2 .ctlframe/x1 PEK2 SUB2 8- 8/ SWP POP #02 DIV DUP #00 NEQ ,&no-a JNZ - ;adsr #00 .track/active PEK #04 MUL ADD2 GET + .adsr .track/active PEK #02 MUL ADD PEK #10 .Mouse/state DEI #10 EQU #e0 MUL ADD ADD - ;adsr #00 .track/active PEK #04 MUL ADD2 PUT &no-a + .adsr .track/active PEK #02 MUL ADD POK &no-a DUP #01 NEQ ,&no-d JNZ - ;adsr #00 .track/active PEK #04 MUL ADD2 #0001 ADD2 GET - #10 .Mouse/state DEI #10 EQU #e0 MUL ADD ADD - ;adsr #00 .track/active PEK #04 MUL ADD2 #0001 ADD2 PUT &no-d + .adsr .track/active PEK #02 MUL ADD PEK + DUP #f0 AND STH #01 .Mouse/state DEI #10 EQU #0e MUL ADD ADD #0f AND STHr ADD + .adsr .track/active PEK #02 MUL ADD POK &no-d DUP #02 NEQ ,&no-s JNZ - ;adsr #00 .track/active PEK #04 MUL ADD2 #0002 ADD2 GET + .adsr .track/active PEK #02 MUL ADD #01 ADD PEK #10 .Mouse/state DEI #10 EQU #e0 MUL ADD ADD - ;adsr #00 .track/active PEK #04 MUL ADD2 #0002 ADD2 PUT &no-s + .adsr .track/active PEK #02 MUL ADD #01 ADD POK &no-s DUP #03 NEQ ,&no-r JNZ - ;adsr #00 .track/active PEK #04 MUL ADD2 #0003 ADD2 GET - #10 .Mouse/state DEI #10 EQU #e0 MUL ADD ADD - ;adsr #00 .track/active PEK #04 MUL ADD2 #0003 ADD2 PUT &no-r + .adsr .track/active PEK #02 MUL ADD #01 ADD PEK + DUP #f0 AND STH #01 .Mouse/state DEI #10 EQU #0e MUL ADD ADD #0f AND STHr ADD + .adsr .track/active PEK #02 MUL ADD #01 ADD POK &no-r DUP #05 NEQ ,&no-left JNZ ;volume #00 .track/active PEK ADD2 GET #10 .Mouse/state DEI #10 EQU #e0 MUL ADD ADD @@ -190,30 +191,44 @@ BRK DUP #ff NEQ ,&skip1 JNZ POP ,&listen2 JMP &skip1 - #00 SWP ;notes ADD2 GET #80 ORA .Audio/pitch DEO + .adsr/ch1 PEK2 .Audio/adsr DEO2 .volume/ch1 PEK .Audio/volume DEO - ;square-wave .Audio/wave DEO2 - #00 .Audio/play DEO + ;square-wave .Audio/addr DEO2 + ;square-wave/end ;square-wave SUB2 #0001 SFT2 .Audio/len DEO2 + #00 SWP ;notes ADD2 GET .Audio/pitch DEO &listen2 ;track/ch2 #00 .head/pos PEK #08 DIV ADD2 GET #01 SUB DUP #ff NEQ ,&skip2 JNZ POP ,&listen3 JMP &skip2 - #00 SWP ;notes ADD2 GET #80 ORA .Audio/pitch DEO + .adsr/ch2 PEK2 .Audio/adsr DEO2 .volume/ch2 PEK .Audio/volume DEO - ;square-wave .Audio/wave DEO2 - #01 .Audio/play DEO + ;triangle-wave .Audio/addr DEO2 + ;triangle-wave/end ;triangle-wave SUB2 #0001 SFT2 .Audio/len DEO2 + #00 SWP ;notes ADD2 GET .Audio/pitch DEO &listen3 ;track/ch3 #00 .head/pos PEK #08 DIV ADD2 GET #01 SUB DUP #ff NEQ ,&skip3 JNZ - POP ,&end JMP + POP ,&listen4 JMP &skip3 - #00 SWP ;notes ADD2 GET #80 ORA .Audio/pitch DEO + .adsr/ch3 PEK2 .Audio/adsr DEO2 .volume/ch3 PEK .Audio/volume DEO - ;triangle-wave .Audio/wave DEO2 - #02 .Audio/play DEO + ;sine-wave .Audio/addr DEO2 + ;sine-wave/end ;sine-wave SUB2 #0001 SFT2 .Audio/len DEO2 + #00 SWP ;notes ADD2 GET .Audio/pitch DEO + &listen4 + ;track/ch4 #00 .head/pos PEK #08 DIV ADD2 GET + #01 SUB + DUP #ff NEQ ,&skip4 JNZ + POP ,&end JMP + &skip4 + #0000 .Audio/adsr DEO2 + .volume/ch4 PEK .Audio/volume DEO + ;noise-wave .Audio/addr DEO2 + ;noise-wave-end ;noise-wave SUB2 #0001 SFT2 .Audio/len DEO2 + #00 SWP ;notes ADD2 GET #80 ORA .Audio/pitch DEO &end RTN @@ -319,14 +334,14 @@ RTN .trkframe/y1 PEK2 #0038 ADD2 ;draw-octave JSR2 .trkframe/x1 PEK2 #0028 SUB2 .Screen/x DEO2 .trkframe/y1 PEK2 #0030 ADD2 .Screen/y DEO2 - ;font_hex #0028 ADD2 .Screen/addr DEO2 + ;font_hex #0020 ADD2 .Screen/addr DEO2 #23 .Screen/color DEO .trkframe/x1 PEK2 #0030 SUB2 .Screen/x DEO2 ;font_hex #0060 ADD2 .Screen/addr DEO2 #23 .Screen/color DEO .trkframe/x1 PEK2 #0028 SUB2 .Screen/x DEO2 .trkframe/y1 PEK2 #0068 ADD2 .Screen/y DEO2 - ;font_hex #0020 ADD2 .Screen/addr DEO2 + ;font_hex #0018 ADD2 .Screen/addr DEO2 #23 .Screen/color DEO .trkframe/x1 PEK2 #0030 SUB2 .Screen/x DEO2 ;font_hex #0060 ADD2 .Screen/addr DEO2 @@ -342,8 +357,11 @@ RTN RTN @draw-knob ( x* y* value -- ) - + .track/active PEK #03 EQU ;&blank JNZ2 + + &force ( load ) .knob/value POK .knob/y POK2 .knob/x POK2 + .knob/x PEK2 .Screen/x DEO2 .knob/y PEK2 .Screen/y DEO2 ;knob_icns .Screen/addr DEO2 #21 .Screen/color DEO .knob/x PEK2 8+ .Screen/x DEO2 ;knob_icns 8+ .Screen/addr DEO2 #21 .Screen/color DEO @@ -360,31 +378,45 @@ RTN RTN + &blank + + ( load ) .knob/value POK .knob/y POK2 .knob/x POK2 + + .knob/x PEK2 .Screen/x DEO2 + .knob/y PEK2 .Screen/y DEO2 #20 .Screen/color DEO + .knob/x PEK2 8+ .Screen/x DEO2 #20 .Screen/color DEO + .knob/y PEK2 8+ .Screen/y DEO2 #20 .Screen/color DEO + .knob/x PEK2 .Screen/x DEO2 #20 .Screen/color DEO + .knob/x PEK2 #0004 ADD2 .Screen/x DEO2 + .knob/y PEK2 #0010 ADD2 .Screen/y DEO2 + #20 .Screen/color DEO +RTN + @draw-controls ( -- ) .ctlframe/x1 PEK2 .ctlframe/y1 PEK2 .ctlframe/x2 PEK2 .ctlframe/y2 PEK2 #01 ;line-rect JSR2 ( env ) .ctlframe/x1 PEK2 8+ .ctlframe/y1 PEK2 8+ #22 ;env_txt ;draw-label JSR2 .ctlframe/x1 PEK2 8+ .ctlframe/y1 PEK2 #0010 ADD2 - ;adsr #00 .track/active PEK #04 MUL ADD2 GET #04 SFT + .adsr .track/active PEK #02 MUL ADD PEK #04 SFT ;draw-knob JSR2 .ctlframe/x1 PEK2 #0018 ADD2 .ctlframe/y1 PEK2 #0010 ADD2 - ;adsr #00 .track/active PEK #04 MUL ADD2 #0001 ADD2 GET #04 SFT + .adsr .track/active PEK #02 MUL ADD PEK #0f AND ;draw-knob JSR2 .ctlframe/x1 PEK2 #0028 ADD2 .ctlframe/y1 PEK2 #0010 ADD2 - ;adsr #00 .track/active PEK #04 MUL ADD2 #0002 ADD2 GET #04 SFT + .adsr .track/active PEK #02 MUL ADD #01 ADD PEK #04 SFT ;draw-knob JSR2 .ctlframe/x1 PEK2 #0038 ADD2 .ctlframe/y1 PEK2 #0010 ADD2 - ;adsr #00 .track/active PEK #04 MUL ADD2 #0003 ADD2 GET #04 SFT + .adsr .track/active PEK #02 MUL ADD #01 ADD PEK #0f AND ;draw-knob JSR2 ( vol ) .ctlframe/x1 PEK2 #0058 ADD2 .ctlframe/y1 PEK2 8+ #22 ;vol_txt ;draw-label JSR2 .ctlframe/x1 PEK2 #0058 ADD2 .ctlframe/y1 PEK2 #0010 ADD2 ;volume #00 .track/active PEK ADD2 GET #04 SFT - ;draw-knob JSR2 + ;draw-knob/force JSR2 .ctlframe/x1 PEK2 #0068 ADD2 .ctlframe/y1 PEK2 #0010 ADD2 ;volume #00 .track/active PEK ADD2 GET #0f AND - ;draw-knob JSR2 + ;draw-knob/force JSR2 RTN @@ -469,31 +501,10 @@ RTN RTN -@adsr-envelope ( -- ) - #ff ;adsr #00 .Audio/play DEI #04 MUL ADD2 GET SOUND - #80 ;adsr #00 .Audio/play DEI #04 MUL ADD2 #0001 ADD2 GET SOUND - #80 ;adsr #00 .Audio/play DEI #04 MUL ADD2 #0002 ADD2 GET SOUND - #00 ;adsr #00 .Audio/play DEI #04 MUL ADD2 #0003 ADD2 GET SOUND - SOUND_FINISH - BRK - -@square-wave ( -- ) - #a800 SOUND - #a880 SOUND - #5800 SOUND - #5880 SOUND - BRK - -@triangle-wave ( -- ) - #ff40 SOUND - #0080 SOUND - #8040 SOUND - BRK - -@ch1_txt [ "CHN0 00 ] -@ch2_txt [ "CHN1 00 ] -@ch3_txt [ "CHN2 00 ] -@ch4_txt [ "---- 00 ] +@ch1_txt [ "SQR 00 ] +@ch2_txt [ "TRI 00 ] +@ch3_txt [ "SINE 00 ] +@ch4_txt [ "DRUM 00 ] @env_txt [ "Envelope 00 ] @vol_txt [ "Volume 00 ] @@ -588,4 +599,62 @@ RTN 0010 103c 1010 0c00 0000 4444 4444 3800 0000 4444 2828 1000 0000 4454 5454 2800 0000 4428 1028 4400 0000 4444 443c 0438 0000 7c08 1020 7c00 000c 0810 1008 0c00 0008 0808 0808 0800 0030 1008 0810 3000 0000 0032 4c00 0000 3c42 99a1 a199 423c -] -\ No newline at end of file +] + +@square-wave + a800 5800 + &end + +@triangle-wave + 8000 81ff 83ff 85ff 87ff 89ff 8bff 8dff 8fff 91ff 93ff 95ff 97ff + 99ff 9bff 9dff 9fff a1ff a3ff a5ff a7ff a9ff abff adff afff b1ff + b3ff b5ff b7ff b9ff bbff bdff bfff c1ff c3ff c5ff c7ff c9ff cbff + cdff cfff d1ff d3ff d5ff d7ff d9ff dbff ddff dfff e1ff e3ff e5ff + e7ff e9ff ebff edff efff f1ff f3ff f5ff f7ff f9ff fbff fdff ffff + fdff fbff f9ff f7ff f5ff f3ff f1ff efff edff ebff e9ff e7ff e5ff + e3ff e1ff dfff ddff dbff d9ff d7ff d5ff d3ff d1ff cfff cdff cbff + c9ff c7ff c5ff c3ff c1ff bfff bdff bbff b9ff b7ff b5ff b3ff b1ff + afff adff abff a9ff a7ff a5ff a3ff a1ff 9fff 9dff 9bff 99ff 97ff + 95ff 93ff 91ff 8fff 8dff 8bff 89ff 87ff 85ff 83ff 81ff 8000 7e00 + 7c00 7a00 7800 7600 7400 7200 7000 6e00 6c00 6a00 6800 6600 6400 + 6200 6000 5e00 5c00 5a00 5800 5600 5400 5200 5000 4e00 4c00 4a00 + 4800 4600 4400 4200 4000 3e00 3c00 3a00 3800 3600 3400 3200 3000 + 2e00 2c00 2a00 2800 2600 2400 2200 2000 1e00 1c00 1a00 1800 1600 + 1400 1200 1000 0e00 0c00 0a00 0800 0600 0400 0200 0001 0200 0400 + 0600 0800 0a00 0c00 0e00 1000 1200 1400 1600 1800 1a00 1c00 1e00 + 2000 2200 2400 2600 2800 2a00 2c00 2e00 3000 3200 3400 3600 3800 + 3a00 3c00 3e00 4000 4200 4400 4600 4800 4a00 4c00 4e00 5000 5200 + 5400 5600 5800 5a00 5c00 5e00 6000 6200 6400 6600 6800 6a00 6c00 + 6e00 7000 7200 7400 7600 7800 7a00 7c00 7e00 + &end + +@sine-wave + 8000 84c5 8989 8e49 9305 97b9 9c66 a108 a59e aa27 aea1 b30a b761 + bba5 bfd3 c3ea c7ea cbcf cf9a d349 d6d9 da4b dd9d e0cd e3db e6c6 + e98b ec2c eea6 f0f8 f322 f523 f6fb f8a8 fa2a fb81 fcac fdaa fe7c + ff20 ff98 ffe2 fffe ffed ffae ff42 fea9 fde3 fcef fbd0 fa84 f90c + f76a f59d f3a6 f186 ef3e ecce ea37 e77b e499 e194 de6c db23 d7b9 + d430 d088 ccc5 c8e6 c4ed c0db bcb2 b874 b422 afbd ab47 a6c1 a22e + 9d8f 98e5 9433 8f79 8ab9 85f6 8131 7c6b 77a7 72e5 6e28 6972 64c4 + 601f 5b85 56f9 527b 4e0e 49b2 4569 4136 3d18 3913 3527 3155 2d9f + 2a07 268d 2333 1ffb 1ce4 19f1 1721 1478 11f4 0f98 0d64 0b58 0976 + 07be 0631 04d0 039a 0290 01b4 0104 0081 002c 0004 0009 003d 009d + 012b 01e7 02cf 03e3 0524 0691 0828 09eb 0bd7 0ded 102b 1292 151f + 17d2 1aaa 1da6 20c6 2407 2769 2aea 2e8a 3247 361f 3a12 3e1d 4240 + 4679 4ac7 4f27 5399 581b 5cab 6147 65ee 6a9f 6f57 7415 78d8 7d9d + 8262 8727 8bea 90a8 9560 9a11 9eb8 a354 a7e4 ac66 b0d8 b538 b986 + bdbf c1e2 c5ed c9e0 cdb8 d175 d515 d896 dbf8 df39 e259 e555 e82d + eae0 ed6d efd4 f212 f428 f614 f7d7 f96e fadb fc1c fd30 fe18 fed4 + ff62 ffc2 fff6 fffb ffd3 ff7e fefb fe4b fd6f fc65 fb2f f9ce f841 + f689 f4a7 f29b f067 ee0b eb87 e8de e60e e31b e004 dccc d972 d5f8 + d260 ceaa cad8 c6ec c2e7 bec9 ba96 b64d b1f1 ad84 a906 a47a 9fe0 + 9b3b 968d 91d7 8d1a 8858 8394 7ece 7a09 7546 7086 6bcc 671a 6270 + 5dd1 593e 54b8 5042 4bdd 478b 434d 3f24 3b12 3719 333a 2f77 2bcf + 2846 24dc 2193 1e6b 1b66 1884 15c8 1331 10c1 0e79 0c59 0a62 0895 + 06f3 057b 042f 0310 021c 0156 00bd 0051 0012 0001 001d 0067 00df + 0183 0255 0353 047e 05d5 0757 0904 0adc 0cdd 0f07 1159 13d3 1674 + 1939 1c24 1f32 2262 25b4 2926 2cb6 3065 3430 3815 3c15 402c 445a + 489e 4cf5 515e 55d8 5a61 5ef7 6399 6846 6cfa 71b6 7676 7b3a + &end + +@noise-wave-end diff --git a/projects/examples/devices/audio.usm b/projects/examples/devices/audio.usm @@ -9,7 +9,7 @@ |00 @System [ &vector $2 &pad $6 &r $2 &g $2 &b $2 ] |20 @Screen [ &vector $2 &width $2 &height $2 &pad $2 &x $2 &y $2 &addr $2 &color $1 ] -|30 @Audio [ &wave $2 &env $2 &pad $4 &vol $1 &pitch $1 &play $1 &value $2 &delay $2 &finish $1 ] +|30 @Audio [ &pad $8 &adsr $2 &len $2 &addr $2 &volume $1 &pitch $1 ] |0100 ( -> ) @@ -19,9 +19,10 @@ #0ff0 .System/b DEO2 ;on-frame .Screen/vector DEO2 ( run on-frame every 1/60th of a second ) - #ff .Audio/vol DEO ( set volume to max ) - ;saw .Audio/wave DEO2 ( set waveform to saw for audio engine ) - ;env .Audio/env DEO2 ( set envelope for audio engine ) + #ff .Audio/volume DEO ( set volume to max ) + ;saw .Audio/addr DEO2 ( set waveform to saw for audio engine ) + ;saw/end ;saw SUB2 #0002 SFT2 .Audio/len DEO2 + #1202 .Audio/adsr DEO2 ( set envelope for audio engine ) BRK @@ -34,8 +35,7 @@ BRK ;melody #00 .progress PEK ADD2 GET ( play note ) - DUP #80 ORA .Audio/pitch DEO - #01 .Audio/play DEO + DUP .Audio/pitch DEO ( erase last note ) #20 .Screen/color DEO @@ -59,23 +59,39 @@ BRK ( defines a sawtooth wave. ) @saw ( -> ) - - #6000 .Audio/value DEO2 - #0000 .Audio/delay DEO2 ( move to volume #600 after 0 delay ) - #0000 .Audio/value DEO2 - #ffff .Audio/delay DEO2 ( reach volume 0 after the whole note. Interpolated linearly ) - -BRK - -( defines an envelope ) - -@env ( -> ) - - #ffff .Audio/value DEO2 - #1000 .Audio/delay DEO2 ( move pretty quickly to volume #ffff (maximum) ) - #0000 .Audio/value DEO2 - #4000 .Audio/delay DEO2 ( interpolating linearly, move to #0000 after a delay of #4000 where #8000 is half a second ) - #00 .Audio/finish DEO ( end the envelope ) + dfa0 df40 dee0 de80 de20 ddc0 dd60 dd00 + dca0 dc40 dbe0 db80 db20 dac0 da60 da00 + d9a0 d940 d8e0 d880 d820 d7c0 d760 d700 + d6a0 d640 d5e0 d580 d520 d4c0 d460 d400 + d3a0 d340 d2e0 d280 d220 d1c0 d160 d100 + d0a0 d040 cfe0 cf80 cf20 cec0 ce60 ce00 + cda0 cd40 cce0 cc80 cc20 cbc0 cb60 cb00 + caa0 ca40 c9e0 c980 c920 c8c0 c860 c800 + c7a0 c740 c6e0 c680 c620 c5c0 c560 c500 + c4a0 c440 c3e0 c380 c320 c2c0 c260 c200 + c1a0 c140 c0e0 c080 c020 bfc0 bf60 bf00 + bea0 be40 bde0 bd80 bd20 bcc0 bc60 bc00 + bba0 bb40 bae0 ba80 ba20 b9c0 b960 b900 + b8a0 b840 b7e0 b780 b720 b6c0 b660 b600 + b5a0 b540 b4e0 b480 b420 b3c0 b360 b300 + b2a0 b240 b1e0 b180 b120 b0c0 b060 b000 + afa0 af40 aee0 ae80 ae20 adc0 ad60 ad00 + aca0 ac40 abe0 ab80 ab20 aac0 aa60 aa00 + a9a0 a940 a8e0 a880 a820 a7c0 a760 a700 + a6a0 a640 a5e0 a580 a520 a4c0 a460 a400 + a3a0 a340 a2e0 a280 a220 a1c0 a160 a100 + a0a0 a040 9fe0 9f80 9f20 9ec0 9e60 9e00 + 9da0 9d40 9ce0 9c80 9c20 9bc0 9b60 9b00 + 9aa0 9a40 99e0 9980 9920 98c0 9860 9800 + 97a0 9740 96e0 9680 9620 95c0 9560 9500 + 94a0 9440 93e0 9380 9320 92c0 9260 9200 + 91a0 9140 90e0 9080 9020 8fc0 8f60 8f00 + 8ea0 8e40 8de0 8d80 8d20 8cc0 8c60 8c00 + 8ba0 8b40 8ae0 8a80 8a20 89c0 8960 8900 + 88a0 8840 87e0 8780 8720 86c0 8660 8600 + 85a0 8540 84e0 8480 8420 83c0 8360 8300 + 82a0 8240 81e0 8180 8120 80c0 8060 8000 + &end BRK diff --git a/src/apu.c b/src/apu.c @@ -13,87 +13,69 @@ WITH REGARD TO THIS SOFTWARE. #include "uxn.h" #include "apu.h" -static Uint32 note_advances[12] = { - 0x82d01286 / (SAMPLE_FREQUENCY / 30), /* C7 */ - 0x8a976073 / (SAMPLE_FREQUENCY / 30), - 0x92d5171d / (SAMPLE_FREQUENCY / 30), /* D7 */ - 0x9b904100 / (SAMPLE_FREQUENCY / 30), - 0xa4d053c8 / (SAMPLE_FREQUENCY / 30), /* E7 */ - 0xae9d36b0 / (SAMPLE_FREQUENCY / 30), /* F7 */ - 0xb8ff493e / (SAMPLE_FREQUENCY / 30), - 0xc3ff6a72 / (SAMPLE_FREQUENCY / 30), /* G7 */ - 0xcfa70054 / (SAMPLE_FREQUENCY / 30), - 0xdc000000 / (SAMPLE_FREQUENCY / 30), /* A7 */ - 0xe914f623 / (SAMPLE_FREQUENCY / 30), - 0xf6f11003 / (SAMPLE_FREQUENCY / 30) /* B7 */ +#define NOTE_PERIOD 0x10000 +#define ADSR_STEP (SAMPLE_FREQUENCY / 0xf) + +/* clang-format off */ + +static Uint32 advances[12] = { + 0x80000, 0x879c8, 0x8facd, 0x9837f, 0xa1451, 0xaadc1, + 0xb504f, 0xbfc88, 0xcb2ff, 0xd7450, 0xe411f, 0xf1a1c }; -static void -render_note(Apu *apu, Uxn *u, int note_i, Sint16 *samples, int n_samples) +/* clang-format on */ + +static Sint32 +envelope(Apu *c, Uint32 age) { - int i; - Note *note = &apu->notes[note_i]; - while(n_samples--) { - Sint32 sample = 1; - for(i = 0; i < 2; ++i) { - WaveformGenerator *wv = &note->wv[i]; - apu->queue = &wv->queue; - wv->count += wv->advance; - while(wv->count > wv->period) { - wv->count -= wv->period; - wv->start_value = wv->end_value; - if(apu->queue->i == apu->queue->n) { - apu->queue->i = apu->queue->n = 0; - if(!apu->queue->finishes) { - *apu->channel_ptr = note_i; - evaluxn(u, wv->vector); - } - } - if(!apu->queue->n) { - note->playing = 0; - return; - } - wv->end_value = (Sint16)apu->queue->dat[apu->queue->i++]; - wv->period = (30 << 4) * apu->queue->dat[apu->queue->i++]; - } - if(wv->period >> 9) - sample *= wv->start_value + (Sint32)(wv->end_value - wv->start_value) * (Sint32)(wv->count >> 10) / (Sint32)(wv->period >> 10); - else - sample *= wv->end_value; - } - for(i = 0; i < 2; ++i) - *(samples++) += sample / 0xf * note->volume[i] / 0x10000; - } + if(!c->r) return 0x0888; + if(age < c->a) return 0x0888 * age / c->a; + if(age < c->d) return 0x0444 * (2 * c->d - c->a - age) / (c->d - c->a); + if(age < c->s) return 0x0444; + if(age < c->r) return 0x0444 * (c->r - age) / (c->r - c->s); + c->advance = 0; + return 0x0000; } void -apu_render(Apu *apu, Uxn *u, Sint16 *samples, int n_samples) +apu_render(Apu *c, Sint16 *sample, Sint16 *end) { - int i; - for(i = 0; i < n_samples * 2; ++i) - samples[i] = 0; - for(i = 0; i < apu->n_notes; ++i) - if(apu->notes[i].playing) render_note(apu, u, i, samples, n_samples); - apu->queue = NULL; + Sint32 s; + if(!c->advance || !c->period) return; + while(sample < end) { + c->count += c->advance; + c->i += c->count / c->period; + c->count %= c->period; + if(c->i >= c->len) { + if(!c->repeat) { + c->advance = 0; + return; + } + c->i %= c->len; + } + s = (Sint16)(mempeek16(c->addr, c->i * 2) + 0x8000) * envelope(c, c->age++); + *sample++ += s * c->volume_l / 0x8000; + *sample++ += s * c->volume_r / 0x8000; + } } void -apu_play_note(Note *note, Uint16 wave_vector, Uint16 envelope_vector, Uint8 volume, Uint8 pitch, Uint8 impl) +apu_start(Apu *c, Uint16 adsr, Uint8 pitch) { - int i; - if(pitch >= 108 || impl == 0) return; - note->playing = 1; - note->impl = impl; - for(i = 0; i < 2; ++i) { - note->volume[i] = 0xf & (volume >> 4 * (1 - i)); - note->wv[i].count = note->wv[i].period = 0; - note->wv[i].end_value = 0x8000 * (1 - i); - note->wv[i].queue.n = note->wv[i].queue.i = 0; - note->wv[i].queue.finishes = 0; - note->wv[i].queue.is_envelope = i; + if(pitch < 108 && c->len) + c->advance = advances[pitch % 12] >> (8 - pitch / 12); + else { + c->advance = 0; + return; } - note->wv[0].vector = wave_vector; - note->wv[0].advance = note_advances[pitch % 12] >> (8 - pitch / 12); - note->wv[1].vector = envelope_vector; - note->wv[1].advance = (30 << 20) / SAMPLE_FREQUENCY; + c->a = ADSR_STEP * (adsr >> 12); + c->d = ADSR_STEP * (adsr >> 8 & 0xf) + c->a; + c->s = ADSR_STEP * (adsr >> 4 & 0xf) + c->d; + c->r = ADSR_STEP * (adsr >> 0 & 0xf) + c->s; + c->age = 0; + c->i = 0; + if(c->len <= 0x100) /* single cycle mode */ + c->period = NOTE_PERIOD * 337 / 2 / c->len; + else /* sample repeat mode */ + c->period = NOTE_PERIOD; } diff --git a/src/apu.h b/src/apu.h @@ -14,31 +14,14 @@ typedef unsigned int Uint32; typedef signed int Sint32; #define SAMPLE_FREQUENCY 44100 +#define POLYPHONY 4 typedef struct { - Uint16 *dat; - Uint8 i, n, sz, finishes, is_envelope; -} Queue; - -typedef struct { - Uint32 count, advance, period; - Uint16 vector; - Sint16 start_value, end_value; - Queue queue; -} WaveformGenerator; - -typedef struct { - WaveformGenerator wv[2]; - Sint8 volume[2]; - Uint8 playing, impl; -} Note; - -typedef struct { - Queue *queue; - Note *notes; - Uint8 *channel_ptr; - int n_notes; + Uint8 *addr; + Uint32 count, advance, period, age, a, d, s, r; + Uint16 i, len; + Uint8 volume_l, volume_r, pitch, repeat; } Apu; -void apu_render(Apu *apu, Uxn *u, Sint16 *samples, int n_samples); -void apu_play_note(Note *note, Uint16 wave_vector, Uint16 envelope_vector, Uint8 volume, Uint8 pitch, Uint8 impl); +void apu_render(Apu *c, Sint16 *sample, Sint16 *end); +void apu_start(Apu *c, Uint16 adsr, Uint8 pitch); diff --git a/src/emulator.c b/src/emulator.c @@ -22,8 +22,8 @@ static SDL_Window *gWindow; static SDL_Renderer *gRenderer; static SDL_Texture *gTexture; static Ppu ppu; -static Apu apu; -static Device *devscreen, *devmouse, *devctrl, *devapu; +static Apu apu[POLYPHONY]; +static Device *devscreen, *devmouse, *devctrl; Uint8 zoom = 0, debug = 0, reqdraw = 0; @@ -43,7 +43,12 @@ error(char *msg, const char *err) static void audio_callback(void *u, Uint8 *stream, int len) { - apu_render(&apu, (Uxn *)u, (Sint16 *)stream, len >> 2); + int i; + Sint16 *samples = (Sint16 *)stream; + SDL_memset(stream, 0, len); + for(i = 0; i < POLYPHONY; ++i) + apu_render(&apu[i], samples, samples + len / 2); + (void)u; } void @@ -92,7 +97,7 @@ quit(void) } int -init(Uxn *u) +init(void) { SDL_AudioSpec as; if(!initppu(&ppu, 48, 32, 16)) @@ -116,7 +121,7 @@ init(Uxn *u) as.channels = 2; as.callback = audio_callback; as.samples = 512; - as.userdata = u; + as.userdata = NULL; audio_id = SDL_OpenAudioDevice(NULL, 0, &as, NULL, 0); if(!audio_id) return error("Audio", SDL_GetError()); @@ -242,22 +247,23 @@ file_talk(Device *d, Uint8 b0, Uint8 w) static void audio_talk(Device *d, Uint8 b0, Uint8 w) { - if(w && b0 == 0xa) { - if(d->dat[0xa] >= apu.n_notes) apu.notes = SDL_realloc(apu.notes, (d->dat[0xa] + 1) * sizeof(Note)); - while(d->dat[0xa] >= apu.n_notes) SDL_zero(apu.notes[apu.n_notes++]); - 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); - } else if(w && b0 == 0xe && apu.queue != NULL) { - if(apu.queue->n == apu.queue->sz) { - apu.queue->sz = apu.queue->sz < 4 ? 4 : apu.queue->sz * 2; - apu.queue->dat = SDL_realloc(apu.queue->dat, apu.queue->sz * sizeof(*apu.queue->dat)); - } - if(apu.queue->is_envelope) - apu.queue->dat[apu.queue->n++] = mempeek16(d->dat, 0xb) >> 1; - else - apu.queue->dat[apu.queue->n++] = mempeek16(d->dat, 0xb) + 0x8000; - apu.queue->dat[apu.queue->n++] = mempeek16(d->dat, 0xd); - } else if(w && b0 == 0xf && apu.queue != NULL) - apu.queue->finishes = 1; + Apu *c; + if(!w) return; + c = &apu[d->dat[0x7] % POLYPHONY]; + SDL_LockAudioDevice(audio_id); + if(b0 == 0x1) c->period -= (Sint16)mempeek16(d->dat, 0x0); + if(b0 == 0x3 || b0 == 0xf) c->len = mempeek16(d->dat, (b0 & 0x8) | 0x2); + if(b0 == 0x5 || b0 == 0xf) c->addr = &d->mem[mempeek16(d->dat, (b0 & 0x8) | 0x4)]; + if(b0 == 0x6 || b0 == 0xf) { + c->volume_l = d->dat[(b0 & 0x8) | 0x6] >> 4; + c->volume_r = d->dat[(b0 & 0x8) | 0x6] & 0xf; + } + if(b0 == 0xf) { + c->repeat = !(d->dat[0xf] & 0x80); + apu_start(c, mempeek16(d->dat, 0x8), d->dat[0xf] & 0x7f); + d->dat[0x7]++; + } + SDL_UnlockAudioDevice(audio_id); } void @@ -297,7 +303,6 @@ start(Uxn *u) while(1) { SDL_Event event; double elapsed, start = SDL_GetPerformanceCounter(); - SDL_LockAudioDevice(audio_id); while(SDL_PollEvent(&event) != 0) { switch(event.type) { case SDL_QUIT: @@ -326,7 +331,6 @@ start(Uxn *u) } } evaluxn(u, mempeek16(devscreen->dat, 0)); - SDL_UnlockAudioDevice(audio_id); if(reqdraw) redraw(ppu.output, u); elapsed = (SDL_GetPerformanceCounter() - start) / (double)SDL_GetPerformanceFrequency() * 1000.0f; @@ -347,13 +351,13 @@ main(int argc, char **argv) return error("Boot", "Failed"); if(!loaduxn(&u, argv[1])) return error("Load", "Failed"); - if(!init(&u)) + if(!init()) return error("Init", "Failed"); portuxn(&u, 0x0, "system", system_talk); portuxn(&u, 0x1, "console", console_talk); devscreen = portuxn(&u, 0x2, "screen", screen_talk); - devapu = portuxn(&u, 0x3, "audio", audio_talk); + portuxn(&u, 0x3, "audio", audio_talk); devctrl = portuxn(&u, 0x4, "controller", nil_talk); portuxn(&u, 0x5, "---", nil_talk); devmouse = portuxn(&u, 0x6, "mouse", nil_talk); @@ -367,8 +371,6 @@ main(int argc, char **argv) portuxn(&u, 0xe, "---", nil_talk); portuxn(&u, 0xf, "---", nil_talk); - apu.channel_ptr = &devapu->dat[0xa]; - /* Write screen size to dev/screen */ mempoke16(devscreen->dat, 2, ppu.hor * 8); mempoke16(devscreen->dat, 4, ppu.ver * 8);