This file is indexed.

/usr/share/festival/sable-mode.scm is in festival 1:2.1~release-6ubuntu1.

This file is owned by root:root, with mode 0o644.

The actual contents of the file can be viewed below.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;                                                                       ;;
;;;                Centre for Speech Technology Research                  ;;
;;;                     University of Edinburgh, UK                       ;;
;;;                         Copyright (c) 1998                            ;;
;;;                        All Rights Reserved.                           ;;
;;;                                                                       ;;
;;;  Permission is hereby granted, free of charge, to use and distribute  ;;
;;;  this software and its documentation without restriction, including   ;;
;;;  without limitation the rights to use, copy, modify, merge, publish,  ;;
;;;  distribute, sublicense, and/or sell copies of this work, and to      ;;
;;;  permit persons to whom this work is furnished to do so, subject to   ;;
;;;  the following conditions:                                            ;;
;;;   1. The code must retain the above copyright notice, this list of    ;;
;;;      conditions and the following disclaimer.                         ;;
;;;   2. Any modifications must be clearly marked as such.                ;;
;;;   3. Original authors' names are not deleted.                         ;;
;;;   4. The authors' names are not used to endorse or promote products   ;;
;;;      derived from this software without specific prior written        ;;
;;;      permission.                                                      ;;
;;;                                                                       ;;
;;;  THE UNIVERSITY OF EDINBURGH AND THE CONTRIBUTORS TO THIS WORK        ;;
;;;  DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING      ;;
;;;  ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT   ;;
;;;  SHALL THE UNIVERSITY OF EDINBURGH NOR THE CONTRIBUTORS BE LIABLE     ;;
;;;  FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES    ;;
;;;  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN   ;;
;;;  AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,          ;;
;;;  ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF       ;;
;;;  THIS SOFTWARE.                                                       ;;
;;;                                                                       ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;                                                                       ;;
;;;  Festival (1.3.X) support for SABLE 0.2 the SGML/XML based mark up    ;;
;;;  language.                                                            ;;
;;;                                                                       ;;
;;;  This is XML version requiring Edinburgh's LTG's rxp XML parser as    ;;
;;;  distributed with Festival                                            ;;
;;;                                                                       ;;

(require_module 'rxp)

;;(set! auto-text-mode-alist
;;      (cons
;;       (cons "\\.sable$" 'sable)
;;       auto-text-mode-alist))

 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 ;;                                                                       ;;
 ;; Remember where to find these two XML entities.                        ;;
 ;;                                                                       ;;
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;


(xml_register_id "-//SABLE//DTD SABLE speech mark up//EN"
		(path-append xml_dtd_dir  "Sable.v0_2.dtd")
		)

(xml_register_id "-//SABLE//ENTITIES Added Latin 1 for SABLE//EN"
		 (path-append xml_dtd_dir  "sable-latin.ent")
		 )

;; (print (xml_registered_ids))

(defvar SABLE_RXDOUBLE "-?\\(\\([0-9]+\\.[0-9]*\\)\\|\\([0-9]+\\)\\|\\(\\.[0-9]+\\)\\)\\([eE][---+]?[0-9]+\\)?")

(defvar sable_pitch_base_map
  '((highest 1.2)
    (high 1.1)
    (medium 1.0)
    (default 1.0)
    (low 0.9)
    (lowest 0.8)))
(defvar sable_pitch_med_map
  '((highest 1.2)
    (high 1.1)
    (medium 1.0)
    (default 1.0)
    (low 0.9)
    (lowest 0.8)))
(defvar sable_pitch_range_map
  '((largest 1.2)
    (large 1.1)
    (medium 1.0)
    (default 1.0)
    (small 0.9)
    (smallest 0.8)))
(defvar sable_rate_speed_map
  '((fastest 1.5)
    (fast 1.2)
    (medium 1.0)
    (default 1.0)
    (slow 0.8)
    (slowest 0.6)))
(defvar sable_volume_level_map
  '((loudest 2.0) 
    (loud 1.5)
    (default 1.0)
    (medium 1.0)
    (quiet 0.5)))

(define (sable_init_globals)
  (set! utts nil)
  (set! sable_omitted_mode nil)
  (set! sable_word_features_stack nil)
  (set! sable_pitch_context nil)
  (set! sable_vol_context nil)
  (set! sable_vol_type 'no_change)
  (set! sable_vol_factor 1.0)
  (set! sable_current_language 'britishenglish)
  (set! sable_unsupported_language nil)
  (set! sable_language_stack nil)
  (set! sable_current_speaker 'voice_kal_diphone)
  (set! sable_speaker_stack nil)
)

(define (sable_token_to_words token name)
  "(sable_token_to_words utt token name)
SABLE mode token specific analysis."
  (cond
   ((or sable_omitted_mode sable_unsupported_language)
    ;; don't say anything (whole utterance)
    nil)
   ((string-equal "1" (item.feat token "done_sable_sub"))
    ;; to catch recursive calls this when splitting up sub expressions
    (sable_previous_token_to_words token name))
   ((and (not (string-equal "0" (item.feat token "sable_sub")))
	 (string-equal "0" (item.feat token "p.sable_sub")))
    (let (words (sub (item.feat token "sable_sub")))
      (item.set_feat token "done_sable_sub" "1")
      (set! words 
	    (apply append
		   (mapcar
		    (lambda (w)
		      (set! www (sable_previous_token_to_words token w))
		      www)
		    (read-from-string sub))))
      (item.set_feat token "done_sable_sub" "0")
      words))
   ((string-equal "1" (item.feat token "sable_ignore"))
    ;; don't say anything (individual word)
    nil)
   ((string-equal "1" (item.feat token "sable_ipa"))
    ;; Each token is an IPA phone
    (item.set_feat token "phonemes" (sable-map-ipa name))
    (list name))
   ((string-equal "1" (item.feat token "sable_literal"))
    ;; Only deal with spell here
    (let ((subwords) (subword))
      (item.set_feat token "pos" token.letter_pos)
      (mapcar
       (lambda (letter)
	 ;; might be symbols or digits
	 (set! subword (sable_previous_token_to_words token letter))
	 (if subwords
	     (set! subwords (append subwords subword))
	     (set! subwords subword)))
       (symbolexplode name))
      subwords))
   ((not (string-equal "0" (item.feat token "token_pos")))
    ;; bypass the prediction stage, if English
    (if (member_string (Parameter.get 'Language)
		       '(britishenglish americanenglish))
	(builtin_english_token_to_words token name)
	(sable_previous_token_to_words token name)))
   ;; could be others here later
   (t  
    (sable_previous_token_to_words token name))))

(defvar sable_elements
'(
  ("(SABLE" (ATTLIST UTT)
    (eval (list sable_current_speaker))  ;; so we know what state we start in
    (sable_setup_voice_params)
    nil
  )
  (")SABLE" (ATTLIST UTT)
    (xxml_synth UTT)  ;;  Synthesis the remaining tokens
    nil
  )
  ;; Utterance break elements
  ("(LANGUAGE" (ATTLIST UTT)
   ;; Status: probably complete 
   (xxml_synth UTT)
   (set! sable_language_stack 
	 (cons 
	  (list sable_current_language sable_unsupported_language)
	  sable_language_stack))
   ;; Select a new language
   (let ((language (upcase (car (xxml_attval "ID" ATTLIST)))))
     (cond
      ((or (string-equal language "SPANISH")
	   (string-equal language "ES"))
       (set! sable_current_language 'spanish)
       (set! sable_unsupported_language nil)
       (select_language 'spanish))
      ((or (string-equal language "ENGLISH")
	   (string-equal language "EN"))
       (set! sable_current_language 'britishenglish)
       (set! sable_unsupported_language nil)
       (select_language 'britishenglish))
      (t  ;; skip languages you don't know
       ;; BUG: if current language isn't English this wont work
       (apply_hooks tts_hooks
		    (eval (list 'Utterance 'Text
				(string-append "Some text in " language))))
       (set! sable_unsupported_language t)))
     nil))
  (")LANGUAGE" (ATTLIST UTT)
   (xxml_synth UTT)
   (set! sable_unsupported_language (car (cdr (car sable_language_stack))))
   (set! sable_current_language (car (car sable_language_stack)))
   (set! sable_language_stack (cdr sable_language_stack))
   (if (not sable_omitted_mode)
       (begin
	 (select_language sable_current_language)
	 (sable_setup_voice_params)))
   nil)
  ("(SPEAKER" (ATTLIST UTT)
   ;; Status: GENDER/AGE ignored, should be done by sable-def-speaker 
   ;;         function to define Festival voices to SABLE
   (xxml_synth UTT)
   (set! sable_speaker_stack (cons sable_current_speaker sable_speaker_stack))
   (cond
    ((not equal? sable_current_language 'britishenglish)
     (print "SABLE: choosen unknown voice, current voice unchanged"))
    ((equal? (car (xxml_attval "NAME" ATTLIST)) 'male1)
     (set! sable_current_speaker 'voice_kal_diphone)
     (voice_kal_diphone))
    ((equal? (car (xxml_attval "NAME" ATTLIST)) 'male2)
     (set! sable_current_speaker 'voice_cmu_us_rms_cg)
     (voice_cmu_us_rms_cg))
    ((equal? (car (xxml_attval "NAME" ATTLIST)) 'male3)
     (set! sable_current_speaker 'voice_ked_diphone)
     (voice_ked_diphone))
    ((equal? (car (xxml_attval "NAME" ATTLIST)) 'male4)
     (set! sable_current_speaker 'voice_rab_diphone)
     (voice_rab_diphone))
    ((equal? (car (xxml_attval "NAME" ATTLIST)) 'male5)
     (set! sable_current_speaker 'voice_cmu_us_awb_cg)
     (voice_cmu_us_awb_cg))
    ((equal? (car (xxml_attval "NAME" ATTLIST)) 'female1)
     (set! sable_current_speaker 'voice_cmu_us_slt_arctic_hts)
     (voice_us1_mbrola))
   (t
      (set! sable_current_speaker (intern (string-append "voice_" (car (xxml_attval "NAME" ATTLIST)))))
      (eval (list sable_current_speaker))))
    (sable_setup_voice_params)
   nil)
  (")SPEAKER" (ATTLIST UTT)
   (xxml_synth UTT)
   (set! sable_utt UTT)
   (set! sable_current_speaker (car sable_speaker_stack))
   (set! sable_speaker_stack (cdr sable_speaker_stack))
   (eval (list sable_current_speaker))
   (sable_setup_voice_params)
   nil)
  ("BREAK" (ATTLIST UTT)
   ;; Status: probably complete
   ;; may cause an utterance break
   (let ((level (upcase (car (xxml_attval "LEVEL" ATTLIST)))))
     (cond
      ((null UTT) nil)
      ((string-equal "LARGE" level)
       (xxml_synth UTT)
       nil)
      (t
       (let ((last_token (utt.relation.last UTT'Token)))
	 (if last_token
	     (item.set_feat last_token "pbreak" "B"))
	 UTT)))))
  ("(DIV" (ATLIST UTT)
   ;; Status: probably complete
   (xxml_synth UTT)
   nil)
  ("AUDIO" (ATTLIST UTT)
   ;; Status: MODE (background) ignored, only insertion supported
   ;; mime type of file also ignored, as its LEVEL
   (let ((tmpfile (make_tmp_filename)))
     ;; ignoring mode-background (and will for sometime)
     ;; ignoring level option
     (xxml_synth UTT)  ;; synthesizing anything ready to be synthesized
     (get_url (car (xxml_attval "SRC" ATTLIST)) tmpfile)
     (apply_hooks tts_hooks
		  (eval (list 'Utterance 'Wave tmpfile)))
     (delete-file tmpfile)
     nil))
  ("(EMPH" (ATTLIST UTT)
   ;; Status: nesting makes no difference, levels ignored
   ;; Festival is particularly bad at adding specific emphasis
   ;; that's what happens when you use statistical methods that
   ;; don't include any notion of emphasis
   ;; This is *not* recursive and only one level of EMPH supported
   (sable_push_word_features)
   (set! xxml_word_features 
	 (cons (list "dur_stretch" 1.6)
	       (cons
		(list "EMPH" "1") xxml_word_features)))
   UTT)
  (")EMPH" (ATTLIST UTT)
   (set! xxml_word_features (sable_pop_word_features))
   UTT)
  ("(PITCH" (ATTLIST UTT)
   ;; Status: probably complete
   ;; At present festival requires an utterance break here
   (xxml_synth UTT)
   (set! sable_pitch_context (cons int_lr_params sable_pitch_context))
   (let ((base (sable_interpret_param
		(car (xxml_attval "BASE" ATTLIST))
		sable_pitch_base_map
		(cadr (assoc 'target_f0_mean int_lr_params))
		sable_pitch_base_original))
	 (med (sable_interpret_param
	       (car (xxml_attval "MED" ATTLIST))
	       sable_pitch_med_map
	       (cadr (assoc 'target_f0_mean int_lr_params))
	       sable_pitch_med_original))
	 (range (sable_interpret_param
		 (car (xxml_attval "RANGE" ATTLIST))
		 sable_pitch_range_map
		 (cadr (assoc 'target_f0_std int_lr_params))
		 sable_pitch_range_original))
	 (oldmean (cadr (assoc 'target_f0_mean int_lr_params))))
     ;; Festival (if it supports anything) supports mean and std
     ;; so we treat base as med if med doesn't seem to do anything
     (if (equal? med oldmean)
	 (set! med base))
     (set! int_lr_params
	   (cons
	    (list 'target_f0_mean med)
	    (cons
	     (list 'target_f0_std range)
	     int_lr_params)))
   nil))
  (")PITCH" (ATTLIST UTT)
   (xxml_synth UTT)
   (set! int_lr_params (car sable_pitch_context))
   (set! sable_pitch_context (cdr sable_pitch_context))
   nil)
  ("(RATE" (ATTLIST UTT)
   ;; Status: can't deal with absolute word per minute SPEED.
   (sable_push_word_features)
   ;; can't deal with words per minute value
   (let ((rate (sable_interpret_param
		(car (xxml_attval "SPEED" ATTLIST))
		sable_rate_speed_map
		(sable_find_fval "dur_stretch" xxml_word_features 1.0)
		sable_rate_speed_original)))
     (set! xxml_word_features 
	   (cons (list "dur_stretch" (/ 1.0 rate)) xxml_word_features))
     UTT))
  (")RATE" (ATTLIST UTT)
   (set! xxml_word_features (sable_pop_word_features))
   UTT)
  ("(VOLUME" (ATTLIST UTT)
   ;; Status: probably complete
   ;; At present festival requires an utterance break here
   (xxml_synth UTT)
   (set! sable_vol_context (cons (list sable_vol_type sable_vol_factor)
				 sable_vol_context))
   (let ((level (sable_interpret_param
		(car (xxml_attval "LEVEL" ATTLIST))
		sable_volume_level_map
		sable_vol_factor
		1.0)))
     (cond
      ((string-matches (car (xxml_attval "LEVEL" ATTLIST)) ".*%")
       (set! sable_vol_type 'relative))
      ((string-matches (car (xxml_attval "LEVEL" ATTLIST))  SABLE_RXDOUBLE)
       (set! sable_vol_type 'absolute))
      (t
       (set! sable_vol_type 'relative)))
     (set! sable_vol_factor level))
   nil)
  (")VOLUME" (ATTLIST UTT)
   (xxml_synth UTT)
   (set! sable_vol_type (car (car sable_vol_context)))
   (set! sable_vol_factor (car (cdr (car sable_vol_context))))
   (set! sable_vol_context (cdr sable_vol_context))
   nil)
  ("(ENGINE" (ATTLIST UTT)
   ;; Status: probably complete
   (xxml_synth UTT)
   (if (string-matches (car (xxml_attval "ID" ATTLIST)) "festival.*")
       (let ((datastr ""))
	 (mapcar
	  (lambda (c) (set! datastr (string-append datastr " " c)))
	  (xxml_attval "DATA" ATTLIST))
	 (apply_hooks tts_hooks (eval (list 'Utterance 'Text datastr)))
	 (set! sable_omitted_mode t)) ;; ignore contents 
       ;; else 
       ;;  its not relevant to me
       )
   nil)
  (")ENGINE" (ATTLIST UTT)
   (xxml_synth UTT)
   (set! sable_omitted_mode nil)
   nil)
  ("MARKER" (ATTLIST UTT)
   ;; Status: does nothing
   ;; Can't support this without low-level control of audio spooler
   (format t "SABLE: marker \"%s\"\n" 
	   (car (xxml_attval "MARK" ATTLIST)))
   UTT)
  ("(PRON" (ATTLIST UTT)
   ;; Status: IPA currently ignored
   (sable_push_word_features)
   ;; can't deal with words per minute value
   (let ((ipa (xxml_attval "IPA" ATTLIST))
	 (sub (xxml_attval "SUB" ATTLIST)))
     (cond
      (ipa
       (format t "SABLE: ipa ignored\n")
       (set! xxml_word_features 
	     (cons (list "sable_ignore" "1") xxml_word_features)))
      (sub
       (set! xxml_word_features 
	     (cons (list "sable_sub" (format nil "%l" sub))
		   xxml_word_features))
       (set! xxml_word_features 
	     (cons (list "sable_ignore" "1") xxml_word_features))))
     UTT))
  (")PRON" (ATTLIST UTT)
   (set! xxml_word_features (sable_pop_word_features))
   UTT)
  ("(SAYAS" (ATTLIST UTT)
   ;; Status: only a few of the types are dealt with
   (sable_push_word_features)
    (set! sable_utt UTT)
   ;; can't deal with words per minute value
   (let ((mode (downcase (car (xxml_attval "MODE" ATTLIST))))
	 (modetype (car (xxml_attval "MODETYPE" ATTLIST))))
     (cond
      ((string-equal mode "literal")
       (set! xxml_word_features 
	     (cons (list "sable_literal" "1") xxml_word_features)))
      ((string-equal mode "phone")
       (set! xxml_word_features 
	     (cons (list "token_pos" "digits") xxml_word_features)))
      ((string-equal mode "ordinal")
       (set! xxml_word_features 
	     (cons (list "token_pos" "ordinal") xxml_word_features)))
      ((string-equal mode "cardinal")
       (set! xxml_word_features 
	     (cons (list "token_pos" "cardinal") xxml_word_features)))
      (t
       ;; blindly trust festival to get it right 
       t))
     UTT))
  (")SAYAS" (ATTLIST UTT)
   (set! xxml_word_features (sable_pop_word_features))
   UTT)

	     
))

(define (sable_init_func)
  "(sable_init_func)
Initialisation for SABLE mode"
  (sable_init_globals)
  (voice_kal_diphone)
  (set! sable_previous_elements xxml_elements)
  (set! xxml_elements sable_elements)
  (set! sable_previous_token_to_words english_token_to_words)
  (set! english_token_to_words sable_token_to_words)
  (set! token_to_words sable_token_to_words))

(define (sable_exit_func)
  "(sable_exit_func)
Exit function for SABLE mode"
  (set! xxml_elements sable_previous_elements)
  (set! token_to_words sable_previous_token_to_words)
  (set! english_token_to_words sable_previous_token_to_words))

(define (sable_push_word_features)
"(sable_push_word_features)
Save current word features on stack."
  (set! sable_word_features_stack 
	(cons xxml_word_features sable_word_features_stack)))

(define (sable_adjust_volume utt)
  "(sable_adjust_volume utt)
Amplify or attenutate signale based on value of sable_vol_factor
and sable_vol_type (absolute or relative)."
  (set! utts (cons utt utts))
  (cond
   ((equal? sable_vol_type 'no_change)
    utt)
   ((equal? sable_vol_type 'absolute)
    (utt.wave.rescale utt sable_vol_factor 'absolute))
   ((equal? sable_vol_type 'relative)
    (utt.wave.rescale utt sable_vol_factor))
   (t
    (format stderr "SABLE: volume unknown type \"%s\"\n" sable_vol_type)
    utt))
   utt)

(define (sable_pop_word_features)
"(sable_pop_word_features)
Pop word features from stack."
  (let ((r (car sable_word_features_stack)))
    (set! sable_word_features_stack (cdr sable_word_features_stack))
    r))

(define (sable_find_fval feat flist def)
  (cond
   ((null flist) def)
   ((string-equal feat (car (car flist)))
    (car (cdr (car flist))))
   (t
    (sable_find_fval feat (cdr flist) def))))

(define (sable_interpret_param ident map original current)
"(sable_interpret_param IDENT MAP ORIGINAL CURRENT)
If IDENT is in map return ORIGINAL times value in map, otherwise
treat IDENT of the form +/-N% and modify CURRENT accordingly."
  (let ((mm (assoc ident map)))
    (cond 
     (mm
      (* original (car (cdr mm))))
     ((string-matches ident SABLE_RXDOUBLE)
      (parse-number ident))
     ((string-matches ident ".*%")
      (+ current (* current (/ (parse-number (string-before ident "%")) 
			       100.0))))
;;     ((string-matches ident ".*%")
;;      (* current (/ (parse-number (string-before ident "%")) 100.0)))
     ((not ident) current)
     (t
      (format stderr "SABLE: modifier \"%s\" not of float, tag or +/-N\n"
	      ident)
      current))))

(define (sable_setup_voice_params)
"(sable_setup_voice_params)
Set up original values for various voice parameters."
 (set! sable_pitch_base_original (cadr (assoc 'target_f0_mean int_lr_params)))
 (set! sable_pitch_med_original (cadr (assoc 'target_f0_mean int_lr_params)))
 (set! sable_pitch_range_original (cadr (assoc 'target_f0_std int_lr_params)))
 (set! sable_rate_speed_original 1.0)
 (if (and after_synth_hooks (not (consp after_synth_hooks)))
     (set! after_synth_hooks 
	   (cons after_synth_hooks (list sable_adjust_volume)))
     (set! after_synth_hooks 
	   (append after_synth_hooks (list sable_adjust_volume))))
)

;;; Declare the new mode to Festival
(set! tts_text_modes
   (cons
    (list
      'sable   ;; mode name
      (list         
       (list 'init_func sable_init_func)
       (list 'exit_func sable_exit_func)
       '(analysis_type xml)
       ))
    tts_text_modes))

(provide 'sable-mode)