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 (safe-car list)
124 (define-public (safe-last list)
130 ;;; LilyPond utility functions
133 (define-public (music-property-value? music property value)
134 "Return true iff MUSIC's PROPERTY is equal to VALUE."
135 (equal? (ly:music-property music property) value))
137 (define-public (music-name? music name)
138 "Return true iff MUSIC's name is NAME."
140 (member (ly:music-property music 'name) name)
141 (music-property-value? music 'name name)))
143 (define-public (music-property? music property)
144 "Return true iff MUSIC is a property setter and sets or unsets PROPERTY."
145 (and (music-name? music '(PropertySet PropertyUnset))
146 (music-property-value? music 'symbol property)))
148 (define-public (music-has-property? music property)
149 "Return true iff MUSIC contains PROPERTY."
150 (not (eq? (ly:music-property music property) '())))
152 (define-public (property-value music)
153 "Return value of a property setter MUSIC.
154 If it unsets the property, return #f."
155 (if (music-name? music 'PropertyUnset)
157 (ly:music-property music 'value)))
159 (define-public (music-elements music)
160 "Return list of all MUSIC's top-level children."
161 (let ((elt (ly:music-property music 'element))
162 (elts (ly:music-property music 'elements)))
163 (if (not (null? elt))
167 (define-public (find-child music predicate)
168 "Find the first node in MUSIC that satisfies PREDICATE."
169 (define (find-child queue)
172 (let ((elt (car queue)))
175 (find-child (append (music-elements elt) (cdr queue)))))))
176 (find-child (list music)))
178 (define-public (find-child-named music name)
179 "Return the first child in MUSIC that is named NAME."
180 (find-child music (lambda (elt) (music-name? elt name))))
182 (define-public (process-music music function)
183 "Process all nodes of MUSIC (including MUSIC) in the DFS order.
184 Apply FUNCTION on each of the nodes.
185 If FUNCTION applied on a node returns true, don't process the node's subtree."
186 (define (process-music queue)
187 (if (not (null? queue))
188 (let* ((elt (car queue))
189 (stop (function elt)))
190 (process-music (if stop
192 (append (music-elements elt) (cdr queue)))))))
193 (process-music (list music)))