Skoar is a high-level language for coding music.
It runs on SuperCollider, a free and fantastic audio programming environment.
Skoarcery is a set of tools to define, test, and build the Skoar language.
<? Zelda Theme - inspired by piano arrangement by Shinobu Amayake ?>
130 => )
.alice @default => @instrument [##### ] => @amp
.bob @default => @instrument [##### ] => @amp
.bass @sawpulse => @instrument [### ] => @amp o~~~~
.hats @hats => @instrument [## ] => @amp
.snare @snare => @instrument [## ] => @amp
.kick @kick => @instrument [### ] => @amp
{! bass_end<x> !! !x ) ) ) ] ] !}
{! bass_climb !! | _e ]] _a# ]] c# ] e ]] a# ]] ~o c# ] e ) } | f ) o~ _f ]] ]] ] ) } | !}
{! bassline_a !!
<a#, g#, f#, c#, b, a#, c>.{: .) ]] ]] ] ) ) :}
!bass_end<f>
!}
{! bassline_b !!
<a#, g#, f#, f>.{: ) ]] ]] ] ) ) :}
!bass_climb !bass_climb
<b, a#, c >.{: ) ]] ]] ] ) ) :} !bass_end<f>
!}
{! intro !!
.hats {: }}} :: 4 times :}
.snare {: }}} :: 4 times :}
.kick {: }}} :: 4 times :}
.alice | _a# )) o/. ]] ]] ]] ] | ]. _g# ]] _a# ) o/. ]] ]] ]] ] |
.bob | _d )) o/. ]] ]] ]] ] | _c ]. ]] ) o/. ]] ]] ]] ] |
.bass | a# ) ]] ]] ] ) ]] ]] ] | g# ) ]] ]] ] ) ]] ]] ] |
.alice | ]. _g# ]] _a# ) o/. ]] ]] ]] ] | ] _f ]] ]] ] ]] ]] ] ]] ]] ] ] |
.bob | _c# ]. ]] ) o/. ]] ]] ]] ] | ] o~ _a ]] ]] ] ]] ]] ] ]] ]] ] ] |
.bass | f# ) ]] ]] ] ) ]] ]] ] | f ) ) ) g ] a ] |
!}
{! melody_a !!
.bass !bassline_a
.hats {: }}} :: 7 times :} | ] ] ] ] ] ] ] ] |
.snare {: }}} :: 8 times :}
.kick {: }}} :: 8 times :}
.alice | _a# ) _f )__ o/. _a# ]] ]] c ]] d ]] d# ]] |
.bob | _d ) ]] ]] _c ] _d ]. ]] ]] _d# ]] _f ]] _g ]] |
.alice | f )) o/ ] f ] f# ]] g# ]] |
.bob | _g# ]. _a# ]] ]] c ]] d ]] d# ]] f ) _g# ] _a# ]] c ]] |
.alice | a# )) o/ a# ] ] g# ]] f# ]] |
.bob | c# ]. _f# ]] ]] _g# ]] _a# ]] c ]] c# ]. ]] ] c ]] _a# ]] |
.alice | g# ]. f# ]] f )) ) |
.bob | c# ]. _g# ]] ]] ]] _f# ] _g# ]. ]] ]] _f# ]] _g# ] |
.alice | d# ] ]] f ]] f# )) f ] d# ] |
.bob | _f# ] ]] _f ]] _f# ] ]] _g# ]] _a# ) _g# ] _f# ] |
.alice | c# ] ]] d# ]] f )) d# ] c# ] |
.bob | _f ] ]] _d# ]] _f ] ]] _f# ]] _g# ) _f# ] _d# ] |
.alice | c ] ]] d ]] e )) g ) |
.bob | _e ] ]] _d ]] _e ] ]] _g ] ]] _a ]] ]] _a# ] c ] |
.alice | f ] _f ]] ]] ] ]] ]] ] ]] ]] ] ] |
.bob | _a ] o~ _a ]] ]] ] ]] ]] ] ]] ]] ] ] ~o |
!}
{! melody_b !!
.bass !bassline_b
.hats {: ] ] ] ] ] ] ] ]] ]] :: 12 times :}
.kick {: ) } ) } :: 12 times :}
.snare {: } ) } ) :: 11 times :} | ] ]] ]] ] ]] ]] ] ] |
.alice | _a# ) _f ) o/. _a# ]] ]] c ]] d ]] d# ]] |
.bob | _d ) ]] ]] _c ] _d ]. ]] ]] _d# ]] _f ]] _g ]] |
.alice | f )) o/ ] f ] f# ]] g# ]] |
.bob | _g# ]. _a# ]] ]] c ]] d ]] d# ]] f ) _g# ] _a# ]] c ]] |
.alice | a# )). ~o c# ) | c ) o~ a )) f ) | f# )). a# ) | a ) f )) ) |
.bob | c# )). e ) | d# ) c )) _a ) | _b )). c# ) | c ) _a )) ) |
.alice | f# )). a# ) | a ) f )) d ) | d# )). f# ) | f ) c# )) _a# ) |
.bob | _b )). c# ) | c ) _a )) ) | _f# )). _b ) | _a# ) _f )) _c# ) |
.alice | c ] ]] d ]] e )) g ) |
.bob | _e ] ]] _d ]] _e ] ]] _f ]] _g ] ]] _a ]] _a# ] c ] |
.alice | f ] _f ]] ]] ] ]] ]] ] ]] ]] ] ] |
.bob | _a ] o~ _a ]] ]] ] ]] ]] ] ]] ]] ] ] ~o |
!}
{! fill !!
.alice | f ] _f ]] ]] ] ]] ]] ] ]] ]] ] ] |
.bob | _a ] o~ _a ]] ]] ] ]] ]] ] ]] ]] ] ] ~o |
.snare | ] ]] ]] ] ]] ]] ] ]] ]] ] ] |
.hats | ] ] ] ] ] ] ] ] |
.kick | ) } ) ) |
.bass !bass_end<f>
!}
!intro
!melody_a
!fill
!melody_b
!fill
More examples: examples.md
See also: listen to dungeontimes
Skoar is a language for coding music, combining a grand-staff-like notation with a flexible programming notation.
__beats_______________ __rests_______________
))) - whole }}} - whole
)) - half }} - quarter
) - quarter } - quarter
] - eighth o/ - eighth
]] - sixteenth oo/ - sixteenth
]]] - thirty secondth ooo/ - thirthy secondth
). - dotted quarter
]]. - dotted sixteenth
o/. - dotted eighth rest
.) - staccato quarter
.]]. - staccato and dotted sixteenth
)__ - quarter with a tie (ties to the next beat)
)__. - dotted quarter with a tie
We call them noats, not notes, you see, notes are already things; nor are these noats the nearly named noads, which are also totally things..
<? use # or b after the noat to sharp or flat it. Or use scale degree numbers. ?>
c ) d ) eb ) f ]] ]] g ] ] g# )
<? you get two octaves to work with, prepend _ for the lower octave. ?>
<_c, _d, _e, _f, _g, _a, _b, c, d, e, f, g, a, b>.choose ]]
{! foo !! <0,1,2,3,4,5,6,7>.{: ]] :} !}
Am => @key !foo
Fb => @key !foo
<E, @dorian> => @key !foo
<F, @minorPentatonic> => @key !foo
Just set @tranpose.
-3 => @transpose
Sort of like a metronome marker, set the bpm to a beat length:
80 => ) <? 80 quarter notes per minute ?>
80 => ] <? 80 eighth notes per minute ?>
A Am A#m Asus2 Adim etc..
Or we can use lists of noats:
<_a,c,e> ) <_a,c#,e> ) <0,2,4> ))
<? up one octave ?> <? down one ?>
~o o~
8va 8vb
ottava alta ottava bassa
<? up two ?> <? down two ?>
~~o o~~
15ma 15mb
alla quindicesima
We have to use the full word forte
, f
is a noat.
fff ffforte ppp pppiano piano mp mf ff pp p
or, use a hash level:
.strings [### ] => @amp
.piano [#### ] => @amp
.bass [#### ] => @amp
Hash levels can be any length:
[ ] is 0
[#] is 1
[## ] is 0.66ish
Colons:
|: _a ]]] c ]]] e ]]] :| g ]]] ooo/ ]]] :|
Segnos and Codas: (Codas currently not implemented)
| _a ) c ) e ) | ,segno` ) ]] ]] e ]] | f D.S. al fine ) ) ) fine
| ,segno` c ]] e ]] (+) ]] ]] D.S. al Coda '.......' (+) | a) c) e) } |
Infinite repeats:
<? from the top ?>
| _a] c] e] | D.C. <? also accept Da Capo ?>
<? from the segno ?>
| _a] c] e] o/ | ,segno` _f] f] _f] o/ Dal Segno |
We can set and get values from a dictionary local to the voice. Anything set here will be copied into the resulting event every beat; which we can use to configure the voice.
<? names of things start with @ ?>
@smooth => @instrument
<0,3,5> => @detune
<? to lookup the values, we use ! in place of @.... more on ! below.. ?>
a# => @foo
!foo )
Very much like a do-while:
{: ]] oo/ ]] ]] :: 8 times :}
You can send a loop to an array as a message to implement a foreach loop:
<_a, _c, c, _e, e, _a>.{: ] ]] ]] :}
If you also put a boolean condition, it will keep foreaching while the condition is true.
<_a, _c, c, _e, e, _a>.{: ] ]] ]] :: !groovy == 5 :}
An if example:
{? !x == !y ?? ]]] ?}
An if with else example:
{? !x == !y ?? ]]] ?? ooo/ ?}
Acceptable boolean operators:
== != >= <= and or xor
At the moment, don't use <
or >
The Skoarpion is like a function, more like a subroutine.
{! name<args> !!
body
...
!}
Let's make a function:
{! drumsBasic<x> !! 4/4
.h {: ] ] ] ] ] ] ] ] :: !x times :}
.s {: } ) } ) :: !x times :}
.k {: )) )) :: !x times :}
!}
If drumsBasic
is called by the .h
voice, then {: ] ] ] ] ] ] ] ] :: !x times :}
is executed;
.s
voice: {: } ) } ) :: !x times :}
, .k
etc..
If any lines are not voiced (like that 4/4
, they will be run by every voice.
Skoarpions normally have scope, but they can be inlined with .inline
, which can be convenient:
{! alice !! ~o mp
<c#, e, _a, g#> => @favorites
<0,5,7> => @detune
@acid => @instrument
!}
{! bob<x> !! o~~ forte
@bass => @instrument
!x => @favorites
!}
<? here's an anonymous skoarpion ?>
{! <x> !! <0, 4, !x> => @detune !} => @charlie
.a !alice.inline
.b !bob<<a,e>>.inline
.c !charlie<<5,7,9>.choose>.inline
...
.rand
for numbers, .choose
for lists.
<? save into @food, a random number between zero and five ?>
5.rand => @food
<? choose a random note ?>
<c,d,e,f,g,a,b>.choose
These are messages passed down to supercollider. You say .rand
because in SC, Integer
has a .rand
method, Array
has .choose
)
0 + 2 == 2
<0> + 2 == <0, 2>
<_a, c> + e == <_a, c, e>
0 x 2 == 0
2 x 2 == 4
increments and decrements:
2 => @x <? x is now 2 ?>
2 +> @x <? x is now 4 ?>
1 -> @x <? x is now 3 ?>
2 x> @x <? x is now 6 ?>
You can wake Cthulhu, crashing the skoar:
^^(;,;)^^
Cthulhu can also make assertions:
^^(;!octave == 5;)^^
If the octave isn't 5, Cthulhu will be so upset that he'll wake up and crash your skoar.
First, clone this git repo to, for example ~/GitHub/Skoarcery
Get the very latest SuperCollider 3.7
Point SuperCollider at the Skoarcery folder: In SuperCollider's interpreter options, include the folder ~/GitHub/Skoarcery
and
restart the interpreter.
Try the examples in examples. Dungeontimes is the simplest.
Skoarcery is the set of tools and tests that builds skoar compilers.
The lexical and syntactic analysers, lex.sc
and rdpp.sc
(ditto .py
) are built with Skoarcery.
They are built and written to .../SuperCollider/Skoar
.
Currently the built code is checked in, you don't need to get Skoarcery working unless you want to work on the language.
-
Tokens by convention are UpperCamelCase.
-
Tokens are defined with regexes that have to work with both SuperCollider and Python. All we do is recognise, no capture groups.
-
Tokens are typically converted to Skoarpuscles in the top section of decorating.sc
-
Defines an LL(1) grammar suitable for building recursive decent predictive parsers for skoar.
-
Nonterminals by convention are like_this
-
+ before a nonterminal indicates this is an intermediate step that can be skipped in the constructed parse tree, it will not create a new skoarnode, instead appending its noads to its parent's children list.
-
Semantics defined in the second half of decorating.sc
- langoids.py - Terminal, Nonterminal, Production objects,
- dragonsets.py - FIRST and FOLLOW sets, from the Dragon Book.
- emissions.py - Implements Python and SuperCollider coding.
- underskoar.py - Templates for lexer code
- These are our unit tests.
- Test the grammar for LL(1), test that it compiles in sclang, test skoars, etc..
-
These are written as unit tests, they build our lexers and parsers. Done this way because we generate some information, test it, build on it, test that ...
-
The important one at the moment is Build_Sc.py, it will run tests, build files, run more tests, etc.. it builds Skoar. This one builds Skoar.
-
Code_Lexer_Py.py, Code_Lexer_Sc.py - Build lex.py, lex.sc
-
Code_Parser_Py.py, Code_Parser_Sc.py - Build rdpp.py, rdpp.sc
-
lex.sc - Lexical analyser, defines classes for each token, extending SkoarToke.
- in Skoar, we call them Tokes, in Skoarcery, they are Terminals, or tokens.
-
rdpp.sc - Recursive descent predictive parser. Builds the parse tree.
- skoar.sc - The skoar object you get from compiling your skoar. From here you get a pattern object and play it.
- apparatus.sc - The parse tree code. Noads, searching, iteration, etc.
- decorating.sc - Second stage, decorate the parse tree.
- koar.sc - Each voice is performed on a koar by a minstrel.
- minstrel.sc - Minstrels are agents who read and perform their own voice of a skoar piece.
- skoarpions.sc - Implements the Skoarpion, our general purpose control-flow construct.
- beaty.sc - The code for beats and rests.
- pitchy.sc - The code for pitchy stuff. Noats, choards, etc.
- toker.sc - Toker for the parser.
- skoarpuscles.sc - Skoarpuscles, the thingy things are all skoarpuscles.