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 <0,3,5> => @detune pp
.bob <0,3,5> => @detune pp
.bass @sawpulse => @instrument p o~~~~
.hats @hats => @instrument pp
.snare @snare => @instrument mp
.kick @kick => @instrument mp
{! four_bars_rest !! }}}}} !}
{! eight_bars_rest !! }}}}}} !}
{! 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 !four_bars_rest
.snare !four_bars_rest
.kick !four_bars_rest
.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
.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
.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>
!}
{! drums !!
.hats {: ] ] ] ] ] ] ] ]] ]] :: !i <= 11 :}
.kick {: ) } ) } :: !i <= 11 :}
.snare {: } ) } ) :: !i <= 10 :} | ] ]] ]] ] ]] ]] ] ]] ]] ] ] |
!}
!intro !melody_a
.kick !eight_bars_rest
.hats !four_bars_rest }}} }}} }}} ] ] ] ] ] ] ] ]
.snare !eight_bars_rest
!fill !melody_b !drums !fill
More examples: examples.md
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 ]]
Choards don't work yet, this is the intention:
A Am A#m Asus2 Adim etc..
But we can use lists of noats:
<_a,c,e> ) <_a,c#,e> )
<? up one octave ?> <? down one ?>
~o o~
8va 8vb
ottava alta ottava bassa
<? up two ?> <? down two ?>
~~o o~~
15ma 15mb
alla quindicesima
Have to use the full word forte
, f
is a noat.
fff ffforte ppp pppiano piano mp mf ff pp p sfp
Colons:
|: _a ]]] c ]]] e ]]] :| g ]]] ooo/ ]]] :|
Segnos and Codas:
| _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 |
Voltas:
|: c )) ) | [1.] _a] c] a]] e]] :| [2.] ]] ]] _c) ||
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/ ]] ]] :: !x <= !y :}
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 :}
You get a monotonic !i
that starts at 0
and is incremented just before the test.
<? executes 8 times ?>
{: <_a, _c, _e, a>.choose ]] ]] ] :: !i <= 8:}
An if example:
{? !x == !y ?? ]]] ?}
An if with else example:
{? !x == !y ?? ]]] ?? ooo/ ?}
The Skoarpion is a flexible device; we can use it as a function or a sequence.
{! name<args> !!
body
...
!! stinger !}
Each time you call the skoarpion the body
; the stinger
runs before every
beat in the body.
Let's make a function:
{! zorp<derp> !!
| ) } |
| ) ]] oo/ ] |
| ] ] ]] ]] ] |
| ] ] ]] ]] oo/ ]] |
!! !derp.choose !}
<? - this calls !zorp.choose, setting @derp to <_a, c#, e>
- !derp.choose in the stinger, means it will pick one noat from @derp each beat.
- the !zorp.choose will choose one line at random ?>
!zorp<<_a, c#, e>>.choose
You can cycle the lines in order with .next
or backwards with .last
<_a, c#, e> => @A
!zorp<!A>.next <? plays | ) } | ?>
!zorp<!A>.next <? plays | ) ]] oo/ ] | ?>
!zorp<!A>.last <? plays | ) } | ?>
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
!}
{! <x> !! <0, 4, !x> => @detune !} => @charlie
.a !alice.inline
.b !bob<<a,e>>.inline
.c !charlie<<5,7,9>.choose>.inline
...
Arrays and arraylike things can be iterated like skoarpions.
<c, e, g> => @food
!food.next ) <? plays: c ?>
!food.next ) <? plays: e ?>
!food.last ) <? plays: c ?>
!food.choose ) <? at random ?>
With messages you can work with the underlying objects (i.e. the SuperCollider objects)
<? save into @food, a random number between zero and five ?>
5.rand => @food
<? print foo to the post screen ?>
'foo'.postln
<? choose a random note and post it to the screen ?>
<c,d,e,f,g,a,b>.choose.postln
Static methods can be called on underlying classes, dereference with !
and send the message:
!Array.fib<20,27,3> => @food
!MyRediculousClass.new<'srsly', 2.1828> => @zagwaggler
!zagwaggler.bringTheWub<'wub'>.wub.wub.wub.wub
You can wake Cthulhu, crashing the skoar.
^^(;,;)^^
Cthulhu can also make assertions.
^^(;!octave == 5;)^^
You need the very latest SuperCollider 3.7
You just need to point SuperCollider at the Skoar folder (that you git cloned) and you're set.
In SuperCollider's interpreter options, include the folder ~/.../Skoar/SuperCollider/Skoar
and
restart the interpreter
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.
-
* after a terminal means there are values we need to pick out of the lexeme: 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.
-
* after a nonterminal means there is corresponding semantic code for this: 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.