【问题标题】:Setting espeak_SetSynthCallback to member function in C++在 C++ 中将 espeak_SetSynthCallback 设置为成员函数
【发布时间】:2014-11-27 05:38:04
【问题描述】:

我的应用程序大量使用文本转语音(通过 libespeak)。它是用 C++/Qt5 编写的,带有基于 QML 的前端。

我没有接受过正式的 C++ 培训(不过我有 Java 背景),因此我不完全确定如何正确实现一些更深奥的功能。

libespeak 支持回调功能,每次合成语音时都会调用该功能。 回调函数接受三个参数,我想用它们来可视化语音。下面的代码在正确调用回调函数的意义上有效,但没有用,因为我无法访问其他成员函数或变量。

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),并且在它发送大约一秒钟后,我的应用程序因分段错误而崩溃,即使信号没有连接到任何地方。否则效果很好。

有什么想法吗?

感谢阅读!

【问题讨论】:

  • 您想在多个 ItemVoices 实例中进行回调吗?
  • * 我的意思是在每个语音识别事件的多个 ItemVoices 实例中调用回调函数
  • 不,我只需要一个实例
  • 如果您真的只需要 ItemVoices 的单个实例,那么您可以将其调整为 Singleton stackoverflow.com/a/1008289/2266412 并调用 getInstance() 静态方法从您的回调函数中访问您的实例...如果您真的需要像下面的答案一样调用 espeak_Synth,然后传递 this 指针看起来也是不错的方法。
  • 感谢您的建议,但我不确定在这里使用单例是否可行,因为该对象是从 QML 而非 C++ 实例化的。它继承了在 QQmlContext 中注册的 QQuickItem

标签: c++ qt espeak


【解决方案1】:

没有直接的方法可以做你想做的事。在您的情况下,您很幸运,因为espeak_EVENT 中有void* user_data 字段。调用espeak_Synth()时可以设置为this

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);
}

【讨论】:

  • 酷,这似乎有效。谢谢你。我遇到了另一个问题。我已经更新了原帖。
  • @RalfVanBogaert 你没有在你的代码中检查numsamples,它真的可以为零吗?无论如何,您应该调试您的应用程序以捕捉崩溃的时刻。
  • numsamples 每次都是 0,反正我不使用它,我删除了它,否则永远不会调用函数。看不出它与崩溃有什么关系。我正在尝试使用 GDB 进行调试,需要一段时间才能弄清楚如何正确使用它
  • @RalfVanBogaert 您可以在 Qt Creator 中进行调试。那会简单得多,在大多数情况下,您不需要了解 gdb。
  • 这就是我正在尝试的,但老实说,我什至不知道我在寻找什么。更新了原帖。
猜你喜欢
  • 1970-01-01
  • 2011-12-12
  • 1970-01-01
  • 1970-01-01
  • 2012-04-29
  • 1970-01-01
  • 1970-01-01
  • 2016-09-11
  • 2019-05-09
相关资源
最近更新 更多