mirror of
https://git.sr.ht/~rabbits/uxn
synced 2024-11-23 22:35:11 +00:00
Added Uxn-based synth
This commit is contained in:
parent
f629564269
commit
f3bf1a74db
6 changed files with 257 additions and 176 deletions
5
build.sh
5
build.sh
|
@ -6,6 +6,7 @@ clang-format -i src/uxn.h
|
|||
clang-format -i src/uxn.c
|
||||
clang-format -i src/emulator.c
|
||||
clang-format -i src/debugger.c
|
||||
clang-format -i src/apu.c
|
||||
|
||||
echo "Cleaning.."
|
||||
rm -f ./bin/assembler
|
||||
|
@ -19,12 +20,12 @@ if [ "${1}" = '--debug' ];
|
|||
then
|
||||
echo "[debug]"
|
||||
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
|
||||
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
|
||||
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
|
||||
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
|
||||
else
|
||||
cc src/assembler.c -std=c89 -Os -DNDEBUG -g0 -s -Wall -Wno-unknown-pragmas -o bin/assembler
|
||||
cc src/uxn.c src/debugger.c -std=c89 -Os -DNDEBUG -g0 -s -Wall -Wno-unknown-pragmas -o bin/debugger
|
||||
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
|
||||
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
|
||||
fi
|
||||
|
||||
echo "Assembling.."
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
|0140 ;Keys { key 1 }
|
||||
|0150 ;Mouse { x 2 y 2 state 1 chord 1 }
|
||||
|0160 ;File { pad 8 name 2 length 2 load 2 save 2 }
|
||||
|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 }
|
||||
|0180 ;Audio { wave 2 envelope 2 pad 4 volume 1 pitch 1 play 1 value 2 delay 2 finish 1 }
|
||||
|01F0 ;System { pad 8 r 2 g 2 b 2 }
|
||||
|
||||
( vectors )
|
||||
|
|
|
@ -6,6 +6,9 @@
|
|||
%++ { #0001 ADD2 }
|
||||
%MOD { DUP2 DIV MUL SUB }
|
||||
%TRACK { ,track.ch1 #00 ~track.active #0020 MUL2 ADD2 }
|
||||
%SOUND { STH #00 =Audio.value STHr #00 =Audio.delay }
|
||||
%SOUND2 { =Audio.value =Audio.delay }
|
||||
%SOUND_FINISH { #00 =Audio.finish }
|
||||
|
||||
( variables )
|
||||
|
||||
|
@ -19,6 +22,8 @@
|
|||
;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 }
|
||||
;volume { ch1 1 ch2 1 ch3 1 ch4 1 }
|
||||
|
||||
( devices )
|
||||
|
||||
|
@ -30,7 +35,7 @@
|
|||
|0150 ;Keys { key 1 }
|
||||
|0160 ;Mouse { vector 2 x 2 y 2 state 1 chord 1 }
|
||||
|0170 ;File { pad 8 name 2 length 2 load 2 save 2 }
|
||||
|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 }
|
||||
|0180 ;Audio { wave 2 envelope 2 pad 4 volume 1 pitch 1 play 1 value 2 delay 2 finish 1 }
|
||||
|
||||
( vectors )
|
||||
|
||||
|
@ -51,9 +56,10 @@
|
|||
~trkframe.x2 =ctlframe.x2 ~chnframe.y2 =ctlframe.y2
|
||||
|
||||
( default settings )
|
||||
#048c =Audio.ch1adsr #88 =Audio.ch1vol
|
||||
#159d =Audio.ch2adsr #88 =Audio.ch2vol
|
||||
#26ae =Audio.ch3adsr #88 =Audio.ch3vol
|
||||
,adsr-envelope =Audio.envelope
|
||||
#00 =adsr.ch1a #40 =adsr.ch1d #80 =adsr.ch1s #c0 =adsr.ch1r #88 =volume.ch1
|
||||
#10 =adsr.ch2a #50 =adsr.ch2d #90 =adsr.ch2s #d0 =adsr.ch2r #88 =volume.ch2
|
||||
#20 =adsr.ch3a #60 =adsr.ch3d #a0 =adsr.ch3s #e0 =adsr.ch3r #88 =volume.ch3
|
||||
|
||||
,draw-timeline JSR2
|
||||
,draw-controls JSR2
|
||||
|
@ -110,29 +116,29 @@ BRK
|
|||
|
||||
~Mouse.x ~ctlframe.x1 SUB2 8- 8/ SWP POP #02 DIV
|
||||
DUP #00 NEQ ^$no-a JNZ
|
||||
,Audio #00 ~track.active #02 MUL ADD2 PEK2
|
||||
#10 ~Mouse.state #10 EQU #e0 MUL ADD ADD
|
||||
,Audio #00 ~track.active #02 MUL ADD2 POK2 $no-a
|
||||
,adsr #00 ~track.active #04 MUL ADD2 PEK2
|
||||
#10 ~Mouse.state #10 EQU #e0 MUL ADD ADD
|
||||
,adsr #00 ~track.active #04 MUL ADD2 POK2 $no-a
|
||||
DUP #01 NEQ ^$no-d JNZ
|
||||
,Audio #00 ~track.active #02 MUL ADD2 PEK2
|
||||
DUP #f0 AND STH #01 ~Mouse.state #10 EQU #0e MUL ADD ADD #0f AND STHr ADD
|
||||
,Audio #00 ~track.active #02 MUL ADD2 POK2 $no-d
|
||||
,adsr #00 ~track.active #04 MUL ADD2 #0001 ADD2 PEK2
|
||||
#10 ~Mouse.state #10 EQU #e0 MUL ADD ADD
|
||||
,adsr #00 ~track.active #04 MUL ADD2 #0001 ADD2 POK2 $no-d
|
||||
DUP #02 NEQ ^$no-s JNZ
|
||||
,Audio #00 ~track.active #02 MUL ADD2 ++ PEK2
|
||||
#10 ~Mouse.state #10 EQU #e0 MUL ADD ADD
|
||||
,Audio #00 ~track.active #02 MUL ADD2 ++ POK2 $no-s
|
||||
,adsr #00 ~track.active #04 MUL ADD2 #0002 ADD2 PEK2
|
||||
#10 ~Mouse.state #10 EQU #e0 MUL ADD ADD
|
||||
,adsr #00 ~track.active #04 MUL ADD2 #0002 ADD2 POK2 $no-s
|
||||
DUP #03 NEQ ^$no-r JNZ
|
||||
,Audio #00 ~track.active #02 MUL ADD2 ++ PEK2
|
||||
DUP #f0 AND STH #01 ~Mouse.state #10 EQU #0e MUL ADD ADD #0f AND STHr ADD
|
||||
,Audio #00 ~track.active #02 MUL ADD2 ++ POK2 $no-r
|
||||
,adsr #00 ~track.active #04 MUL ADD2 #0003 ADD2 PEK2
|
||||
#10 ~Mouse.state #10 EQU #e0 MUL ADD ADD
|
||||
,adsr #00 ~track.active #04 MUL ADD2 #0003 ADD2 POK2 $no-r
|
||||
DUP #05 NEQ ^$no-left JNZ
|
||||
,Audio 8+ #00 ~track.active #02 MUL ADD2 PEK2
|
||||
#10 ~Mouse.state #10 EQU #e0 MUL ADD ADD
|
||||
,Audio 8+ #00 ~track.active #02 MUL ADD2 POK2 $no-left
|
||||
,volume #00 ~track.active ADD2 PEK2
|
||||
#10 ~Mouse.state #10 EQU #e0 MUL ADD ADD
|
||||
,volume #00 ~track.active ADD2 POK2 $no-left
|
||||
DUP #06 NEQ ^$no-right JNZ
|
||||
,Audio 8+ #00 ~track.active #02 MUL ADD2 PEK2
|
||||
,volume #00 ~track.active ADD2 PEK2
|
||||
DUP #f0 AND STH #01 ~Mouse.state #10 EQU #0e MUL ADD ADD #0f AND STHr ADD
|
||||
,Audio 8+ #00 ~track.active #02 MUL ADD2 POK2 $no-right
|
||||
,volume #00 ~track.active ADD2 POK2 $no-right
|
||||
POP
|
||||
( release ) #00 =Mouse.state
|
||||
,draw-controls JSR2
|
||||
|
@ -146,21 +152,30 @@ BRK
|
|||
DUP #ff NEQ ^$skip1 JNZ
|
||||
POP ^$listen2 JMP
|
||||
$skip1
|
||||
#00 SWP ,notes ADD2 PEK2 =Audio.ch1pitch
|
||||
#00 SWP ,notes ADD2 PEK2 =Audio.pitch
|
||||
~volume.ch1 =Audio.volume
|
||||
,square-wave =Audio.wave
|
||||
#00 =Audio.play
|
||||
$listen2
|
||||
,track.ch2 #00 ~head.pos #08 DIV ADD2 PEK2
|
||||
#01 SUB
|
||||
DUP #ff NEQ ^$skip2 JNZ
|
||||
POP ^$listen3 JMP
|
||||
$skip2
|
||||
#00 SWP ,notes ADD2 PEK2 =Audio.ch2pitch
|
||||
#00 SWP ,notes ADD2 PEK2 =Audio.pitch
|
||||
~volume.ch2 =Audio.volume
|
||||
,square-wave =Audio.wave
|
||||
#01 =Audio.play
|
||||
$listen3
|
||||
,track.ch3 #00 ~head.pos #08 DIV ADD2 PEK2
|
||||
#01 SUB
|
||||
DUP #ff NEQ ^$skip3 JNZ
|
||||
POP ^$end JMP
|
||||
$skip3
|
||||
#00 SWP ,notes ADD2 PEK2 =Audio.ch3pitch
|
||||
#00 SWP ,notes ADD2 PEK2 =Audio.pitch
|
||||
~volume.ch3 =Audio.volume
|
||||
,triangle-wave =Audio.wave
|
||||
#02 =Audio.play
|
||||
$end
|
||||
|
||||
RTN
|
||||
|
@ -264,18 +279,18 @@ RTN
|
|||
~trkframe.x1 #0018 SUB2 DUP2 ~trkframe.y1 ,draw-octave JSR2
|
||||
~trkframe.y1 #0038 ADD2 ,draw-octave JSR2
|
||||
~trkframe.x1 #0028 SUB2 =Sprite.x
|
||||
~trkframe.y1 =Sprite.y
|
||||
,font_hex #0060 ADD2 =Sprite.addr
|
||||
~trkframe.y1 #0030 ADD2 =Sprite.y
|
||||
,font_hex #0028 ADD2 =Sprite.addr
|
||||
#03 =Sprite.color
|
||||
~trkframe.x1 #0030 SUB2 =Sprite.x
|
||||
,font_hex #0020 ADD2 =Sprite.addr
|
||||
,font_hex #0060 ADD2 =Sprite.addr
|
||||
#03 =Sprite.color
|
||||
~trkframe.x1 #0028 SUB2 =Sprite.x
|
||||
~trkframe.y1 #0038 ADD2 =Sprite.y
|
||||
,font_hex #0060 ADD2 =Sprite.addr
|
||||
~trkframe.y1 #0068 ADD2 =Sprite.y
|
||||
,font_hex #0020 ADD2 =Sprite.addr
|
||||
#03 =Sprite.color
|
||||
~trkframe.x1 #0030 SUB2 =Sprite.x
|
||||
,font_hex #0018 ADD2 =Sprite.addr
|
||||
,font_hex #0060 ADD2 =Sprite.addr
|
||||
#03 =Sprite.color
|
||||
|
||||
RTN
|
||||
|
@ -312,24 +327,24 @@ RTN
|
|||
( env )
|
||||
~ctlframe.x1 8+ ~ctlframe.y1 8+ #02 ,env_txt ,draw-label JSR2
|
||||
~ctlframe.x1 8+ ~ctlframe.y1 #0010 ADD2
|
||||
,Audio #00 ~track.active #02 MUL ADD2 PEK2 #04 SFT
|
||||
,adsr #00 ~track.active #04 MUL ADD2 PEK2 #04 SFT
|
||||
,draw-knob JSR2
|
||||
~ctlframe.x1 #0018 ADD2 ~ctlframe.y1 #0010 ADD2
|
||||
,Audio #00 ~track.active #02 MUL ADD2 PEK2 #0f AND
|
||||
,adsr #00 ~track.active #04 MUL ADD2 #0001 ADD2 PEK2 #04 SFT
|
||||
,draw-knob JSR2
|
||||
~ctlframe.x1 #0028 ADD2 ~ctlframe.y1 #0010 ADD2
|
||||
,Audio #00 ~track.active #02 MUL ADD2 ++ PEK2 #04 SFT
|
||||
,adsr #00 ~track.active #04 MUL ADD2 #0002 ADD2 PEK2 #04 SFT
|
||||
,draw-knob JSR2
|
||||
~ctlframe.x1 #0038 ADD2 ~ctlframe.y1 #0010 ADD2
|
||||
,Audio #00 ~track.active #02 MUL ADD2 ++ PEK2 #0f AND
|
||||
,adsr #00 ~track.active #04 MUL ADD2 #0003 ADD2 PEK2 #04 SFT
|
||||
,draw-knob JSR2
|
||||
( vol )
|
||||
~ctlframe.x1 #0058 ADD2 ~ctlframe.y1 8+ #02 ,vol_txt ,draw-label JSR2
|
||||
~ctlframe.x1 #0058 ADD2 ~ctlframe.y1 #0010 ADD2
|
||||
,Audio 8+ #00 ~track.active #02 MUL ADD2 PEK2 #04 SFT
|
||||
,volume #00 ~track.active ADD2 PEK2 #04 SFT
|
||||
,draw-knob JSR2
|
||||
~ctlframe.x1 #0068 ADD2 ~ctlframe.y1 #0010 ADD2
|
||||
,Audio 8+ #00 ~track.active #02 MUL ADD2 PEK2 #0f AND
|
||||
,volume #00 ~track.active ADD2 PEK2 #0f AND
|
||||
,draw-knob JSR2
|
||||
|
||||
RTN
|
||||
|
@ -415,6 +430,27 @@ RTN
|
|||
|
||||
RTN
|
||||
|
||||
@adsr-envelope ( -- )
|
||||
#7f ,adsr #00 ~Audio.play #04 MUL ADD2 PEK2 SOUND
|
||||
#40 ,adsr #00 ~Audio.play #04 MUL ADD2 #0001 ADD2 PEK2 SOUND
|
||||
#40 ,adsr #00 ~Audio.play #04 MUL ADD2 #0002 ADD2 PEK2 SOUND
|
||||
#00 ,adsr #00 ~Audio.play #04 MUL ADD2 #0003 ADD2 PEK2 SOUND
|
||||
SOUND_FINISH
|
||||
BRK
|
||||
|
||||
@square-wave ( -- )
|
||||
#5800 SOUND
|
||||
#5880 SOUND
|
||||
#a800 SOUND
|
||||
#a880 SOUND
|
||||
BRK
|
||||
|
||||
@triangle-wave ( -- )
|
||||
#7f40 SOUND
|
||||
#8180 SOUND
|
||||
#0040 SOUND
|
||||
BRK
|
||||
|
||||
@ch1_txt [ CHN0 00 ]
|
||||
@ch2_txt [ CHN1 00 ]
|
||||
@ch3_txt [ CHN2 00 ]
|
||||
|
@ -458,13 +494,13 @@ RTN
|
|||
]
|
||||
|
||||
@knob_offsetx [
|
||||
04 05 06 07 08 07 06 05
|
||||
04 04 03 02 01 00 01 02
|
||||
01 00 00 00 00 01 02 03
|
||||
05 06 07 08 08 08 08 07
|
||||
]
|
||||
|
||||
@knob_offsety [
|
||||
00 01 02 03 04 05 06 07
|
||||
08 07 06 05 04 04 03 02
|
||||
07 06 05 03 02 01 00 00
|
||||
00 00 01 02 03 05 06 07
|
||||
]
|
||||
|
||||
@font_hex ( 0-F )
|
||||
|
|
|
@ -17,7 +17,6 @@
|
|||
|0100 ;System { vector 2 pad 6 r 2 g 2 b 2 }
|
||||
|0120 ;Screen { vector 2 width 2 height 2 pad 2 x 2 y 2 color 1 }
|
||||
|0130 ;Sprite { vector 2 pad 6 x 2 y 2 addr 2 color 1 }
|
||||
|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 }
|
||||
|01a0 ;Time { year 2 month 1 day 1 hour 1 minute 1 second 1 dow 1 doy 2 isdst 1 get 1 }
|
||||
|
||||
( program )
|
||||
|
@ -27,12 +26,6 @@
|
|||
( theme ) #0ff8 =System.r #0f08 =System.g #0f08 =System.b
|
||||
( vectors ) ,FRAME =Screen.vector
|
||||
|
||||
#1000 =Audio.ch1adsr
|
||||
#66 =Audio.ch1vol
|
||||
|
||||
#0003 =Audio.ch2adsr
|
||||
#66 =Audio.ch2vol
|
||||
|
||||
BRK
|
||||
|
||||
@FRAME
|
||||
|
@ -43,13 +36,6 @@ BRK
|
|||
~Time.second ~current.second NEQ #01 JNZ BRK
|
||||
~Time.second =current.second
|
||||
|
||||
( play sounds )
|
||||
#0d =Audio.ch1pitch
|
||||
|
||||
~Time.second #0f MOD #00 NEQ ^$no-tone JNZ
|
||||
#0d #02 MUL =Audio.ch2pitch
|
||||
$no-tone
|
||||
|
||||
( clear )
|
||||
#0080 SCALEX #0080 SCALEY ~needles.sx ~needles.sy #00 ,draw-line JSR2
|
||||
#0080 SCALEX #0080 SCALEY ~needles.mx ~needles.my #00 ,draw-line JSR2
|
||||
|
|
164
src/apu.c
Normal file
164
src/apu.c
Normal file
|
@ -0,0 +1,164 @@
|
|||
#include <SDL2/SDL.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
/*
|
||||
Copyright (c) 2021 Devine Lu Linvega
|
||||
Copyright (c) 2021 Andrew Alderwick
|
||||
|
||||
Permission to use, copy, modify, and distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "uxn.h"
|
||||
|
||||
#define SAMPLE_FREQUENCY 48000
|
||||
|
||||
extern SDL_AudioDeviceID audio_id;
|
||||
int error(char *msg, const char *err);
|
||||
|
||||
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 */
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
Uint16 *dat;
|
||||
Uint8 i, n, sz, ends;
|
||||
} 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], playing;
|
||||
} Note;
|
||||
|
||||
static Note *notes = NULL;
|
||||
static int n_notes = 0;
|
||||
static Queue *q;
|
||||
static Uint16 id_addr;
|
||||
|
||||
static void
|
||||
play_note(Uxn *u, int note_i, Sint16 *samples, int n_samples)
|
||||
{
|
||||
int i;
|
||||
Note *note = ¬es[note_i];
|
||||
while(n_samples--) {
|
||||
Sint32 sample = 1;
|
||||
for(i = 0; i < 2; ++i) {
|
||||
WaveformGenerator *wv = ¬e->wv[i];
|
||||
q = &wv->queue;
|
||||
wv->count += wv->advance;
|
||||
while(wv->count > wv->period) {
|
||||
wv->count -= wv->period;
|
||||
wv->start_value = wv->end_value;
|
||||
if(q->i == q->n) {
|
||||
q->i = q->n = 0;
|
||||
if(!q->ends) {
|
||||
u->ram.dat[id_addr] = note_i;
|
||||
evaluxn(u, wv->vector);
|
||||
}
|
||||
}
|
||||
if(!q->n) {
|
||||
note->playing = 0;
|
||||
return;
|
||||
}
|
||||
wv->end_value = (Sint16)q->dat[q->i++];
|
||||
wv->period = (30 << 4) * q->dat[q->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;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
play_all_notes(void *u, Uint8 *stream, int len)
|
||||
{
|
||||
int i;
|
||||
SDL_memset(stream, 0, len);
|
||||
for(i = 0; i < n_notes; ++i)
|
||||
if(notes[i].playing) play_note(u, i, (Sint16 *)stream, len >> 2);
|
||||
q = NULL;
|
||||
}
|
||||
|
||||
static Note *
|
||||
get_note(Uint8 i)
|
||||
{
|
||||
if(i >= n_notes) notes = SDL_realloc(notes, (i + 1) * sizeof(Note));
|
||||
while(i >= n_notes) SDL_zero(notes[n_notes++]);
|
||||
return ¬es[i];
|
||||
}
|
||||
|
||||
static Uint8
|
||||
audio_poke(Uxn *u, Uint16 ptr, Uint8 b0, Uint8 b1)
|
||||
{
|
||||
Uint8 *m = u->ram.dat + ptr;
|
||||
int i;
|
||||
if(b0 == 0xa) {
|
||||
Note *note = get_note(b1);
|
||||
note->playing = 1;
|
||||
for(i = 0; i < 2; ++i) {
|
||||
note->volume[i] = 0xf & (m[0x8] >> 4 * (1 - i));
|
||||
note->wv[i].vector = (m[0x0 + i * 2] << 8) + m[0x1 + i * 2];
|
||||
note->wv[i].count = note->wv[i].period = 0;
|
||||
note->wv[i].end_value = 0;
|
||||
note->wv[i].queue.n = note->wv[i].queue.i = 0;
|
||||
note->wv[i].queue.ends = 0;
|
||||
}
|
||||
note->wv[0].advance = note_advances[m[0x9] % 12] >> (8 - m[0x9] / 12);
|
||||
note->wv[1].advance = (30 << 20) / SAMPLE_FREQUENCY;
|
||||
} else if(b0 == 0xe && q != NULL) {
|
||||
if(q->n == q->sz) {
|
||||
q->sz = q->sz < 4 ? 4 : q->sz * 2;
|
||||
q->dat = SDL_realloc(q->dat, q->sz * sizeof(*q->dat));
|
||||
}
|
||||
q->dat[q->n++] = (m[0xb] << 8) + m[0xc];
|
||||
q->dat[q->n++] = (m[0xd] << 8) + b1;
|
||||
} else if(b0 == 0xf && q != NULL) {
|
||||
q->ends = 1;
|
||||
return b1;
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
initapu(Uxn *u, Uint8 id)
|
||||
{
|
||||
SDL_AudioSpec as;
|
||||
SDL_zero(as);
|
||||
as.freq = SAMPLE_FREQUENCY;
|
||||
as.format = AUDIO_S16;
|
||||
as.channels = 2;
|
||||
as.callback = play_all_notes;
|
||||
as.samples = 2048;
|
||||
as.userdata = u;
|
||||
audio_id = SDL_OpenAudioDevice(NULL, 0, &as, NULL, 0);
|
||||
if(!audio_id)
|
||||
return error("Audio", SDL_GetError());
|
||||
id_addr = portuxn(u, id, "audio", audio_poke)->addr + 0xa;
|
||||
SDL_PauseAudioDevice(audio_id, 0);
|
||||
return 1;
|
||||
}
|
132
src/emulator.c
132
src/emulator.c
|
@ -15,6 +15,9 @@ WITH REGARD TO THIS SOFTWARE.
|
|||
|
||||
#include "uxn.h"
|
||||
|
||||
int initapu(Uxn *u, Uint8 id);
|
||||
void stepapu(Uxn *u);
|
||||
|
||||
#define HOR 48
|
||||
#define VER 32
|
||||
#define PAD 2
|
||||
|
@ -49,38 +52,12 @@ Uint8 font[][8] = {
|
|||
{0x00, 0x7e, 0x40, 0x7c, 0x40, 0x40, 0x7e, 0x00},
|
||||
{0x00, 0x7e, 0x40, 0x40, 0x7c, 0x40, 0x40, 0x00}};
|
||||
|
||||
#define SAMPLE_FREQUENCY 48000
|
||||
|
||||
static Uint32 note_periods[12] = {
|
||||
/* middle C (C4) is note 60 */
|
||||
(Uint32)0xfa7e * SAMPLE_FREQUENCY, /* C-1 */
|
||||
(Uint32)0xec6f * SAMPLE_FREQUENCY,
|
||||
(Uint32)0xdf2a * SAMPLE_FREQUENCY, /* D-1 */
|
||||
(Uint32)0xd2a4 * SAMPLE_FREQUENCY,
|
||||
(Uint32)0xc6d1 * SAMPLE_FREQUENCY, /* E-1 */
|
||||
(Uint32)0xbba8 * SAMPLE_FREQUENCY, /* F-1 */
|
||||
(Uint32)0xb120 * SAMPLE_FREQUENCY,
|
||||
(Uint32)0xa72f * SAMPLE_FREQUENCY, /* G-1 */
|
||||
(Uint32)0x9dcd * SAMPLE_FREQUENCY,
|
||||
(Uint32)0x94f2 * SAMPLE_FREQUENCY, /* A-1 */
|
||||
(Uint32)0x8c95 * SAMPLE_FREQUENCY,
|
||||
(Uint32)0x84b2 * SAMPLE_FREQUENCY /* B-1 */
|
||||
};
|
||||
|
||||
typedef struct audio_channel {
|
||||
Uint32 period, count;
|
||||
Sint32 age, a, d, s, r;
|
||||
Sint16 value[2];
|
||||
Sint8 volume[2], phase;
|
||||
} Channel;
|
||||
Channel channels[4];
|
||||
|
||||
static SDL_Window *gWindow;
|
||||
static SDL_Renderer *gRenderer;
|
||||
static SDL_Texture *gTexture;
|
||||
static SDL_AudioDeviceID audio_id;
|
||||
SDL_AudioDeviceID audio_id;
|
||||
static Screen screen;
|
||||
static Device *devsystem, *devscreen, *devmouse, *devkey, *devctrl, *devaudio;
|
||||
static Device *devsystem, *devscreen, *devmouse, *devkey, *devctrl;
|
||||
|
||||
#pragma mark - Helpers
|
||||
|
||||
|
@ -241,61 +218,6 @@ togglezoom(Uxn *u)
|
|||
redraw(pixels, u);
|
||||
}
|
||||
|
||||
Sint16
|
||||
audio_envelope(Channel *c)
|
||||
{
|
||||
if(c->age < c->a)
|
||||
return 0x0888 * c->age / c->a;
|
||||
else if(c->age < c->d)
|
||||
return 0x0444 * (2 * c->d - c->a - c->age) / (c->d - c->a);
|
||||
else if(c->age < c->s)
|
||||
return 0x0444;
|
||||
else if(c->age < c->r)
|
||||
return 0x0444 * (c->r - c->age) / (c->r - c->s);
|
||||
else
|
||||
return 0x0000;
|
||||
}
|
||||
|
||||
void
|
||||
audio_callback(void *userdata, Uint8 *stream, int len)
|
||||
{
|
||||
Sint16 *samples = (Sint16 *)stream;
|
||||
int i, j;
|
||||
len >>= 2; /* use len for number of samples, not bytes */
|
||||
for(j = len * 2 - 1; j >= 0; --j) samples[j] = 0;
|
||||
for(i = 0; i < 4; ++i) {
|
||||
Channel *c = &channels[i];
|
||||
if(c->period < (1 << 20)) continue;
|
||||
for(j = 0; j < len; ++j) {
|
||||
c->age += 1;
|
||||
c->count += 1 << 20;
|
||||
while(c->count > c->period) {
|
||||
Sint16 mul;
|
||||
c->count -= c->period;
|
||||
c->phase = !c->phase;
|
||||
mul = (c->phase * 2 - 1) * audio_envelope(c);
|
||||
c->value[0] = mul * c->volume[0];
|
||||
c->value[1] = mul * c->volume[1];
|
||||
}
|
||||
samples[j * 2] += c->value[0];
|
||||
samples[j * 2 + 1] += c->value[1];
|
||||
}
|
||||
}
|
||||
(void)userdata;
|
||||
}
|
||||
|
||||
void
|
||||
silence(void)
|
||||
{
|
||||
int i;
|
||||
for(i = 0; i < 4; ++i) {
|
||||
Channel *c = &channels[i];
|
||||
c->volume[0] = 0;
|
||||
c->volume[1] = 0;
|
||||
c->period = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
quit(void)
|
||||
{
|
||||
|
@ -313,7 +235,6 @@ quit(void)
|
|||
int
|
||||
init(void)
|
||||
{
|
||||
SDL_AudioSpec as;
|
||||
if(SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO) < 0)
|
||||
return error("Init", SDL_GetError());
|
||||
gWindow = SDL_CreateWindow("Uxn", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, WIDTH * ZOOM, HEIGHT * ZOOM, SDL_WINDOW_SHOWN);
|
||||
|
@ -328,18 +249,8 @@ init(void)
|
|||
if(!(pixels = (Uint32 *)malloc(WIDTH * HEIGHT * sizeof(Uint32))))
|
||||
return error("Pixels", "Failed to allocate memory");
|
||||
clear(pixels);
|
||||
silence();
|
||||
SDL_StartTextInput();
|
||||
SDL_ShowCursor(SDL_DISABLE);
|
||||
as.freq = SAMPLE_FREQUENCY;
|
||||
as.format = AUDIO_S16;
|
||||
as.channels = 2;
|
||||
as.callback = audio_callback;
|
||||
as.samples = 2048;
|
||||
audio_id = SDL_OpenAudioDevice(NULL, 0, &as, NULL, 0);
|
||||
if(!audio_id)
|
||||
return error("Audio", SDL_GetError());
|
||||
SDL_PauseAudioDevice(audio_id, 0);
|
||||
screen.x1 = PAD * 8;
|
||||
screen.x2 = WIDTH - PAD * 8 - 1;
|
||||
screen.y1 = PAD * 8;
|
||||
|
@ -504,29 +415,6 @@ file_poke(Uxn *u, Uint16 ptr, Uint8 b0, Uint8 b1)
|
|||
return b1;
|
||||
}
|
||||
|
||||
Uint8
|
||||
audio_poke(Uxn *u, Uint16 ptr, Uint8 b0, Uint8 b1)
|
||||
{
|
||||
Uint8 *m = u->ram.dat;
|
||||
m[PAGE_DEVICE + 0x0070 + b0] = b1;
|
||||
if(b0 > 0x08 && b0 & 1) {
|
||||
Uint16 addr = ptr + (b0 & 0x6);
|
||||
Channel *c = &channels[(b0 & 0x6) >> 1];
|
||||
SDL_LockAudioDevice(audio_id);
|
||||
c->period = note_periods[m[addr + 9] % 12] >> (m[addr + 9] / 12);
|
||||
c->count %= c->period;
|
||||
c->volume[0] = (m[addr + 8] >> 4) & 0xf;
|
||||
c->volume[1] = m[addr + 8] & 0xf;
|
||||
c->age = 0;
|
||||
c->a = (SAMPLE_FREQUENCY >> 4) * ((m[addr] >> 4) & 0xf);
|
||||
c->d = c->a + (SAMPLE_FREQUENCY >> 4) * (m[addr] & 0xf);
|
||||
c->s = c->d + (SAMPLE_FREQUENCY >> 4) * ((m[addr + 1] >> 4) & 0xf);
|
||||
c->r = c->s + (SAMPLE_FREQUENCY >> 4) * (m[addr + 1] & 0xf);
|
||||
SDL_UnlockAudioDevice(audio_id);
|
||||
}
|
||||
return b1;
|
||||
}
|
||||
|
||||
Uint8
|
||||
midi_poke(Uxn *u, Uint16 ptr, Uint8 b0, Uint8 b1)
|
||||
{
|
||||
|
@ -586,9 +474,13 @@ 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: quit(); break;
|
||||
case SDL_QUIT:
|
||||
SDL_UnlockAudioDevice(audio_id);
|
||||
quit();
|
||||
break;
|
||||
case SDL_MOUSEBUTTONUP:
|
||||
case SDL_MOUSEBUTTONDOWN:
|
||||
case SDL_MOUSEMOTION:
|
||||
|
@ -611,6 +503,7 @@ start(Uxn *u)
|
|||
}
|
||||
}
|
||||
evaluxn(u, devscreen->vector);
|
||||
SDL_UnlockAudioDevice(audio_id);
|
||||
if(screen.reqdraw)
|
||||
redraw(pixels, u);
|
||||
elapsed = (SDL_GetPerformanceCounter() - start) / (double)SDL_GetPerformanceFrequency() * 1000.0f;
|
||||
|
@ -641,7 +534,8 @@ main(int argc, char **argv)
|
|||
devkey = portuxn(&u, 0x05, "key", ppnil);
|
||||
devmouse = portuxn(&u, 0x06, "mouse", ppnil);
|
||||
portuxn(&u, 0x07, "file", file_poke);
|
||||
devaudio = portuxn(&u, 0x08, "audio", audio_poke);
|
||||
if(!initapu(&u, 0x08))
|
||||
return 1;
|
||||
portuxn(&u, 0x09, "midi", ppnil);
|
||||
portuxn(&u, 0x0a, "datetime", datetime_poke);
|
||||
portuxn(&u, 0x0b, "---", ppnil);
|
||||
|
|
Loading…
Reference in a new issue