使用 PHP 代理向音频系统提供信息

本文介绍使用开源工具收集、编辑并向中央数据库提供信息的系统,它演示的方式不是通过屏幕,而是通过语音系统的声音,这样做是为了照顾存在视力障碍的用户。该系统使用了许多 PHP 代理,它们可以独立地生成、编辑、组织和读出信息。

Colin Beckingham, 研究员, 自由职业者

Colin Beckingham 居住在加拿大安大略省东部,是一名自由研究员、撰稿人和程序员。他拥有金斯顿市皇后大学 以及温莎大学的学位,在银行业、园艺、赛马、教育、公共服务、零售和旅游/观光等广泛领域都有涉猎。他是数据库应用程序编写者,也是大量报纸、杂志和在线文章的撰稿人,他的研究兴趣包括 Linux 上的开源编程以及语音控制应用程序。



2009 年 12 月 17 日

团队主管要求您准备一份与收购 XYZ 有限公司有关的意见书。这份意见书将用在当地时间下午 4:00 整开始的会议中,这次会议要做出是否收购的决定。下午 3:50,您正在办公桌前静静地将完成的材料放到意见书中,这是计算机突然发出了声音(通过您的耳机传来):“等等,还有一条来自 BCD feed 的消息与 XYZ 有限公司有关。” 您访问 feed 阅读器,查看该信息,调整了意见书,最新的信息让与会者的印象深刻。在这种情况下,您的视力障碍根本没有任何影响。

常用术语

  • FIFO: 先入先出
  • HTML: 超文本标记语言
  • RDBMS: 关系数据库管理系统
  • RSS: 真正简单聚合
  • SQL: 结构化查询语言
  • TTS: 文本到语音转换
  • XML: 可扩展标记语言

您的优势在于有多个代理为您工作,收集和组织信息。其他人都碰巧没有读到该 feed,因为新闻几分钟就不再是新闻了。该系统没有在窗口增加一个弹出窗口,而是通过声音抓住了您的注意,这是合理的,因为屏幕对您的用处不大。

计算机音频输出是一个有用的备用和可访问呈现机制,对于有视力障碍的人尤其如此。计算机语音的概念并不新鲜 —— 电影爱好者应该很熟悉上面引用的 “等一等” 这段语音,它来自电影 2001—太空漫游(1968),计算机用语音宣布了一项通讯阵列组件错误。但是,如今大部分计算机的输出仍然使用屏幕。

为什么 Arthur C. Clarke 和 Stanley Kubrick 选择语音交互而不是屏幕或打印展示?并不是因为有视觉障碍才更需要通过听觉而不是视觉来获取信息。

不要打印,说出来

HAL 的语音温柔而友好,快节奏的世界正需要这样的变化。语音能做很多屏幕做不到的事情,听觉也比视觉更加可靠。走路的时候您的思维也可能更加敏锐。在某种情况下,对于那些没有视力障碍的人也有优势。假设您正在监控电源供应的线路电压。屏幕上不断刷新线路情况可能无法注意到它的变化。在这种情况下,代理可以在电压正常时发出低沉的声音,而在出现问题是发出高亢的声音。蜜蜂就是这样做的:干扰它们的时候,声音就会变。只要不关闭耳机或扬声器,您就可以通过听觉获取消息。

Festival TTS 引擎

Festival 是一个文本到语音转换 (TTS) 引擎。给定文本字符串,引擎将通过计算机音频系统进行朗读。(参见 参考资料 了解详情)。要快速验证 Festival 安装是否运行正确,请运行 $ festival,然后运行 festival> (SayText "Hello world")

但是,语音也存在一些问题。有些消息中包含字符串 "PM late",这表示下午晚些时候还是说 Prime Minister 来晚了?人可以通过语境和思考来确定含义,但是 PHP 代理怎么做到这一点?这种情况非常复杂,但是您可以在出现问题时进行处理,就像蜜蜂,可以从各种花蜜中生产蜂蜜。

电影中的计算机 HAL 并不符合实际情况,实际上还有很多细节需要研究和准备。不管怎样,就让我们假设在某些情况下音频比文字要好,首先来了解该系统的工作方式。


mashup 概述

假设云中 RDBMS 数据库包含消息。这些消息可以是文本消息,也可以是确定一段声音文件的文件名,扫描这些消息的 PHP 脚本可以智能选择 TTS 引擎(如 Festival)或音频播放器(如 MPlayer)呈现内容。新消息通过 PHP 中的脚本提交到数据库,该脚本查看各种信息源,比如 新闻 feed (XML)、网页 (HTML)、bash 脚本的输出等等,还包括一项特定的作业:在队列中构建有意义的消息。图 1 提供了这种系统的图示。

图 1. 音频系统
音频系统图,展示了代理、设备数据库存储库和服务器都通过云彼此交互。

图 1 展示了一种可能的组合。左边是输入;右边是输出。中间是云,表示系统的任何部分在物理上都可以与其余部分隔离,只需要逻辑连接。云的上下都是特殊的服务(比如数据库存储器和 HTTP 服务器),前后一致地交付消息需要这些服务。图中显示可能有多个输出 —— 例如,几个用户收到相同的消息就是这种情况。

看起来直接明了,实际上也是如此,只不过每个处理阶段还有更多的细节问题。现在让我们了解这些细节。


后端存储

样例后端数据库只包含一个表格和几个简单的字段。该数据库在清单 1 中解释。

清单 1. 后端 MySQL 数据库定义
CREATE TABLE IF NOT EXISTS `queue` (
  `qid` int(11) NOT NULL AUTO_INCREMENT,
  `enunc` text COLLATE latin1_general_ci NOT NULL,
  `ts` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  `sys` tinyint(1) NOT NULL DEFAULT '0',
  `engine` tinyint(4) NOT NULL DEFAULT '0',
  `procd` tinyint(4) NOT NULL DEFAULT '0',
  `pause` tinyint(4) NOT NULL DEFAULT '0',
  PRIMARY KEY (`qid`)
) ENGINE=InnoDB  DEFAULT CHARSET=latin1 COLLATE=latin1_general_ci AUTO_INCREMENT=607 ;

该代码是从 MySQL 导出的(其他 RDBMS 请自行调整),如果不存在表格,该代码将创建一个。第一个字段是每个记录都必须使用的唯一 ID 标识符。后面是一个 text 类型字段(如果喜欢也可以选择 varchar 类型),也就是实际的消息。接下来是时间戳,默认为记录条目的时间。Sys 是一个逻辑字段,其值可以为真或假。它指示这是否是一个高优先级消息,必须在所有其他消息之前进行展示 —— 例如,一个关键脚本停止工作。接下来的字段是 engine,这是一个数字,表示使用哪个程序来阅读信息,是 Festival、MPlayer 还是其他的声音管理程序。接下来的字段 procd 记录该消息是否已经阅读。该字段使您能记录所有消息,而只根据需要阅读最新的消息。最后一个字段 pause 使系统能够在发生其他情况时将消息靠后处理。您可以根据需要添加其他字段 —— 具体来说,可以使用其他 procd 字段指示消息是否已经在其他机器上阅读。我选择使用 InnoDB 数据库引擎支持后面的表格连接和关系创建。在简单的情况下,默认的 MyISAM 引擎就足够了。


播音

后端就位并准备好存储消息后,您需要一个播音程序。该脚本查看队列表格中的记录,如果记录没有暂停并且在 FIFO 顺序中没有播放,则阅读它们,所有系统消息优先(忽略时间)。

清单 2 展示了一个简短的 PHP 指令集,可以提取需要的数据。

清单 2. 播音程序的 PHP 代码片段
<?php
$mysqli = new mysqli($server,$user,$password,$database);
$sql = "select qid,enunc,engine from queue
	  where procd=0 and pause=0
	  order by sys desc,qid asc";
$result = $mysqli->query($sql) or die("Error:".$mysqli->error);
if ($result->num_rows > 0) {
  $row = $result->fetch_array();
  echo $row['enunc']."\n";
  switch ($row['engine']) {
    case 0: // festival TTS
      saytext($row['enunc']);
    break;
    case 1: // mplayer sounds/music
      playsound($row['enunc']);
    break;
    default: // must be something else
      echo "I m sorry Dave, I can t do that, 
	  you did not define an engine for ".$row['enunc']."\n";
    break;
  }
  $qid = $row['qid'];
  set_pron($qid);
}
?>

在上述代码中,SQL 语句调用没有播放也没有暂停的记录列表。然后循环这些记录并在 switch 语句中根据使用的播放器采取相应的行动;在本例中,数字 0 指示 Festival 引擎,数字 1 通知 MPlayer 播放声音。有时会将诊断信息打印到屏幕上,帮助调试。当播放或阅读一条消息时,set_pron() 调用简单的函数在后端将字段 procd 设置为非零,以便再次运行脚本时,该消息不会重复。

调用播放器的函数根据使用的播放器阅读消息。清单 3 展示了 Festival 的示例。

清单 3. 调用 Festival 的 PHP 函数
function saytext($phrase) {
global $debug;
    if ($debug) echo $phrase."\n";
    exec('festival -b \'(SayText "'.$phrase.'")\'');
}

注意,该函数调用 PHP 中的 exec() 函数。您依赖于该函数在完成播音之后停止执行,并在尝试下一次记录之前完成语音呈现。此外,必须检查传递给 Festival 的短语,以确保它的语法对于 TTS 引擎是可接受的,并确保发音对于听众是可以理解的(下文有更多讨论)。同样,播音程序负责降低一个声道的音量,以让另一个声道可以发声。

注意:尽管可以根据需要手动运行脚本,但是将其作为 cron 作业重复运行可能更加合理,注意在重复的间隙设置合适的延迟。


勤劳的代理

在后台运行的播音程序一直等待着新消息,现在有必要在队列中放入一些内容。开始的示例引用一个扫描 RSS feed 的代理,因此 清单 4 中的代码延续了这个思路。RSS feed 使用 XML 标准,因此您可以使用 PHP XML 函数从 feed 中提取相关的记录。

清单 4. 用于 RSS 扫描代理的基本 PHP 代码
<?php
    $mysqli = new mysqli($server,$user,$password,$database);
    $rss = "URI_of_feed_goes_here";
    $xmlstr = file_get_contents($rss);
    $xml = new SimpleXMLElement($xmlstr);
    $crit = "XYZ";
    $jam = "Just a moment";
    foreach ($xml->channel->item as $item) {
	$thistitle = $item->title;
	if (strpos($thistitle,$crit) > 0) {
	    // $titl = speakize($item->title,'news');
	    // $detl = speakize($item->description,'news');
	    $alert = "$jam, $jam, there is a news item in the feed";
	    echo "$titl : $detl\n";
	    // table has `qid`, `enunc`, `ts`, `sys`, `engine`, `procd`, `pause`
	    $sql = "insert into queue values(NULL,'$alert',NULL,0,0,0,0)";
	    $result = $mysqli->query($sql) or die($mysqli->error);
	}
    }
?>

扫描代理执行以下任务:

  • 建立到数据库的 mysqli 连接之后,它定义查看哪个 RSS feed 并将其放入字符串变量。
  • 然后将该字符串读入 XML 对象。变量 $crit 设置为 XYZ,这是您自己确定的标准 —— 有关 XYZ 的任何 feed 项目就是我们关注的。
  • foreach 语句循环新项目,查找标题与 XYZ 有关的内容。
    注意: 该任务假设标题中存在引用,但是您可以使用 OR 语句扩展逻辑条件,搜索更多的 feed。
  • 如果找到项目,脚本将把该记录添加到队列。
    在本例中,可以向数据库添加三个字符串:feed 项目的标题;描述;一般行,通知存在符合标准的 feed 项目。最后一个最安全,因为它包含 Festival 可以呈现的已知关键字。在本例中,标题和描述将输出到屏幕以便调试。
  • 最后,注意参数序列 NULL,'$alert',NULL,0,0,0,0,其中:
    • NULL 是您喜欢的任何 ID。
    • '$alert' 替代 alert 变量的内容。
    • NULL 是服务器的时间戳。
    • 0 表示该消息不是优先消息。
    • 0 表示引擎为 Festival。
    • 0 表示消息还没有阅读。
    • 0 表示消息没有暂停。

细心的读者可能会发现清单 4 使用了 speakize() 函数,这里我们不打算详细解释。知道它能够将为屏幕设计的文本转换为声音就足够了,可以根据输出进行适当的更改。

现在,周期已经完成了:代理向数据库提供信息,播音程序等待阅读的消息,所有需要的就是新的 feed,允许单个代理挑选 XYZ 项目并发布消息。当发生这些情况时,您就会得到音频警告。在正常情况下,可能存在多个代理,所有代理都根据需要轮流工作并展示。


管理程序

显然,该系统运行后,如果有一种方法能够管理后端数据库中的记录将很有帮助。可能有必要进行以下操作:重复最后一项;将最后 10 项标记为未读,以便在下一次发声时可以重复;快进到最新的项;从头部开始阅读所有项目等等。尽管这些管理功能超出了本文的范围,您可以看到后端数据库的结构允许使用相应的 SQL 操作查看记录并重新排序。


问题

计算机系统上的音频本质上存在许多陷阱。这些陷阱包括:

  • 文本字符晦涩和缩写。speakize() 函数的目的是扫描传入文本并将其转换为可读形式。正如上文所述,“PM” 表示下午还是 Prime Minister?因为涉及到一个具体的 feed,您可以定制代理,使它根据上下文考虑缩写。Speakize 是这些功能之一,您可以根据需要进行修改。
  • 如果系统正在使用 TTS 阅读一个很长的句子,这时队列中插入了一个紧急的系统声音文件怎么办? 因为 exec 函数停止特定脚本的执行,系统可能无法感知新的消息。一种可能性是有两个播音程序,一个关注 Festival 操作,另一个关注系统声音。这种解决方案需要调用多个输出设备(在许多系统中,Festival 和 MPlayer 等程序可以将输出发送到特定的设备)或者在一个设备上协调输入。人们可以同时处理语音和声音(就像音乐与和弦)。但是,两个并发的 TTS 声道可能导致问题。
  • 如果喜欢在用计算机时听重金属音乐怎么办?输入消息会被淹没吗?只要将音量水平的控制权交给系统,计算机就可以自动降低一个声道的声音,让语音发出悦耳的声音。
  • feed 的编辑不一定会为了让 TTS 应用程序能够处理而格式化标题和描述。假设可以打印,这些项常常是从打印的页面上下文中提取的,这样一来,标题和描述都不太好理解,除非查看整个文章。“他又这么做了!” 谁做了什么?仔细地挑选您的新闻来源,为出现意外做好准备。

结束语

本文介绍了如何结合 PHP 和其他开源技术来形成有用的 mashup,将有用的消息发送到音频系统。这只是引起人们注意的众多方式之一。如果用户有视力障碍,或者不方便或者不可能查看屏幕时,这将非常有用。代理执行阅读功能往往比人类更快、更可靠,虽然它们很难考虑上下文。

这些 PHP 代理就像是小小的蜜蜂,每只蜜蜂都有自己的工作,收集信息、消化并在数据库的蜂巢中集中存储,然后让其他代理根据需要提取蜂蜜。但是,如果有一天您的计算机突然说 “对不起,Dave,我恐怕做不了”,那么您真的要非常担心了。

参考资料

学习

获得产品和技术

  • Festival:从 Festival Speech Synthesis System 的创建者爱丁堡大学了解更多有关的信息。
  • MPlayer:了解更多有关 MPlayer 播放器的信息。
  • PHP Builder 的代码示例:PHP 代码示例的资源
  • IBM 产品评估版:立即下载并开始使用来自 DB2®、Lotus®、Rational®、Tivoli® 和 WebSphere® 的应用程序开发工具和中间件产品。

条评论

developerWorks: 登录

标有星(*)号的字段是必填字段。


需要一个 IBM ID?
忘记 IBM ID?


忘记密码?
更改您的密码

单击提交则表示您同意developerWorks 的条款和条件。 查看条款和条件

 


在您首次登录 developerWorks 时,会为您创建一份个人概要。您的个人概要中的信息(您的姓名、国家/地区,以及公司名称)是公开显示的,而且会随着您发布的任何内容一起显示,除非您选择隐藏您的公司名称。您可以随时更新您的 IBM 帐户。

所有提交的信息确保安全。

选择您的昵称



当您初次登录到 developerWorks 时,将会为您创建一份概要信息,您需要指定一个昵称。您的昵称将和您在 developerWorks 发布的内容显示在一起。

昵称长度在 3 至 31 个字符之间。 您的昵称在 developerWorks 社区中必须是唯一的,并且出于隐私保护的原因,不能是您的电子邮件地址。

标有星(*)号的字段是必填字段。

(昵称长度在 3 至 31 个字符之间)

单击提交则表示您同意developerWorks 的条款和条件。 查看条款和条件.

 


所有提交的信息确保安全。


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=10
Zone=Web development
ArticleID=457291
ArticleTitle=使用 PHP 代理向音频系统提供信息
publish-date=12172009