4

私のアプリケーションは、(libespeak を介して) テキスト読み上げを多用しています。QML ベースのフロントエンドを備えた C++/Qt5 で書かれています。

私は正式な C++ トレーニングを受けていません (ただし、Java のバックグラウンドは持っています)。そのため、難解な機能のいくつかを適切に実装する方法が完全にはわかりません。

libespeak は、音声が合成されるたびに呼び出されるコールバック機能をサポートしています。コールバック関数は、音声を視覚化するために使用する 3 つの引数を取ります。以下のコードは、コールバック関数が正しく呼び出されるという意味で機能しますが、他のメンバー関数や変数にアクセスできないため、役に立ちません。

itemvoice.h

#include "espeak/speak_lib.h"
int callback(short *wav, int numsamples, espeak_EVENT *events);

class ItemVoice : public Item
{
public:
explicit ItemVoice(QQuickItem *parent = 0);
};

itemvoice.cpp

#include "itemvoice.h"
extern int callback(short *wav, int numsamples, espeak_EVENT *events)
{
// do stuff
}
ItemVoice::ItemVoice(QQuickItem *parent):Item(parent)
{
espeak_Initialize(AUDIO_OUTPUT_PLAYBACK,500,NULL,0);
espeak_SetSynthCallback(callback);
}

コールバック関数を ItemVoice クラスのメンバーにしたいと考えています。ただし、試してみて、espeak_SetSynthCallback(ItemVoice::callback) でコールバック関数を設定すると、変換できない引数のためにコードがコンパイルされなくなります。


更新:以下の提案は機能します。しかし、私は今別の問題に遭遇しました。クラスは次のようになります。

itemvoice.h

#include "espeak/speak_lib.h"
int staticCallback(short *wav, int numsamples, espeak_EVENT *events);
class ItemVoice : public Item
{
    Q_OBJECT

public:
    explicit ItemVoice(QQuickItem *parent = 0);
    void startSpeaking();
    void stopSpeaking();

signals:
    void updateGUI();
}

itemvoice.cpp

#include "itemvoice.h"
ItemVoice::ItemVoice(QQuickItem *parent):Item(parent)
{
    espeak_Initialize(AUDIO_OUTPUT_PLAYBACK,500,NULL,0);
    espeak_SetSynthCallback(staticCallback);
}

int staticCallback(short *wav, int numsamples, espeak_EVENT *events)
{
espeak_EVENT_TYPE type=events->type;
if(type==2) // start sentence
    (static_cast<ItemVoice*>(events[0].user_data))->startSpeaking();
else if(type==6) // stop sentence
    (static_cast<ItemVoice*>(events[0].user_data))->stopSpeaking(); 
}

void ItemVoice::startSpeaking()
{
    //do stuff
    updateGUI();    
}

void ItemVoice::stopSpeaking()
{
    // do stuff
    updateGUI();
}

これは正しく動作します。startSpeaking() は合成の開始時に呼び出され、stopSpeaking() は合成の停止時に呼び出されます。問題は、Qt シグナルを送信して GUI (updateGUI) を更新する必要があることです。シグナルがどこにも接続されていなくても、送信後約 1 秒でアプリケーションがセグメンテーション違反でクラッシュします。それ以外の場合は完全に機能します。

何か案が?

読んでくれてありがとう!

4

1 に答える 1

2

やりたいことを直接行う方法はありません。void* user_dataにフィールドがあるので、あなたの場合はラッキーですespeak_EVENTthis呼び出すときに設定できますespeak_Synth()

void ItemVoice::synthSpeech() {
    espeak_Synth(...., this);
}

したがって、コールバック (これは依然としてグローバル関数、または の静的関数ItemVoice) では、大まかに次のことができます。

int staticCallback(short *wav, int numsamples, espeak_EVENT *events) {
    if (numsamples > 0)
        return (static_cast<ItemVoice*>(events[0].user_data))->nonStaticCallback(wav, numsamples, events);
}
于 2014-10-02T09:12:16.113 に答える