Skip to content

Instantly share code, notes, and snippets.

@TheNothingMan
Last active April 21, 2024 18:49
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save TheNothingMan/6f5daf540d55b4ad9138e3ff5d3ffbd0 to your computer and use it in GitHub Desktop.
Save TheNothingMan/6f5daf540d55b4ad9138e3ff5d3ffbd0 to your computer and use it in GitHub Desktop.
School.ly

school.ly cheatsheet

This is a quick overview over available functions and their arguments.

Note: The code is written for German students. It can be easily changed to any other language, but be aware that all LilyPond code uses German note names as well.

Global settings and functions

\setSeed num

\showSeed

\randomizeSeed

\showSolution

\setBE

\setTasks ##t|##f - default: ##t

\resetTaskCounter

\testHeader #'("Title" ["Subtitle"] [...])

\arrow-up (or -down, -left, -right)

Task functions

\makeNoteNameTest [allowDuplicates] numTreble [b] numBass [b] numC [b] num8va [b] num8vb [accidentals] [cClef] mode mode: ##\r | ##\w | ##\s

\makeTimeSignatureTest [allowDuplicateSignatures] [allowDuplicates] num

\makeBarLineTest [allowDuplicateSignature] [allowDuplicates] numSignatures numBars

\makeContainsTest [allowDuplicates] unit num

\makeIntervalTest [allowDuplicates] [interval-list] [accidentals] num mode mode: ##\r | ##\w

\makeScaleTest [allowDuplicates] [scales] num [maxAcc] mode mode: ##\r | ##\w

\makeChordTest [allowDuplicates] [chords] num mode mode: ##\r | ##\w

Default value lists

name description
#no-accidentals natural notes only
#accidentals-no-doubles natural notes and single accidentals
#accidentals-with-doubles natrual notes, single and double accidentals
name description
#easy-intervals perfect, major and minor intervals only
#normal-intervals like #easy-intervals, with additional augmented or diminuished variants of perfect intervals (1,4,5,8)
#all-intervals like #normal-intervals, with additional augmented or diminuished variants of major/minor intervals (2,3,6,7)

school.ly documentation

school.ly is a comprehensive test generator for music education. It allows to quickly generate various tests for notenames, clef readings, rhythms and time signatures. Interval and scale tasks will be added.

Global settings and functions

\setSeed

Usage: \setSeed num

This will set the *random-state* variable to num. Useful for generating more consistent results.

\showSeed

Usage: \showSeed

Shows the seed that was used to generate the output file. Use outside of \score!

\randomizeSeed

Usage: \randomizeSeed

Sets the seed to a random value after it has been set with \setSeed.

\showSolution

Usage: \showSolution Displays solutions to the tasks in red for all tasks after this command. Use this in combination with \showSeed and \setSeed to generate the solution for a specific set of tasks.

\setBE

Usage: \setBE Use "BE" instead of "P." in tasks. This also changes all tasks to use "Sie" instead of "Du".

\setTasks

Usage: \setTasks ##t|##f Set if task descriptions and points should be included automatically with each function. Default: ##t

\testHeader

Usage: \testHeader #'("Title" ["Subtitle"] [...])

Prints a header with name, class and date fields as well as each given String in a separate line.

\arrow-up|-down|-left-|right

Usage: {note}\arrow-up (or -down, -left, -right)

Prints an arrow above the given note. Useful for choreographies, conducting or the like.

Task functions

Each of these functions will generate a specific task with configurable number of questions, including a task description and the sum of possible points. Some functions have a mode parameter that switches from "reading" and "writing".

Note: Due to lilypond syntax, it is required to give all [optional] parameters at the left, e.g. if you want to set the cClef, you have to set the accidentals, too.

\makeTimeSignatureTest

Usage: \makeTimeSignatureTest [allowDuplicateSignatures] [allowDuplicates] num

Generate num bars with a random time signature for each bar. It is possible to allow duplicate signatures or duplicate bars by passing ##t as optional arguments. The settings default to ##tand ##f, respectively.

\makeBarLineTest

Usage: \makeBarLineTest [allowDuplicateSignature] [allowDuplicates] numSignatures numBars

Generate numSignatures sets of numBars bars without barlines. Bars will always be complete, without pickups.

It is possible to allow duplicate signatures or duplicate bars by passing ##t as optional arguments. Both settings default to ##f.

\makeNoteNameTest

Usage:

\makeNoteNameTest [allowDuplicates] numTreble [br] numBass [br] numC [br] num8va [br] num8vb [accidentals] [cClef] mode

Generate a test for reading and writing note names with or without accidentals in various clefs.

Parameters

  • [allowDuplicates] (boolean) - set to ##t if you want to allow duplicate pitches in a single clef, for instance to make a 100 item test in treble clef for beginners
Name type description
[allowDuplicates] boolean set to ##t if you want to allow duplicate pitches in a single clef, for instance to make a 100 item test in treble clef for beginners
numTreble, numBass, numC, num8va, num8vb integer specify the number of notes to generate for each clef; set to 0 to omit this clef
[br] boolean set to ##t to force a line break at the given position
[accidentals] list Specify which accidentals should be used, one of: #no-accidentals,#accidentals-no-doubles,#accidentals-with-doubles; default: #accidentals-no-doubles
[cClef] string Specify which clef to use for the cClef. Currently supported: "alto", "tenor", "soprano"; default: "alto"
mode char Specify the task mode: ##\r will generate notes with blank names to test reading, ##\w will generate an empty staff with notenames underneath to test writing, and ##\s will solve the test, giving both notenames and written notes.

\makeClefTest

Usage: \makeClefTest [allowDuplicates] num clef [accidentals] Generate a series of pitches in a single clef.

name type description
[allowDuplicates] boolean set to ##t if you want to allow duplicate pitches in a single clef, for instance to make a 100 item test in treble clef for beginners
num integer number of pitches to generate
clef string the clef in lilypond syntax, e.g. "treble^8"
[accidentals] list Specify which accidentals should be used, one of: #no-accidentals,#accidentals-no-doubles,#accidentals-with-doubles. Use \default for #accidentals-no-doubles

\makeIntervalTest

Usage: \makeIntervalTest [allowDuplicates] [interval-list] [accidentals] mode num

Generate some intervals. They could be used to practice interval names or to train interval writing.

name type description
[allowDuplicates] boolean Set to ##t if you want to allow duplicate intervals. Intervals are compared by name, not by the actual pitches. Default: ##f
[interval-list] list Specify which intervals should bes use, one of: #easy-intervals (major, minor, perfect 1-8), normal-intervals(like easy, with additional augmented or diminuished 1,4,5 & 8), all-intervals(like normal, with additional augmented or diminuished 2,3,6&7)
[accidentals] list Specify which accidentals should be used, one of: #no-accidentals,#accidentals-no-doubles,#accidentals-with-doubles. Use \default for #accidentals-no-doubles
mode char ##\r for reading tasks (given notes), ##\w for wrtiting tasks (given interval names, starting note and direction)
num integer number of intervals to generate

\makeContainsTest

Usage: \makeContainsTest [allowDuplicates] unit num

Generate some notes of random length and ask, how many notes of unit would fit them.

name type description
[allowDuplicates] boolean Set to ##t if you want to allow duplicate intervals. Intervals are compared by name, not by the actual pitches. Default: ##f
unit integer 8 for 8th notes, 16 for 16th notes
num integer number of notes to generate

Default value lists

Accidentals:

These are the lists that are available by default for all accidental parameters:

name description
#no-accidentals natural notes only
#accidentals-no-doubles natural notes and single accidentals
#accidentals-with-doubles natrual notes, single and double accidentals

Intervals:

These lists are available for the \makeIntervalTest task. Currently all of them only use intervals up to an octave.

name description
#easy-intervals perfect, major and minor intervals only
#normal-intervals like #easy-intervals, with additional augmented or diminuished variants of perfect intervals (1,4,5,8)
#all-intervals like #normal-intervals, with additional augmented or diminuished variants of major/minor intervals (2,3,6,7)
\version "2.19.82"
\language deutsch
#(define seed 0)
#(define solution #f)
#(define useBE #f)
#(define includeTasks #t)
setSeed =
#(define-void-function (newSeed) (number?)
(begin
(set! seed newSeed)
(set! *random-state*
(seed->random-state seed)))
)
randomizeSeed =
#(define-void-function () ()
(let* ((time (gettimeofday)))
(begin (set! seed (+ (car time) (cdr time)))
#{ \setSeed #seed #} ))
)
\randomizeSeed
showSeed =
#(define-scheme-function (parser location) ()
#{
\markup {
seed = #(number->string seed)
}
#}
)
showSolution =
#(define-scheme-function () ()
(set! solution #t)
)
%turn solution of (only has effect after manually enabling it before)
hideSolution =
#(define-scheme-function () ()
(set! solution #f)
)
setBE =
#(define-scheme-function () ()
(begin (set! useBE #t) (initTaskList #t))
)
setTasks =
#(define-scheme-function (tasksOn) (boolean?)
(set! includeTasks tasksOn)
)
%%% Number of tasks, is updated automatically with every call to \task
#(define taskNr 0)
%%% Sum of points in all tasks
#(define pointSum 0)
%%%Predefined tasks
taskList = #'()
#(define (initTaskList BE)
(if BE
(set! taskList '(
("timeSignatureTest" . "Bestimmen Sie die Taktart für jeden Takt!")
("barLineTest" . "Setzen Sie Taktstriche, sodass vollständige Takte entstehen!")
(("noteNameTest" . #\r) . "Benennen Sie die Töne inklusive Oktavbezeichnung!")
(("noteNameTest" . #\w) . "Notieren Sie die angegebenen Töne!")
(("noteNameTest" . #\s) . "Notieren Sie die angegebenen Töne! Wählen Sie einen passenden Schlüssel für jede Zeile!")
(("containsTest" . 8) . "Geben Sie an, wie vielen Achtelnoten die folgenden Notenwerte entsprechen!")
(("containsTest" . 16) . "Geben Sie an, wie vielen Achtelnoten die folgenden Notenwerte entsprechen!")
(("intervalTest" . #\r) . "Bestimmen Sie die Intervalle!")
(("intervalTest" . #\w) . "Bilden Sie die angegebenen Intervalle ausgehend von den jeweiligen Tönen!")
(("scaleTest" . #\r) . "Bestimmen Sie folgende Tonleitern!")
(("scaleTest" . #\w) . "Notieren Sie folgende Tonleitern!")
(("chordTest" . #\r) . "Bestimmen Sie folgende Akkorde!")
(("chordTest" . #\w) . "Notieren Sie folgende Akkorde!")
))
(set! taskList '(
("timeSignatureTest" . "Bestimme die Taktart für jeden Takt!")
("barLineTest" . "Setze Taktstriche, sodass vollständige Takte entstehen!")
(("noteNameTest" . #\r) . "Benenne die Töne inklusive Oktavbezeichnung!")
(("noteNameTest" . #\w) . "Notiere die angegebenen Töne!")
(("noteNameTest" . #\s) . "Notiere die angegebenen Töne! Wähle einen passenden Schlüssel für jede Zeile!")
(("containsTest" . 8) . "Gib an, wie vielen Achtelnoten die folgenden Notenwerte entsprechen!")
(("containsTest" . 16) . "Gib an, wie vielen Achtelnoten die folgenden Notenwerte entsprechen!")
(("intervalTest" . #\r) . "Bestimme die Intervalle!")
(("intervalTest" . #\w) . "Bilde die angegebenen Intervalle ausgehend von den jeweiligen Tönen!")
(("scaleTest" . #\r) . "Bestimme folgende Tonleitern!")
(("scaleTest" . #\w) . "Notiere folgende Tonleitern!")
(("chordTest" . #\r) . "Bestimme folgende Akkorde!")
(("chordTest" . #\w) . "Notiere folgende Akkorde!")
)))
)
#(initTaskList useBE)
%%% Task markup generator
task =
#(define-scheme-function (add text points) ((boolean?) string? number?)
(begin (if (not add) (begin (set! taskNr (1+ taskNr)) (set! pointSum (+ pointSum points))) #{ #}) #{
\markup {
\bold
%\override #'(line-width . 100 )
\fill-with-pattern #1 #RIGHT " "
\line { #(if add "Zusatz: " (string-append (number->string taskNr) ". ")) #text }
\line { "/" #(number->string points) " " \concat {#(if add "Z" "") #(if useBE "BE" "P.")} }
}
#})
)
resetTaskCounter =
#(define-scheme-function () ()
(set! taskNr 0)
(set! pointSum 0)
)
%%% Markup for keyboard
whiteKeys =
#(define-scheme-function (key-height key-width) ((number? 8) (number? 3))
#{ \markup {
\concat {
\combine
\with-color #black
\filled-box #(cons 0 (/ key-width 2)) #(cons 0 key-height) #0
\with-color #white
\filled-box #(cons 0 (-(/ key-width 2) 0.1)) #(cons 0.1 (- key-height 0.1)) #0
\combine
\with-color #black
\filled-box #(cons 0 key-width) #(cons 0 key-height) #0
\with-color #white
\filled-box #(cons 0 (- key-width 0.1)) #(cons 0.1 (- key-height 0.1)) #0
\combine
\with-color #black
\filled-box #(cons 0 key-width) #(cons 0 key-height) #0
\with-color #white
\filled-box #(cons 0 (- key-width 0.1)) #(cons 0.1 (- key-height 0.1)) #0
\combine
\with-color #black
\filled-box #(cons 0 key-width) #(cons 0 key-height) #0
\with-color #white
\filled-box #(cons 0 (- key-width 0.1)) #(cons 0.1 (- key-height 0.1)) #0
\combine
\with-color #black
\filled-box #(cons 0 key-width) #(cons 0 key-height) #0
\with-color #white
\filled-box #(cons 0 (- key-width 0.1)) #(cons 0.1 (- key-height 0.1)) #0
\combine
\with-color #black
\filled-box #(cons 0 key-width) #(cons 0 key-height) #0
\with-color #white
\filled-box #(cons 0 (- key-width 0.1)) #(cons 0.1 (- key-height 0.1)) #0
\combine
\with-color #black
\filled-box #(cons 0 key-width) #(cons 0 key-height) #0
\with-color #white
\filled-box #(cons 0 (- key-width 0.1)) #(cons 0.1 (- key-height 0.1)) #0
\combine
\with-color #black
\filled-box #(cons 0 key-width) #(cons 0 key-height) #0
\with-color #white
\filled-box #(cons 0 (- key-width 0.1)) #(cons 0.1 (- key-height 0.1)) #0
\combine
\with-color #black
\filled-box #(cons 0 (/ key-width 2)) #(cons 0 key-height) #0
\with-color #white
\filled-box #(cons 0.1 (/ key-width 2)) #(cons 0.1 (- key-height 0.1)) #0
}
}
#}
)
blackKeys =
#(define-scheme-function (key-height key-width) ((number? 8) (number? 3))
#{
\markup {
\concat {
\filled-box #(cons (/ (* key-width 7) 6) (/ (* key-width 11) 6)) #(cons (/ key-height 3) key-height) #0
\filled-box #(cons (/ key-width 3) key-width) #(cons (/ key-height 3) key-height) #0
\filled-box #(cons (/ (* key-width 4) 3) (* key-width 2)) #(cons (/ key-height 3) key-height) #0
\filled-box #(cons (/ key-width 3) key-width) #(cons (/ key-height 3) key-height) #0
\filled-box #(cons (/ key-width 3) key-width) #(cons (/ key-height 3) key-height) #0
}
}
#}
)
keyboard =
#(define-scheme-function (key-height key-width) ((number? 8) (number? 3))
#{
\markup{
\combine
\whiteKeys #key-height #key-width
\blackKeys #key-height #key-width
}
#}
)
%%% Markup for test result block
result =
#(define-scheme-function () ()
#{
\markup {
\override #'(line-width . 35 )
\bold
\column {
\null
\fill-line { #(if useBE "BE:" "Punkte:") \line { "/"#(number->string pointSum)} \line { "Note:" } }
} }
#}
)
%%% Markup for test result block
resultKeyboard =
#(define-scheme-function () ()
#{
\markup {
\override #'(line-width . 105 )
\bold
\column {
\null
\fill-line {
\override #'(line-width . 35 )
\fill-line { #(if useBE "BE:" "Punkte:") \line { "/"#(number->string pointSum)} \line { "Note:" } }
\line { \keyboard #10 #3.5 }
}
} }
#}
)
%%% Markup for a test header table
testHeader =
#(define-scheme-function (date title) ((string? " ") list?)
#{ \markup {
%\override #'(line-width . 20 )
\column {
\fill-line { Name: Klasse: \concat { "Datum: " #date }}
\null
\bold
\fill-line { \fontsize #4 \center-column { #@title \null }}
} } #}
)
%%% Blank box
blankBox =
#(define-scheme-function (width height) (number? number?)
#{
\markup {
\combine
\combine
%\filled-box #`(0 . ,width) #`(0 . ,height) #0
%\with-color #white
%\filled-box #`(0.2 . ,(- width 0.2)) #`(0.2 . ,(- height 0.2)) #0
\draw-line #`(0 . ,height)
\draw-line #`(,width . 0)
\translate #`(,width . ,height) {
\combine
\draw-line #`(0 . ,(- 0 height))
\draw-line #`(,(- 0 width) . 0) }
} #}
)
%% Readable ranges for allowed clefs
#(define trebleRange '())
#(define bassRange '())
#(define altoRange '())
#(define tenorRange '())
#(define sopranoRange '())
#(define treble8vaRange '())
#(define treble8vbRange '())
#(define clef-ranges '())
setRange =
#(define-scheme-function (clef pitch1 pitch2) (string? ly:pitch? ly:pitch?)
(begin
(cond
((equal? clef "treble") (set! trebleRange (list pitch1 pitch2)))
((equal? clef "alto") (set! altoRange (list pitch1 pitch2)))
((equal? clef "tenor") (set! tenorRange (list pitch1 pitch2)))
((equal? clef "soprano") (set! sopranoRange (list pitch1 pitch2)))
((equal? clef "bass") (set! bassRange (list pitch1 pitch2)))
((equal? clef "treble^8") (set! treble8vaRange (list pitch1 pitch2)))
((equal? clef "treble_8") (set! treble8vbRange (list pitch1 pitch2))))
(set! clef-ranges
`(("treble" . ,trebleRange)
("alto" . ,altoRange)
("tenor" . ,tenorRange)
("soprano" . ,sopranoRange)
("bass" . ,bassRange)
("treble^8" . ,treble8vaRange)
("treble_8" . ,treble8vbRange))))
)
%initialize all clef ranges
\setRange "treble" a c'''
\setRange "bass" c, c'
\setRange "alto" d d'
\setRange "tenor" c h'
\setRange "soprano" g f''
\setRange "treble^8" a' c''''
\setRange "treble_8" a, c''
#(define accidentals-no-doubles
(list -1/2 0 0 0 1/2))
%weighted list to make double accidentals less likely
#(define accidentals-doubles
(list -1 -1/2 -1/2 0 0 0 1/2 1/2 1))
#(define no-accidentals
(list 0))
newnames =
#`(
("c''''" . ,(markup "c" #:super "4")) ("cis''''" . ,(markup "cis" #:super "4")) ("ces''''" . ,(markup "ces" #:super "4")) ("cisis''''" . ,(markup "cisis" #:super "4")) ("ceses''''" . ,(markup "ceses" #:super "4"))
("d''''" . ,(markup "d" #:super "4")) ("dis''''" . ,(markup "dis" #:super "4")) ("des''''" . ,(markup "des" #:super "4")) ("disis''''" . ,(markup "disis" #:super "4")) ("deses''''" . ,(markup "deses" #:super "4"))
("e''''" . ,(markup "e" #:super "4")) ("eis''''" . ,(markup "eis" #:super "4")) ("ees''''" . ,(markup "es" #:super "4")) ("eisis''''" . ,(markup "eisis" #:super "4")) ("eeses''''" . ,(markup "eses" #:super "4"))
("f''''" . ,(markup "f" #:super "4")) ("fis''''" . ,(markup "fis" #:super "4")) ("fes''''" . ,(markup "fes" #:super "4")) ("fisis''''" . ,(markup "fisis" #:super "4")) ("feses''''" . ,(markup "feses" #:super "4"))
("g''''" . ,(markup "g" #:super "4")) ("gis''''" . ,(markup "gis" #:super "4")) ("ges''''" . ,(markup "ges" #:super "4")) ("gisis''''" . ,(markup "gisis" #:super "4")) ("geses''''" . ,(markup "geses" #:super "4"))
("a''''" . ,(markup "a" #:super "4")) ("ais''''" . ,(markup "ais" #:super "4")) ("aes''''" . ,(markup "as" #:super "4")) ("aisis''''" . ,(markup "aisis" #:super "4")) ("aeses''''" . ,(markup "ases" #:super "4"))
("b''''" . ,(markup "h" #:super "4")) ("bis''''" . ,(markup "his" #:super "4")) ("bes''''" . ,(markup "b" #:super "4")) ("bisis''''" . ,(markup "hisis" #:super "4")) ("beses''''" . ,(markup "hes" #:super "4"))
("c'''" . "c³") ("cis'''" . "cis³") ("ces'''" . "ces³") ("cisis'''" . "cisis³") ("ceses'''" . "ceses³")
("d'''" . "d³") ("dis'''" . "dis³") ("des'''" . "des³") ("disis'''" . "disis³") ("deses'''" . "deses³")
("e'''" . "e³") ("eis'''" . "eis³") ("ees'''" . "es³") ("eisis'''" . "eisis³") ("eeses'''" . "eses³")
("f'''" . "f³") ("fis'''" . "fis³") ("fes'''" . "fes³") ("fisis'''" . "fisis³") ("feses'''" . "feses³")
("g'''" . "g³") ("gis'''" . "gis³") ("ges'''" . "ges³") ("gisis'''" . "gisis³") ("geses'''" . "geses³")
("a'''" . "a³") ("ais'''" . "ais³") ("aes'''" . "as³") ("aisis'''" . "aisis³") ("aeses'''" . "ases³")
("b'''" . "h³") ("bis'''" . "his³") ("bes'''" . "b³") ("bisis'''" . "hisis³") ("beses'''" . "hes³")
("c''" . "c²") ("cis''" . "cis²") ("ces''" . "ces²") ("cisis''" . "cisis²") ("ceses''" . "ceses²")
("d''" . "d²") ("dis''" . "dis²") ("des''" . "des²") ("disis''" . "disis²") ("deses''" . "deses²")
("e''" . "e²") ("eis''" . "eis²") ("ees''" . "es²") ("eisis''" . "eisis²") ("eeses''" . "eses²")
("f''" . "f²") ("fis''" . "fis²") ("fes''" . "fes²") ("fisis''" . "fisis²") ("feses''" . "feses²")
("g''" . "g²") ("gis''" . "gis²") ("ges''" . "ges²") ("gisis''" . "gisis²") ("geses''" . "geses²")
("a''" . "a²") ("ais''" . "ais²") ("aes''" . "as²") ("aisis''" . "aisis²") ("aeses''" . "ases²")
("b''" . "h²") ("bis''" . "his²") ("bes''" . "b²") ("bisis''" . "hisis²") ("beses''" . "hes²")
("c'" . "c¹") ("cis'" . "cis¹") ("ces'" . "ces¹") ("cisis'" . "cisis¹") ("ceses'" . "ceses¹")
("d'" . "d¹") ("dis'" . "dis¹") ("des'" . "des¹") ("disis'" . "disis¹") ("deses'" . "deses¹")
("e'" . "e¹") ("eis'" . "eis¹") ("ees'" . "es¹") ("eisis'" . "eisis¹") ("eeses'" . "eses¹")
("f'" . "f¹") ("fis'" . "fis¹") ("fes'" . "fes¹") ("fisis'" . "fisis¹") ("feses'" . "feses¹")
("g'" . "g¹") ("gis'" . "gis¹") ("ges'" . "ges¹") ("gisis'" . "gisis¹") ("geses'" . "geses¹")
("a'" . "a¹") ("ais'" . "ais¹") ("aes'" . "as¹") ("aisis'" . "aisis¹") ("aeses'" . "ases¹")
("b'" . "h¹") ("bis'" . "his¹") ("bes'" . "b¹") ("bisis'" . "hisis¹") ("beses'" . "hes¹")
("c" . "c") ("cis" . "cis") ("ces" . "ces") ("cisis" . "cisis") ("ceses" . "ceses")
("d" . "d") ("dis" . "dis") ("des" . "des") ("disis" . "disis") ("deses" . "deses")
("e" . "e") ("eis" . "eis") ("ees" . "es") ("eisis" . "eisis") ("eeses" . "eses")
("f" . "f") ("fis" . "fis") ("fes" . "fes") ("fisis" . "fisis") ("feses" . "feses")
("g" . "g") ("gis" . "gis") ("ges" . "ges") ("gisis" . "gisis") ("geses" . "geses")
("a" . "a") ("ais" . "ais") ("aes" . "as") ("aisis" . "aisis") ("aeses" . "ases")
("b" . "h") ("bis" . "his") ("bes" . "b") ("bisis" . "hisis") ("beses" . "hes")
("c," . "C") ("cis," . "Cis") ("ces," . "Ces") ("cisis," . "Cisis") ("ceses," . "Ceses")
("d," . "D") ("dis," . "Dis") ("des," . "Des") ("disis," . "Disis") ("deses," . "Deses")
("e," . "E") ("eis," . "Eis") ("ees," . "Es") ("eisis," . "Eisis") ("eeses," . "Eses")
("f," . "F") ("fis," . "Fis") ("fes," . "Fes") ("fisis," . "Fisis") ("feses," . "Feses")
("g," . "G") ("gis," . "Gis") ("ges," . "Ges") ("gisis," . "Gisis") ("geses," . "Geses")
("a," . "A") ("ais," . "Ais") ("aes," . "As") ("aisis," . "Aisis") ("aeses," . "Ases")
("b," . "H") ("bis," . "His") ("bes," . "B") ("bisis," . "Hisis") ("beses," . "Hes")
("c,," . "C¹") ("cis,," . "Cis¹") ("ces,," . "Ces¹") ("cisis,," . "Cisis¹") ("ceses,," . "Ceses¹")
("d,," . "D¹") ("dis,," . "Dis¹") ("des,," . "Des¹") ("disis,," . "Disis¹") ("deses,," . "Deses¹")
("e,," . "E¹") ("eis,," . "Eis¹") ("ees,," . "Es¹") ("eisis,," . "Eisis¹") ("eeses,," . "Eses¹")
("f,," . "F¹") ("fis,," . "Fis¹") ("fes,," . "Fes¹") ("fisis,," . "Fisis¹") ("feses,," . "Feses¹")
("g,," . "G¹") ("gis,," . "Gis¹") ("ges,," . "Ges¹") ("gisis,," . "Gisis¹") ("geses,," . "Geses¹")
("a,," . "A¹") ("ais,," . "Ais¹") ("aes,," . "As¹") ("aisis,," . "Aisis¹") ("aeses,," . "Ases¹")
("b,," . "H¹") ("bis,," . "His¹") ("bes,," . "B¹") ("bisis,," . "Hisis¹") ("beses,," . "Hes¹")
)
germanNoteNames =
#(lambda (grob)
(let* ((default-name (ly:grob-property grob 'text))
(new-name (assoc-get default-name newnames)))
(ly:grob-set-property! grob 'text new-name)
(ly:text-interface::print grob)))
blankNoteNames =
#(lambda (grob)
(let* ((default-name (ly:grob-property grob 'text)))
(ly:grob-set-property! grob 'text "___")
(ly:text-interface::print grob)))
%% Calculate base interval
#(define (calc-white-note-span note1 note2)
(let ((o1 (ly:pitch-octave note1))
(o2 (ly:pitch-octave note2))
(nn1 (ly:pitch-notename note1))
(nn2 (ly:pitch-notename note2)))
(+ (* (- o2 o1) 7)
(1+ (- nn2 nn1)))))
#(define (make-note pitch dur)
(make-music
'NoteEvent
'duration
(ly:make-duration dur)
'pitch
pitch))
#(define (get-random-pitch clef acc-pool)
(let* ((range (ly:assoc-get clef clef-ranges))
(white-notes (calc-white-note-span (car range) (cadr range)))
(random-pitch-diff (random white-notes)))
(ly:make-pitch
(ly:pitch-octave (car range))
(+ random-pitch-diff (ly:pitch-notename (car range)))
(list-ref acc-pool (random (length acc-pool))))))
%Check for double accidentals
#(define (has-doubles mus)
(any (lambda (p) (not (< -1 (ly:pitch-alteration p) 1)))
(music-pitches mus)))
makeClefTest =
#(define-music-function (allowDuplicates num clef acc-pool)
(boolean? number? string? (list? accidentals-no-doubles))
(let loop ((x 0) (return '()))
(if (< x num)
(let ((item (make-note (get-random-pitch clef acc-pool) 0)))
(if (or allowDuplicates (not (member item return)))
(loop
(1+ x)
(append return
(list
#{ \clef #clef #}
item))
)
;;; retry to get a new pitch that doesn't exist for the current clef
(loop x return)
))
#{
{
#@return
}
#})))
makeNoteNameTest =
#(define-scheme-function (allowDuplicates numTreble brTreble numBass brBass numC brC num8va br8va num8vb
acc-pool cClef mode)
( (boolean? #f) number? (boolean? #f) number? (boolean? #f) number? (boolean? #f) number? (boolean? #f) number?
(list? accidentals-no-doubles) (string? "alto") char?)
(let ((content #{
{
\makeClefTest #allowDuplicates #numTreble "treble" #acc-pool #(if brTreble #{ \break #} #{ #})
\makeClefTest #allowDuplicates #numBass "bass" #acc-pool #(if brBass #{ \break #} #{ #})
\makeClefTest #allowDuplicates #numC #cClef #acc-pool #(if brC #{ \break #} #{ #})
\makeClefTest #allowDuplicates #num8va "treble^8" #acc-pool #(if br8va #{ \break #} #{ #})
\makeClefTest #allowDuplicates #num8vb "treble_8" #acc-pool
\undo \omit Score.BarLine
\bar "|."
}
#}))
(let ((result (scorify-music #{
\time 4/4
\omit Score.TimeSignature
% \omit Score.BarLine
%%%strict mode - hide clefs
#(if (and (not solution) (equal? mode #\s)) #{
\override Score.Clef.stencil = #ly:text-interface::print
\override Score.Clef #'Y-offset = -3
\hide Score.ClefModifier
\override Score.Clef.text = \blankBox 5 8
#} #{ #})
\set Score.explicitClefVisibility = #end-of-line-invisible
%%%Format solutions
#(if solution (if (equal? mode #\r)
#{ \override Score.NoteName.color = #red #}
#{ \override Score.NoteHead.color = #red
\override Score.Stem.color = #red
\override Score.Accidental.color = #red
\override Score.LedgerLineSpanner.color = #red
#(if (equal? mode #\s) #{
\override Score.Clef.color = #red
\override Score.ClefModifier.color = #red #} #{ #} ) #} ) #{ #})
\new Staff {
\set Staff.explicitCueClefVisibility = ##f
#(if (and (not solution) (or (equal? mode #\w) (equal? mode #\s))) #{ \hideNotes #} #{ #})
<<
#content
\context NoteNames \with {
%%% Show notenames only for reading tasks
\override NoteName #'stencil = #(if (and (not solution) (equal? mode #\r)) blankNoteNames germanNoteNames)
\override NoteName #'Y-offset = -2
\override NoteName #'X-offset = -1
} {
\set printOctaveNames = ##t
#content
}
>>
}
#})))
(if includeTasks (add-text
#{
\task #(assoc-get (cons "noteNameTest" mode) taskList) #(+ numTreble numBass numC num8va num8vb)
#} ) '())
(add-score #{ #result #})
)
)
)
blanks =
#(define-scheme-function (num) (number?)
(let ((str (map (lambda (n) "___")
(iota num 1))))
#{
\lyricmode { #@str }
#}))
%%% RHYTHM TEST GENERATORS
%list of possible time signature denominators - corresponding to numerators below
#(define denominators
(list 4 4 4 4 4 8 8)
)
%list of possible time signature numerators
#(define numerators
(list 2 3 4 5 6 3 6)
)
%list of possible rhythm patterns in various time signatures
#(define note-pool
(list
;;; 2/4
(ly:music-property #{
{g'4 g'4} {g'8 g' g'4} {g'4. g'8} {g'8 g'4.} {g'8 g' g' g'}
{g'16 g' g' g' g'4} {g'16 g' g'8 g' g'}
{g'2}
#}
'elements)
;;; 3/4
(ly:music-property #{
{g'4 g'4 g'4} {g'8 g' g'2} {g'4. g'8 g'4} {g'8 g'4. g'16 g' g'8} {g'8 g'4 g'8 g'4}
{g'8 g' g'4 g'16 g' g' g'} {g'8 g'16 g'~ g'8. g'16 g'4}
{g'2.}
#}
'elements)
;;; 4/4
(ly:music-property #{
{g'2 g'2} {g'8 g' g'4. g'8 g'16 g' g'8 }
{g'4. g'8 g'2} {g'8 g'4.~ g'8 g' g'4} {g'8 g'4 g' g' g'8}
{g'16 g' g' g' g'4. g'8 g'4} {g'16 g' g'8 g' g' g' g'4.}
{g'1}
#}
'elements)
;;; 5/4
(ly:music-property #{
{g'1 g'4} {g'4 g'8 g' g'4 g'2} {g'4 g'16 g' g'8 g' g'16 g' g'8 g'4.}
#}
'elements)
;;; 6/4
(ly:music-property #{
{g'2. g'} {g'8 g' g'4 g' g'2 g'8 g'} {g'2 g' g'}
{g'4. g'8 g'16 g' g'8 g'2.}
#}
'elements)
;;; 3/8
(ly:music-property #{
{g'4.} {g'16 g' g'8 g'8} {g'8 g'16 g' g'8} {g'8 g'4}
#}
'elements)
;;; 6/8
(ly:music-property #{
{g'8 g' g' g'4.} {g'4 g'8 g'16 g' g'8 g'}
#}
'elements)
)
)
%generate a time signature based on the lists above
ts =
#(define-music-function (idx) (number?)
(let ((index (random (length denominators))))(make-music
'TimeSignatureMusic
'beat-structure
'()
'denominator
(list-ref denominators idx)
'numerator
(list-ref numerators idx)))
)
%Time Signature Test Generator
makeTimeSignatureTest =
#(define-scheme-function (allowDuplicateSignature allowDuplicates num) ((boolean? #t) (boolean?) number?)
(let loop ((i 0) (return '()))
(if (< i num)
(let* ((index (random (length denominators))) (timeSignature #{ \ts #index #}) (notes (list-ref note-pool index)) (item (list-ref notes (random (length notes)))))
;;;check if unallowed duplicates exist
(if (and (or allowDuplicates (not (member item return))) (or allowDuplicateSignature (not (member timeSignature return))))
(loop
(1+ i)
(append return
(list
timeSignature
item)))
(loop i return)
))
(let ((result (scorify-music #{
{
#(if (not solution) #{ \hide Score.TimeSignature #} #{ \numericTimeSignature \override Score.TimeSignature.color = #red #})
\override Score.TimeSignature.break-visibility = #end-of-line-invisible
#@return
}
#})))
(if includeTasks (add-text
#{
\task #(assoc-get "timeSignatureTest" taskList) #num
#} ) '())
(add-score #{ #result #}))))
)
%Barline Test Generator
makeBarLineTest =
#(define-scheme-function (allowDuplicateSignature allowDuplicates numSignatures numBars) ((boolean? #f) (boolean?) number? number?)
(let loop ((i 0) (return '()))
(if (< i numSignatures)
(let* ((index (random 3)) (timeSignature #{ \ts #index #}) (notes (list-ref note-pool index)) )
;;;check if unallowed duplicates exist
(if (or allowDuplicateSignature (not (member timeSignature return)))
(loop
(1+ i)
(append return
(list
timeSignature
(let iLoop ((k 0) (bars '()))
(if (< k numBars)
(let ((item (list-ref notes (random (length notes)))))
(if (not (member item bars))
(iLoop (1+ k) (append bars (list item)))
(iLoop k bars)))
#{ { #@bars } #})
)
#{ \break #} )))
(loop i return)
))
(let ((result (scorify-music #{
{
\numericTimeSignature
#(if (not solution) #{ \omit Score.BarLine #} #{ \override Score.BarLine.color = #red #})
\override Score.TimeSignature.break-visibility = #end-of-line-invisible
#@return
}
#})))
(if includeTasks (add-text
#{
\task #(assoc-get "barLineTest" taskList) #(* numSignatures numBars)
#} ) '())
(add-score #{ #result #})
)))
)
%"How many notes contains..." test generator
%pool of values
#(define duration-pool
(list
;;; for 8th's
(ly:music-property #{
{g'4} {g'8} {g'2} {g'1}
{g'1.} {g'2.} {g'4.}
{g'1~ g'4} {g'1~ g'8} {g'2~ g'8} {g'4~ g'1} {g'4~ g'8}
{g'1~ g'4.} {g'2~ g'4.} {g'2.~ g'4} {g'2.~ g'4.}
{g'16~ g'16}
#}
'elements)
;;; for 16th's
(ly:music-property #{
{g'4} {g'8} {g'2} {g'1}
{g'1.} {g'2.} {g'4.}
{g'1~ g'4} {g'1~ g'8} {g'2~ g'8} {g'4~ g'1} {g'4~ g'8}
{g'1~ g'4.} {g'2~ g'4.} {g'2.~ g'4} {g'2.~ g'4.}
{g'16~ g'16}
{g'4~ g'16} {g'4~ g'8.} {g'8.}
#}
'elements)
)
)
%main function for providing How-many-tests
makeContainsTest =
#(define-scheme-function (allowDuplicates unit num) ((boolean?) number? number?)
(let loop ((x 0) (return '()))
(if (< x num)
(let* ( (pool (list-ref duration-pool (if (equal? unit 8) 0 1))) (index (random (length pool))) (item (list-ref pool index)))
(if (or allowDuplicates (not (member item return)))
(loop
(1+ x)
(append return
(list
item
#{ \bar "|" #} ))
)
;;; retry to get a new pitch that doesn't exist for the current clef
(loop x return)
))
(let ((result (scorify-music #{
{
\time 99/4
\omit Score.TimeSignature
{ #@return } \addlyrics {
#(if (not solution)
#{ \blanks #num #}
#{\override Score.LyricText.color = #red
\solveContains #unit #return #})
}
}
#})))
(if includeTasks (add-text
#{
\task #(assoc-get (cons "containsTest" unit) taskList) #num
#} ) '())
(add-score #{ #result #} ))))
)
%helper predicate to check if a string equals "0"
#(define (stringZero? str)
(if (equal? str "0")
#t
#f)
)
%provide solution for How many-tasks
solveContains =
#(define-music-function (unit music) (number? list?)
(let ((solution (remove stringZero?
(map (lambda (mus) (let* (
(mom (ly:music-length mus ))
(p (ly:moment-main-numerator mom))
(q (ly:moment-main-denominator mom)))
(number->string (* (/ unit q) p))))
music))))
#{ \lyricmode { #@solution } #}
)
)
%%%Interval test suite
tone-steps =
#'(((1 . 1)."HT") ((1 . 2)."GT"))
easy-intervals =
#'(
((0 . 0)."r1")
((1 . 1)."k2") ((1 . 2)."g2")
((2 . 3)."k3") ((2 . 4)."g3")
((3 . 5)."r4")
((4 . 7)."r5")
((5 . 8)."k6") ((5 . 9)."g6")
((6 . 10)."k7") ((6 . 11)."g7")
((7 . 12)."r8"))
normal-intervals =
#'(
((0 . 0)."r1") ((0 . 1)."ü1")
((1 . 1)."k2") ((1 . 2)."g2")
((2 . 3)."k3") ((2 . 4)."g3")
((3 . 5)."r4") ((3 . 4)."v4") ((3 . 6)."ü4")
((4 . 7)."r5") ((4 . 6)."v5") ((4 . 8)."ü5")
((5 . 8)."k6") ((5 . 9)."g6")
((6 . 10)."k7") ((6 . 11)."g7")
((7 . 12)."r8") ((7 . 11)."v8") ((7 . 13)."ü8")
)
all-intervals =
#'(
((0 . 0)."r1") ((0 . 1)."ü1")
((1 . 1)."k2") ((1 . 2)."g2")
((2 . 3)."k3") ((2 . 4)."g3")
((3 . 5)."r4") ((3 . 4)."v4") ((3 . 6)."ü4")
((4 . 7)."r5") ((4 . 6)."v5") ((4 . 8)."ü5")
((5 . 8)."k6") ((5 . 9)."g6")
((6 . 10)."k7") ((6 . 11)."g7")
((7 . 12)."r8") ((7 . 11)."v8") ((7 . 13)."ü8")
((1 . 0)."v2") ((1 . 3)."ü2")
((2 . 2)."v3") ((2 . 5)."ü3")
((5 . 7)."v6") ((5 . 10)."ü6")
((6 . 9)."v7") ((6 . 12)."ü7"))
#(define (getIntervalName p1 p2 intervalList)
(let ((steps (abs (- (ly:pitch-steps p1) (ly:pitch-steps p2))))
(semitones (abs (- (ly:pitch-semitones p1) (ly:pitch-semitones p2)))))
(begin
(assoc-get (cons steps semitones) intervalList)))
)
#(define (arrow p1 p2)
(cond
((< (ly:pitch-semitones p1) (ly:pitch-semitones p2))
#{ \markup {
\override #'(thickness . 1)
\combine
\translate #'(0 . 2) \arrow-head #Y #UP ##t
\draw-line #'(0 . 2)
} #} )
((equal? p1 p2) " ")
(else #{ \markup {
\override #'(thickness . 1)
\combine
\draw-line #'(0 . 2)
\arrow-head #Y #DOWN ##t
}#}))
)
#(define arrow-up
#{ \markup {
\override #'(thickness . 1)
\combine
\translate #'(0 . 2) \arrow-head #Y #UP ##t
\draw-line #'(0 . 2)
} #} )
#(define arrow-down
#{ \markup {
\override #'(thickness . 1)
\combine
\draw-line #'(0 . 2) \arrow-head #Y #DOWN ##t
} #} )
#(define arrow-left
#{ \markup {
\override #'(thickness . 1)
\combine
\arrow-head #X #LEFT ##t
\draw-line #'(2 . 0)
} #} )
#(define arrow-right
#{ \markup {
\override #'(thickness . 1)
\combine
\draw-line #'(2 . 0)
\translate #'(2 . 0) \arrow-head #X #RIGHT ##t
} #} )
makeIntervalTest =
#(define-scheme-function (allowDuplicates interval-list acc-pool num mode)
((boolean?) (list? normal-intervals) (list? accidentals-no-doubles) number? char?)
(let loop ((x 0) (return '()) (solutionList '()))
(if (< x num)
(let* ((p1 (get-random-pitch "treble" acc-pool))
(p2 (get-random-pitch "treble" acc-pool))
(name (getIntervalName p1 p2 interval-list)))
(if (and name (or allowDuplicates (not (member name solutionList))))
(loop (1+ x)
(append return (list (make-note p1 2) (if
(equal? mode #\w)
(if (not solution)
#{ \hideNotes #}
#{ \override Score.NoteHead.color = #red
\override Score.Stem.color = #red
\override Score.Accidental.color = #red
\override Score.LedgerLineSpanner.color = #red
#})
#{ #})
(make-note p2 2)
(if (equal? mode #\w)
(if (not solution)
#{ \unHideNotes #}
#{ \revert Score.NoteHead.color
\revert Score.Stem.color
\revert Score.Accidental.color #})
#{ #})))
(append solutionList (list name (arrow p1 p2))))
(loop x return solutionList)
)
)
(let ((result (scorify-music #{
\time 2/4
\omit Score.TimeSignature
{
#@return
} \addlyrics {
#(if (not solution)
(if (equal? mode #\r)
#{ \lyricmode { \repeat unfold #num { "___" " " } } #}
#{ \lyricmode { #@solutionList } #})
(if (equal? mode #\r)
#{\override Score.LyricText.color = #red
\lyricmode { #@solutionList } #}
#{ \lyricmode { #@solutionList } #}))
}
#})))
(if includeTasks (add-text
#{
\task #(assoc-get (cons "intervalTest" mode) taskList) #num
#} ) '())
(add-score
#{ #result #}))
)
)
)
%%% Scale test generator
noteNameLookup =
#`(
(0 . "c")
(1 . "d")
(2 . "e")
(3 . "f")
(4 . "g")
(5 . "a")
(6 . "b")
)
scaleNoteLookup =
#`(
("ionisch" . ,#{ \key c \major c'1 d' e' f' g' a' h' c'' #})
("Dur" . ,#{ \key c \major c'1 d' e' f' g' a' h' c'' #})
("dorisch" . ,#{ \key c \dorian c'1 d' es' f' g' a' b' c'' #})
("phrygisch" . ,#{ \key c \phrygian c'1 des' es' f' g' as' b' c'' #})
("lydisch" . ,#{ \key c \lydian c'1 d' e' fis' g' a' h' c'' #})
("mixolydisch" . ,#{ \key c \mixolydian c'1 d' e' f' g' a' b' c'' #})
("äolisch" . ,#{ \key c \minor c'1 d' es' f' g' as' b' c'' #})
("moll" . ,#{ \key c \minor c'1 d' es' f' g' as' b' c'' #})
("äolisch melodisch" . ,#{ \key c \minor c'1 d' es' f' g' a' h' c'' #})
("äolisch harmonisch" . ,#{ \key c \minor c'1 d' es' f' g' as' h' c'' #})
("moll melodisch" . ,#{ \key c \minor c'1 d' es' f' g' a' h' c'' #})
("moll harmonisch" . ,#{ \key c \minor c'1 d' es' f' g' as' h' c'' #})
("lokrisch" . ,#{ \key c \locrian c'1 des' es' f' ges' as' b' c'' #})
)
#(define majorRoots (list
(ly:make-pitch 0 0 0) ;;;C
(ly:make-pitch 0 3 0) ;;;F
(ly:make-pitch 0 4 0) ;;;G
(ly:make-pitch 0 6 -1/2) ;;;Bb
(ly:make-pitch 0 1 0) ;;;D
(ly:make-pitch 0 2 -1/2) ;;;Es
(ly:make-pitch 0 5 0) ;;;A
(ly:make-pitch 0 5 -1/2) ;;;As
(ly:make-pitch 0 2 0) ;;;E
(ly:make-pitch 0 1 -1/2) ;;;Des
(ly:make-pitch 0 6 0) ;;;H
(ly:make-pitch 0 4 -1/2) ;;;Ges
(ly:make-pitch 0 3 1/2) ;;;Fis
))
#(define minorRoots (list
(ly:make-pitch 0 5 0) ;;;a
(ly:make-pitch 0 1 0) ;;;d
(ly:make-pitch 0 2 0) ;;;e
(ly:make-pitch 0 4 0) ;;;g
(ly:make-pitch 0 6 0) ;;;h
(ly:make-pitch 0 0 0) ;;;c
(ly:make-pitch 0 3 0) ;;;f
(ly:make-pitch 0 3 1/2) ;;;fis
(ly:make-pitch 0 0 1/2) ;;;cis
(ly:make-pitch 0 6 -1/2) ;;;b
(ly:make-pitch 0 4 1/2) ;;;gis
(ly:make-pitch 0 2 -1/2) ;;;es
(ly:make-pitch 0 1 1/2) ;;;dis
))
scaleRoots =
#`(
("ionisch" . ,majorRoots )
("Dur" . ,majorRoots)
("dorisch" . ,minorRoots)
("phrygisch" . ,minorRoots)
("lydisch" . ,majorRoots)
("mixolydisch" . ,majorRoots)
("äolisch" . ,minorRoots)
("moll" . ,minorRoots)
("moll melodisch" . ,minorRoots)
("moll harmonisch" . ,minorRoots)
("äolisch melodisch" . ,minorRoots)
("äolisch harmonisch" . ,minorRoots)
("lokrisch" . ,minorRoots)
)
easy-scales =
#(list
"Dur" "moll"
)
normal-scales =
#(list
"Dur" "moll" "moll melodisch" "moll harmonisch"
)
modal-scales =
#(list
"ionisch" "dorisch" "phrygisch" "lydisch" "mixolydisch" "äolisch" "äolisch harmonisch" "äolisch melodisch" "lokrisch"
)
#(define (getNoteName pitch)
(let* ((pitchName (assoc-get (ly:pitch-notename pitch) noteNameLookup))
(alteration (cond ((< (ly:pitch-alteration pitch) 0) "es")
((> (ly:pitch-alteration pitch) 0) "is")
(else "")))
(dutchName (string-append pitchName alteration ",")))
(assoc-get dutchName newnames))
)
makeScaleTest =
#(define-scheme-function (allowDuplicates scales num maxAcc mode) ( (boolean?) (list? normal-scales) number? (number? 6) char?)
(let loop ((x 0) (return '()) (lyrics '()))
(if (< x num)
(let* ((scale (list-ref scales (random (length scales))))
(rootIndex (if (> maxAcc 0) (random (* 2 maxAcc)) 0))
(possibleRoots (assoc-get scale scaleRoots))
(root (list-ref possibleRoots rootIndex))
(music #{
\transpose c' #root
#(ly:music-deep-copy (assoc-get scale scaleNoteLookup)) #})
(solution (list (string-append (getNoteName root) " " scale) #{ \lyricmode { \repeat unfold 7 _ } #} )))
(if (and (not (member music return)) (not (has-doubles music)))
(loop
(1+ x)
(append return
(list music))
(append lyrics solution))
(loop x return lyrics)))
(let ((result (scorify-music #{
\time 8/1
\omit Score.TimeSignature
\override Score.KeySignature.break-visibility = #end-of-line-invisible
\set Score.explicitKeySignatureVisibility = #end-of-line-invisible
\set Staff.printKeyCancellation = ##f
\override Score.KeyCancellation.break-visibility = #end-of-line-invisible
{
#(if (equal? mode #\r)
#{ \omit Score.KeyCancellation #}
(if solution
#{ \override Score.NoteHead.color = #red
\override Score.Stem.color = #red
\override Score.Accidental.color = #red
\override Score.KeySignature.color = #red
\omit Score.KeyCancellation
\override Score.KeyCancellation.color = #red
\override Score.LedgerLineSpanner.color = #red
#}
#{
%\override Staff.KeyCancellation #'stencil = ##f
\omit Score.KeyCancellation
\hideNotes
\hide Score.KeySignature #}))
#@return
} \addlyrics {
#(if (equal? mode #\r)
(if solution
#{ \override Score.LyricText.color = #red
\lyricmode { #@lyrics } #}
#{ \override Score.LyricText.Y-offset = -1
\lyricmode { \repeat unfold #num { "__________________________" \repeat unfold 7 _ } } #})
#{ \lyricmode { #@lyrics } #})
} #})))
(if includeTasks
(add-text
#{
\task #(assoc-get (cons "scaleTest" mode) taskList) #num
#} ) '())
(add-score
#{ #result #}))
)
)
)
%%Chord test suite
chordNoteLookup =
#`(
("Dur" . ,#{<c' e' g'>1#})
("Moll" . ,#{<c' es' g'>1#})
("Dur 1.U" . ,#{<e' g' c''>1#})
("Moll 1.U" . ,#{<es' g' c''>1#})
("Dur 2.U" . ,#{<g' c'' e''>1#})
("Moll 2.U" . ,#{<g' c'' es''>1#})
("verm." . ,#{<c' es' ges'>1#})
("überm." . ,#{<c' e' gis'>1#})
("7" . ,#{<c' e' g' b'>1#})
("m7" . ,#{<c' es' g' b'>1#})
("Major7" . ,#{<c' e' g' h'>1#})
)
#(define chordRoots (list
(ly:make-pitch 0 0 0) ;;;C
(ly:make-pitch 0 3 0) ;;;F
(ly:make-pitch 0 4 0) ;;;G
(ly:make-pitch 0 6 -1/2) ;;;Bb
(ly:make-pitch 0 1 0) ;;;D
(ly:make-pitch 0 2 -1/2) ;;;Es
(ly:make-pitch 0 5 0) ;;;A
(ly:make-pitch 0 5 -1/2) ;;;As
(ly:make-pitch 0 2 0) ;;;E
(ly:make-pitch 0 1 -1/2) ;;;Des
(ly:make-pitch 0 6 0) ;;;H
(ly:make-pitch 0 4 -1/2) ;;;Ges
(ly:make-pitch 0 3 1/2) ;;;Fis
(ly:make-pitch 0 0 1/2) ;;;cis
(ly:make-pitch 0 4 1/2) ;;;gis
))
easy-chords =
#(list "Dur" "Moll")
normal-chords =
#(list "Dur" "Moll" "Dur" "Moll" "verm." "überm.")
umkehrungen =
#(list "Dur" "Moll" "Dur" "Moll" "Dur 1.U" "Dur 2.U" "Moll 1.U" "Moll 2.U")
all-chords =
#(list "Dur" "Moll" "Dur" "Moll" "verm." "überm." "7" "m7" "Major7")
makeChordTest =
#(define-scheme-function (allowDuplicates chords num mode) ( (boolean?) (list? normal-chords) number? char?)
(let loop ((x 0) (return '()) (lyrics '()))
(if (< x num)
(let* ((chord (list-ref chords (random (length chords))))
(root (list-ref chordRoots (random (length chordRoots))))
(music #{
\transpose c' #root
#(ly:music-deep-copy (assoc-get chord chordNoteLookup)) #})
(solution (list (string-append (getNoteName root) " " chord))))
(if (and (not (member music return)) (not (has-doubles music)))
(loop
(1+ x)
(append return
(list music))
(append lyrics solution))
(loop x return lyrics)))
(let ((result (scorify-music #{
\time 1/1
\omit Score.TimeSignature
\set Staff.printKeyCancellation = ##f
\override Score.KeySignature.break-visibility = #end-of-line-invisible
\set Score.explicitKeySignatureVisibility = #end-of-line-invisible
\override Score.LyricText.self-alignment-X = #LEFT
{
#(if (equal? mode #\r)
#{ #}
(if solution
#{ \override Score.NoteHead.color = #red
\override Score.Stem.color = #red
\override Score.Accidental.color = #red
\override Score.KeySignature.color = #red
\override Score.KeyCancellation.color = #red
\override Score.LedgerLineSpanner.color = #red
#}
#{
\hideNotes
\hide Score.KeySignature #}))
#@return } \addlyrics {
#(if (equal? mode #\r)
(if solution
#{ \override Score.LyricText.color = #red
\lyricmode { #@lyrics } #}
#{ \override Score.LyricText.Y-offset = -1
\lyricmode { \repeat unfold #num { "________________" } } #})
#{ \lyricmode { #@lyrics } #})
} #})))
(if includeTasks
(add-text
#{
\task #(assoc-get (cons "chordTest" mode) taskList) #num
#} ) '())
(add-score
#{ #result #}))
)
)
)
\paper {
indent = 0\mm
oddFooterMarkup =##f
evenFooterMarkup = ##f
evenHeaderMarkup = ##f
oddHeaderMarkup =##f
bookTitleMarkup = ##f
scoreTitleMarkup = ##f
ragged-bottom = ##t
#(define fonts
(make-pango-font-tree "Calibri"
"Calibri"
"Calibri"
(/ staff-height pt 20)))
}
\layout {
ragged-right = ##f
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment