X-Git-Url: https://git.donarmstrong.com/?a=blobdiff_plain;f=scm%2Fmusic-functions.scm;h=7693d1b7745531fb899234c5c413de098bc8d59e;hb=1f274c5493bf80bc93d42ccf4e519f010bc8c7f6;hp=01e84057bf053c25fd1153b82f33a8d4799f9441;hpb=f414cbfa3953c3134fdacf8723283067c5ca130a;p=lilypond.git diff --git a/scm/music-functions.scm b/scm/music-functions.scm index 01e84057bf..7693d1b774 100644 --- a/scm/music-functions.scm +++ b/scm/music-functions.scm @@ -1,74 +1,138 @@ ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; tuplets. -(define-public (denominator-tuplet-formatter mus) - (number->string (ly:get-mus-property mus 'denominator))) - -(define-public (fraction-tuplet-formatter mus) - (string-append (number->string (ly:get-mus-property mus 'numerator)) - ":" - (number->string (ly:get-mus-property mus 'denominator)) - )) - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -(define-public (music-map music function) +(define-public (music-map function music) "Apply @var{function} to @var{music} and all of the music it contains. " (let* ((es (ly:get-mus-property music 'elements)) (e (ly:get-mus-property music 'element)) ) (ly:set-mus-property! music 'elements - (map (lambda (y) (music-map y function)) es)) + (map (lambda (y) (music-map function y)) es)) (if (ly:music? e) - (ly:set-mus-property! music 'element (music-map e function))) + (ly:set-mus-property! music 'element (music-map function e))) (function music) )) +(define-public (music-filter pred? music) + "Filter out music expressions that do not satisfy PRED." + + (define (inner-music-filter pred? music) + "Recursive function." + (let* ((es (ly:get-mus-property music 'elements)) + (e (ly:get-mus-property music 'element)) + (as (ly:get-mus-property music 'articulations)) + (filtered-as (filter ly:music? (map (lambda (y) (inner-music-filter pred? y)) as))) + (filtered-e (if (ly:music? e) + (inner-music-filter pred? e) + e)) + (filtered-es (filter ly:music? (map (lambda (y) (inner-music-filter pred? y)) es))) + ) + + (ly:set-mus-property! music 'element filtered-e) + (ly:set-mus-property! music 'elements filtered-es) + (ly:set-mus-property! music 'articulations filtered-as) + + ;; if filtering emptied the expression, we remove it completely. + (if (or (pred? music) + (and (eq? filtered-es '()) (not (ly:music? e)) + (or (not (eq? es '())) + (ly:music? e)))) + (set! music '())) + + music)) + + (set! music (inner-music-filter pred? music)) + (if (ly:music? music) + music + (make-music-by-name 'Music) ;must return music. + )) +(define-public (remove-tag tag) + (lambda (mus) + (music-filter + (lambda (m) + (let* ((tags (ly:get-mus-property m 'tags)) + (res (memq tag tags))) + res)) mus))) + +(define-public (display-music music) + "Display music, not done with music-map for clarity of presentation." + (display music) + (display ": { ") + + (let* ((es (ly:get-mus-property music 'elements)) + (e (ly:get-mus-property music 'element)) + ) -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + (display (ly:get-mutable-properties music)) + (if (pair? es) + (begin (display "\nElements: {\n") + (map display-music es) + (display "}\n") + )) + + + (if (ly:music? e) + (begin + (display "\nChild:") + (display-music e) + ) + ) + ) + (display " }\n") + music + ) -(define-public (shift-duration-log music shift dot) - "Recurse through music, adding SHIFT to ly:duration-log and optionally + + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(define (shift-one-duration-log music shift dot) + " add SHIFT to ly:duration-log and optionally a dot to any note encountered. This scales the music up by a factor 2^shift * (2 - (1/2)^dot)" - (let* ((es (ly:get-mus-property music 'elements)) - (e (ly:get-mus-property music 'element)) - (n (ly:music-name music)) - (f (lambda (x) (shift-duration-log x shift dot))) - ) - ;; FIXME: broken by the great music rename. - (if (or (equal? n "Note_req") - (equal? n "Rest_req")) + (let* + ( + (d (ly:get-mus-property music 'duration)) + ) + (if (ly:duration? d) (let* ( - (d (ly:get-mus-property music 'duration)) (cp (ly:duration-factor d)) (nd (ly:make-duration (+ shift (ly:duration-log d)) - (+ dot (duration-dot-count d)) - (car cp) - (cdr cp))) + (+ dot (ly:duration-dot-count d)) + (car cp) + (cdr cp))) ) (ly:set-mus-property! music 'duration nd) )) - - (if (pair? es) - (ly:set-mus-property! - music 'elements - (map f es))) - - (if (ly:music? e) - (ly:set-mus-property! - music 'element - (f e))) - music)) +(define-public (shift-duration-log music shift dot) + (music-map (lambda (x) (shift-one-duration-log x shift dot)) + music)) + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; clusters. + +(define-public (note-to-cluster music) + "Replace NoteEvents by ClusterNoteEvents." + (if (eq? (ly:get-mus-property music 'name) 'NoteEvent) + (let* ((cn (make-music-by-name 'ClusterNoteEvent))) + + (ly:set-mus-property! cn 'pitch (ly:get-mus-property music 'pitch)) + (ly:set-mus-property! cn 'duration (ly:get-mus-property music 'duration)) + cn) + music)) + +(define-public (notes-to-clusters music) + (music-map note-to-cluster music)) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; repeats. @@ -108,48 +172,13 @@ written by Rune Zedeler. " music)) -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -(define (pitchify-scripts music) - "Copy the pitch fields of the Note_requests into Text_script_requests, to aid -Fingering_engraver." - (define (find-note musics) - (filter-list (lambda (m) (equal? (ly:music-name m) "Note_req")) musics) - ) - (define (find-scripts musics) - (filter-list (lambda (m) (equal? (ly:music-name m) "Text_script_req")) musics)) - - (let* ( - (e (ly:get-mus-property music 'element)) - (es (ly:get-mus-property music 'elements)) - (notes (find-note es)) - (pitch (if (pair? notes) (ly:get-mus-property (car notes) 'pitch) #f)) - ) - - (if pitch - (map (lambda (x) (ly:set-mus-property! x 'pitch pitch)) (find-scripts es)) - ) - - (if (pair? es) - (ly:set-mus-property! - music 'elements - (map pitchify-scripts es))) - - (if (ly:music? e) - (ly:set-mus-property! - music 'element - (pitchify-scripts e))) - - music)) - - ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; property setting music objs. + (define-public (make-grob-property-set grob gprop val) - "Make a M-exp that sets GPROP to VAL in GROBS. Does a pop first, i.e. -this is not an override -" + + "Make a Music expression that sets GPROP to VAL in GROB. Does a pop first, +i.e. this is not an override" (let* ((m (make-music-by-name 'OverrideProperty))) (ly:set-mus-property! m 'symbol grob) @@ -160,6 +189,19 @@ this is not an override m )) +(define-public (make-grob-property-override grob gprop val) + + "Make a Music expression that sets GPROP to VAL in GROB. Does a pop first, +i.e. this is not an override" + + (let* ((m (make-music-by-name 'OverrideProperty))) + (ly:set-mus-property! m 'symbol grob) + (ly:set-mus-property! m 'grob-property gprop) + (ly:set-mus-property! m 'grob-value val) + + m + + )) (define-public (make-grob-property-revert grob gprop) @@ -171,13 +213,16 @@ this is not an override m )) - + +(define direction-polyphonic-grobs + '(Tie Slur Script TextScript Stem Dots DotColumn)) + (define-public (make-voice-props-set n) (make-sequential-music (append (map (lambda (x) (make-grob-property-set x 'direction (if (odd? n) -1 1))) - '(Tie Slur Stem Dots)) + direction-polyphonic-grobs) (list (make-grob-property-set 'NoteColumn 'horizontal-shift (quotient n 2)) (make-grob-property-set 'MultiMeasureRest 'staff-position @@ -190,17 +235,17 @@ this is not an override (define-public (make-voice-props-revert) (make-sequential-music - (list - (make-grob-property-revert 'Tie 'direction) - (make-grob-property-revert 'Dots 'direction) - (make-grob-property-revert 'Stem 'direction) - (make-grob-property-revert 'Slur 'direction) - (make-grob-property-revert 'NoteColumn 'horizontal-shift) + (append + (map (lambda (x) (make-grob-property-revert x 'direction)) + direction-polyphonic-grobs) + + (list (make-grob-property-revert 'NoteColumn 'horizontal-shift)) )) ) + (define-public (context-spec-music m context . rest) - "Add \context CONTEXT = foo to M. " + "Add \\context CONTEXT = foo to M. " (let* ((cm (make-music-by-name 'ContextSpeccedMusic))) (ly:set-mus-property! cm 'element m) @@ -211,6 +256,14 @@ this is not an override cm )) +(define-public (make-apply-context func) + (let* + ((m (make-music-by-name 'ApplyContext))) + + (ly:set-mus-property! m 'procedure func) + m + )) + (define-public (make-sequential-music elts) (let* ((m (make-music-by-name 'SequentialMusic))) (ly:set-mus-property! m 'elements elts) @@ -230,31 +283,74 @@ this is not an override )) +(define-public (make-skip-music dur) + (let* ((m (make-music-by-name 'SkipMusic))) + (ly:set-mus-property! m 'duration dur) + m + )) + +;;;;;;;;;;;;;;;; + +;; mmrest (define-public (make-multi-measure-rest duration location) (let* ( (start (make-music-by-name 'MultiMeasureRestEvent)) - (stop (make-music-by-name 'MultiMeasureRestEvent)) - (skip ( make-music-by-name 'SkipEvent)) (ch (make-music-by-name 'BarCheck)) (ch2 (make-music-by-name 'BarCheck)) + (seq (make-music-by-name 'MultiMeasureRestMusicGroup)) ) - (ly:set-mus-property! start 'span-direction START) - (ly:set-mus-property! stop 'span-direction STOP) - (ly:set-mus-property! skip 'duration duration) (map (lambda (x) (ly:set-mus-property! x 'origin location)) - (list start stop skip ch ch2)) - (make-sequential-music + (list start ch ch2 seq)) + (ly:set-mus-property! start 'duration duration) + (ly:set-mus-property! seq 'elements (list ch (make-event-chord (list start)) - (make-event-chord (list skip)) - (make-event-chord (list stop)) ch2 )) + + seq )) +(define-public (glue-mm-rest-texts music) + "Check if we have R1*4-\\markup { .. }, and if applicable convert to +a property set for MultiMeasureRestNumber." + + (define (script-to-mmrest-text script-music) + "Extract 'direction and 'text from SCRIPT-MUSIC, and transform into property sets." + + (let* + ( + (text (ly:get-mus-property script-music 'text)) + (dir (ly:get-mus-property script-music 'direction)) + (p (make-music-by-name 'MultiMeasureTextEvent)) + ) + + (if (ly:dir? dir) + (ly:set-mus-property! p 'direction dir)) + (ly:set-mus-property! p 'text text) + p + )) + + (if (eq? (ly:get-mus-property music 'name) 'MultiMeasureRestMusicGroup) + (let* + ( + (text? (lambda (x) (memq 'script-event (ly:get-mus-property x 'types)))) + (es (ly:get-mus-property music 'elements)) + (texts (map script-to-mmrest-text (filter text? es))) + (others (remove text? es)) + ) + (if (pair? texts) + (ly:set-mus-property! + music 'elements + (cons (make-event-chord texts) others) + )) + )) + music + ) + (define-public (make-property-set sym val) (let* @@ -266,6 +362,53 @@ this is not an override m )) + + +(define-public (make-ottava-set octavation) + (let* + ((m (make-music-by-name 'ApplyContext))) + + + (define (ottava-modify context) + "Either reset centralCPosition to the stored original, or remember +old centralCPosition, add OCTAVATION to centralCPosition, and set +OTTAVATION to `8va', or whatever appropriate." + + (if (number? (ly:get-context-property context 'centralCPosition)) + + (if (= octavation 0) + (let* + ((where (ly:context-property-where-defined context 'centralCPosition)) + (oc0 (ly:get-context-property context 'originalCentralCPosition))) + + (ly:set-context-property! context 'centralCPosition oc0) + (ly:unset-context-property where 'originalCentralCPosition) + (ly:unset-context-property where 'ottavation)) + + (let* + ((where (ly:context-property-where-defined context 'centralCPosition)) + (c0 (ly:get-context-property context 'centralCPosition)) + (new-c0 (+ c0 (* -7 octavation))) + (string (cdr + (assoc octavation '((2 . "15ma") + (1 . "8va") + (0 . #f) + (-1 . "8va bassa") + (-2 . "15ma bassa")))))) + + (ly:set-context-property! context 'centralCPosition new-c0) + (ly:set-context-property! context 'originalCentralCPosition c0) + (ly:set-context-property! context 'ottavation string) + + )))) + + (ly:set-mus-property! m 'procedure ottava-modify) + (context-spec-music m 'Staff) + )) + +(define-public (set-octavation ottavation) + (ly:export (make-ottava-set ottavation))) + (define-public (make-time-signature-set num den . rest) " Set properties for time signature NUM/DEN. Rest can contain a list of beat groupings @@ -282,16 +425,34 @@ Rest can contain a list of beat groupings (set4 (make-property-set 'beatGrouping (if (pair? rest) (car rest) '()))) - (basic (list set1 set2 set3 set4)) - - ) + (basic (list set1 set2 set3 set4))) (context-spec-music - (make-sequential-music basic) "Timing"))) + (make-sequential-music basic) 'Timing))) + +(define-public (make-mark-set label) + "make the music for the \\mark command." + + (let* + ((set (if (integer? label) + (context-spec-music (make-property-set 'rehearsalMark label) + 'Score) + #f)) + (ev (make-music-by-name 'MarkEvent)) + (ch (make-event-chord (list ev))) + ) + + + (if set + (make-sequential-music (list set ch)) + (begin + (ly:set-mus-property! ev 'label label) + ch)))) + + (define-public (set-time-signature num den . rest) - (ly:export (apply make-time-signature-set `(,num ,den . ,rest))) - ) + (ly:export (apply make-time-signature-set `(,num ,den . ,rest)))) (define-public (make-penalty-music pen) (let @@ -307,6 +468,13 @@ Rest can contain a list of beat groupings m )) +(define-public (make-lyric-event string duration) + (let* ((m (make-music-by-name 'LyricEvent))) + + (ly:set-mus-property! m 'duration duration) + (ly:set-mus-property! m 'text string) + m)) + (define-public (make-span-event type spandir) (let* ( (m (make-music-by-name type)) @@ -323,42 +491,16 @@ Rest can contain a list of beat groupings (set-mus-properties! m (cdr alist))) )) + + (define-public (music-separator? m) "Is M a separator?" (let* ((ts (ly:get-mus-property m 'types ))) (memq 'separator ts) )) -(define (split-one sep? l acc) - "Split off the first parts before separator and return both parts. - -" - (if (null? l) - (cons acc '()) - (if (sep? (car l)) - (cons acc (cdr l)) - (split-one sep? (cdr l) (cons (car l) acc)) - ) - )) - -(define-public (split-list l sep?) - " - -(display (split-list '(a b c / d e f / g) (lambda (x) (equal? x '/))) ) -=> - ... - -" - (if (null? l) - '() - (let* ((c (split-one sep? l '()))) - (cons (reverse! (car c) '()) (split-list (cdr c) sep?)) - ) - ) - ) ;;; splitting chords into voices. - (define (voicify-list lst number) "Make a list of Musics. @@ -373,16 +515,15 @@ Rest can contain a list of beat groupings (make-voice-props-set number) (make-simultaneous-music (car lst)))) - "Voice" (number->string number)) - (voicify-list (cdr lst) (+ number 1)) + 'Voice (number->string number)) + (voicify-list (cdr lst) (1+ number)) )) ) (define (voicify-chord ch) "Split the parts of a chord into different Voices using separator" (let* ((es (ly:get-mus-property ch 'elements))) - - + (ly:set-mus-property! ch 'elements (voicify-list (split-list es music-separator?) 0)) ch @@ -405,8 +546,8 @@ Rest can contain a list of beat groupings (ly:set-mus-property! m 'element (voicify-music e))) (if (and (equal? (ly:music-name m) "Simultaneous_music") - (reduce (lambda (x y ) (or x y)) (map music-separator? es))) - (voicify-chord m) + (reduce (lambda (x y ) (or x y)) #f (map music-separator? es))) + (set! m (context-spec-music (voicify-chord m) 'Staff)) ) m @@ -424,6 +565,25 @@ Rest can contain a list of beat groupings ;;(eq? #t (ly:get-grob-property elt symbol)) (not (eq? #f (memq symbol (ly:get-grob-property elt 'interfaces)))))) +(define-public ((outputproperty-compatibility func sym val) grob g-context ao-context) + (if (func grob) + (ly:set-grob-property! grob sym val))) + + +(define-public ((set-output-property grob-name symbol val) grob grob-c context) + "Usage: + +\\applyoutput #(set-output-property 'Clef 'extra-offset '(0 . 1)) + +" + + (let* + ((meta (ly:get-grob-property grob 'meta))) + + (if (equal? (cdr (assoc 'name meta)) grob-name) + (ly:set-grob-property! grob symbol val) + ))) + ;; (define-public (smart-bar-check n) @@ -451,12 +611,13 @@ Rest can contain a list of beat groupings ;; warn for bare chords at start. (define (has-request-chord elts) - (reduce (lambda (x y) (or x y)) (map (lambda (x) (equal? (ly:music-name x) + (reduce (lambda (x y) (or x y)) #f (map (lambda (x) (equal? (ly:music-name x) "Request_chord")) elts) )) (define (ly:music-message music msg) - (let* ( + (let* + ( (ip (ly:get-mus-property music 'origin)) ) @@ -492,21 +653,136 @@ Rest can contain a list of beat groupings ) + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; switch it on here, so parsing and init isn't checked (too slow!) +;; +;; setting stuff for grace context. +;; + +(define (vector-extend v x) + "Make a new vector consisting of V, with X added to the end." + (let* + ((n (vector-length v)) + (nv (make-vector (+ n 1) '()))) + + + (vector-move-left! v 0 n nv 0) + (vector-set! nv n x) + nv)) + + +(define (vector-map f v) + "Map F over V. This function returns nothing." + (do + ((n (vector-length v)) + (i 0 (+ i 1))) + ((>= i n)) + + (f (vector-ref v i)))) + +(define (vector-reverse-map f v) + "Map F over V, N to 0 order. This function returns nothing." + (do + ((i (- (vector-length v) 1) (- i 1))) + ((< i 0)) + + (f (vector-ref v i)))) + +;; TODO: make a remove-grace-property too. +(define-public (add-grace-property context-name grob sym val) + "Set SYM=VAL for GROB in CONTEXT-NAME. " + (define (set-prop context) + (let* + ((where (ly:context-property-where-defined context 'graceSettings)) + (current (ly:get-context-property where 'graceSettings)) + (new-settings (vector-extend current (list context-name grob sym val))) + ) + (ly:set-context-property! where 'graceSettings new-settings))) + + (ly:export (context-spec-music (make-apply-context set-prop) 'Voice))) + + +(define-public (set-start-grace-properties context) + (define (execute-1 x) + (let* + ((tr (ly:translator-find context (car x)))) + (if (ly:context? tr) + (ly:context-pushpop-property tr (cadr x) (caddr x) (cadddr x)) + ))) + + (let* + ((props (ly:get-context-property context 'graceSettings))) + (if (vector? props) + (vector-map execute-1 props)))) + +(define-public (set-stop-grace-properties context) + (define (execute-1 x) + (let* + ((tr (ly:translator-find context (car x)))) + (if (ly:context? tr) + (ly:context-pushpop-property tr (cadr x) (caddr x)) + ))) + + (let* + ((props (ly:get-context-property context 'graceSettings))) + (if (vector? props) + (vector-reverse-map execute-1 props)))) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; switch it on here, so parsing and init isn't checked (too slow!) +;; ;; automatic music transformations. (define (switch-on-debugging m) (set-debug-cell-accesses! 15000) - m - ) + m) (define-public toplevel-music-functions - (list check-start-chords + (list +;; check-start-chords ; ; no longer needed with chord syntax. voicify-music - + (lambda (x) (music-map glue-mm-rest-texts x)) ; switch-on-debugging )) + + +;;;;;;;;;;;;;;;;; +;; lyrics + +(define (apply-durations lyric-music durations) + (define (apply-duration music) + (if (and (not (equal? (ly:music-length music) ZERO-MOMENT)) + (ly:duration? (ly:get-mus-property music 'duration))) + (begin + (ly:set-mus-property! music 'duration (car durations)) + (set! durations (cdr durations)) + ))) + + (music-map apply-duration lyric-music)) + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; + + +(define-public ((add-balloon-text object-name text off) grob orig-context cur-context) + "Usage: see input/regression/balloon.ly " + (let* + ((meta (ly:get-grob-property grob 'meta)) + (nm (if (pair? meta) (cdr (assoc 'name meta)) "nonexistant")) + (cb (ly:get-grob-property grob 'molecule-callback))) + + (if (equal? nm object-name) + (begin + (ly:set-grob-property! grob 'molecule-callback Balloon_interface::brew_molecule) + (ly:set-grob-property! grob 'balloon-original-callback cb) + (ly:set-grob-property! grob 'balloon-text text) + (ly:set-grob-property! grob 'balloon-text-offset off) + (ly:set-grob-property! grob 'balloon-text-props '((font-family . roman))) + + )))) + +