1 ;;; festival.scm --- Festival singing mode output
3 ;; Copyright (C) 2006, 2007 Brailcom, o.p.s.
5 ;; Author: Milan Zamazal <pdm@brailcom.org>
9 ;; This program is free software; you can redistribute it and/or modify
10 ;; it under the terms of the GNU General Public License as published by
11 ;; the Free Software Foundation; either version 2 of the License, or
12 ;; (at your option) any later version.
14 ;; This program is distributed in the hope that it will be useful, but
15 ;; WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
16 ;; or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
19 ;; You should have received a copy of the GNU General Public License
20 ;; along with this program; if not, write to the Free Software
21 ;; Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
24 (define-module (scm song-util))
26 (use-modules (srfi srfi-1))
27 (use-modules (ice-9 optargs))
28 (use-modules (ice-9 pretty-print))
33 ;;; Debugging utilities
36 ;; Iff true, enable a lot of debugging output
37 (define-public *debug* #f)
39 (define-macro (assert condition . data)
42 (error "Assertion failed" (quote ,condition) ,@data))
46 (define-macro (debug message object)
48 `(debug* ,message ,object)
52 (define (debug* message object)
53 (display "[[") (display message) (display "]] ") (pretty-print object)
60 (define-macro (defstruct name . slots)
61 ;; Similar as in Common Lisp, but much simplier -- no structure and slot options, no docstring
62 (let* ((slots* (map (lambda (s) (if (pair? s) s (list s))) slots))
63 (make-symbol (lambda (format% . extra-args)
64 (string->symbol (apply format #f format% name extra-args))))
65 ($record? (make-symbol "~a?"))
66 ($make-record (make-symbol "make-~a"))
67 ($copy-record (make-symbol "copy-~a"))
68 (reader-format "~a-~a")
69 (writer-format "set-~a-~a!")
73 (define ,$make-record #f)
74 (define ,$copy-record #f)
75 ,@(map (lambda (s) `(define ,(make-symbol reader-format (car s)) #f)) slots*)
76 ,@(map (lambda (s) `(define ,(make-symbol writer-format (car s)) #f)) slots*)
77 (let ((,record ,(make-record-type name (map car slots*))))
79 (lambda (record) ((record-predicate ,record) record)))
81 (lambda* (#:key ,@slots)
82 ((record-constructor ,record) ,@(map car slots*))))
85 (,$make-record ,@(apply
88 (list (symbol->keyword slot)
89 (list (make-symbol reader-format slot) 'record)))
92 `(set! ,(make-symbol reader-format (car s))
93 (record-accessor ,record (quote ,(car s)))))
96 `(set! ,(make-symbol writer-format (car s))
97 (record-modifier ,record (quote ,(car s)))))
101 (define-public (compose . functions)
102 (let ((functions* (drop-right functions 1))
103 (last-function (last functions)))
104 (letrec ((reduce (lambda (x functions)
105 (if (null? functions)
107 (reduce ((car functions) x) (cdr functions))))))
108 (lambda args (reduce (apply (last functions) args) (reverse functions*))))))
110 (define-macro (push! object list-var)
111 ;; The same as in Common Lisp
112 `(set! ,list-var (cons ,object ,list-var)))
115 (define-macro (add! object list-var)
116 `(set! ,list-var (append ,list-var (list ,object))))
119 (define-public (flatten lst)
124 (append (flatten (car lst)) (flatten (cdr lst))))
126 (cons (car lst) (flatten (cdr lst))))))
128 (define-public (safe-car list)
133 (define-public (safe-last list)
139 ;;; LilyPond utility functions
142 (define-public (music-property-value? music property value)
143 "Return true iff MUSIC's PROPERTY is equal to VALUE."
144 (equal? (ly:music-property music property) value))
146 (define-public (music-name? music name)
147 "Return true iff MUSIC's name is NAME."
149 (member (ly:music-property music 'name) name)
150 (music-property-value? music 'name name)))
152 (define-public (music-property? music property)
153 "Return true iff MUSIC is a property setter and sets or unsets PROPERTY."
154 (and (music-name? music '(PropertySet PropertyUnset))
155 (music-property-value? music 'symbol property)))
157 (define-public (music-has-property? music property)
158 "Return true iff MUSIC contains PROPERTY."
159 (not (eq? (ly:music-property music property) '())))
161 (define-public (property-value music)
162 "Return value of a property setter MUSIC.
163 If it unsets the property, return #f."
164 (if (music-name? music 'PropertyUnset)
166 (ly:music-property music 'value)))
168 (define-public (music-elements music)
169 "Return list of all MUSIC's top-level children."
170 (let ((elt (ly:music-property music 'element))
171 (elts (ly:music-property music 'elements)))
172 (if (not (null? elt))
176 (define-public (find-child music predicate)
177 "Find the first node in MUSIC that satisfies PREDICATE."
178 (define (find-child queue)
181 (let ((elt (car queue)))
184 (find-child (append (music-elements elt) (cdr queue)))))))
185 (find-child (list music)))
187 (define-public (find-child-named music name)
188 "Return the first child in MUSIC that is named NAME."
189 (find-child music (lambda (elt) (music-name? elt name))))
191 (define-public (process-music music function)
192 "Process all nodes of MUSIC (including MUSIC) in the DFS order.
193 Apply FUNCTION on each of the nodes.
194 If FUNCTION applied on a node returns true, don't process the node's subtree."
195 (define (process-music queue)
196 (if (not (null? queue))
197 (let* ((elt (car queue))
198 (stop (function elt)))
199 (process-music (if stop
201 (append (music-elements elt) (cdr queue)))))))
202 (process-music (list music)))