[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

ssが音を出さなくなる問題と白藤さんの解決案 (長文)



渡辺@湘南工科大です。
CC:小出様、

今日 白藤さんから、スピーチサーバーが音を出さなくなる問題に関して、解
決のアイデアを頂きました。BEP MLで議論した方がよいように思ったので、
MLに白藤さんへの返事を書きます。

今、ソースを見て思い出したのですが、stop()はBishopさんのオリジナルでは
以下のようになっていたのですが、このままでは日本語エンジンを使ったときに
stop()でハングアップするので、小出さんに調べてもらって修正しました。

A オリジナル:
void VoiceManager::stop()
{
  m_speaking_cs.Lock();
  QueueBlock *qb = speaking_head;

  for(int i=0; i<NUMENGINES; i++) {
    engines[i].mpITTSCentral->AudioReset();
  }
  while(qb != 0) { // empty the blocks but keep them...
    qb->QText = "";
    qb->QInit = "";
    qb = qb->next;
  }

  m_speaking_cs.Unlock();
}

B 小出改良版:
void VoiceManager::stop()
{
  m_speaking_cs.Lock();
  QueueBlock *qb = speaking_head;
  // BEP 
  while(qb != 0) { // empty the blocks but keep them because...
    qb->QText = "";
    qb->QInit = "";
    qb = qb->next;
  }
  for(int i=0; i<NUMENGINES; i++) {
    engines[i].mpITTSCentral->AudioReset();
  }

  m_speaking_cs.Unlock();
}

英語と日本語ではエンジンの応答速さが異なり、英語で問題なかったところ
が日本語で問題化したようです。AudioResetをかけると TextDataDoneが発生
してqueueを更新してしまい、そのあとで qbを操作すると不法なアクセスに
なりました。


C 白藤改良版は:
> しゃべらない場合、start_queue() のif文の
> 最後の else に入ってきています。
> 
> if(head == 0) {
> } else if(speaking_head == 0) {
> } else {
> //しゃべらないときはここに入ってくる。
> }
> 
> この部分に問題があるのか、speaking_head
> から続くリストが長くて、そのほとんどのQInit, QTextが空で
> あるために、TTS の解析で時間だけがかかってしまい、結果として
> 人間側が求める時間以内にしゃべり始められないのが原因の
> 可能性があります。
> 今回のソースは問答無用(SAPIと同期もとらずに)に、リストの
> 長さを0にすることで、SAPIが空の文を解析する必要がないように
> しています。

void VoiceManager::stop()
{
  m_speaking_cs.Lock();
  QueueBlock *qb = speaking_head;
  // BEP 
  while(qb != 0) { // empty the blocks but keep them
                   // because  notifications may come in...
    qb->QText = "";
    qb->QInit = "";
    qb = qb->next;
  }
  for(int i=0; i<NUMENGINES; i++) {
    engines[i].mpITTSCentral->AudioReset();
  }

  // BEP
  while(qb != 0) {
    QueueBlock *qbnext = qb->next;
    delete qb;
    qb = qbnext;
  }
  speaking_head = NULL;
  speaking_tail = NULL;

  m_speaking_cs.Unlock();
}

として、立て続けにstopを送った際に音がでなくなる問題を回避しようとい
うわけですね。
全エンジンにAudioResetをかけたら、キューを空にしても問題ないような気
がするからこれで構わないような気がします。
と思ったのですが、前の文の途中でstop()をかけて次の文を音声化すると、
次の文のキューの音声化がフライングします。

たとえば、
    MyPrintMessage();
    for ( i=0 ; i < MAX_SIZE ; i++ ){
の場合、MyPrintMessageを読んでいる途中でC-nで次の文を読ますと、

以下 VoiceManger.cppの関数呼び出しのログ:
//  MyPrintMessage();を読んでいる途中でstop()がかかった
(行664) Stopped speaking
//  次の行を読む
(行522) Start queue
(行549) :np-indent TextData '\rst\\spd=225\indent 4'
(行875) TextDataDone dw=0
(行579) next queue
(行586) deleting 7960e0
(行619) BEP; :nu nq TextData '\rst\\spd=225\for'
(行875) TextDataDone dw=0
(行579) next queue
(行586) deleting 796120
(行619) BEP; :np nq TextData '\rst\\spd=225\ open parenthesis i
equal 0 semicolon i less than'
(行875) TextDataDone dw=0
(行579) next queue
(行586) deleting 796320
(行619) BEP; :np-smooth nq TextData '\rst\\spd=225\MAX underscore SIZE'
(行875) TextDataDone dw=0
(行579) next queue
(行586) deleting 796e20
(行619) BEP; :np nq TextData '\rst\\spd=225\ semicolon i plus plus
close parenthesis left brace '
(行875) TextDataDone dw=0
(行579) next queue
(行586) deleting 797da0

と正しい順番にキューイングしてキューを処理しているのに、この文の読み
上げに参加する 3つのキューが重なって聞こえてしまいます。
なぜだろう? TextDataDoneが返ってくるタイミングが早すぎるからというの
が正しい答えだと思いますが、ではなぜBだとタイミングが合うのだろう?

Bのバージョンに戻すとちゃんと同期して喋ります。
またCでも、MyPrintMessage()を読み終わるまで待ってから次の行を読ますと
ちゃんと喋ります。

というわけでなんかおかしくなっていますが、なぜだろう、難しい...
白藤さんや小出様、理由がわかりますか?