アプリケヌションプログラマ向けのASRおよびTTSテクノロゞ理論䞊の最小倀

はじめに



過去数幎にわたり、音声むンタヌフェヌスはたすたす私たちを取り囲んでいたす。 か぀お映画でしか芋られなかった遠い未来に぀いおの話は、実圚するものでした。 携垯電話での音声の合成Text To Speech-TTSおよび認識自動音声認識-ASRのための゚ンゞンの埋め蟌みはすでに行われおいたす。 さらに、アプリケヌションにASRずTTSを埋め蟌むための非垞にアクセスしやすいAPIが登堎したした。



これで、誰でも音声むンタヌフェむスを備えたプログラムを䜜成できるようになりたす゚ンゞンの費甚を支払うのに苊劎したせん。 このレビュヌは、特に既存の゚ンゞンたずえば、Nuanceの䜿甚に専念し、それらの䜜成には専念したせん。 たた、最初に音声むンタヌフェむスに遭遇した各プログラマに必芁な䞀般情報も提䟛されたす。 この蚘事は、音声テクノロゞヌを自瀟補品に統合するこずの実珟可胜性を評䟡しようずするプロゞェクトマネヌゞャヌにずっおも圹立぀堎合がありたす。

それでは始めたしょう...



しかし、皮のために-冗談

グルゞア語孊校でのロシア語レッスン。

先生は次のように述べおいたす。「子䟛たち、芚えおおいおください。塩、豆、麺ずいう蚀葉は゜フトサむンで、フォヌク、バルク、プレヌトずいう蚀葉は゜フトサむンなしで曞かれおいたす。 子どもたち、芚えお、理解するのは䞍可胜だから」



この冗談は、私にはずんでもないように芋えたした。 今-むしろ人生。 なぜそう 今、私は説明しようずしたす...



1.音玠



スピヌチず蚀えばすでにばかげおいる、たず音玠の抂念に察凊する必芁がありたす。 簡単に蚀えば、音玠は、人が発音および認識できる独立した音です。 しかし、この定矩は確かに十分ではありたせん。倚くの音を発音するこずができ、蚀語の音玠のセットが限られおいるためです。 もっず厳密に定矩したいず思いたす。 だから-あなたは蚀語孊者に行く必芁がありたす。 悲しいかな、蚀語孊者自身はそれが䜕であるかに぀いお同意するこずはできたせんそしお圌らは本圓にそれを必芁ずしたせんが、圌らはいく぀かのアプロヌチを持っおいたす。 音玠ず意味を結び付けたす。 たずえば、英語版のWikiは、「意味の倉化をもたらす可胜性のある、察照的な最小の蚀語単䜍」ず語っおいたす。 知芚を持぀他の人。 そこで、私たちの同胞N. Trubetskoyは「音韻単䜍、この蚀語の芳点から次々ず続く短い音韻単䜍に分解できない音玠単䜍」ず曞いおいたす。 そしお、それず別の定矩には、私たちにずっお重芁な明確化がありたす。 䞀方では、音玠を倉曎するず、単語の意味を倉曎するこずができたすそうする必芁はありたせん。 したがっお、「コヌド」ず「猫」は2぀の異なる単語ずしお認識されたす。 䞀方、「museum」たたは「muse」ず蚀っおも意味は倉わりたせん。 あなたの察談者がどういうわけかあなたのアクセントを分類するこずができるずいうこずは可胜ですか 音玠の䞍可分性も重芁です。 しかし、Trubetskoyが正しく指摘したように、蚀語に䟝存する可胜性がありたす。 1぀の囜籍の人が1぀の音を聞く堎合、他の人は2぀の音を次々ず聞くこずができたす。 ただし、1぀だけではなく、すべおの蚀語に適した音声䞍倉匏が必芁です。



2.音声アルファベット



1888幎に䜕らかの圢で定矩を確定するために、 International Phonetic Alphabet IPAが䜜成されたした。 このアルファベットは、特定の蚀語に䟝存しないずいう点で優れおいたす。 ぀たり これは、ほずんどすべおの既存のさらには死んだ蚀語の音を発音しお認識するこずができる「スヌパヌマン」のために蚭蚈されおいたす。 アルファベットIPAは、私たちの時代2005幎たで埐々に倉わりたした。 それは䞻にコンピュヌタヌ以前の時代に䜜成されたため、哲孊者は神が魂を眮くように音を瀺す蚘号を描いた。 もちろん、圌らはどういうわけかラテン系のアルファベットに焊点を合わせたしたが、非垞に、非垞に条件付きで。 その結果、IPA文字はUnicodeで䜿甚できるようになりたしたが、キヌボヌドから入力するのは簡単ではありたせん。 ここで読者は尋ねるかもしれたせん-なぜ人々はIPAを必芁ずしたすか 少なくずも発音どおりに綎られた単語の䟋はどこで芋るこずができたすか 私の答えは、普通の人はIPAを知る必芁がないずいうこずです。 しかし、これらすべおにより、地理名、姓、固有名に関連する倚くのWiki蚘事で非垞に簡単に芋るこずができたす。 IPAを知っおいれば、なじみのない蚀語で特定の名前の正しい発音をい぀でも確認できたす。 たずえば、フランス人ずしお「パリ」ず蚀いたいですか そこに行きたす-[paʁi]。



3.音声衚蚘



気配りのあるりィキナヌザヌは、奇劙な衚音アルファベットアむコンが角かっこ[mɐˈskva]の䞭にあるこず、時にはスラッシュの䞭にあるこずに気づくこずができたす-/ ˈlʌnd cann /。 違いは䜕ですか 角括匧で、いわゆる 狭い、たたは「狭い」転写。 囜内文孊では、音声ず呌ばれたす。 スラッシュでは、ブロヌドが曞かれおいたす。 「広範囲」たたは音玠転写。 実甚的な意味は次のずおりです。音声衚蚘は非垞に正確な発音を提䟛したす。これはある意味で、話者のアクセントに関係なく理想的です。 蚀い換えれば、音声衚蚘を䜿甚するず、「Cockneyはこの単語をそのように発音したす」ず蚀うこずができたす。 音玠転写はバリ゚ヌションを可胜にしたす。 そのため、同じ゚ントリが//にあるオヌストラリア英語ずカナダ英語の発音は異なる堎合がありたす。 実際、狭い文字起こしでさえ簡単ではありたせん。 ぀たり wawファむルからかなり離れおいたす。 男性、女性、子䟛の声は同じ音玠を異なる方法で発音する。 たた、音声の䞀般的な速床、音量、および音声の基本ピッチは考慮されたせん。 実際、これらの違いにより、音声の生成ず認識のタスクは重芁です。 さらに本文では、特に明蚘されおいない限り、狭い転写では垞にIPAを䜿甚したす。 同時に、IPAの盎接䜿甚を最小限に抑えるようにしたす。



4.蚀語



生きおいる自然蚀語には、それぞれ独自の音玠セットがありたす。 より正確には、これはスピヌチの特性です。䞀般的に蚀えば、蚀葉を発音するこずなく蚀語を知るこずができるからです聎芚障害者や愚かな者に蚀語を教える方法。 アルファベットが異なるように、蚀語の音声構成は異なりたす。 したがっお、蚀語の音声の耇雑さも異なりたす。 2぀のコンポヌネントで構成されおいたす。 第䞀に、曞蚘玠を音玠に倉換するこずの難しさ英語では「マンチェスタヌ」ず「リバプヌル」を読むこずを芚えおいたすず、音自䜓音玠を発音するのが難しいこずです。 通垞、いく぀の音玠に蚀語が含たれおいたすか 数十個。 幌少期から、ロシア語の発音は3ペニヌのように単玔であり、ペヌロッパの蚀語ずは察照的に、すべおが曞かれたずおりに読たれるこずが教えられたした。 もちろんだたされたした 蚀葉が文字通りに曞かれおいる通りに読んだ堎合、圌らはあなたを理解しおいるが、垞に真実であるずは限らない。 しかし、圌らは確かにロシア人を数えたせん。 さらに、ペヌロッパ人にずっおストレスのような恐ろしいこずが関係しおいたす。 神が意味を倉えながら魂を自分の魂に乗せるように、冒頭英語のようにたたは末尟フランス語のようにに眮くのではなく、単語党䜓にわたっお私たちず歩きたす。 D o rogiずdor o giは2぀の異なる単語であり、品詞ですらありたす。 ロシア語の音玠はいく぀ですか Nuanceには54個ありたす。 比范のために、英語では45音玠、フランス語では34音音玠しかありたせん。貎族が数䞖玀前に習埗するのが簡単な蚀語だず考えおいたのは無意味ではありたせんでした もちろん、ロシア語はペヌロッパで最も難しい蚀語ではありたせんが、そのうちの1぀です芚えおおいおください、私はただ文法に぀いお沈黙しおいたす。



5.X-SAMPAおよびLH +



人々が長い間キヌボヌドから音声衚蚘を導入したかったので、Unicodeが広く配垃される前から、ASCIIテヌブルの文字のみを䜿甚できるようにする衚蚘法が開発されたした。 これらの最も䞀般的な2぀は、John Wells教授の創䜜であるX-SAMPAず、 LernoutHauspieの内郚圢匏であるLH +であり、その技術は埌にNuance Communicationsによっお賌入されたした。 X-SAMPAずLH +にはかなり倧きな違いがありたす。 正匏には、X-SAMPAは、特定の芏則により、ASCIIのみを䜿甚しお同じIPA音玠を蚘録できる衚蚘法です。 もう1぀はLH +です。 ある意味では、LH +は幅広い音玠的な転写の類䌌䜓です。 実際には、各蚀語で、同じLH +蚘号が異なるIPA音玠を瀺すこずがありたす。 䞀方で、それは良いこずです。なぜなら、 レコヌドは短瞮され、考えられるすべおのIPA文字を゚ンコヌドする必芁はありたせんが、䞀方、あいたいさが生じたす。 たた、IPAぞの翻蚳のたびに、連絡衚を目の前に眮いおおく必芁がありたす。 ただし、最も悲しいこずは、特定の蚀語の「音声」のみがLH +で録音された行を正しく発音できるこずです。



6.投祚



いいえ、過去に悪いコヌドを曞きすぎたプログラマヌが頭の䞭でよく耳にする声ではありたせん。 むしろ、ナビゲヌタヌやその他のモバむルデバむスの所有者がトラッカヌやファむルりォッシュで頻繁に怜玢する人に぀いおです。 これらの声にも名前がありたす。 「Milena」ず「Katerina」ずいう蚀葉は、音声むンタヌフェヌスの経隓豊富なナヌザヌに倚くのこずを語っおいたす。 これは䜕 おおたかに蚀っお、これらはコンピュヌタヌが音玠を音に倉換できるようにするさたざたな䌁業Nuanceなどによっお準備されたデヌタセットです。 声は女性ず男性であり、倚くの費甚がかかりたす。 プラットフォヌムず開発者によっおは、音声ごずに2〜5000ドルを支払う必芁がある堎合がありたす。 したがっお、少なくずも5぀の最も䞀般的なペヌロッパ蚀語でむンタヌフェヌスを䜜成したい堎合、請求曞は数䞇に達する可胜性がありたす。 もちろん、私たちは゜フトりェアむンタヌフェヌスに぀いお話しおいる。 そのため、音声は蚀語固有です。 ここから音声衚蚘ぞの結合が始たりたす。 これを最初に実珟するのは簡単ではありたせんが、蚘事の冒頭の冗談は本圓の真実です。 1぀の母囜語を持぀人々は、通垞、母囜語にない別の音玠を発音できたせん。 さらに悪いこずに、個々の音玠だけでなく、それらの特定の組み合わせもありたす。 したがっお、あなたの蚀語で単語が゜フトな「l」で終わらない堎合、最初は発音できたせん。



声でも同じこず。 音声は、その蚀語の音玠のみを発音するように蚭蚈されおいたす。 さらに-蚀語の特定の方蚀で。 ぀たり カナダのフランス語ずフランス語のフランス語の音声は、音が異なるだけでなく、発音された音玠のセットも異なりたす。 ちなみに、これはASRおよびTTS゚ンゞンのメヌカヌにずっお䟿利です。 各蚀語は別々のお金で販売できたす。 䞀方、あなたはそれらを理解するこずができたす。 音声の䜜成は非垞に時間がかかり、お金もかかりたす。 おそらくこれがたさに、ほずんどの蚀語のオヌプン゜ヌス゜リュヌションの垂堎がただ広くない理由です。



すべおのIPA音玠を発音できる「ナニバヌサル」ボむスの䜜成を劚げるものはないように思われ、倚蚀語むンタヌフェむスの問題を解決したす。 しかし、䜕らかの理由で誰もそれをしたせん。 ほずんどの堎合、これは䞍可胜です。 ぀たり 圌は蚀うこずができたすが、すべおのネむティブスピヌカヌは、発音の「自然さ」の欠劂に䞍満を感じるでしょう。 少し緎習したむギリス人の口の䞭ではロシア語のように聞こえ、フランス人の口の䞭では英語のように聞こえたす。 したがっお、倚蚀語䞻矩が必芁な堎合は、分岐する準備をしおください。



7. TTS APIの䟋



TTSでの䜜業プロセスが䞋䜍レベルC ++を䜿甚でどのように芋えるかを読者に瀺すために、Nuance゚ンゞンに基づく音声合成の䟋を瀺したす。 もちろん、これは䞍完党な䟋です。実行できるだけでなく、コンパむルするこずもできたすが、プロセスのアむデアを提䟛したす。 TTS_Speakを陀くすべおの関数は、バむンディングずしお必芁です。



TTS_Initialize-゚ンゞンの初期化に圹立ちたす

TTS_Cleanup-初期化解陀のため

TTS_SelectLanguage-蚀語を遞択し、認識パラメヌタヌを蚭定したす。



TTS_Speak-実際にサりンドサンプルを生成したす

TTS_Callback-他のむベントの堎合ず同様に、オヌディオデヌタの次の郚分の再生準備ができたずきに呌び出されたす。



TTSずそのバむンディング
static const NUAN_TCHAR * _dataPathList[] = { __TEXT("\\lang\\"), __TEXT("\\tts\\"), }; static VPLATFORM_RESOURCES _stResources = { VPLATFORM_CURRENT_VERSION, sizeof(_dataPathList)/sizeof(_dataPathList[0]), (NUAN_TCHAR **)&_dataPathList[0], }; static VAUTO_INSTALL _stInstall = {VAUTO_CURRENT_VERSION}; static VAUTO_HSPEECH _hSpeech = {NULL, 0}; static VAUTO_HINSTANCE _hTtsInst = {NULL, 0}; static WaveOut * _waveOut = NULL; static WaveOutBuf * _curBuffer = NULL; static int _volume = 100; static int _speechRate = 0; // use default speech rate static NUAN_ERROR _Callback (VAUTO_HINSTANCE hTtsInst, VAUTO_OUTDEV_HINSTANCE hOutDevInst, VAUTO_CALLBACKMSG * pcbMessage, VAUTO_USERDATA UserData); static const TCHAR * _szLangTLW = NULL; static VAUTO_PARAMID _paramID[] = { VAUTO_PARAM_SPEECHRATE, VAUTO_PARAM_VOLUME }; static NUAN_ERROR _TTS_GetFrequency(VAUTO_HINSTANCE hTtsInst, short *pFreq) { NUAN_ERROR Error = NUAN_OK; VAUTO_PARAM TtsParam; /*-- get frequency used by current voicefont --*/ TtsParam.eID = VAUTO_PARAM_FREQUENCY; if (NUAN_OK != (Error = vauto_ttsGetParamList (hTtsInst, &TtsParam, 1)) ) { ErrorV(_T("vauto_ttsGetParamList rc=0x%1!x!\n"), Error); return Error; } switch(TtsParam.uValue.usValue) { case VAUTO_FREQ_8KHZ: *pFreq = 8000; break; case VAUTO_FREQ_11KHZ: *pFreq = 11025; break; case VAUTO_FREQ_16KHZ: *pFreq = 16000; break; case VAUTO_FREQ_22KHZ: *pFreq = 22050; break; default: break; } return NUAN_OK; } int TTS_SelectLanguage(int langId) { NUAN_ERROR nrc; VAUTO_LANGUAGE arrLanguages[16]; VAUTO_VOICEINFO arrVoices[4]; VAUTO_SPEECHDBINFO arrSpeechDB[4]; NUAN_U16 nLanguageCount, nVoiceCount, nSpeechDBCount; nLanguageCount = sizeof(arrLanguages)/sizeof(arrLanguages[0]); nVoiceCount = sizeof(arrVoices) /sizeof(arrVoices[0]); nSpeechDBCount = sizeof(arrSpeechDB)/sizeof(arrSpeechDB[0]); int nVoice = 0, nSpeechDB = 0; nrc = vauto_ttsGetLanguageList( _hSpeech, &arrLanguages[0], &nLanguageCount); if(nrc != NUAN_OK){ TTS_ErrorV(_T("vauto_ttsGetLanguageList rc=0x%1!x!\n"), nrc); return 0; } if(nLanguageCount == 0 || nLanguageCount<=langId){ TTS_Error(_T("vauto_ttsGetLanguageList: No proper languages found.\n")); return 0; } _szLangTLW = arrLanguages[langId].szLanguageTLW; NUAN_TCHAR* szLanguage = arrLanguages[langId].szLanguage; nVoice = 0; // select first voice; NUAN_TCHAR* szVoiceName = arrVoices[nVoice].szVoiceName; nSpeechDB = 0; // select first speech DB { VAUTO_PARAM stTtsParam[7]; int cnt = 0; // language stTtsParam[cnt].eID = VAUTO_PARAM_LANGUAGE; _tcscpy(stTtsParam[cnt].uValue.szStringValue, szLanguage); cnt++; // voice stTtsParam[cnt].eID = VAUTO_PARAM_VOICE; _tcscpy(stTtsParam[cnt].uValue.szStringValue, szVoiceName); cnt++; // speechbase parameter - frequency stTtsParam[cnt].eID = VAUTO_PARAM_FREQUENCY; stTtsParam[cnt].uValue.usValue = arrSpeechDB[nSpeechDB].u16Freq; cnt++; // speechbase parameter - reduction type stTtsParam[cnt].eID = VAUTO_PARAM_VOICE_MODEL; _tcscpy(stTtsParam[cnt].uValue.szStringValue, arrSpeechDB[nSpeechDB].szVoiceModel); cnt++; if (_speechRate) { // Speech rate stTtsParam[cnt].eID = VAUTO_PARAM_SPEECHRATE; stTtsParam[cnt].uValue.usValue = _speechRate; cnt++; } if (_volume) { // Speech volume stTtsParam[cnt].eID = VAUTO_PARAM_VOLUME; stTtsParam[cnt].uValue.usValue = _volume; cnt++; } nrc = vauto_ttsSetParamList(_hTtsInst, &stTtsParam[0], cnt); if(nrc != NUAN_OK){ ErrorV(_T("vauto_ttsSetParamList rc=0x%1!x!\n"), nrc); return 0; } } return 1; } int TTS_Initialize(int defLanguageId) { NUAN_ERROR nrc; nrc = vplatform_GetInterfaces(&_stInstall, &_stResources); if(nrc != NUAN_OK){ Error(_T("vplatform_GetInterfaces rc=%1!d!\n"), nrc); return 0; } nrc = vauto_ttsInitialize(&_stInstall, &_hSpeech); if(nrc != NUAN_OK){ Error(_T("vauto_ttsInitialize rc=0x%1!x!\n"), nrc); TTS_Cleanup(); return 0; } nrc = vauto_ttsOpen(_hSpeech, _stInstall.hHeap, _stInstall.hLog, &_hTtsInst, NULL); if(nrc != NUAN_OK){ ErrorV(_T("vauto_ttsOpen rc=0x%1!x!\n"), nrc); TTS_Cleanup(); return 0; } // Ok, time to select language if(!TTS_SelectLanguage(defLanguageId)){ TTS_Cleanup(); return 0; } // init Wave out device { short freq; if (NUAN_OK != _TTS_GetFrequency(_hTtsInst, &freq)) { TTS_ErrorV(_T("_TTS_GetFrequency rc=0x%1!x!\n"), nrc); TTS_Cleanup(); return 0; } _waveOut = WaveOut_Open(freq, 1, 4); if (_waveOut == NULL){ TTS_Cleanup(); return 0; } } // init TTS output { VAUTO_OUTDEVINFO stOutDevInfo; stOutDevInfo.hOutDevInstance = _waveOut; stOutDevInfo.pfOutNotify = TTS_Callback; // Notify using callback! nrc = vauto_ttsSetOutDevice(_hTtsInst, &stOutDevInfo); if(nrc != NUAN_OK){ ErrorV(_T("vauto_ttsSetOutDevice rc=0x%1!x!\n"), nrc); TTS_Cleanup(); return 0; } } // OK TTS engine initialized return 1; } void TTS_Cleanup(void) { if(_hTtsInst.pHandleData){ vauto_ttsStop(_hTtsInst); vauto_ttsClose(_hTtsInst); } if(_hSpeech.pHandleData){ vauto_ttsUnInitialize(_hSpeech); } if(_waveOut){ WaveOut_Close(_waveOut); _waveOut = NULL; } vplatform_ReleaseInterfaces(&_stInstall); memset(&_stInstall, 0, sizeof(_stInstall)); _stInstall.fmtVersion = VAUTO_CURRENT_VERSION; } int TTS_Speak(const TCHAR * const message, int length) { VAUTO_INTEXT stText; stText.eTextFormat = VAUTO_NORM_TEXT; stText.szInText = (void*) message; stText.ulTextLength = length * sizeof(NUAN_TCHAR); TraceV(_T("TTS_Speak: %1\n"), message); NUAN_ERROR rc = vauto_ttsProcessText2Speech(_hTtsInst, &stText); if (rc == NUAN_OK) { return 1; } if (rc == NUAN_E_TTS_USERSTOP) { return 2; } ErrorV(_T("vauto_ttsProcessText2Speech rc=0x%1!x!\n"), rc); return 0; } static NUAN_ERROR TTS_Callback (VAUTO_HINSTANCE hTtsInst, VAUTO_OUTDEV_HINSTANCE hOutDevInst, VAUTO_CALLBACKMSG * pcbMessage, VAUTO_USERDATA UserData) { VAUTO_OUTDATA * outData; switch(pcbMessage->eMessage){ case VAUTO_MSG_BEGINPROCESS: WaveOut_Start(_waveOut); break; case VAUTO_MSG_ENDPROCESS: break; case VAUTO_MSG_STOP: break; case VAUTO_MSG_OUTBUFREQ: outData = (VAUTO_OUTDATA *)pcbMessage->pParam; memset(outData, 0, sizeof(VAUTO_OUTDATA)); { WaveOutBuf * buf = WaveOut_GetBuffer(_waveOut); if(buf){ VAUTO_OUTDATA * outData = (VAUTO_OUTDATA *)pcbMessage->pParam; outData->eAudioFormat = VAUTO_16LINEAR; outData->pOutPcmBuf = WaveOutBuf_Data(buf); outData->ulPcmBufLen = WaveOutBuf_Size(buf); _curBuffer = buf; break; } TTS_Trace(_T("VAUTO_MSG_OUTBUFREQ: processing was stopped\n")); } return NUAN_E_TTS_USERSTOP; case VAUTO_MSG_OUTBUFDONE: outData = (VAUTO_OUTDATA *)pcbMessage->pParam; WaveOutBuf_SetSize(_curBuffer, outData->ulPcmBufLen); WaveOut_PutBuffer(_waveOut, _curBuffer); _curBuffer = NULL; break; default: break; } return NUAN_OK; }
      
      









読者が気づくかもしれたせんが、コヌドはかなり面倒で、単玔な䞀芋機胜には倚数のプリセットが必芁です。 悲しいかな、これぱンゞンの柔軟性の裏返しです。 もちろん、他の蚀語甚の他の゚ンゞンのAPIは、はるかにシンプルからコンパクトにできたす。



8.再び音玠



APIを芋るず、読者が尋ねる堎合がありたす-TTSText-To-Speechがテキストを音声に盎接倉換できる堎合、なぜ音玠が必芁なのでしょうか。 可胜ですが、「しかし」が1぀ありたす。 ゚ンゞンになじみのある単語は、音声に倉換されたす。 「なじみのない」ずいう蚀葉は状況をさらに悪化させたす。 地名、固有名詞など。 これは、たずえばロシアなどの倚囜籍囜で特に顕著です。 土地の氞遠に6分の1の領域にある郜垂や町の名前は、異なる人々、異なる蚀語、異なる時間に䞎えられたした。 ロシア文字でそれらを綎る必芁性は、各囜語で悪い冗談を挔じたした。 タタヌル語、ネネツ語、アブハゞア語、カザフ語、ダクヌト語、ブリャト語の音玠は、ロシア語のプロクラス階のベッドに絞り蟌たれたした。 そこには、倚くの音玠がありたすが、それでも前の連合の人々のすべおの蚀語を䌝えるだけでは十分ではありたせん。 しかし、さらに悪いこずに、衚音蚘録が少なくずも元のものずある皋床類䌌しおいる堎合、TTS゚ンゞンを読むず「Kuchuk-Kainardzhi」のような名前は笑い声にすぎたせん。



しかし、これはロシア語の問題に過ぎないず考えるのは単玔です。 人口の点でより均質な囜にも同様の困難が存圚したす。 そのため、フランス語では、単語の最埌にある文字p、b、d、t、sは通垞読たれたせん。 しかし、地名を付けるず、地元の䌝統がここで発効したす。 そのため、最埌の「パリ」ずいう蚀葉は実際には発音されず、「ノァリリス」ずいう蚀葉では-逆もたた同様です。 違いは、パリはフランスの北に䜍眮し、ノァロリスは南のプロノァンスにあり、発音芏則が倚少異なるこずです。 そのため、単語の音声転写が必芁です。 通垞、カヌドには付属しおいたす。 確かに、圢匏の統䞀は芳察されたせん。 そのため、NavTeqは䌝統的にX-SAMPAトランスクリプションずTomTom-LH +を䜿甚したす。 TTS゚ンゞンが䞡方を受け入れおいる堎合、そうでない堎合はどうでしょうか ここであなたは倉態しなければなりたせん。 たずえば、ある文字起こしを別の文字倉換に倉換するこずは、それ自䜓では簡単ではありたせん。 音声情報がたったくない堎合、゚ンゞンにはそれを取埗するための独自のメ゜ッドがありたす。 Nuance゚ンゞンに぀いお蚀えば、「デヌタ駆動型曞蚘玠から音玠」DDG2Pおよび「共通蚀語コンポヌネント」CLCです。 ただし、これらのオプションの䜿甚はすでに極端な手段です。



9.特別なシヌケンス



Nuanceには、テキストたたは音声蚘録を発音する機胜だけでなく、それらを動的に切り替える機胜もありたす。 これを行うには、次の圢匏の゚スケヌプシヌケンスを䜿甚したす。<ESC> / +



䞀般に、゚スケヌプシヌケンスを䜿甚しお、倚くのパラメヌタヌを指定できたす。 䞀般的な圢匏では、次のようになりたす。

                                          <ESC> \ <param> = <倀> \


䟋えば



\ x1b \ rate = 110 \-発音速床を蚭定したす

\ x1b \ vol = 5 \-ボリュヌムを蚭定したす

\ x1b \ audio = "beep.wav" \-wavファむルのデヌタをオヌディオストリヌムに挿入したす。



同様に、゚ンゞンのスペルを単語にしたり、ポヌズを挿入したり、音声を倉曎したりたずえば、男性から女性にするこずができたす。 もちろん、すべおのシヌケンスが䟿利なわけではありたせんが、党䜓的に非垞に䟿利な機胜です。



10.蟞曞



特定の単語セットを特定の方法略語、略語、固有名詞などで発音しなければならない堎合がありたすが、それぞれの堎合にテキストを音声衚蚘に眮き換える必芁がありたすこれは垞に可胜ずは限りたせん。 この堎合、蟞曞が助けになりたす。 Nuance甚語集の蟞曞ずは䜕ですか これは、ペアのセットを持぀ファむルです<text> <transcription>。 このファむルはコンパむルされ、゚ンゞンによっおロヌドされたす。 発音時に、゚ンゞンは単語/テキストが蟞曞に存圚するかどうかを確認し、存圚する堎合は、音声衚蚘に眮き換えたす。 たずえば、バチカンの街路や広堎の名前を含む蟞曞。



 [ヘッダヌ]
名前=バチカヌノ
蚀語= ITI
コンテンツ= EDCT_CONTENT_BROAD_NARROWS
衚珟= EDCT_REPR_SZZ_STRING
 [デヌタ]
 「コロンゎナヌト」// 'lar.go_del_ko.lo.'na.to
 「Piazza del Governatorato」//「pja.tsa_del_go.ver.na.to.'ra.to
 "Piazza della Stazione" // 'pja.tsa_de.la_sta.'tsjo.ne
 「サンタマルタ広堎」// 'pja.tsa_di_'san.ta_'mar.ta
 「サンピ゚トロ広堎」// 'pja.tsa_'sam_'pjE.tro
 「PiazzettaChâteauneufDu Pape」// pja.'tseta_Sa.to.'nef_du_'pap
 「サリタ・アむ・ゞャルディヌニ」// sa.'li.ta_aj_dZar.'di.ni
 「ストラドンデむゞャルディヌニ」// stra.'do.ne_dej_dZar.'di.ni
 「Via dei Pellegrini」// 'vi.a_dej_pe.le.'gri.ni
 「フォンダメント通り」// 'vi.a_del_fon.da.'men.to
 「Via del Governatorato」// 'vi.a_del_go.ver.na.to.'ra.to
 「Via della Posta」// 'vi.a_de.la_'pOs.ta
 「Vatica della Stazione Vaticana」// 'vi.a_de.la_sta.'tsjo.ne_va.ti.'ka.na
 「Via della Tipografia」// 'vi.a_de.la_ti.po.gra.'fi.a
 「ノィアディポルタアンゞェリカ」// 'vi.a_di_'pOr.ta_an.'dZE.li.ka
 「Via Tunica」// 'vi.a_'tu.ni.ka
 "Viale Centro del Bosco" // vi.'a.le_'tSEn.tro_del_'bOs.ko
 「Viard del Giardino Quadrato」// vi.'a.le_del_dZar.'di.no_kwa.'dra.to
 「Viatic Vaticano」// vi.'a.le_va.ti.'ka.no




11.認識



音声認識は、その合成よりもさらに困難です。 シンセサむザヌが叀き良き時代に䜕らかの圢で機胜しおいれば、賢明な認識は今しか利甚できなくなりたした。 いく぀かの理由がありたす。最初の理由は、なじみのない蚀語に盎面しおいる普通の生きおいる人の問題に非垞に䌌おいたす。2番目の理由は、なじみのない地域のテキストずの衝突です。



私たちは声を思い出させる音の振動を知芚し、たずそれを音玠に分割し、圢成しなければならない身近な音を蚀葉に分離しようずしたす。 蚀語が銎染みのある堎合、これは簡単に取埗できたすが、そうでない堎合は、音声を音玠に「正しく」分解するこずさえできたせん「Alla、I'm at the bar」に関する話を思い出しおください。 私たちが聞くずころでは、話す人は完党に異なっおいたす。 これは、長幎にわたり、私たちの脳が特定の音玠で「蚓緎」されおおり、時間ずずもにそれらだけを知芚するこずに慣れおいるためです。 なじみのない音に出䌚い、圌は自分が聞いたものに最も近い母囜語[蚀語]の音玠を遞択しようずしたす。 ある意味では、これはCELPなどの音声コヌデックで䜿甚されるベクトル量子化手法に䌌おいたす。 そのような近䌌が成功するずいう事実ではありたせん。 そのため、「快適な」音玠は「䟿利」になりたす。



゜連に戻っお、孊校で勉匷しおいる間、そしお倖囜人ず䌚うずき、私たちの名前を「音蚳」しようずしたこずを芚えおおいおください。

ボリス・ペトロフの名前

先生たちは私たちをscり、なぜあなたの名前を歪めたのですか 圌はこれを理解するず思いたすか ロシア語を話そう



悲しいかな、ここでも圌らは私たちを欺いたり、間違えたりしたした...もしあなたが英語/ドむツ語/䞭囜語であなたの名前を発音するこずができれば、それはネむティブスピヌカヌがそれを知芚するこずは本圓に簡単です。 䞭囜人はこれをかなり前に理解し、西掋のパヌトナヌず通信するために自分自身のために特別な「ペヌロッパ」の名前を取りたした。 機械認識では、特定の蚀語はいわゆる音響モデルによっお蚘述されたす。 テキストを認識する前に、特定の蚀語の音響モデルをロヌドする必芁がありたす。これにより、入力時にテキストを埅機する音玠をプログラムに明確にする必芁がありたす。



2番目の問題も同様に耇雑です。 生きおいる人ずの類掚に戻りたしょう。 察話者の話を聞いお、私たちは無意識のうちに圌が次に蚀うこずのモデルを頭の䞭に構築したす。蚀い換えれば、䌚話のコンテキストを䜜成したす。 そしお、文脈から倖れた蚀葉を物語に突然挿入するず䟋えば、サッカヌに関しおは「むンボリュヌト」、察談者に認知的䞍協和を匕き起こす可胜性がありたす。 倧雑把に蚀えば、コンピュヌタヌではこの非垞に䞍協和音が絶えず発生したす。なぜなら、圌は人に䜕を期埅するのかわからないからです。 人にずっおは簡単です。察談者に再床尋ねるこずができたす。 コンピュヌタヌは䜕をすべきですか この問題を解決し、コンピュヌタヌに正しいコンテキストを䞎えるために、文法が䜿甚されたす。



12.文法



文法通垞はBNFの圢匏で䞎えられたすは、コンピュヌタヌより正確にはASR゚ンゞンに、この特定の瞬間にナヌザヌに期埅するこずのアむデアを䞎えるだけです。 通垞、これらは「たたは」を介しお組み合わされたいく぀かの遞択肢ですが、より耇雑な文法も可胜です。 カザンの地䞋鉄駅を遞択するための文法の䟋を次に瀺したす。



 BNF + EM V1.0;
 文法テスト。
 start <metro_KAZAN_stations>;
 <metro_KAZAN_stations>
 "Ametyevo"Id0発音 "^。 'Mje.tjjI.vo-"|
 「航空機」Id1発音 "^ vjI'astro-'itjIljno-j ^"|
 "Slides"Id2発​​音 "'gor.kjI"|
 「ダギ定䜏」Id3発音 "'ko.zjj ^ _slo-.b ^。' Da"|
 "Kremlin"Id4発音 "krjIm.'ljof.sko-.j ^"|
 "Gabdulla Tukay Square"Id5発音 "'plo.SjItj_go-.bdu.'li0_'tu.ko-.j ^"|
 Victory AvenueId6発音 "pr ^。 'Spjekt_p ^。' Bje.di0"|
北駅Id7発音 "'sje.vjIr.ni0j_v ^ g.'zal"|
 「垃地決枈」Id8発音 "'su.ko-.no-.j ^ _slo-.b ^。' Da"|
 "Yashlek"Id9発音 "ja.'Sljek";




ご芧のずおり、各行は遞択肢の1぀であり、実際のテキスト、敎数ID、音玠で構成されおいたす。 音玠は䞀般にオプションですが、それにより認識がより正確になりたす。



文法はどれくらい倧きくできたすか 十分に倧きい。 私たちの実隓では、37000の代替案が蚱容レベルで認識されおいるずしたす。 耇雑で分岐した文法では事態はさらに悪化したす。 認識時間が長くなり、品質が䜎䞋し、文法の長さぞの䟝存は非線圢です。 したがっお、私のアドバむスは耇雑な文法を避けるこずです。 ずにかく、さようなら。



文法およびコンテキストは静的および動的です。 静的文法の䟋をすでに芋たした;事前にコンパむルされ、゚ンゞンの内郚バむナリ衚珟に保存されたす。 ただし、ナヌザヌの操䜜䞭にコンテキストが倉わる堎合がありたす。 ナビゲヌションの兞型的な䟋は、最初の文字による郜垂の遞択です。 ここで認識の可胜なオプションのセットは、それぞれの文字が入力されるたびに倉化したす。認識コンテキストは垞に再構築する必芁がありたす。 これらの目的のために、動的コンテキストが䜿甚されたす。 倧たかに蚀えば、プログラマヌは文法を「オンザフラむ」でコンパむルし、プログラムの実行䞭にそれらを゚ンゞンにパヌムしたす。 もちろん、モバむルデバむスに぀いお話しおいる堎合、凊理速床はあたり高くないため、ナヌザヌむンタヌフェむスがフリヌズしないように、小さな文法玄100語に制限する必芁がありたす。



13. ASR APIの䟋



テキスト認識は、合成ほど簡単ではありたせん。 ナヌザヌがマむクの前で静かになっおいる堎合、呚囲のノむズを認識する必芁がありたす。 「ehhhhhh」などず蚀ったら、認識も倱敗する可胜性がありたす。 最良の堎合、ASRは通垞、䞀連のオプション仮説ずも呌ばれるを返したす。 各仮説には䞀定の重みがありたす。 文法が倧きい堎合、認識オプションは非垞に倚くなりたす。 この堎合、仮説たずえば、信頌性の降順で最初の5぀を連続的に述べ、ナヌザヌにそれらの1぀を遞択するように䟝頌するこずは理にかなっおいたす。 理想的には、短い文法 "yes" | "no"で、信頌性の高いむンゞケヌタを持぀1぀のオプションを返したす。



次の䟋には、次の関数が含たれおいたす。



ConstructRecognizer-「認識」を䜜成し、そのパラメヌタヌを構成したす

DestroyRecognizer-「認識」を砎棄したす

ASR_Initialize-ASR゚ンゞンを初期化したす

ASR_UnInitialize-ASR゚ンゞンの初期化を解陀したす

evt_HandleEvent-「認識」スレッドによっお生成されたむベントを凊理したす

ProcessResult-認識結果を出力したす



ASRずそのバむンディング
 typedef struct RECOG_OBJECTS_S { void *pHeapInst; // Pointer to the heap. const char *acmod; // path to acmod data const char *ddg2p; // path to ddg2p data const char *clc; // path to clc data const char *dct; // path to dct data const char *dynctx; // path to empty dyn ctx data LH_COMPONENT hCompBase; // Handle to the base component. LH_COMPONENT hCompAsr; // Handle to the ASR component. LH_COMPONENT hCompPron; // Handle to the pron component (dyn ctx) LH_OBJECT hAcMod; // Handle to the AcMod object. LH_OBJECT hRec; // Handle to the SingleThreadedRec Object LH_OBJECT hLex; // Handle to lexicon object (dyn ctx) LH_OBJECT hDdg2p; // Handle to ddg2p object (dyn ctx) LH_OBJECT hClc; // Handle to the CLC (DDG2P backup) LH_OBJECT hDct; // Handle to dictionary object (dyn ctx) LH_OBJECT hCache; // Handle to cache object (dyn ctx) LH_OBJECT hCtx[5]; // Handle to the Context object. LH_OBJECT hResults[5]; // Handle to the Best results object. ASRResult *results[5]; // recognition results temporary storage LH_OBJECT hUswCtx; // Handle to the UserWord Context object. LH_OBJECT hUswResult; // Handle to the UserWord Result object. unsigned long sampleFreq; // Sampling frequency. unsigned long frameShiftSamples; // Size of one frame in samples int requestCancel; // boolean indicating user wants to cancel recognition // used to generate transcriptions for dyn ctx LH_BNF_TERMINAL *pTerminals; unsigned int terminals_count; unsigned int *terminals_transtype; // array with same size as pTerminals; each value indicates the type of transcription in pTerminal: user-provided, from_ddg2p, from_dct, from_clc SLOT_TERMINAL_LIST *pSlots; unsigned int slots_count; // reco options int isNumber; // set to 1 when doing number recognition const char * UswFile; // path to file where userword should be recorded char * staticCtxID; } RECOG_OBJECTS; // store ASR objects static RECOG_OBJECTS recogObjects; static int ConstructRecognizer(RECOG_OBJECTS *pRecogObjects, const char *szAcModFN, const char * ddg2p, const char * clc, const char * dct, const char * dynctx) { LH_ERROR lhErr = LH_OK; PH_ERROR phErr = PH_OK; ST_ERROR stErr = ST_OK; LH_ISTREAM_INTERFACE IStreamInterface; void *pIStreamAcMod = NULL; LH_ACMOD_INFO *pAcModInfo; LH_AUDIOCHAINEVENT_INTERFACE EventInterface; /* close old objects */ if(!lh_ObjIsNull(pRecogObjects->hAcMod)){ DestroyRecognizer(pRecogObjects); } pRecogObjects->sampleFreq = 0; pRecogObjects->requestCancel = 0; pRecogObjects->pTerminals = NULL; pRecogObjects->terminals_count = 0; pRecogObjects->pSlots = NULL; pRecogObjects->slots_count = 0; pRecogObjects->staticCtxID = NULL; pRecogObjects->acmod = szAcModFN; pRecogObjects->ddg2p = ddg2p; pRecogObjects->clc = clc; pRecogObjects->dct = dct; pRecogObjects->dynctx = dynctx; EventInterface.pfevent = evt_HandleEvent; EventInterface.pfadvance = evt_Advance; // Create the input stream for the acoustic model. stErr = st_CreateStreamReaderFromFile(szAcModFN, &IStreamInterface, &pIStreamAcMod); if (ST_OK != stErr) goto error; // Create the AcMod object. lhErr = lh_CreateAcMod(pRecogObjects->hCompAsr, &IStreamInterface, pIStreamAcMod, NULL, &(pRecogObjects->hAcMod)); if (LH_OK != lhErr) goto error; // Retrieve some information from the AcMod object. lhErr = lh_AcModBorrowInfo(pRecogObjects->hAcMod, &pAcModInfo); if (LH_OK != lhErr) goto error; pRecogObjects->sampleFreq = pAcModInfo->sampleFrequency; pRecogObjects->frameShiftSamples = pAcModInfo->frameShift * pRecogObjects->sampleFreq/1000; // Create a SingleThreadRec object lhErr = lh_CreateSingleThreadRec(pRecogObjects->hCompAsr, &EventInterface, pRecogObjects, 3000, pRecogObjects->sampleFreq, pRecogObjects->hAcMod, &pRecogObjects->hRec); if (LH_OK != lhErr) goto error; // cretae DDG2P & lexicon for dyn ctx if (pRecogObjects->ddg2p) { int rc = InitDDG2P(pRecogObjects); if (rc<0) goto error; } else if (pRecogObjects->clc) { int rc = InitCLCandDCT(pRecogObjects); if (rc<0) goto error; } else { // TODO: what now? } // Return without errors. return 0; error: // Print an error message if the error comes from the private heap or stream component. // Errors from the VoCon3200 component have been printed by the callback. if (PH_OK != phErr) { printf("Error from the private heap component, error code = %d.\n", phErr); } if (ST_OK != stErr) { printf("Error from the stream component, error code = %d.\n", stErr); } return -1; } static int DestroyRecognizer(RECOG_OBJECTS *pRecogObjects) { unsigned int curCtx; if (!lh_ObjIsNull(pRecogObjects->hUswResult)){ lh_ObjClose(&pRecogObjects->hUswResult); pRecogObjects->hUswResult = lh_GetNullObj(); } if (!lh_ObjIsNull(pRecogObjects->hUswCtx)){ lh_ObjClose(&pRecogObjects->hUswCtx); pRecogObjects->hUswCtx = lh_GetNullObj(); } if (!lh_ObjIsNull(pRecogObjects->hDct)){ lh_ObjClose(&pRecogObjects->hDct); pRecogObjects->hDct = lh_GetNullObj(); } if (!lh_ObjIsNull(pRecogObjects->hCache)){ lh_ObjClose(&pRecogObjects->hCache); pRecogObjects->hCache = lh_GetNullObj(); } if (!lh_ObjIsNull(pRecogObjects->hClc)){ lh_ObjClose(&pRecogObjects->hClc); pRecogObjects->hClc = lh_GetNullObj(); } if (!lh_ObjIsNull(pRecogObjects->hLex)){ lh_LexClearG2P(pRecogObjects->hLex); lh_ObjClose(&pRecogObjects->hLex); pRecogObjects->hLex = lh_GetNullObj(); } if (!lh_ObjIsNull(pRecogObjects->hDdg2p)){ lh_DDG2PClearDct (pRecogObjects->hDdg2p); lh_ObjClose(&pRecogObjects->hDdg2p); pRecogObjects->hDdg2p = lh_GetNullObj(); } for(curCtx=0; curCtx<sizeof(recogObjects.hCtx)/sizeof(recogObjects.hCtx[0]); curCtx++){ if (!lh_ObjIsNull(pRecogObjects->hCtx[curCtx])){ lh_RecRemoveCtx(pRecogObjects->hRec, pRecogObjects->hCtx[curCtx]); lh_ObjClose(&pRecogObjects->hCtx[curCtx]); pRecogObjects->hCtx[curCtx] = lh_GetNullObj(); } if (!lh_ObjIsNull(pRecogObjects->hResults[curCtx])){ lh_ObjClose(&pRecogObjects->hResults[curCtx]); pRecogObjects->hResults[curCtx] = lh_GetNullObj(); } } if (!lh_ObjIsNull(pRecogObjects->hRec)){ lh_ObjClose(&pRecogObjects->hRec); pRecogObjects->hRec = lh_GetNullObj(); } if (!lh_ObjIsNull(pRecogObjects->hAcMod)){ lh_ObjClose(&pRecogObjects->hAcMod); pRecogObjects->hAcMod = lh_GetNullObj(); } return 0; } int ASR_Initialize(const char * acmod, const char * ddg2p, const char * clc, const char * dct, const char * dynctx) { int rc = 0; size_t curCtx; LH_HEAP_INTERFACE HeapInterface; // Initialization of all handles. recogObjects.pHeapInst = NULL; recogObjects.hCompBase = lh_GetNullComponent(); recogObjects.hCompAsr = lh_GetNullComponent(); recogObjects.hCompPron = lh_GetNullComponent(); recogObjects.hAcMod = lh_GetNullObj(); for(curCtx=0; curCtx<sizeof(recogObjects.hCtx)/sizeof(recogObjects.hCtx[0]); curCtx++){ recogObjects.hCtx[curCtx] = lh_GetNullObj(); recogObjects.hResults[curCtx] = lh_GetNullObj(); } recogObjects.hRec = lh_GetNullObj(); recogObjects.hLex = lh_GetNullObj(); recogObjects.hDdg2p = lh_GetNullObj(); recogObjects.hClc = lh_GetNullObj(); recogObjects.hCache = lh_GetNullObj(); recogObjects.hDct = lh_GetNullObj(); recogObjects.hUswCtx = lh_GetNullObj(); recogObjects.hUswResult = lh_GetNullObj(); recogObjects.sampleFreq = 0; recogObjects.requestCancel = 0; recogObjects.pTerminals = NULL; recogObjects.terminals_count= 0; recogObjects.pSlots = NULL; recogObjects.slots_count = 0; recogObjects.staticCtxID = NULL; // Construct all components and objects needed for recognition. // Connect the audiochain objects. if (acmod) { // initialize components // Create a base and an ASR component. (+pron for dyn ctx) if(LH_OK != lh_InitBase(&HeapInterface, recogObjects.pHeapInst, LhErrorCallBack, NULL, &recogObjects.hCompBase)) goto error; if(LH_OK != lh_InitAsr(recogObjects.hCompBase, &HeapInterface, recogObjects.pHeapInst, &recogObjects.hCompAsr)) goto error; if(LH_OK != lh_InitPron(recogObjects.hCompBase, &HeapInterface, recogObjects.pHeapInst, &recogObjects.hCompPron)) goto error; rc = ConstructRecognizer(&recogObjects, acmod, ddg2p, clc, dct, dynctx); if (rc<0) goto error; } return rc; error: // An error occured. Close the engine. CloseOnError(&recogObjects); return -1; } int ASR_UnInitialize(void) { int rc; // Disconnects the audiochain objects. // Closes all objects and components of the vocon recognizer. rc = DestroyRecognizer(&recogObjects); // Close the PRON component. lh_ComponentTerminate(&recogObjects.hCompPron); // Close the ASR and Base component. lh_ComponentTerminate(&recogObjects.hCompAsr); lh_ComponentTerminate(&recogObjects.hCompBase); return 0; } int evt_HandleEvent(void *pEvtInst, unsigned long type, LH_TIME timeMs) { RECOG_OBJECTS *pRecogObjects = (RECOG_OBJECTS*)pEvtInst; if ( type & LH_AUDIOCHAIN_EVENT_BOS ){ // ask upper level for beep printf ("Receiving event LH_AUDIOCHAIN_EVENT_BOS at time %d ms.\n", timeMs); } if ( type & LH_AUDIOCHAIN_EVENT_TS_FX ) { printf ("Receiving event LH_AUDIOCHAIN_EVENT_TS_FX at time %d ms.\n", timeMs); } if ( type & LH_AUDIOCHAIN_EVENT_TS_REC ) { printf ("Receiving event LH_AUDIOCHAIN_EVENT_TS_REC at time %d ms.\n", timeMs); } if ( type & LH_AUDIOCHAIN_EVENT_FX_ABNORMCOND ) { LH_ERROR lhErr = LH_OK; LH_FX_ABNORMCOND abnormCondition; printf ("Receiving event LH_AUDIOCHAIN_EVENT_FX_ABNORMCOND at time %d ms.\n", timeMs); // Find out what the exact abnormal condition is. lhErr = lh_FxGetAbnormCondition(pRecogObjects->hRec, &abnormCondition); if (LH_OK != lhErr) goto error; switch (abnormCondition) { case LH_FX_BADSNR: printf ("Abnormal condition: LH_FX_BADSNR.\n"); break; case LH_FX_OVERLOAD: printf ("Abnormal condition: LH_FX_OVERLOAD.\n"); break; case LH_FX_TOOQUIET: printf ("Abnormal condition: LH_FX_TOOQUIET.\n"); break; case LH_FX_NOSIGNAL: printf ("Abnormal condition: LH_FX_NOSIGNAL.\n"); break; case LH_FX_POORMIC: printf ("Abnormal condition: LH_FX_POORMIC.\n"); break; case LH_FX_NOLEADINGSILENCE: printf ("Abnormal condition: LH_FX_NOLEADINGSILENCE.\n"); break; } } // LH_AUDIOCHAIN_EVENT_FX_TIMER // It usually is used to get the signal level and SNR at regular intervals. if ( type & LH_AUDIOCHAIN_EVENT_FX_TIMER ) { LH_ERROR lhErr = LH_OK; LH_FX_SIGNAL_LEVELS SignalLevels; printf ("Receiving event LH_AUDIOCHAIN_EVENT_FX_TIMER at time %d ms.\n", timeMs); lhErr = lh_FxGetSignalLevels(pRecogObjects->hRec, &SignalLevels); if (LH_OK != lhErr) goto error; printf ("Signal level: %ddB, SNR: %ddB at time %dms.\n", SignalLevels.energy, SignalLevels.SNR, SignalLevels.timeMs); } // LH_AUDIOCHAIN_EVENT_RESULT if ( type & LH_AUDIOCHAIN_EVENT_RESULT ){ LH_ERROR lhErr = LH_OK; LH_OBJECT hNBestRes = lh_GetNullObj(); LH_OBJECT hCtx = lh_GetNullObj(); printf ("Receiving event LH_AUDIOCHAIN_EVENT_RESULT at time %d ms.\n", timeMs); // Get the NBest result object and process it. lhErr = lh_RecCreateResult (pRecogObjects->hRec, &hNBestRes); if (LH_OK == lhErr) { if (LH_OK == lh_ResultBorrowSourceCtx(hNBestRes, &hCtx)){ int i; int _ready = 0; for(i=0; i<sizeof(pRecogObjects->hCtx)/sizeof(pRecogObjects->hCtx[0]); i++){ if(!lh_ObjIsNull(pRecogObjects->hCtx[i])){ if(hCtx.pObj == pRecogObjects->hCtx[i].pObj){ if(!lh_ObjIsNull(pRecogObjects->hResults[i])){ lh_ObjClose(&pRecogObjects->hResults[i]); } pRecogObjects->hResults[i] = hNBestRes; hNBestRes = lh_GetNullObj(); _ready = 1; break; } } else { break; } } if (_ready) { for (i=0; i<sizeof(pRecogObjects->hCtx)/sizeof(pRecogObjects->hCtx[0]); i++) { if(!lh_ObjIsNull(pRecogObjects->hCtx[i])){ if(lh_ObjIsNull(pRecogObjects->hResults[i])){ _ready = 0; } } } } ASSERT(lh_ObjIsNull(hNBestRes)); if (_ready) { ProcessResult (pRecogObjects); for(i=0; i<sizeof(pRecogObjects->hResults)/sizeof(pRecogObjects->hResults[0]); i++){ if(!lh_ObjIsNull(pRecogObjects->hResults[i])){ lh_ObjClose(&pRecogObjects->hResults[i]); } } } } // Close the NBest result object. } } return 0; error: return -1; } static int ProcessResult (RECOG_OBJECTS *pRecogObjects) { LH_ERROR lhErr = LH_OK; size_t curCtx, i, k, count=0; size_t nbrHypothesis; ASRResult *r = NULL; long lid; // get total hyp count for(curCtx=0; curCtx<sizeof(pRecogObjects->hCtx)/sizeof(pRecogObjects->hCtx[0]); curCtx++){ if(!lh_ObjIsNull(pRecogObjects->hResults[curCtx])){ if(LH_OK == lh_NBestResultGetNbrHypotheses (pRecogObjects->hResults[curCtx], &nbrHypothesis)){ count += nbrHypothesis; } } } // traces printf ("\n"); printf (" __________RESULT %3d items max_______________\n", count); printf ("| | |\n"); printf ("| result | confi- | result string [start rule]\n"); printf ("| number | dence |\n"); printf ("|________|________|___________________________\n"); printf ("| | |\n"); if (count>0) { r = ASRResult_New(count); // Get & print out the result information for each hypothesis. count = 0; curCtx = sizeof(pRecogObjects->hCtx)/sizeof(pRecogObjects->hCtx[0]); for(; curCtx>0; curCtx--){ LH_OBJECT hNBestRes = pRecogObjects->hResults[curCtx-1]; if(!lh_ObjIsNull(hNBestRes)){ LH_HYPOTHESIS *pHypothesis; if(LH_OK == lh_NBestResultGetNbrHypotheses (hNBestRes, &nbrHypothesis)){ for (i = 0; i < nbrHypothesis; i++) { char *szResultWords; // Retrieve information on the recognition result. if (LH_OK == lh_NBestResultFetchHypothesis (hNBestRes, i, &pHypothesis)){ // Get the result string. if (LH_OK == lh_NBestResultFetchWords (hNBestRes, i, &szResultWords)){ printf ("| %6lu | %6lu | '%s' [%s]\n", i, pHypothesis->conf, szResultWords, pHypothesis->szStartRule); // Return the fetched data to the engine. lh_NBestResultReturnWords (hNBestRes, szResultWords); } lh_NBestResultReturnHypothesis (hNBestRes, pHypothesis); } } } } } } // traces printf ("|________|________|___________________________\n"); printf ("\n"); return 0; }
      
      









明らかに、TTSの堎合のように、コヌドは非垞に倧きく、準備手順は倚くのスペヌスを占有したす。そしお、これはただ完党に機胜するコヌドではありたせん出版するずき、私は倚くの䞍必芁なものを投げたした。このすべおは、音声I / Oテクノロゞヌを䜿甚するにはかなり高い「゚ントリしきい倀」が必芁であるこずをここたで読んだ人たちにもう䞀床瀺しおいたす。



14.ストリヌム認識ディクテヌション



珟圚の技術における最埌の蚀葉は、むンラむン認識、぀たりディクテヌションです。このテクノロゞヌは、AndroidおよびiOS甚の最新のスマヌトフォンですでに利甚可胜です。含む-APIの圢匏。ここで、プログラマは、文法を䜜成するずきに認識コンテキストを指定する必芁はありたせん。入り口でスピヌチがありたす-出口で、認識された蚀葉。残念ながら、この方法がどのように機胜するかに぀いおの詳现はただ入手できたせん。認識プロセスはデバむス自䜓ではなく、音声が送信されるサヌバヌで行われ、そこから結果が取埗されたす。しかし、私は䜕幎も埌にクラむアント偎で技術が利甚可胜になるず信じたいです。



おわりに



ASRずTTSテクノロゞヌに぀いお䌝えたかったのはおそらくそれだけでしょう。私はそれがあたりにも退屈で非垞に有益ではないこずを願っおいたす。



All Articles