Мое приложение активно использует преобразование текста в речь (через 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), и примерно через секунду после его отправки мое приложение падает с ошибкой сегментации, даже если сигнал нигде не подключен. Это работает совершенно иначе.
Любая идея?
Спасибо за прочтение!
Нет прямого способа сделать то, что вы хотите. В вашем случае вам повезло, потому что есть void* user_data
поле в espeak_EVENT
, Вы можете установить его на this
когда ты звонишь 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);
}