北京网站网页设计,重庆公司网站搭建公司推荐,专门做奢侈品的网站,wordpress语言切换器前言
之前随便看了几眼QTextToSpeech的帮助就封装使用了#xff0c;达到了效果就没再管了#xff0c;最近需要在上面加功能#xff08;变换语速#xff09;#xff0c;就写了个小Demo后#xff0c;发现不对劲了。 出现的问题
场景
写了个队列添加到语音播放子线程中达到了效果就没再管了最近需要在上面加功能变换语速就写了个小Demo后发现不对劲了。 出现的问题
场景
写了个队列添加到语音播放子线程中在run循环查询tts引擎状态来依次播放。代码如下
void AudioThread::run()
{while (m_iRunning){if(m_audioQueue.size() ! 0m_pTextToSpeech-state() QTextToSpeech::Ready){SingleAudio aud m_audioQueue.dequeue();double rate 0.0; //-1.0 ~ 1.0if(aud.iType 1){rate 0.4;}m_pTextToSpeech-setRate(rate);m_pTextToSpeech-say(aud.strContent);}msleep(200);}
} 单看代码没有毛病只是多条文本一起投入就会出现tts的状态一直为Ready一直执行say没合成一条语音都执行完了导致没有播放一条语音。 分析及措施
出现这问题怀疑是我用错了所以我又仔细看了下Qt帮助文档看自己的使用是否有问题看完后确实有疏漏。 void QTextToSpeech::say(const QString text) Start synthesizing the text. This function will start the asynchronous reading of the text. The current state is available using the state property. Once the synthesis is done, a stateChanged() signal with the Ready state is emitted. 大致说这个行为是异步的属性state记录当前状态当完成后会发出stateChanged信号(Ready)。 状态有以上四种如果tts引擎还在合成比如文本过于长所需时长大于等待时长状态还是没有改变即Ready状态这样确实会出现这种问题。
这种问题是可以避免的 具体如下
方法一
通过信号stateChanged来控制播放确实会避免这种问题。代码如下
void AudioController::addAudioQueue(const QQueueSingleAudio audioQueue)
{m_audioQueue.append(audioQueue);if(m_pTextToSpeech-state() QTextToSpeech::Ready){playAudio();}
}void AudioController::onStateChanged(QTextToSpeech::State state)
{if(state QTextToSpeech::Ready){playAudio();}
}void AudioController::playAudio()
{if(m_audioQueue.size() 0)return;SingleAudio aud m_audioQueue.dequeue();double rate 0.0; //-1.0 ~ 1.0if(aud.iType 1){rate 0.4;}m_pTextToSpeech-setRate(rate);m_pTextToSpeech-say(aud.strContent);
} 以上在主线程中执行的没有任何问题。
后面我试图将QTextToSpeech对象移入子线程想让它在子线程中执行所有操作失败了移入后感觉整个停住了状态也不会变化感觉它只能在主线程中使用后面的测试也给我这样的感觉。
void init()
{m_pTextToSpeech new QTextToSpeech;m_pTextToSpeech-moveToThread(m_ttsThread);connect(m_pTextToSpeech,QTextToSpeech::stateChanged,this,AudioController::onStateChanged);m_ttsThread.start();}void AudioController::playAudio()
{if(m_audioQueue.size() 0)return;SingleAudio aud m_audioQueue.dequeue();double rate 0.0; //-1.0 ~ 1.0if(aud.iType 1){rate 0.4;}QMetaObject::invokeMethod(m_pTextToSpeech,setRate,Qt::AutoConnection,Q_ARG(double,rate));QMetaObject::invokeMethod(m_pTextToSpeech,say,Qt::AutoConnection,Q_ARG(QString,aud.strContent));} 方法二
由于之前语音模块的代码是在子线程执行QThread的run中执行这种结构是变不了的信号控制的方式又无法嵌入所以只能在原基础上更改。
为保证状态更改过Ready - Speaking -Ready所以添加了个标识符进行标记代码如下
void AudioThread::run()
{bool isStateChanged true;while (m_iRunning){if(m_pTextToSpeech-state() QTextToSpeech::Ready){if(m_audioQueue.size() ! 0isStateChanged){SingleAudio aud m_audioQueue.dequeue();double rate 0.0; //-1.0 ~ 1.0if(aud.iType 1){rate 0.4;}m_pTextToSpeech-setRate(rate);m_pTextToSpeech-say(aud.strContent);isStateChanged false;}}else{isStateChanged true;}msleep(200);}}
此代码在安卓平台下是正常的然而在Windows下是不能正常运行的QTextToSpeech状态是不变的类似上面在子线程中运行卡住但是如果在主线程的其他地方先say一下然后子线程中就正常了。我看了一点点源码不同平台调用的是不同的引擎Windows封装的代码中也没看到线程之类的东西异步的实现是回调在子线程中会影响回调或者阻碍语音的合成这个其中的道理我也搞不清只能根据代码运行后的效果进行猜测跟线程有关系。
因为猜测和线程有关所以就换了调用QTextToSpeech方法的方式如下,更换为此种方式调用后Windows平台和安卓平台都可以正常运行了。 QMetaObject::invokeMethod(m_pTextToSpeech,setRate,Qt::QueuedConnection,Q_ARG(double,rate));QMetaObject::invokeMethod(m_pTextToSpeech,say,Qt::QueuedConnection,Q_ARG(QString,aud.strContent)); 使用
完整的使用的代码如下
#include AudioThread.h
#include QTimer
#include QDebugAudioThread::AudioThread(QObject *parent):QThread{parent},m_pTextToSpeech(new QTextToSpeech(this)),m_iRunning(true),m_bPause(false)
{//0.5秒后再初始化tts(tts引擎启动时异步的)QTimer::singleShot(500,this,[](){m_pTextToSpeech-setRate(-0.1);const QVectorQLocale locales m_pTextToSpeech-availableLocales();for(int i 0; i locales.count(); i){if(locales.at(i).language() QLocale::Chinese){m_pTextToSpeech-setLocale(locales.at(i));break;}}});}AudioThread::~AudioThread()
{
}void AudioThread::addAudioQueue(const QQueueSingleAudio audioQueue)
{m_audioQueue.append(audioQueue);
}void AudioThread::addSingleAudio(const SingleAudio audio)
{m_audioQueue.enqueue(audio);
}void AudioThread::stop()
{m_iRunning false;m_audioQueue.clear();quit();wait();
}void AudioThread::run()
{while (m_iRunning){static bool isStateChanged true;if(m_pTextToSpeech-state() QTextToSpeech::Ready){if(m_audioQueue.size() ! 0isStateChanged){SingleAudio aud m_audioQueue.dequeue();double rate 0.0; //-1.0 ~ 1.0if(aud.iType 1){rate 0.4;}QMetaObject::invokeMethod(m_pTextToSpeech,setRate,Qt::QueuedConnection,Q_ARG(double,rate));QMetaObject::invokeMethod(m_pTextToSpeech,say,Qt::QueuedConnection,Q_ARG(QString,aud.strContent));isStateChanged false;}}else{isStateChanged true;}msleep(200);}
}结束语
很多时候发现只有帮助文档是不够的源码才是真理。