nycki.net/static/qrplay/v3f.html
nycki bb8793316d
All checks were successful
/ build (push) Successful in 57s
bump version number
2025-07-13 13:38:39 -07:00

148 lines
No EOL
4.7 KiB
HTML

<!DOCTYPE html>
<meta name="twitter:image" content="http://nycki.net/qrplay/qrplay-v3e-WiL-the-forest-chase.png">
<meta property="og:image" content="http://nycki.net/qrplay/qrplay-v3e-WiL-the-forest-chase.png">
<!--
nycki's modified version of SArpnt's player (see /golf3.html)
compatible with ZZM files or with a reduced instruction set for QR Alphanumeric efficiency.
commands, case-insensitive
ABCDEFG play note
* or # sharp
: or ! flat
X rest
P go one note length to the Past
JTSIQHW set note length to sixtyfourth, Thirtytwoth, Sixteenth, eIghth, Quarter, Half, Whole. (persistent)
. increase note length by 50% (persistent)
3 divide note length by 3 (persistent)
+ octave up (persistent)
- octave down (persistent)
R or \n reset note length and octave
Vxx set volume
Uxxx set bpm
$x.../ set sample
01245679 play sample
ZZM mode
comments begin with ; and metadata begins with $ (inside a comment)
for instance:
; $SONG TITLE 1 The Forest Chase
QR Alphanumeric Mode
0-9A-Z $%*+-./:
no lowercase, no newline, limited punctuation.
to convert, use R for reset, * for sharp, : for flat, /.../ for comment.
Chords
to play multiple notes at once, use P after each note to back up and play another one in parallel. to play multiple channels at once, convert each channel to a sample and use P after playing each sample.
variable reference
A: Audio context
C: Command Character
D: character after C, or other Data
F: main Function, also object that holds samples
G: Gain node
H: textarea Html element
I: Index into M
J: lookahead (for sharps and flats)
L: note Length
M: Music text
N: semitone Note offset (for octave changes)
O: Oscillator node
P: current time step
Q: numeric command id
S: find Sample
T: note Time
U: bpm
W: realtime When the song starts
-->
<!--
data:TEXT/HTML,<META%20NAME=VIEWPORT%20CONTENT=initial-scale=1><CENTER><P><A%20HREF=http://nycki.net/qrplay>qrplay%20v3f</A><br>by%20nycki%20and%20SArpnt</P><TEXTAREA%20ID=H%20STYLE=WIDTH:MIN(40EM,99%);HEIGHT:9LH></TEXTAREA><P><BUTTON%20onclick="H.A?.close();H.A=A=new%20AudioContext;W=Date.now();U=137;F=(M='',T=0,L,N,I=0,K=0,S=D=>D?S(D+(M[++I]==C)-(M[I]=='/')):I)=>{for(;C=M[I++];D=M[I],J=E=>E.indexOf(D)>=0,P=L/U*15/8,Q>25?K=!K:Q>23?(K=Q-24)||(L=N=4):K?0:Q>22?T-=P:Q>20?N-=C+12:Q>7?(Q-=9)?Q>9?L/=+C||2/3:Q>7?(D=D+M[++I,I++]|0)?Q>8?U=D+M[I++]:G=new%20GainNode(A,{gain:2**(D/5-11)}):0:L=~Q?1<<Q:(K=0,N=4):F[D]=M.slice(++I,S(1),I++):~Q?(O=new%20OscillatorNode(A,{type:%20'square',detune:100*(Q*2-(Q>3)-J('!:')+J('%23*')-N-7)}),Q&&O.connect(G),G.connect(A.destination),O.start(T),O.stop(T+=P)):C>'@'||1+C>1?(D=>setTimeout(_=>F(...D),1e3*D[1]-Date.now()+W-5e3,))([F[C],T+=P,L,N]):0)Q='XCDEFGABR$JTSIQHWVU3.-+P\n;/'.indexOf(C)},F('RV35'+H.value.toUpperCase())">%23play</BUTTON>%20<BUTTON%20onclick=H.A.close()>%23stop
-->
<!-- snip -->
<META NAME=VIEWPORT CONTENT=initial-scale=1>
<CENTER>
<P><A HREF=http://nycki.net/qrplay>qrplay v3f</A><br>by nycki and SArpnt</P>
<TEXTAREA ID=H STYLE=WIDTH:MIN(40EM,99%);HEIGHT:9LH>; $TITLE WiL Best :: Atrocity
; $SONG TITLE 1 The Forest Chase
S-A+EBAEAGEA+C-BGAE+C-GRS-DA+DEGF*DAGEDF*DCEF*RS-A+EBAEAGEA+C-BGAE+C-GRS-DA+DEGF*DAGEDF*DEGARS--G+G+BAGDBF*BGDF*GAB+CRSCG+CE-G+E-G+CD-G+CC-G+CD-GRS-F+CF+C-AFCFGCFACEFERS-EB+EFG*AB+CD*EFG*AB+FE
</TEXTAREA><P>
<BUTTON onclick="
H.A?.close();
H.A=A=new AudioContext;
W=Date.now();
U=137;
F=(
M='',
T=0,
L,
N,
I=0,
K=0,
S=D=>D?S(D+(M[++I]==C)-(M[I]=='/')):I
)=>{for(;
C=M[I++];
D=M[I],
J=E=>E.indexOf(D)>=0,
P=L/U*15/8,
// skip comments and reset on newline
Q>25?K=!K:
Q>23?(K=Q-24)||(L=N=4):
K?0:
// backspace
Q>22?T-=P:
// change octave
Q>20?N-=C+12:
Q>7?
// DECREMENT Q, and then...
(Q-=9)?
// triplet or dotted rhythm, C is 3 or .
Q>9?L/=+C||2/3:
Q>7?
// extended command. read two digits, then...
(D=D+M[++I,I++]|0)?
// set bpm (U)
Q>8?U=D+M[I++]:
// set volume (V)
G=new GainNode(A,{gain:2**(D/5-11)}):
0:
// else, set length to...
L=
// fixed length (JTSIQHW)
~Q?1<<Q:
// reset L=N=4
(K=0,N=4):
// save sample ($x.../)
F[D]=M.slice(++I,S(1),I++):
~Q?(
// play note or rest
O=new OscillatorNode(A,{
type: 'square',
// !: are flat, #* are sharp
detune:100*(Q*2-(Q>3)-J('!:')+J('#*')-N-7)
}),
Q&&O.connect(G),
G.connect(A.destination),
O.start(T),
O.stop(T+=P)
):
// any unused non-whitespace letter or number
C>'@'||1+C>1?(D=>setTimeout(
// play a sample async
_=>F(...D),
// don't async too far ahead or you'll lag us out
1e3*D[1]-Date.now()+W-5e3,
))([F[C],T+=P,L,N]):
// do nothing
0
)Q='XCDEFGABR$JTSIQHWVU3.-+P\n;/'.indexOf(C)},
F('RV35'+H.value.toUpperCase())
">#play</BUTTON> <BUTTON onclick=H.A.close()>#stop
<!-- snip -->
</BUTTON></P>
<img src="qrplay-v3e-WiL-the-forest-chase.png">
</center>