アスタリスク+ UniMRCP + VoiceNavigator。 アスタリスクの音声合成と認識。 パート3

パート1

パート2

パート4



前回の記事では 、合成タグと認識文法の構築について説明しました。

このパートでは、アスタリスクでの特定の音声アプリケーションの構築を紹介したいと思います。 「Horns and Hooves」ストアの音声メニューが表示されないようにするために、Habréで以前に実装した例を簡単に見つけて、合成と認識を使用する利点を明確に示すことにしました。



この投稿は、かつて非常に活発に議論されていたHabréで見つかりました。 著者は、ギズメテオのウェブサイトから事前に記録された多くのファイルとxml-informersを使用して、電話で天気予報を聞くことを提案しています。 このアプリケーションを改善し、IVRを構築して動的な情報を取得するときに、合成と認識がいかに簡単になるかを示したいと思います。



アプリケーションは、天気を知りたい都市を要求し、次に時間(今日の午後、明日の夜など)を要求し、必要な情報を提供します。



天気xmlファイル



Gismeteoファイルは次のとおりです。

 <?xml version="1.0" encoding="utf-8"?> <MMWEATHER> <REPORT type="frc3"> <TOWN index="10381" sname="%C1%E5%F0%EB%E8%ED" latitude="52" longitude="13"> <FORECAST day="02" month="08" year="2011" hour="20" tod="3" predict="0" weekday="3"> <PHENOMENA cloudiness="0" precipitation="10" rpower="0" spower="0"/> <PRESSURE max="760" min="758"/> <TEMPERATURE max="21" min="19"/> <WIND min="2" max="4" direction="1"/> <RELWET max="74" min="72"/> <HEAT min="19" max="21"/> </FORECAST> <FORECAST day="03" month="08" year="2011" hour="02" tod="0" predict="0" weekday="4"> <PHENOMENA cloudiness="0" precipitation="10" rpower="0" spower="0"/> <PRESSURE max="761" min="759"/> <TEMPERATURE max="15" min="13"/> <WIND min="1" max="3" direction="1"/> <RELWET max="83" min="81"/> <HEAT min="13" max="15"/> </FORECAST> <FORECAST day="03" month="08" year="2011" hour="08" tod="1" predict="0" weekday="4"> <PHENOMENA cloudiness="0" precipitation="10" rpower="0" spower="0"/> <PRESSURE max="761" min="759"/> <TEMPERATURE max="18" min="16"/> <WIND min="2" max="4" direction="2"/> <RELWET max="80" min="78"/> <HEAT min="16" max="18"/> </FORECAST> <FORECAST day="03" month="08" year="2011" hour="14" tod="2" predict="0" weekday="4"> <PHENOMENA cloudiness="1" precipitation="10" rpower="0" spower="0"/> <PRESSURE max="760" min="758"/> <TEMPERATURE max="26" min="24"/> <WIND min="2" max="4" direction="2"/> <RELWET max="56" min="54"/> <HEAT min="22" max="24"/> </FORECAST> </TOWN> </REPORT> </MMWEATHER>
      
      





毎日は、夜、朝、昼、夜の4回に分けられます。

ファイルには、更新された時点から常に4つの期間の天気が表示されます。 ファイルの更新は、1日4回発生します:2.30、8.30、14.30、20.30 MSK。 上記のファイルでは、8月2日の夜、8月3日の夜、朝、および日の天気。 ファイルを処理するときとアプリケーションでこのロジックを考慮します。

次のパラメーターを使用します。

平日 -曜日(1-日曜日、2-月曜日など)

tod-予測が行われる時刻(0-夜1-朝、2-日、3-夜)

曇り -グラデーションによる曇り(0-明確、1- 部分的に曇り、2-曇り、3-曇り)

降水 - 降水のタイプ(4-雨、5-雨、6.7-雪、8-雷雨、9-データなし、10-降水なし)

TEMPERATURE-気温(摂氏)



XMLファイルの自動受信



常に実際のxmlファイルを使用できるようにするには、スクリプトを作成してcronに追加します。 スクリプトは、必要な都市のファイルを取得し、指定されたフォルダーに配置します。 それから、都市の名前を取ります。

 #!/bin/bash DIR=/var/www/html/gismeteo/xml /usr/bin/wget 'http://informer.gismeteo.ru/xml/27612_1.xml' -O $DIR/27612_1.xml # /usr/bin/wget 'http://informer.gismeteo.ru/xml/26063_1.xml' -O $DIR/26063_1.xml #- /usr/bin/wget 'http://informer.gismeteo.ru/xml/22892_1.xml' -O $DIR/22892_1.xml # /usr/bin/wget 'http://informer.gismeteo.ru/xml/29634_1.xml' -O $DIR/29634_1.xml # /usr/bin/wget 'http://informer.gismeteo.ru/xml/31960_1.xml' -O $DIR/31960_1.xml # /usr/bin/wget 'http://informer.gismeteo.ru/xml/26850_1.xml' -O $DIR/26850_1.xml # /usr/bin/wget 'http://informer.gismeteo.ru/xml/33345_1.xml' -O $DIR/33345_1.xml # /usr/bin/wget 'http://informer.gismeteo.ru/xml/36870_1.xml' -O $DIR/36870_1.xml # /usr/bin/wget 'http://informer.gismeteo.ru/xml/76680_1.xml' -O $DIR/76680_1.xml # /usr/bin/wget 'http://informer.gismeteo.ru/xml/2974_1.xml' -O $DIR/2974_1.xml # /usr/bin/wget 'http://informer.gismeteo.ru/xml/10381_1.xml' -O $DIR/10381_1.xml # /usr/bin/wget 'http://informer.gismeteo.ru/xml/48454_1.xml' -O $DIR/48454_1.xml #
      
      





文法



文法を構築することから始めましょう。 3つのファイルが必要です。

towns.xml-天気を知りたい都市のファイル。 Gismeteoサーバーからのxmlファイルの名前は、セマンティックタグとして使用されます。 天気に興味があるかもしれないすべての都市を追加できます))

 <?xml version="1.0" encoding="utf-8"?> <grammar xml:lang="ru-RU" root="speak" mode="voice" version="1.0" xmlns="http://www.w3.org/2001/06/grammar" tag-format="semantics/1.0-literals"> <rule id="speak" scope="public"> <one-of> <item>!SYLLABLES</item> <item>!SYLLABLES <ruleref uri="#town"/> !SYLLABLES</item> </one-of> </rule> <rule id="town"> <one-of> <item><tag>27612_1.xml</tag></item> <item><tag>27612_1.xml</tag></item> <item>-<tag>26063_1.xml</tag></item> <item><tag>26063_1.xml</tag></item> <item><tag>26063_1.xml</tag></item> <item><tag>22892_1.xml</tag></item> <item><tag>29634_1.xml</tag></item> <item><tag>31960_1.xml</tag></item> <item><tag>26850_1.xml</tag></item> <item><tag>33345_1.xml</tag></item> <item><tag>36870_1.xml</tag></item> <item>-<tag>36870_1.xml</tag></item> <item><tag>36870_1.xml</tag></item> <item><tag>76680_1.xml</tag></item> <item><tag>2974_1.xml</tag></item> <item><tag>10381_1.xml</tag></item> <item><tag>48454_1.xml</tag></item> </one-of> </rule> </grammar>
      
      





DTMFに対する認識の主な明らかな利点は、サブスクライバーが3(販売部門、テクニカルサポートまたは経理)から1つの項目ではなく、たとえば300(都市または通りのリスト)から1つを選択する必要がある場合に複数選択できることです。 この例では、簡単にするためにいくつかの都市を作成しましたが、何百もの選択を妨げるものは何もありません。



time.xml-時刻を選択するためのオプションを含むファイル。 セマンティックタグでは、最初の数字は0今日、1明日です。 2番目の数字は、weather xmlファイルのtodパラメーターに似た時刻です。



 <?xml version="1.0" encoding="utf-8"?> <grammar xml:lang="ru-RU" root="speak" mode="voice" version="1.0" xmlns="http://www.w3.org/2001/06/grammar" tag-format="semantics/1.0-literals"> <rule id="speak" scope="public"> <one-of> <item>!SYLLABLES</item> <item>!SYLLABLES <ruleref uri="#time"/> !SYLLABLES</item> </one-of> </rule> <rule id="time"> <one-of> <item> <tag>00</tag></item> <item> <tag>01</tag></item> <item> <tag>02</tag></item> <item> <tag>03</tag></item> <item> <tag>10</tag></item> <item> <tag>11</tag></item> <item> <tag>12</tag></item> <item> <tag>13</tag></item> </one-of> </rule> </grammar>
      
      





end_next.xmlは3つの項目のみで構成される単純な文法で、最後にアプリケーションでの作業を続行したり、アプリケーションを完了するために使用されます。

 <?xml version="1.0" encoding="utf-8"?> <grammar xml:lang="ru-RU" root="speak" mode="voice" version="1.0" xmlns="http://www.w3.org/2001/06/grammar" tag-format="semantics/1.0-literals"> <rule id="speak" scope="public"> <one-of> <item>!SYLLABLES</item> <item>!SYLLABLES <ruleref uri="#check"/> !SYLLABLES</item> </one-of> </rule> <rule id="check"> <one-of> <item> <tag>city_choice</tag></item> <item> <tag>time_choice</tag></item> <item><tag>bye</tag></item> </one-of> </rule> </grammar>
      
      





録音済みの音声ファイル



合成は、動的に生成されたフレーズやユーザーの選択に対する応答に使用すると有益です。 静的フレーズの場合は、事前に記述する方がより正確です。 このアプローチには、主に2つの利点があります。

-合成リソースの負荷を軽減

-事前に記録されたファイルは、機能パラメーターMRCPRecogによって転送でき、割り込みモード(ファイルを中断して音声の開始時に認識を開始する機能)を使用できます。



ファイルはMRCPSynthおよびモニター機能を使用して記録できます。 このアプリケーションでは、次のファイルを使用します。

city_choice.wav 天気に興味がある都市の名前を発声してください。
no_input.wav 声を上げてください。
error_city.wav 申し訳ありませんが、理解できません。 都市名を繰り返します。
error.wav 分かりません。 繰り返してください。
time_choice.wav 興味のある期間の天気を言います。 たとえば、「明日の午後」。
end_next.wav 別の都市の天気を調べるには、「都市を選択」と言います。 その日の別の時間の天気を調べるには、「別の時間」と言います。 通話を終了するには、「終了」と言います。
bye.wav お電話ありがとうございます。 さようなら!
not_found_time.wav 選択した時間の天気データはありません。 別の時間を選択してください。


XML天気予報ファイルを操作するためのスクリプト



AGIスクリプトgismeteo.agiを作成します。これは、ダイヤルプランから天気と認識された時刻を含むxmlファイルの名前を受け取り、このファイルで天気情報を検索します。

アスタリスク:: AGIは、アスタリスク、XMLとの対話に使用されます:: XMLの解析が簡単

 #!/usr/bin/perl use XML::Simple; use Asterisk::AGI; use Time::localtime; use strict; $|=1; my $AGI = new Asterisk::AGI; my %var = $AGI->ReadParse(); #   my $xml_file=$AGI->get_variable("xml_file"); my $xml_source="/var/www/html/gismeteo/xml/$xml_file"; if ($ARGV[0] eq "city"){ #   = city,        #     xml- open (LIST, "/var/www/html/gismeteo/agi-bin/get_xml.sh") || die "   get_xml.sh"; my $city=""; while (<LIST>) { if(m/$xml_file/) { ($city)=/#(.*)/; last; } } close(LIST); $AGI->set_variable('city' => $city); exit; } elsif ($ARGV[0] eq "time") { #          #     my @cl_time=$AGI->get_variable("RECOG_INT0")=~/(.)/g; #    my $present_time=localtime(time()); my $present_weekday=$present_time->wday; #       my @day=('',''); my @tod=('','','',''); my @cloudiness=('','','',''); my %precipitation=('4'=>'', '5'=>'', '6'=>'', '7'=>'', '8'=>'', '9'=>'', '10'=>' '); # XML- my $xmlWeather = new XML::Simple(keeproot => 1,searchpath => ".", forcearray => 1, suppressempty => ''); my $xmlTown = $xmlWeather->XMLin($xml_source); my $xmlData = $xmlTown->{MMWEATHER}[ 0]->{REPORT}[ 0]->{TOWN}[ 0]->{FORECAST}; my $i=0; #      ,    for ($i=0; $i<4; $i++) { print "$xmlData->[$i]->{weekday}, $present_weekday%7+1+$cl_time[0], $xmlData->[$i]->{tod}\n"; if ($xmlData->[$i]->{weekday}==($present_weekday%7+1+$cl_time[0]) && $xmlData->[$i]->{tod}==$cl_time[1]) { $AGI->set_variable('speech_text' => "$day[$cl_time[0]] $tod[$xmlData->[$i]->{tod}]    $xmlData->[$i]->{TEMPERATURE}[ 0]->{min}  $xmlData->[$i]->{TEMPERATURE}[ 0]->{max} . $cloudiness[$xmlData->[$i]->{PHENOMENA}[ 0]->{cloudiness}]. $precipitation{$xmlData->[$i]->{PHENOMENA}[ 0]->{precipitation}}."); $AGI->set_priority('found'); exit; } } #    ,     $AGI->set_priority('not_found'); }
      
      





extensions.confのアプリケーション



別のファイルにアプリケーションを記述し、includeで/etc/asterisk/extensions.confに含めることを好みます。

gismeteo.confファイルを作成します。



認識マクロ


まず、認識に直接対処するマクロを作成します。

 [macro-recog-gismeteo] ;ARG1 -  , ARG2 -  , ARG3 -     , ARG4 -   exten => s,1,MRCPRecog(${GRAMMARS_PATH}/${ARG1},ct=0.20&b=1&f=${SND_PATH}/${ARG2}) exten => s,n(recog),SET(RECOG_HYP_NUM=0) exten => s,n,SET(RECOG_UTR0=) ;   NLSML- exten => s,n,AGI(NLSML.agi,${QUOTE(${RECOG_RESULT})}) ;  no-input exten => s,n,GotoIf(${REGEX("Completion-Cause: 002" ${RECOG_RESULT})}?$[${PRIORITY}+1]:check_error) exten => s,n,MRCPRecog(${GRAMMARS_PATH}/${ARG1},ct=0.20&b=1&f=${SND_PATH}/no_input) exten => s,n,Goto(recog) ;    exten => s,n(check_error),GotoIf($["${RECOG_UTR0}" = ""]?$[${PRIORITY}+1]:ok) exten => s,n,MRCPRecog(${GRAMMARS_PATH}/${ARG1},ct=0.20&b=1&f=${SND_PATH}/${ARG3}) exten => s,n,Goto(recog) ;    exten => s,n(ok),Goto(${MACRO_CONTEXT},${MACRO_EXTEN},${ARG4})
      
      





マクロは、パラメータとして、文法ファイル、サウンドメッセージファイル、認識エラーメッセージファイル、および認識が成功したときに渡す必要のある優先度を受け取ります。

認識エラー(エラーを報告し、繰り返すように要求する)および入力なし(チャネルで音声が検出されなかった場合、より大きな声で話すように要求する)を処理できる。

ConfidenceTreshhold =20。これは、低い認識信頼度でオプションを除外するのに十分なはずです。

NLSML.agiパーサーは入力変数$ {RECOG_RESULT}を受け取り、作業の結果として変数をダイヤルプランに返します。

$ {RECOG_UTR0} -文法から認識されたフレーズ、

$ {RECOG_INT0} -セマンティックタグ、

$ {RECOG_CNF0} -信頼レベル、

$ {RECOG_SNR0} -信号対雑音比。



ギスメテオアプリ


 [gismeteo] exten => 6853,1,Goto(gismeteo,1) ; ,      exten => gismeteo,1,Answer() ;  exten => gismeteo,n,Set(SND_PATH=/var/www/html/gismeteo/sounds) exten => gismeteo,n,Set(GRAMMARS_PATH=http://192.168.2.103/gismeteo/grammars) exten => gismeteo,n,Set(AGI_PATH=/var/www/html/gismeteo/agi-bin) ;  exten => gismeteo,n(city_choice),Macro(recog-gismeteo,towns.xml,city_choice,error_city,$[${PRIORITY}+1]) exten => gismeteo,n,SET(xml_file=${RECOG_INT0}) ;   AGI-     exten => gismeteo,n,AGI(${AGI_PATH}/gismeteo.agi,city) exten => gismeteo,n,MRCPSynth(<?xml version=\"1.0\"?><speak version=\"1.0\" xml:lang=\"ru-ru\" xmlns=\"http://www.w3.org/2001/10/synthesis\"><voice name=\"8000\">   ${city}.</voice></speak>) ;  exten => gismeteo,n(time_choice),Macro(recog-gismeteo,time.xml,time_choice,error,agi_check) ;   AGI-    exten => gismeteo,n(agi_check),AGI(${AGI_PATH}/gismeteo.agi,time) ;         exten => gismeteo,n(not_found),Macro(recog-gismeteo,time.xml,not_found_time,error,agi_check) ;     exten => gismeteo,n(found),MRCPSynth(<?xml version=\"1.0\"?><speak version=\"1.0\" xml:lang=\"ru-ru\" xmlns=\"http://www.w3.org/2001/10/synthesis\"><voice name=\"8000\">${speech_text}</voice></speak>) ;   exten => gismeteo,n,Macro(recog-gismeteo,end_next.xml,end_next,error,$[${PRIORITY}+1]) exten => gismeteo,n,Goto(${RECOG_INT0}) exten => gismeteo,n(bye),Playback(${SND_PATH}/bye) exten => gismeteo,n,Hangup()
      
      





アプリケーションロジック


システム :天気が興味のある都市の名前を言います。

加入者 :モスクワ

都市が認識されない、または静かに発言されない場合、システムは対応する音声メッセージを発行します。 認識に成功すると、システムは結果を報告します。

システム :モスクワの都市を選択しました。

システム :興味のある期間の天気を言います。 たとえば、「明日の午後」。

加入者 :明日の朝。

時刻が認識されない、またはあまりにも静かに言われない場合、システムは適切な音声メッセージを発行します。 認識に成功すると、gismeteo.agiが起動され、weather xmlファイルで必要な情報が検索されます。 ファイルに特定の時刻に関する情報がない場合、たとえば、加入者が「今朝」と今夜言うと、「選択された時間の天気データがありません。 別の時間を選択してください。」情報が見つかった場合、システムは結果を報告します。

システム :明日の朝、気温は16〜18度です。 なるほど。 降水なし。

システム :別の都市の天気を言うには、「都市を選択」と言います。 その日の別の時間の天気を調べるには、「別の時間」と言います。 通話を終了するには、「終了」と言います。

「都市を選択」というフレーズは、アプリケーションの最初に戻ります。 「別の時間」というフレーズを使用すると、選択した都市の天気を別の時間に見つけることができます。

サブスクライバー :完了。

システム :お電話ありがとうございます。 さようなら!



解決策は非常にシンプルでエレガントであることが判明したようです。 元の記事に記載されているソリューションと比較できます;)

さらに、NLSMLパーサーの使用、認識をマクロにすることによるダイヤルプランの簡素化、割り込みのための事前に記述されたフレーズの使用など、アスタリスクでの作業の多くの機能と「トリック」を示しました。 同時に、彼は外部データを操作するためのAGIスクリプトの作成に触れました。 同様に、xml処理は、データベースまたはその他のソースからアクセスおよび取得できます。



次の記事では、合成と認識を使用するという文脈で、プラットフォームとしてのアスタリスクのいくつかの問題と制限について説明します。



質問、コメント、提案を待っています。



PS:ここでアプリケーションを確認できます(812)3258848、ext。 6853

PPS:友人たち、電話をかけたい人があまりいないことを望み、企業電話をかけないことを望みます))



All Articles