Hi Josh,
On Mon, Jan 8, 2018 at 4:34 PM, Joshua Nichols <josh.d.nich...@gmail.com> wrote:
> Hi! This is for education!
>
> On Jan 8, 2018 3:08 PM, "Karlin High" <karlinh...@gmail.com> wrote:
>>
>> On 1/8/2018 11:44 AM, Joshua Nichols wrote:
>>>
>>> I'm looking for a resource that can generate pitches at different
>>> octaves, using different spellings, including accidentals.
>>
>>
>> How will this be used? As an educational tool, perhaps? Describing the use
>> case might get better help.
>> --
>> Karlin High
>> Missouri, USA
Here is a worksheet I created with answers and different pitch content
every time you run it. Perhaps you will find something useful in it.
(Note: if you're running a development version, you can cut out the
definition of the table command. I included it so I could run
this--and many other "auto" worksheets--using lilybin.
Hope this helps,
David
\version "2.18.2"
% This file generates a customizable number of random white note pitches
% for identification. Answers can be displayed.
% The following function is copied from scm/define-markup-commands.scm.
% Not needed in newer versions of LilyPond.
#(define-markup-list-command (table layout props column-align lst)
(number-list? markup-list?)
#:properties ((padding 0)
(baseline-skip))
(define (split-lst initial-lst lngth result-lst)
;; split a list into a list of sublists of length lngth
;; eg. (split-lst '(1 2 3 4 5 6) 2 '())
;; -> ((1 2) (3 4) (5 6))
(cond ((not (integer? (/ (length initial-lst) lngth)))
(ly:warning
"Can't split list of length ~a into ~a parts, returning empty list"
(length initial-lst) lngth)
'())
((null? initial-lst)
(reverse result-lst))
(else
(split-lst
(drop initial-lst lngth)
lngth
(cons (take initial-lst lngth) result-lst)))))
(define (dists-list init padding lst)
;; Returns a list, where each element of `lst' is
;; added to the sum of the previous elements of `lst' plus padding.
;; `init' will be the first element of the resulting list. The addition
;; starts with the values of `init', `padding' and `(car lst)'.
;; eg. (dists-list 0.01 0.1 '(1 2 3 4)))
;; -> (0.01 1.11 3.21 6.31 10.41)
(if (or (not (number? init))
(not (number? padding))
(not (number-list? lst)))
(begin
(ly:warning
"not fitting argument for `dists-list', return empty lst ")
'())
(reverse
(fold (lambda (elem rl) (cons (+ elem padding (car rl)) rl))
(list init)
lst))))
(let* (;; get the number of columns
(columns (length column-align))
(init-stils (interpret-markup-list layout props lst))
;; If the given markup-list is the result of a markup-list call, their
;; length may not be easily predictable, thus we add point-stencils
;; to fill last row of the table.
(rem (remainder (length init-stils) columns))
(filled-stils
(if (zero? rem)
init-stils
(append init-stils (make-list (- columns rem) point-stencil))))
;; get the stencils in sublists of length `columns'
(stils
(split-lst filled-stils columns '()))
;; procedure to return stencil-length
;; If it is nan, return 0
(lengths-proc
(lambda (m)
(let ((lngth (interval-length (ly:stencil-extent m X))))
(if (nan? lngth) 0 lngth))))
;; get the max width of each column in a list
(columns-max-x-lengths
(map
(lambda (x)
(apply max 0
(map
lengths-proc
(map (lambda (l) (list-ref l x)) stils))))
(iota columns)))
;; create a list of (basic) distances, which each column should
;; moved, using `dists-list'. Some padding may be added.
(dist-sequence
(dists-list 0 padding columns-max-x-lengths))
;; Get all stencils of a row, moved accurately to build columns.
;; If the items of a column are aligned other than left, we need to
;; move them to avoid collisions:
;; center aligned: move all items half the width of the widest item
;; right aligned: move all items the full width of the widest item.
;; Added to the default-offset calculated in `dist-sequence'.
;; `stencils-for-row-proc' needs four arguments:
;; stil - a stencil
;; dist - a numerical value as basic offset in X direction
;; column - a numerical value for the column we're in
;; x-align - a numerical value how current column should be
;; aligned, where (-1, 0, 1) means (LEFT, CENTER, RIGHT)
(stencils-for-row-proc
(lambda (stil dist column x-align)
(ly:stencil-translate-axis
(ly:stencil-aligned-to stil X x-align)
(cond ((member x-align '(0 1))
(let* (;; get the stuff for relevant column
(stuff-for-column
(map
(lambda (s) (list-ref s column))
stils))
;; get length of every column-item
(lengths-for-column
(map lengths-proc stuff-for-column))
(widest
(apply max 0 lengths-for-column)))
(+ dist (/ widest (if (= x-align 0) 2 1)))))
(else dist))
X)))
;; get a list of rows using `ly:stencil-add' on a list of stencils
(rows
(map
(lambda (stil-list)
(apply ly:stencil-add
(map
;; the procedure creating the stencils:
stencils-for-row-proc
;; the procedure's args:
stil-list
dist-sequence
(iota columns)
column-align)))
stils)))
(space-lines baseline-skip rows)))
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
#(let ((time (gettimeofday)))
(set! *random-state*
(seed->random-state (+ (car time)
(cdr time)))))
%% Readable ranges for allowed clefs
#(define clef-ranges
`(
("treble" . (,(ly:make-pitch -1 2) ,(ly:make-pitch 2 6)))
("alto" . (,(ly:make-pitch -1 0) ,(ly:make-pitch 1 0)))
("tenor" . (,(ly:make-pitch -1 0) ,(ly:make-pitch 1 0)))
("bass" . (,(ly:make-pitch -2 0) ,(ly:make-pitch 0 1)))
))
#(define accidentals-no-doubles
(list -1/2 0 1/2))
%% 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))))))
questions =
#(define-music-function (parser location num acc-pool)
(number? list?)
(let ((clef-list
(circular-list "treble" "bass" "alto" "bass" "treble" "tenor")))
(let loop ((x 0) (cl clef-list) (return '()))
(if (< x num)
(loop
(1+ x)
(cdr cl)
(append return
(list
#{ \clef #(car cl) #}
(make-note (get-random-pitch (car cl) acc-pool) 0))))
#{
{
#@return
}
#}))))
rep = #'("C" "D" "E" "F" "G" "A" "B")
#(define alteration-symbols
`(
(-1/2 . ,#{ \markup \general-align #Y #DOWN \fontsize #-2 \flat #})
(0 . ,#{ \markup \null #})
(1/2 . ,#{ \markup { \hspace #0.1 \general-align #Y #-0.5 \fontsize #-2 \sharp } #})
))
#(define (format-note-name p)
(let* ((letter (list-ref rep (ly:pitch-notename p)))
(alteration
(ly:assoc-get (ly:pitch-alteration p) alteration-symbols)))
#{
\markup {
\concat {
\sans #letter
#alteration
}
}
#}))
#(define (format-answers answers)
(let* ((notes (extract-named-music answers 'NoteEvent))
(pitches (map (lambda (n) (ly:music-property n 'pitch)) notes))
(name-str
(map (lambda (n)
(make-concat-markup
(list (format-note-name n)
(number->string (+ (ly:pitch-octave n) 4)))))
pitches)))
name-str))
#(define (build-answer-list-for-table answers show?)
(let ((answers (format-answers answers)))
(let loop ((idx 1) (return '()) (ls answers))
(if (null? ls)
(reverse return)
(loop
(1+ idx)
(cons
#{
\markup \concat {
#(string-append (number->string idx) ". ")
#(if show?
(make-italic-markup (car ls))
"_____")
}
#}
return)
(cdr ls))))))
blanks =
#(define-scheme-function (parser location num) (number?)
(let ((str (map (lambda (n) (string-append (number->string n) ". ____"))
(iota num 1))))
#{
\lyricmode { #@str }
#}))
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
#(set-default-paper-size "letter")
\paper {
left-margin = 0.75\in
right-margin = #left-margin
top-margin = 1\in
bottom-margin = #top-margin
markup-markup-spacing.padding = 3
markup-system-spacing.padding = 3
system-system-spacing.padding = 4
page-breaking = #ly:minimal-breaking
tagline = ##f
}
\header {
title = "Note Reading Practice (All clefs)"
}
\markup \wordwrap {
Identify the following pitches with name and
octave number. Answers are given below.
}
test = \questions 30 #accidentals-no-doubles
\new Staff <<
\new Voice = "notes" {
\bar "" % so first bar number printed
\test
}
\new Lyrics \lyricsto "notes" {
\blanks #30
}
>>
\bookpart { }
\markup \fill-line { "" \bold \huge ANSWERS "" }
\markuplist {
\override #'(padding . 3)
\override #'(baseline-skip . 3)
\override #'(font-size . -1)
\table
#`,(make-list 10 -1)
{
#@(build-answer-list-for-table test #t)
}
}
\layout {
\context {
\Staff
\omit TimeSignature
\override Clef.full-size-change = ##t
explicitClefVisibility = #end-of-line-invisible
}
\context {
\Lyrics
\override LyricText.font-shape = #'italic
\override VerticalAxisGroup.nonstaff-relatedstaff-spacing =
#'((basic-distance . 6) (padding . 4))
}
\context {
\Score
\omit BarNumber
\override SpacingSpanner.base-shortest-duration = #(ly:make-moment 1/32)
\override BreakAlignment.break-align-orders =
##((left-edge ;; end-of-line
cue-end-clef
ambitus
breathing-sign
clef
cue-clef
staff-bar
key-cancellation
key-signature
time-signature
custos)
(left-edge ;; unbroken
cue-end-clef
ambitus
breathing-sign
staff-bar
clef
cue-clef
key-cancellation
key-signature
time-signature
custos)
(left-edge ;; beginning-of-line
ambitus
breathing-sign
clef
key-cancellation
key-signature
time-signature
staff-bar
cue-clef
custos))
}
}
_______________________________________________
lilypond-user mailing list
lilypond-user@gnu.org
https://lists.gnu.org/mailman/listinfo/lilypond-user