home *** CD-ROM | disk | FTP | other *** search
- # Orca
- #
- # Copyright 2005-2008 Google Inc.
- #
- # This library is free software; you can redistribute it and/or
- # modify it under the terms of the GNU Library General Public
- # License as published by the Free Software Foundation; either
- # version 2 of the License, or (at your option) any later version.
- #
- # This library is distributed in the hope that it will be useful,
- # but WITHOUT ANY WARRANTY; without even the implied warranty of
- # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- # Library General Public License for more details.
- #
- # You should have received a copy of the GNU Library General Public
- # License along with this library; if not, write to the
- # Free Software Foundation, Inc., Franklin Street, Fifth Floor,
- # Boston MA 02110-1301 USA.
- #
- """Dectalk voice definitions using ACSS.
-
- This module encapsulates Dectalk-specific voice definitions. It
- maps device-independent ACSS voice definitions into appropriate
- Dectalk voice parameter settings.
-
- """
-
- __id__ = "$Id: dectalk.py 4281 2008-10-08 22:05:56Z wwalker $"
- __author__ = "T. V. Raman"
- __version__ = "$Revision: 4281 $"
- __date__ = "$Date: 2008-10-08 18:05:56 -0400 (Wed, 08 Oct 2008) $"
- __copyright__ = "Copyright (c) 2005-2008 Google Inc."
- __license__ = "LGPL"
-
- import chnames
-
- # Handling of special characters
- #
- # Emacspeak uses Tcl syntax to communicate with its speech servers. It
- # embraces text in curly braces, so that at least {, }, and \ must be quoted
- # when sending text to speech server. But individual speech engines have
- # their own special characters in addition to those of Tcl. Dectalk
- # perceives speech parameters enclosed in square brackets, and Emacspeak
- # exploits this to transmit speech settings to Dectalk. Thus we must quote
- # [ and ] too.
-
- def makeSpecialCharMap():
- """Returns list of pairs mapping characters which are special for
- Dectalk speech server to their replacements.
- """
- chars = r'{\}[]'
- return [(ch, ' '+chnames.getCharacterName(ch)+' ') for ch in chars]
-
-
- # Speech parameters
-
- _defined_voices = {}
-
- # Map from ACSS dimensions to Dectalk settings:
-
- _table = {}
- #family codes:
-
- _table['family'] = {
- 'male' : ' :np ',
- 'paul' : ':np',
- 'man' : ':nh',
- 'harry' : ' :nh ',
- 'dennis' : ':nd',
- 'frank' : ':nf',
- 'betty' : ':nb',
- 'female' : ' :nb ',
- 'ursula' : ':nu',
- 'wendy' : ':nw',
- 'rita' : ':nr',
- 'kid' : ':nk',
- 'child' : ' :nk '
- }
-
- # average-pitch :
- # Average pitch for standard male voice is 122hz --this is mapped to
- # a setting of 5.
- # Average pitch varies inversely with speaker head size --a child
- # has a small head and a higher pitched voice.
- # We change parameter head-size in conjunction with average pitch to
- # produce a more natural change on the Dectalk.
-
- #male average pitch
-
- def _update_map(table, key, format, settings):
- """Internal function to update acss->synth mapping."""
- table[key] = {}
- for setting in settings:
- _table[key][setting[0]] = format % setting[1:]
-
- _male_ap = [
- (0, 96, 115),
- (1, 101, 112),
- (2, 108, 109),
- (3, 112, 106),
- (4, 118, 103),
- (5, 122, 100),
- (6, 128, 98),
- (7, 134, 96),
- (8, 140, 94),
- (9, 147, 91)
- ]
-
- _update_map(_table, ('male', 'average-pitch'),
- " ap %s hs %s ", _male_ap)
- _update_map(_table, ('paul', 'average-pitch'),
- " ap %s hs %s ", _male_ap)
-
- #Man has a big head --and a lower pitch for the middle setting
- _man_ap = [
- (0, 50, 125),
- (1, 59, 123),
- (2, 68, 121),
- (3, 77, 120),
- (4, 83, 118),
- (5, 89, 115),
- (6, 95, 112),
- (7, 110, 105),
- (8, 125, 100),
- (9, 140, 95)
- ]
-
- _update_map(_table, ('man', 'average-pitch'),
- " ap %s hs %s ",_man_ap)
- _update_map(_table, ('harry', 'average-pitch'),
- " ap %s hs %s ",_man_ap)
-
- _female_ap = [
- (0, 160, 115),
- (1, 170, 112),
- (2, 181, 109),
- (3, 192, 106),
- (4, 200, 103),
- (5, 208, 100),
- (6, 219, 98),
- (7, 225, 96),
- (8, 240, 94),
- (9, 260, 91)
- ]
-
- _update_map(_table, ('female', 'average-pitch'),
- " ap %s hs %s ",_female_ap)
- _update_map(_table, ('betty', 'average-pitch'),
- " ap %s hs %s ",_female_ap)
-
- # The default DECtalk values for the pitch of the other voices seem
- # to be as follows:
- # Frank = 155, Dennis = 110, Ursula = 240, Rita = 106, Wendy = 200
- # Kit = Child = 306
- # Therefore, follow TV Raman's lead:
-
- _frank_ap = [
- (0, 129, 115),
- (1, 134, 112),
- (2, 141, 109),
- (3, 145, 106),
- (4, 151, 103),
- (5, 155, 100),
- (6, 159, 98),
- (7, 165, 96),
- (8, 171, 94),
- (9, 178, 91)
- ]
-
- _update_map(_table, ('frank', 'average-pitch'),
- " ap %s hs %s ", _frank_ap)
-
- _dennis_ap = [
- (0, 84, 115),
- (1, 89, 112),
- (2, 96, 109),
- (3, 100, 106),
- (4, 106, 103),
- (5, 110, 100),
- (6, 116, 98),
- (7, 122, 96),
- (8, 128, 94),
- (9, 135, 91)
- ]
-
- _update_map(_table, ('dennis', 'average-pitch'),
- " ap %s hs %s ", _dennis_ap)
-
- _ursula_ap = [
- (0, 196, 115),
- (1, 206, 112),
- (2, 215, 109),
- (3, 224, 106),
- (4, 232, 103),
- (5, 240, 100),
- (6, 251, 98),
- (7, 265, 96),
- (8, 280, 94),
- (9, 300, 91)
- ]
-
- _update_map(_table, ('ursula', 'average-pitch'),
- " ap %s hs %s ", _ursula_ap)
-
- _rita_ap = [
- (0, 62, 115),
- (1, 72, 112),
- (2, 81, 109),
- (3, 90, 106),
- (4, 98, 103),
- (5, 106, 100),
- (6, 117, 98),
- (7, 131, 96),
- (8, 146, 94),
- (9, 166, 91)
- ]
-
- _update_map(_table, ('rita', 'average-pitch'),
- " ap %s hs %s ", _rita_ap)
-
- # For some reason, Wendy at a high pitch causes the
- # synthesizer to click and eventually make a feedback sound!
- # It doesn't seem to be the result of the pitch.
- # Keeping head size constant for higher pitch seems to eliminate
- # the problem.
-
- _wendy_ap = [
- (0, 156, 115),
- (1, 166, 112),
- (2, 175, 109),
- (3, 184, 106),
- (4, 192, 103),
- (5, 200, 100),
- (6, 211, 100),
- (7, 225, 100),
- (8, 240, 100),
- (9, 260, 100)
- ]
-
- _update_map(_table, ('wendy', 'average-pitch'),
- " ap %s hs %s ", _wendy_ap)
-
- # Kit/Child can't have the traditional adult head size
- # Setting the largest head size is the smallest adult
- # female head size.
-
- _child_ap = [
- (0, 256, 91),
- (1, 266, 89),
- (2, 276, 87),
- (3, 286, 85),
- (4, 296, 83),
- (5, 306, 81),
- (6, 316, 79),
- (7, 326, 77),
- (8, 336, 75),
- (9, 346, 73)
- ]
-
- _update_map(_table, ('child', 'average-pitch'),
- " ap %s hs %s ", _child_ap)
- _update_map(_table, ('kit', 'average-pitch'),
- " ap %s hs %s ", _child_ap)
-
- # pitch-range for male:
-
- # Standard pitch range is 100 and is mapped to
- # a setting of 5.
- # A value of 0 produces a flat monotone voice --maximum value of 250
- # produces a highly animated voice.
- # Additionally, we also set the assertiveness of the voice so the
- # voice is less assertive at lower pitch ranges.
-
- _male_pr = [
- (0, 0, 0),
- (1, 20, 10),
- (2, 40, 20),
- (3, 60, 30),
- (4, 80, 40, ),
- (5, 100, 50, ),
- (6, 137, 60),
- (7, 174, 70),
- (8, 211, 80),
- (9, 250, 100),
- ]
-
- _update_map(_table, ('male', 'pitch-range'),
- " pr %s as %s ", _male_pr)
- _update_map(_table, ('paul', 'pitch-range'),
- " pr %s as %s ", _male_pr)
-
- # For now, assume that standard pitch range is reasonably
- # consistent for all male voices with the execption of harry
-
- _update_map(_table, ('frank', 'pitch-range'),
- " pr %s as %s ", _male_pr)
- _update_map(_table, ('dennis', 'pitch-range'),
- " pr %s as %s ", _male_pr)
-
-
- _man_pr = [
- (0, 0, 0),
- (1, 16, 20),
- (2, 32, 40),
- (3, 48, 60),
- (4, 64, 80, ),
- (5, 80, 100, ),
- (6, 137, 100),
- (7, 174, 100),
- (8, 211, 100),
- (9, 250, 100)
- ]
-
- _update_map(_table, ('man', 'pitch-range'),
- " pr %s as %s ", _man_pr)
- _update_map(_table, ('harry', 'pitch-range'),
- " pr %s as %s ", _man_pr)
-
- _female_pr = [
- (0, 0, 0),
- (1, 50, 10),
- (2, 80, 20),
- (3, 100, 25),
- (4, 110, 30, ),
- (5, 140, 35),
- (6, 165, 57),
- (7, 190, 75),
- (8, 220, 87),
- (9, 250, 100)
- ]
-
- _update_map(_table, ('female', 'pitch-range'),
- " pr %s as %s ", _female_pr)
- _update_map(_table, ('betty', 'pitch-range'),
- " pr %s as %s ", _female_pr)
-
- # For now, assume that standard pitch range is reasonably
- # consistent for all female voices, including kit
-
- _update_map(_table, ('ursula', 'pitch-range'),
- " pr %s as %s ", _female_pr)
- _update_map(_table, ('rita', 'pitch-range'),
- " pr %s as %s ", _female_pr)
- _update_map(_table, ('wendy', 'pitch-range'),
- " pr %s as %s ", _female_pr)
- _update_map(_table, ('kit', 'pitch-range'),
- " pr %s as %s ", _female_pr)
- _update_map(_table, ('child', 'pitch-range'),
- " pr %s as %s ", _female_pr)
-
- # Stress:
-
- # On the Dectalk we vary four parameters
- # The hat rise which controls the overall shape of the F0 contour
- # for sentence level intonation and stress,
- # The stress rise that controls the level of stress on stressed
- # syllables,
- # the baseline fall for paragraph level intonation
- # and the quickness --a parameter that controls whether the final
- # frequency targets are completely achieved in the phonetic transitions.
-
- _male_stress = [
- (0, 0, 0, 0, 0),
- (1, 3, 6, 20, 3),
- (2, 6, 12, 40, 6),
- (3, 9, 18, 60, 9, ),
- (4, 12, 24, 80, 14),
- (5, 18, 32, 100, 18),
- (6, 34, 50, 100, 20),
- (7, 48, 65, 100, 35),
- (8, 63, 82, 100, 60),
- (9, 80, 90, 100, 40)
- ]
-
- _update_map(_table, ('male', 'stress'),
- " hr %s sr %s qu %s bf %s ", _male_stress)
- _update_map(_table, ('paul', 'stress'),
- " hr %s sr %s qu %s bf %s ", _male_stress)
-
- # For now, grabbing these values for all males but Harry
-
- _update_map(_table, ('frank', 'stress'),
- " hr %s sr %s qu %s bf %s ", _male_stress)
- _update_map(_table, ('dennis', 'stress'),
- " hr %s sr %s qu %s bf %s ", _male_stress)
-
-
- _man_stress = [
- (0, 0, 0, 0, 0),
- (1, 4, 6, 2, 2),
- (2, 8, 12, 4, 4),
- (3, 12, 18, 6, 6),
- (4, 16, 24, 8, 8),
- (5, 20, 30, 10, 9),
- (6, 40, 48, 32, 16),
- (7, 60, 66, 54, 22),
- (8, 80, 78, 77, 34),
- (9, 100, 100, 100, 40)
- ]
-
- _update_map(_table, ('man', 'stress'),
- " hr %s sr %s qu %s bf %s ", _man_stress)
- _update_map(_table, ('harry', 'stress'),
- " hr %s sr %s qu %s bf %s ", _man_stress)
-
- _female_stress = [
- (0, 1, 1, 0, 0),
- (1, 3, 4, 11, 0),
- (2, 5, 8, 22, 0),
- (3, 8, 12, 33, 0),
- (4, 11, 16, 44, 0),
- (5, 14, 20, 55, 0),
- (6, 35, 40, 65, 10),
- (7, 56, 80, 75, 20),
- (8, 77, 90, 85, 30),
- (9, 100, 100, 100, 40)
- ]
-
- _update_map(_table, ('female', 'stress'),
- " hr %s sr %s qu %s bf %s ", _female_stress)
- _update_map(_table, ('betty', 'stress'),
- " hr %s sr %s qu %s bf %s ", _female_stress)
-
- # For now, grabbing these values for all females including kit
-
- _update_map(_table, ('ursula', 'stress'),
- " hr %s sr %s qu %s bf %s ", _female_stress)
- _update_map(_table, ('rita', 'stress'),
- " hr %s sr %s qu %s bf %s ", _female_stress)
- _update_map(_table, ('wendy', 'stress'),
- " hr %s sr %s qu %s bf %s ", _female_stress)
- _update_map(_table, ('kit', 'stress'),
- " hr %s sr %s qu %s bf %s ", _female_stress)
- _update_map(_table, ('child', 'stress'),
- " hr %s sr %s qu %s bf %s ", _female_stress)
-
- #richness
-
- # Smoothness and richness vary inversely.
- # a maximally smooth voice produces a quieter effect
- # a rich voice is "bright" in contrast.
-
- _male_richness = [
- (0, 0, 100),
- (1, 14, 80),
- (2, 28, 60),
- (3, 42, 40),
- (4, 56, 30),
- (5, 70, 28),
- (6, 60, 24 ),
- (7, 70, 16),
- (8, 80, 8),
- (9, 100, 0)
- ]
-
- _update_map(_table, ('male', 'richness'),
- " ri %s sm %s " ,_male_richness)
- _update_map(_table, ('paul', 'richness'),
- " ri %s sm %s " ,_male_richness)
-
- # For now, grabbing these values for all males but Harry
-
- _update_map(_table, ('frank', 'richness'),
- " ri %s sm %s " ,_male_richness)
- _update_map(_table, ('dennis', 'richness'),
- " ri %s sm %s " ,_male_richness)
-
- _man_richness = [
- (0, 100, 0),
- (1, 96, 3),
- (2, 93, 6),
- (3, 90, 9),
- (4, 88, 11),
- (5, 86, 12),
- (6, 60, 24, ),
- (7, 40, 44),
- (8, 20, 65),
- (9, 0, 70)
- ]
-
- _update_map(_table, ('man', 'richness'),
- " ri %s sm %s " , _man_richness)
- _update_map(_table, ('harry', 'richness'),
- " ri %s sm %s " , _man_richness)
-
- _female_richness = [
- (0, 0, 100),
- (1, 8, 76),
- (2, 16, 52),
- (3, 24,28),
- (4, 32, 10),
- (5, 40, 4),
- (6, 50, 3),
- (7, 65, 3),
- (8, 80, 2),
- (9, 100, 0)
- ]
-
- _update_map(_table, ('female', 'richness'),
- " ri %s sm %s ", _female_richness)
- _update_map(_table, ('betty', 'richness'),
- " ri %s sm %s ", _female_richness)
-
- # For now, grabbing these values for all females including kit
-
- _update_map(_table, ('ursula', 'richness'),
- " ri %s sm %s ", _female_richness)
- _update_map(_table, ('rita', 'richness'),
- " ri %s sm %s ", _female_richness)
- _update_map(_table, ('wendy', 'richness'),
- " ri %s sm %s ", _female_richness)
- _update_map(_table, ('kit', 'richness'),
- " ri %s sm %s ", _female_richness)
- _update_map(_table, ('child', 'richness'),
- " ri %s sm %s ", _female_richness)
-
-
- def getrate(r):
- return int(180 + 4*r)
-
- def getvolume(v):
- return int(10*v)
-
- def getvoicelist():
- return _table['family'].keys()
-
- def getvoice(acss):
- """Memoized function that returns synthesizer code for
- specified ACSS setting.
- Synthesizer code is a tupple of the form (open,close)
- where open sets the voice, and close resets it."""
- name = acss.name()
- if name in _defined_voices:
- return _defined_voices[name]
- _defined_voices[name] = acss2voice(acss)
- return _defined_voices[name]
-
- def acss2voice(acss):
- """Return synthesizer code."""
- code = ""
- familyName = 'male'
- if 'family' in acss:
- familyName = acss['family']['name']
- if familyName in _table['family']:
- code += _table['family'][familyName]
- if 'rate' in acss:
- code += " :ra %s" % getrate(acss['rate'])
- if 'punctuations' in acss:
- code += " :punc %s" % acss['punctuations']
- if 'gain' in acss:
- code += " :volume set %s" % getvolume(acss['gain'])
- voice = ""
- dv = ""
- for d in ['average-pitch', 'pitch-range',
- 'richness', 'stress']:
- if d in acss:
- if (familyName, d) in _table:
- voice += _table[(familyName, d)][int(acss[d])]
- if voice:
- dv = " :dv %s" % voice
- if code or voice:
- code = "[%s %s]" % (code, dv)
- return (code, " [:np] ")
-