X-Git-Url: https://git.donarmstrong.com/?a=blobdiff_plain;f=scm%2Flily-library.scm;h=f772d0527e931efa53cdf0917fcf359012e911bb;hb=f2d3356afabababe82484a3ded193e7cc779f46a;hp=5b08393b80570b00d1e052823e58e3f6a2436689;hpb=edafc974676f121db77fc9ad675417ccfb7a88b0;p=lilypond.git diff --git a/scm/lily-library.scm b/scm/lily-library.scm index 5b08393b80..f772d0527e 100644 --- a/scm/lily-library.scm +++ b/scm/lily-library.scm @@ -1,10 +1,13 @@ +;;;; ;;;; lily-library.scm -- utilities ;;;; ;;;; source file of the GNU LilyPond music typesetter ;;;; -;;;; (c) 1998--2005 Jan Nieuwenhuizen -;;;; Han-Wen Nienhuys +;;;; (c) 1998--2007 Jan Nieuwenhuizen +;;;; Han-Wen Nienhuys +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; constants. (define-public X 0) (define-public Y 1) @@ -16,61 +19,131 @@ (define-public DOWN -1) (define-public CENTER 0) -(define-safe-public DOUBLE-FLAT -4) -(define-safe-public THREE-Q-FLAT -3) -(define-safe-public FLAT -2) -(define-safe-public SEMI-FLAT -1) +(define-safe-public DOUBLE-FLAT-QTS -4) +(define-safe-public THREE-Q-FLAT-QTS -3) +(define-safe-public FLAT-QTS -2) +(define-safe-public SEMI-FLAT-QTS -1) +(define-safe-public NATURAL-QTS 0) +(define-safe-public SEMI-SHARP-QTS 1) +(define-safe-public SHARP-QTS 2) +(define-safe-public THREE-Q-SHARP-QTS 3) +(define-safe-public DOUBLE-SHARP-QTS 4) +(define-safe-public SEMI-TONE-QTS 2) + +(define-safe-public DOUBLE-FLAT -1) +(define-safe-public THREE-Q-FLAT -3/4) +(define-safe-public FLAT -1/2) +(define-safe-public SEMI-FLAT -1/4) (define-safe-public NATURAL 0) -(define-safe-public SEMI-SHARP 1) -(define-safe-public SHARP 2) -(define-safe-public THREE-Q-SHARP 3) -(define-safe-public DOUBLE-SHARP 4) -(define-safe-public SEMI-TONE 2) +(define-safe-public SEMI-SHARP 1/4) +(define-safe-public SHARP 1/2) +(define-safe-public THREE-Q-SHARP 3/4) +(define-safe-public DOUBLE-SHARP 1) +(define-safe-public SEMI-TONE 1/2) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; moments (define-public ZERO-MOMENT (ly:make-moment 0 1)) (define-public (moment-min a b) (if (ly:moment output hooks. -(define-public (print-score-as-book parser score) - (let* ((head (ly:parser-lookup parser '$globalheader)) - (book (ly:make-book (ly:parser-lookup parser $defaultpaper) - head score))) - (ly:parser-print-book parser book))) - -(define-public (print-score parser score) - (let* ((head (ly:parser-lookup parser '$globalheader)) - (book (ly:make-book (ly:parser-lookup parser $defaultpaper) - head score))) - (ly:parser-print-score parser book))) (define-public (collect-scores-for-book parser score) - (ly:parser-define + (ly:parser-define! parser 'toplevel-scores (cons score (ly:parser-lookup parser 'toplevel-scores)))) - -(define-public (collect-music-for-book parser music) - (collect-scores-for-book parser (ly:music-scorify music parser))) +(define (collect-music-aux score-handler parser music) + (define (music-property symbol) + (let ((value (ly:music-property music symbol))) + (if (not (null? value)) + value + #f))) + (cond ((music-property 'page-marker) + ;; a page marker: set page break/turn permissions or label + (begin + (let ((label (music-property 'page-label))) + (if (symbol? label) + (score-handler (ly:make-page-label-marker label)))) + (for-each (lambda (symbol) + (let ((permission (music-property symbol))) + (if (symbol? permission) + (score-handler + (ly:make-page-permission-marker symbol + (if (eqv? 'forbid permission) + '() + permission)))))) + (list 'line-break-permission 'page-break-permission + 'page-turn-permission)))) + ((not (music-property 'void)) + ;; a regular music expression: make a score with this music + ;; void music is discarded + (score-handler (scorify-music music parser))))) + +(define-public (collect-music-for-book parser music) + "Top-level music handler" + (collect-music-aux (lambda (score) + (collect-scores-for-book parser score)) + parser + music)) + +(define-public (collect-book-music-for-book parser book music) + "Book music handler" + (collect-music-aux (lambda (score) + (ly:book-add-score! book score)) + parser + music)) + +(define-public (scorify-music music parser) + "Preprocess MUSIC." + (for-each (lambda (func) + (set! music (func music parser))) + toplevel-music-functions) + + (ly:make-score music)) + +(define (print-book-with parser book process-procedure) + (let* + ((paper (ly:parser-lookup parser '$defaultpaper)) + (layout (ly:parser-lookup parser '$defaultlayout)) + (count (ly:parser-lookup parser 'output-count)) + (base (ly:parser-output-name parser)) + (output-suffix (ly:parser-lookup parser 'output-suffix)) ) + + (if (string? output-suffix) + (set! base (format "~a-~a" base (string-regexp-substitute + "[^a-zA-Z0-9-]" "_" output-suffix)))) + + ;; must be careful: output-count is under user control. + (if (not (integer? count)) + (set! count 0)) + + (if (> count 0) + (set! base (format #f "~a-~a" base count))) + (ly:parser-define! parser 'output-count (1+ count)) + (process-procedure book paper layout base) + )) + +(define-public (print-book-with-defaults parser book) + (print-book-with parser book ly:book-process)) + +(define-public (print-book-with-defaults-as-systems parser book) + (print-book-with parser book ly:book-process-to-systems)) + ;;;;;;;;;;;;;;;; -; alist +;; alist + (define-public assoc-get ly:assoc-get) (define-public (uniqued-alist alist acc) @@ -83,14 +156,6 @@ (stringstring (car x)) (symbol->string (car y)))) -(define-public (chain-assoc x alist-list) - (if (null? alist-list) - #f - (let* ((handle (assoc x (car alist-list)))) - (if (pair? handle) - handle - (chain-assoc x (cdr alist-list)))))) - (define-public (chain-assoc-get x alist-list . default) "Return ALIST entry for X. Return DEFAULT (optional, else #f) if not found." @@ -143,6 +208,7 @@ found." ;;;;;;;;;;;;;;;; ;; vector + (define-public (vector-for-each proc vec) (do ((i 0 (1+ i))) @@ -152,19 +218,9 @@ found." ;;;;;;;;;;;;;;;; ;; hash -(if (not (defined? 'hash-table?)) ;; guile 1.6 compat - (begin - (define hash-table? vector?) - - (define-public (hash-table->alist t) - "Convert table t to list" - (apply append (vector->list t)))) - - ;; native hashtabs. - (begin - (define-public (hash-table->alist t) - (hash-fold (lambda (k v acc) (acons k v acc)) - '() t)))) +(define-public (hash-table->alist t) + (hash-fold (lambda (k v acc) (acons k v acc)) + '() t)) ;; todo: code dup with C++. (define-safe-public (alist->hash-table lst) @@ -174,7 +230,71 @@ found." m)) ;;;;;;;;;;;;;;;; -; list +;; list + +(define (functional-or . rest) + (if (pair? rest) + (or (car rest) + (apply functional-and (cdr rest))) + #f)) + +(define (functional-and . rest) + (if (pair? rest) + (and (car rest) + (apply functional-and (cdr rest))) + #t)) + +(define (split-list lst n) + "Split LST in N equal sized parts" + + (define (helper todo acc-vector k) + (if (null? todo) + acc-vector + (begin + (if (< k 0) + (set! k (+ n k))) + + (vector-set! acc-vector k (cons (car todo) (vector-ref acc-vector k))) + (helper (cdr todo) acc-vector (1- k))))) + + (helper lst (make-vector n '()) (1- n))) + +(define (list-element-index lst x) + (define (helper todo k) + (cond + ((null? todo) #f) + ((equal? (car todo) x) k) + (else + (helper (cdr todo) (1+ k))))) + + (helper lst 0)) + +(define-public (count-list lst) + "Given lst (E1 E2 .. ) return ((E1 . 1) (E2 . 2) ... ) " + + (define (helper l acc count) + (if (pair? l) + (helper (cdr l) (cons (cons (car l) count) acc) (1+ count)) + acc)) + + + (reverse (helper lst '() 1))) + +(define-public (list-join lst intermediate) + "put INTERMEDIATE between all elts of LST." + + (fold-right + (lambda (elem prev) + (if (pair? prev) + (cons elem (cons intermediate prev)) + (list elem))) + '() lst)) + +(define-public (filtered-map proc lst) + (filter + (lambda (x) x) + (map proc lst))) + (define (flatten-list lst) "Unnest LST" @@ -188,19 +308,17 @@ found." "Return list of elements in A that are not in B." (lset-difference eq? a b)) -;; TODO: use the srfi-1 partition function. (define-public (uniq-list lst) - - "Uniq LST, assuming that it is sorted" - (define (helper acc lst) - (if (null? lst) - acc - (if (null? (cdr lst)) - (cons (car lst) acc) - (if (equal? (car lst) (cadr lst)) - (helper acc (cdr lst)) - (helper (cons (car lst) acc) (cdr lst)))))) - (reverse! (helper '() lst) '())) + "Uniq LST, assuming that it is sorted. Uses equal? for comparisons." + + (reverse! + (fold (lambda (x acc) + (if (null? acc) + (list x) + (if (equal? x (car acc)) + acc + (cons x acc)))) + '() lst) '())) (define (split-at-predicate predicate lst) "Split LST = (a_1 a_2 ... a_k b_1 ... b_k) @@ -209,6 +327,7 @@ found." L1 is copied, L2 not. (split-at-predicate (lambda (x y) (= (- y x) 2)) '(1 3 5 9 11) (cons '() '()))" + ;; " Emacs is broken (define (inner-split predicate lst acc) @@ -230,8 +349,8 @@ found." (set-car! c (reverse! (car c))) c)) -(define-public (split-list lst sep?) - "(display (split-list '(a b c / d e f / g) (lambda (x) (equal? x '/)))) +(define-public (split-list-by-separator lst sep?) + "(display (split-list-by-separator '(a b c / d e f / g) (lambda (x) (equal? x '/)))) => ((a b c) (d e f) (g)) " @@ -247,7 +366,7 @@ found." (if (null? lst) '() (let* ((c (split-one sep? lst '()))) - (cons (reverse! (car c) '()) (split-list (cdr c) sep?))))) + (cons (reverse! (car c) '()) (split-list-by-separator (cdr c) sep?))))) (define-public (offset-add a b) (cons (+ (car a) (car b)) @@ -256,18 +375,62 @@ found." (define-public (offset-flip-y o) (cons (car o) (- (cdr o)))) +(define-public (offset-scale o scale) + (cons (* (car o) scale) + (* (cdr o) scale))) + (define-public (ly:list->offsets accum coords) (if (null? coords) accum (cons (cons (car coords) (cadr coords)) (ly:list->offsets accum (cddr coords))))) +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; numbers + +(if (not (defined? 'nan?)) ;; guile 1.6 compat + (define-public (nan? x) (not (or (< 0.0 x) + (> 0.0 x) + (= 0.0 x))))) + +(if (not (defined? 'inf?)) + (define-public (inf? x) (= (/ 1.0 x) 0.0))) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; intervals + (define-public (interval-length x) "Length of the number-pair X, when an interval" (max 0 (- (cdr x) (car x)))) +(define-public interval-start car) +(define-public (ordered-cons a b) + (cons (min a b) + (max a b))) + +(define-public interval-end cdr) + +(define-public (interval-bound interval dir) + ((if (= dir RIGHT) cdr car) interval)) + +(define-public (interval-index interval dir) + "Interpolate INTERVAL between between left (DIR=-1) and right (DIR=+1)" + + (* (+ (interval-start interval) (interval-end interval) + (* dir (- (interval-end interval) (interval-start interval)))) + 0.5)) + +(define-public (interval-center x) + "Center the number-pair X, when an interval" + (if (interval-empty? x) + 0.0 + (/ (+ (car x) (cdr x)) 2))) + (define-public interval-start car) (define-public interval-end cdr) +(define-public (interval-translate iv amount) + (cons (+ amount (car iv)) + (+ amount (cdr iv)))) (define (other-axis a) (remainder (+ a 1) 2)) @@ -276,10 +439,64 @@ found." (cons (- (car iv) amount) (+ (cdr iv) amount))) + +(define-public (interval-empty? iv) + (> (car iv) (cdr iv))) + (define-public (interval-union i1 i2) (cons (min (car i1) (car i2)) (max (cdr i1) (cdr i2)))) +(define-public (interval-sane? i) + (not (or (nan? (car i)) + (inf? (car i)) + (nan? (cdr i)) + (inf? (cdr i)) + (> (car i) (cdr i))))) + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; string + +(define-public (string-endswith s suffix) + (equal? suffix (substring s + (max 0 (- (string-length s) (string-length suffix))) + (string-length s)))) + +(define-public (string-startswith s prefix) + (equal? prefix (substring s 0 (min (string-length s) (string-length prefix))))) + +(define-public (string-encode-integer i) + (cond + ((= i 0) "o") + ((< i 0) (string-append "n" (string-encode-integer (- i)))) + (else (string-append + (make-string 1 (integer->char (+ 65 (modulo i 26)))) + (string-encode-integer (quotient i 26)))))) + +(define (number->octal-string x) + (let* ((n (inexact->exact x)) + (n64 (quotient n 64)) + (n8 (quotient (- n (* n64 64)) 8))) + (string-append + (number->string n64) + (number->string n8) + (number->string (remainder (- n (+ (* n64 64) (* n8 8))) 8))))) + +(define-public (ly:inexact->string x radix) + (let ((n (inexact->exact x))) + (number->string n radix))) + +(define-public (ly:number-pair->string c) + (string-append (ly:number->string (car c)) " " + (ly:number->string (cdr c)))) + +(define-public (dir-basename file . rest) + "Strip suffixes in REST, but leave directory component for FILE." + (define (inverse-basename x y) (basename y x)) + (simple-format #f "~a/~a" (dirname file) + (fold inverse-basename file rest))) + (define-public (write-me message x) "Return X. Display MESSAGE and write X. Handy for debugging, possibly turned off." @@ -314,11 +531,11 @@ possibly turned off." (define-public (string-regexp-substitute a b str) (regexp-substitute/global #f a str 'pre b 'post)) - (define (regexp-split str regex) (define matches '()) (define end-of-prev-match 0) (define (notice match) + (set! matches (cons (substring (match:string match) end-of-prev-match (match:start match)) @@ -341,9 +558,16 @@ possibly turned off." 0 (if (< x 0) -1 1))) + +(define-public (car< a b) + (< (car a) (car b))) + (define-public (symbolstring lst) (symbol->string r))) +(define-public (symbol-keystring (car lst)) (symbol->string (car r)))) + ;; ;; don't confuse users with # syntax. ;; @@ -391,14 +615,16 @@ possibly turned off." (define-public (version-not-seen-message input-file-name) (ly:message - (string-append - input-file-name ": 0: " (_ "warning: ") - (format #f - (_ "no \\version statement found, add~afor future compatibility") - (format #f "\n\n\\version ~s\n\n" (lilypond-version)))))) + "~a:0: ~a: ~a" + input-file-name + (_ "warning: ") + (format #f + (_ "no \\version statement found, please add~afor future compatibility") + (format #f "\n\n\\version ~s\n\n" (lilypond-version))))) (define-public (old-relative-not-used-message input-file-name) (ly:message - (string-append - input-file-name ": 0: " (_ "warning: ") - (_ "old relative compatibility not used")))) + "~a:0: ~a: ~a" + input-file-name + (_ "warning: ") + (_ "old relative compatibility not used")))