nycki.net/static/qrplay/v3d.html
2025-07-12 11:53:23 -07:00

100 lines
1.9 KiB
HTML

<META NAME=VIEWPORT CONTENT=initial-scale=1>
<CENTER>
<P><A HREF=http://nycki.net/qrplay>qrplay v3b</A><br>by nycki and SArpnt</P>
<TEXTAREA ID=F STYLE=WIDTH:MIN(40EM,99%);HEIGHT:9LH>; @title nupa's theme
; @authors nycki bsp
; single channel version
1[@0000]
2[
0[c-c+]1 1
0[e-c+]1 0[g-c+]1
0[c-b!+]1 1
0[e-b!+]1 0[g-b!+]1
0[d-a+]1 0[c-a+]1
]
2
0[d-a+]1 0[c-a+]1
0[d#-a!+]1 0[d-a!+]1
1 1
2
0[d#-a!+]1 0[d-g+]1
0[c--c++]1 1
1 1
</TEXTAREA><P>
<BUTTON onclick="
F.A?.close();
F.A=A=new AudioContext;
D=Date.now();
S='@'+F.value.toLowerCase();
// stack
B=[];
// stack variables
K=L=O=T=Z=0;
U=137;
V=40;
// samples
C='+g,-b+c#deff#g,g++ddb--g++ddb,,+ea-g--b++g++c#,a#ga#g-a#+g-b+g,-a++c#-aae+e--a,-ffed#ddc#,dddddd#e,--ba#bb-a+a#-b'.split(',').map(c=>`{@++m${c}}x`);
// main loop
M=_=>{
[a,b,c,d]=S;
// next index into S
s=1;
// opcode
z='\n;+-@.3cdefgabxjtsiqhwmuv{}012456789'.indexOf(a);
// do nothing
z<0?0:
// comment
z<2?K=z:K?0:
// octave
z<4?O+=a+1|0:
// reset octave and length
z<5?(O=0,L=1):
// dots and triplets
z<7?L*=3/(z<6?2:9):
// note or rest
z<15?(
z-=7,
o=new OscillatorNode(A,{
type:'square',
detune:100*(z*2-(z>2)-(b=='!')+(b=='#')+12*O-9)
}),
g=new GainNode(A,{gain:z<7&&.5**(13-V/5)}),
o.connect(g),
g.connect(A.destination),
o.start(T),
o.stop(T+=7.5*L/U)
):
// set note length
z<22?L=2**(z-16):
// set note length (microtone)
z<23?L=U/2**11:
// change tempo in bpm
z<24?(U=b+c+d,s+=3):
// change volume
z<25?(V=b+c,s+=2):
// play simultaneously
z<26?B[Z++]={L,O,T,U,V}:
z<27?{L,O,T,U,V}=B[--Z]:
// store sample
b=='['?(
m=0,
d=B.findIndex.call(S,c=>
c=='['?!++m:
c==']'?!--m:
0
),
C[a]=S.slice(s,d),
s=d+1
):
// play sample
S=C[a]+S.slice(s--);
// loop (but don't get too far ahead)
A.state!='closed'&&(S=S.slice(s))&&setTimeout(M,1e3*T-Date.now()+D);
};M()
">#play</BUTTON> <BUTTON ONCLICK=F.A.close()>#stop