语音

警告

注意!这只是字母编码(ALPHA CODE)。

随着开发的不断进行,我们保留更改此API的权利。

语音的质量不够好,只能说“差强人意”。 鉴于设备的限制,播放过程中可能会遇到内存错误和(或)意外的杂音。 现在是开发初期,我们一直在改进语音合成器的代码。欢迎大家提交错误报告和代码合并请求(pull requests)。

电脑和机器人讲起话来更加的“人性化”。

我们经常通过图形用户界面(GUI)来了解电脑性能。对于BBC micro:bit,图形用户界面是一个5x5的LED矩阵,还有很多需要改进的地方。

让micro:bit和你谈话是一种有趣、高效且实用的传达信息的方式。为此,我们整合了一个简单的语音合成器,它基于20世纪80年代早期反工程(反工程指将软件及硬件分解或者解析,解明它们的结构,使用方法及目的,组成部品与要素技术的原理。在程序的领域,包含阐明组件间的关系的和分析系统的基本方法的行为)版本的合成器原理。听起来很可爱,是一种 “人类最终都会死亡” 一类的方法原理。

考虑到这一点,我们将使用语音合成器来创建……

DALEK(戴立克)诗歌

../_images/dalek.jpg

极少人知道DALEKs喜欢诗歌,尤其是五行打油诗。它们对严格的AABBA抑扬格痴狂,谁能想得到呢?

(实际上,正如我们下面正要学到的那样,DALEKs喜欢五行诗,是博士的错,也让Davros大为恼火。[The Doctor, DALEK 和Davros 都是《神秘博士》(Doctor Who)中的角色])

总之,我们会根据需要创建一个DALEK诗歌朗诵会。

发声

在设备能够讲话之前你需要像下面这样插入扬声器:

../_images/speech1.png

使设备讲话最简单的方式是像这样引用 speech 模块,调用 say 函数:

import speech

speech.say("Hello, World")

虽然这很可爱,但是DALEK的品味对于我们来说当然是不够的,因此我们需要改变语音合成器用于生成语音的一些参数。我们的语音合成器在这方面很强大,是因为我们可以改变以下四个参数:

  • pitch - (音调)声音的高低 (0=高,255=Barry White(低音));
  • speed - (音速)设备讲话的快慢(0=impossible(此处推测为讲话快),255 = bedtime story(“睡前故事”,此处推测为讲话慢));
  • mouth - 发声时嘴唇的开合度 (0 = ventriloquist’s dummy似腹语者的呢喃, 255 = Foghorn Leghorn似美国卡通电影《小鹰抓大鸡》中的大声尖叫);
  • throat - 声调放松还是紧张 (0 = 紧张(falling apart), 255 = 完全放松(totally chilled))

这些参数共同控制着声音的质量,亦称为音色。说实在的,获得你想要的声音的最好方法就是通过你的判断和调整去尝试。

你可以调整设置,将它们作为参数传递给 say 函数。更多详情可以在 speech 模块的API文档中找到。

经过一些尝试,我们发现下面这个声音听起来很有DALEK风格:

speech.say("I am a DALEK - EXTERMINATE", speed=120, pitch=100, throat=100, mouth=200)

“随需应变”的诗歌

作为半机械人DALEKs,运用他们的机器人功能来写诗,事实证明,他们使用的算法是用Python编写的,像这样:

# DALEK poetry generator, by The Doctor
import speech
import random
from microbit import sleep

# Randomly select fragments to interpolate into the template.
location = random.choice(["brent", "trent", "kent", "tashkent"])
action = random.choice(["wrapped up", "covered", "sang to", "played games with"])
obj = random.choice(["head", "hand", "dog", "foot"])
prop = random.choice(["in a tent", "with cement", "with some scent",
                     "that was bent"])
result = random.choice(["it ran off", "it glowed", "it blew up",
                       "it turned blue"])
attitude = random.choice(["in the park", "like a shark", "for a lark",
                         "with a bark"])
conclusion = random.choice(["where it went", "its intent", "why it went",
                           "what it meant"])

# A template of the poem. The {} are replaced by the named fragments.
poem = [
    "there was a young man from {}".format(location),
    "who {} his {} {}".format(action, obj, prop),
    "one night after dark",
    "{} {}".format(result, attitude),
    "and he never worked out {}".format(conclusion),
    "EXTERMINATE",
]

# Loop over each line in the poem and use the speech module to recite it.
for line in poem:
    speech.say(line, speed=120, pitch=100, throat=100, mouth=200)
    sleep(500)

正如注释所说明的一样,这是一个非常简单的设计:

  • 指定代码片段(fragments)(location, prop, attitude 等等)是从预定义的可能值列表中随机生成的。请注意使用 random.choice 从列表中选择单个项目。
  • 一首诗的模板被定义为一个带有 “预留部分” (holes)的节列表(由 {} 表示),指定的代码片段将会被引用 format 方法填入其中。
  • 最后,Python循环填充完诗歌节列表中的每个数据项,接着使用含DALEK声音设定的 speech.say 背诵该诗歌。每行之间有500毫秒暂停,因为即使是DALEK,也需要喘口气。

有趣的是,最初诗歌的源码是由Davros用 FORTRAN (一种将全部代码用大写字母输入以后,适用于DALEKS的语言)写的 。然而,博士及时回到了Davros的单元测试进程(单元测试是指对软件中的最小可测试单元进行检查和验证)和 部署流水线 (通过部署流水线可以持续交付)开始生效之间的时间点,在那个瞬间,他将MicroPython解释器插入到DALEK的操作系统,并将上面你看到的代码传输到DALEK的存储中,作为神出鬼没的时间领主的 彩蛋Rickroll (一种互联网恶作剧,如误导性链接)。

音素

你会注意到,有时 say 函数并不能准确地将英文单词转化成正确的声音。 要对输出进行精细控制,请使用音素:语音的组成单位。

使用音素的好处是你不用知道如何拼写!而是仅需要知道如何说出单词并用音素拼写它的发音。

语音合成器能够理解的音素完整列表可以在speech的API文档中找到。或者,你可以将单词传递给 translate 函数以节省更多时间,它会返回用于生成音频的音素的第一个近似值。这个结果可以手动编辑,以提高准确性、修改词形变化和完善强调语气,所以听上去更为自然。

pronounce 函数像这样被用于音素输出:

speech.pronounce("/HEH5EH4EH3EH2EH2EH3EH4EH5EHLP.”)

你将如何改进博士的代码使其使用音素呢?

让Micro:bit唱一曲

通过改变 pitch 设置以及调用 sing 函数可以让设备唱歌(尽管它不能很快赢得欧洲歌唱大赛(Eurovision)的奖杯)。

下面展示的是音调数字与音符互相对应的乐谱:

../_images/speech-pitch1.png

sing 函数必须像下面这样输入音素和音调:

speech.sing("#115DOWWWW")

请注意,将要唱出的音调使用了井号(#)被预先添加到音素中。直到有新的音调之前,对于随后连续的音素来说,该音调将保持不变。

下面的例子演示了如何使用所有三种母函数[generative functions](say, pronouncesing) 来生成类似输出的语音:

import speech
from microbit import sleep

# The say method attempts to convert English into phonemes.
speech.say("I can sing!")
sleep(1000)
speech.say("Listen to me!")
sleep(1000)

# Clearing the throat requires the use of phonemes. Changing
# the pitch and speed also helps create the right effect.
speech.pronounce("AEAE/HAEMM", pitch=200, speed=100)  # Ahem
sleep(1000)

# Singing requires a phoneme with an annotated pitch for each syllable.
solfa = [
    "#115DOWWWWWW",   # Doh
    "#103REYYYYYY",   # Re
    "#94MIYYYYYY",    # Mi
    "#88FAOAOAOAOR",  # Fa
    "#78SOHWWWWW",    # Soh
    "#70LAOAOAOAOR",  # La
    "#62TIYYYYYY",    # Ti
    "#58DOWWWWWW",    # Doh
]

# Sing the scale ascending in pitch.
song = ''.join(solfa)
speech.sing(song, speed=100)
# Reverse the list of syllables.
solfa.reverse()
song = ''.join(solfa)
# Sing the scale descending in pitch.
speech.sing(song, speed=100)