QTextToSpeech的使用——Qt

前言

之前随便看了几眼QTextToSpeech的帮助就封装使用了,达到了效果就没再管了,最近需要在上面加功能(变换语速),就写了个小Demo后,发现不对劲了。

出现的问题

场景

写了个队列添加到语音播放子线程中,在run循环查询tts引擎状态,来依次播放。代码如下:

void AudioThread::run()
{while (m_iRunning){if(m_audioQueue.size() != 0&&m_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 QQueue<SingleAudio> &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() != 0&&isStateChanged){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 <QDebug>AudioThread::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 QVector<QLocale>& 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 QQueue<SingleAudio> &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() != 0&&isStateChanged){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);}
}

结束语

很多时候发现只有帮助文档是不够的,源码才是真理。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/745695.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

HTTPS基础

目录 HTTPS简介 HTTP与HTTPS的区别 CA证书 案例 服务器生成私钥与证书 查看证书和私钥存放路径 Cockpit(图像化服务管理工具) HTTPS简介 超文本传输协议HTTP协议被用于在Web浏览器和网站服务器之间传递信息。HTTP协议以明文方式发送内容&#xff0c;不提供任何方式的数据加密&…

C++——类和对象(1)

1. 面向对象和面向过程对比 当涉及到编程范式时&#xff0c;两个主要的方法是面向对象编程&#xff08;Object-Oriented Programming&#xff0c;OOP&#xff09;和面向过程编程&#xff08;Procedural Programming&#xff09;。这两种编程范式在解决问题和组织代码时有着不同…

COX回归影响因素分析的基本过程与方法

在科学研究中&#xff0c;经常遇到分类的结局&#xff0c;主要是二分类结局&#xff08;阴性/阳性&#xff1b;生存/死亡&#xff09;&#xff0c;研究者可以通过logistic回归来探讨影响结局的因素&#xff0c;但很多时候logistic回归方法无法使用。如比较两种手段治疗新冠肺炎…

Annaconda环境下ChromeDriver配置及爬虫编写

Anaconda环境的chromedriver安装配置_anaconda 配置chromedriver-CSDN博客 Chromedriver驱动( 121.0.6167.85 ) - 知乎 下载好的驱动文件解压&#xff0c;将exe程序复制到Annaconda/Scripts目录以及Chrome/Application目录下 注意要提前pip install selenium包才能运行成功&a…

BEV系列一:BEV介绍和常用BEV算法简介

BEV系列一&#xff1a;BEV介绍和常用BEV算法简介 自动驾驶最全学习资料获取&#xff1a;链接

Linux操作系统——线程概念

1.什么是线程&#xff1f; 在一个程序里的一个执行路线就叫做线程&#xff08;thread&#xff09;。更准确的定义是&#xff1a;线程是“一个进程内部的控制序列”一切进程至少都有一个执行线程线程在进程内部运行&#xff0c;本质是在进程地址空间内运行在Linux系统中&#x…

openGauss学习笔记-242 openGauss性能调优-SQL调优-典型SQL调优点-SQL自诊断

文章目录 openGauss学习笔记-242 openGauss性能调优-SQL调优-典型SQL调优点-SQL自诊断242.1 SQL自诊断242.1.1 告警场景242.1.2 规格约束 openGauss学习笔记-242 openGauss性能调优-SQL调优-典型SQL调优点-SQL自诊断 SQL调优是一个不断分析与尝试的过程&#xff1a;试跑Query&…

【Qt】常用控件或属性(1)

需要云服务器等云产品来学习Linux可以移步/-->腾讯云<--/官网&#xff0c;轻量型云服务器低至112元/年&#xff0c;新用户首次下单享超低折扣。 目录 一、QWidget属性一览 二、控件button、属性enabled(可用状态) 三、属性geometry(修改位置和尺寸) 1、QRect类型的结…

微信小程序之tabBar

1、tabBar 如果小程序是一个多 tab 应用&#xff08;客户端窗口的底部或顶部有 tab 栏可以切换页面&#xff09;&#xff0c;可以通过 tabBar 配置项指定 tab 栏的表现&#xff0c;以及 tab 切换时显示的对应页面。 属性类型必填默认值描述colorHexColor是tab 上的文字默认颜色…

Leetcode 3.14

Leetcode hot100 二叉树1.二叉树的层序遍历2.验证二叉搜索树3.二叉树的右视图 二叉树 1.二叉树的层序遍历 二叉树的层序遍历 二叉树的层序遍历可以用先进先出的队列来实现。 将每一层的所有node都添加到队列中&#xff0c;记录下当前队列的长度&#xff0c;即该层的元素数量&…

『 Linux 』进程替换( Process replacement ) 及 简单Shell的实现(万字)

文章目录 &#x1f984; 进程替换&#x1f9a9; execl()函数&#x1f9a9; execlp()函数&#x1f9a9; execle()函数&#x1f9a9; execv()函数&#x1f9a9; execvp()函数&#x1f9a9; execvpe()函数&#x1f9a9; execve()函数 &#x1f984; 简单Shell命令行解释器的实现&a…

【漏洞复现】ChatGPT-Next-Web SSRF漏洞

免责声明&#xff1a;文章来源互联网收集整理&#xff0c;请勿利用文章内的相关技术从事非法测试&#xff0c;由于传播、利用此文所提供的信息或者工具而造成的任何直接或者间接的后果及损失&#xff0c;均由使用者本人负责&#xff0c;所产生的一切不良后果与文章作者无关。该…

Python-sklearn-diabetes项目实战

目录 1 下载数据集和预处理 1.1 加载/下载数据集 1.2 数据可视化 1.3 数据清洗 1.4 特征工程 1.5 构建特征集和标签集 1.6 拆分训练集和测试集 2 训练模型 2.1 选择算法和确定模型 2.2 训练拟合模型 3 评估并优化模型性能 本文以糖尿病数据集diabetes为基础进行线性…

【Node.js从基础到高级运用】十一、构建RESTful API

在本篇博客中&#xff0c;我们将综合之前讨论的内容&#xff0c;深入探索如何使用Node.js构建一个RESTful API。我们将重点讨论设计合理的API端点&#xff0c;展示如何通过代码实现这些端点&#xff0c;并指导如何使用Postman测试我们的API&#xff0c;确保其按预期工作。 前提…

完美解决 RabbitMQ可视化界面Overview不显示折线图和队列不显示Messages

问题场景&#xff1a; 今天使用docker部署了一个RabbitMQ&#xff0c;浏览器打开15672可视化页面发送消息后不显示Overview中的折线图&#xff0c;还有队列中的Messages&#xff0c;因为我要看队列中的消息数量。 解决方案&#xff1a; 进入容器内部 docker exec -it 容器id…

Spring Cloud Gateway如何实现熔断

Spring Cloud Gateway熔断集成 熔断应用&#xff1a; 金融市场中的熔断机制&#xff1a;在金融交易系统中&#xff0c;熔断机制&#xff08;Circuit Breaker&#xff09;是一种市场保护措施&#xff0c;旨在预防市场剧烈波动时可能导致的系统性风险。当某个基准指数&#xff08…

MySQL数据库(5)——逻辑处理

逻辑处理 在MySQL中&#xff0c;可以使用逻辑运算符和条件语句来进行逻辑处理。 1.逻辑运算符 and 与or 或not 非 这里用法与Java中的用法相同&#xff0c;只是语法发生了部分变化。 2.case——when逻辑处理 语法&#xff1a; case when 条件 then 结果1 else 结果2 end 注…

基于springboot实现驾校信息管理系统项目【项目源码+论文说明】计算机毕业设计

基于springboot实现驾校信息管理系统演示 摘要 随着人们生活水平的不断提高&#xff0c;出行方式多样化&#xff0c;也以私家车为主&#xff0c;那么既然私家车的需求不断增长&#xff0c;那么基于驾校的考核管理也就不断增强&#xff0c;那么业务系统也就慢慢的随之加大。信息…

15. jwt认证中间件

在上一篇登录功能的实现中&#xff0c;我们使用了jwt作为鉴权组件&#xff0c;其中登录后会颁发token。前端在访问后续请求时&#xff0c;可以带上这个token。对于一些需要权限校验的请求&#xff0c;我们就需要验证这个token&#xff0c;从token中获取到用户id&#xff08;有了…

MQTT.fx实现(MQTT Client)连接物联网平台(ThingsCloud )

目录 概述 1 ThingsCloud平台介绍 2 ThingsCloud创建项目 2.1 创建项目 2.2 创建数据类型 2.3 创建设备类型 3 发布和订阅主题 3.1 发布Topic 3.2 订阅主题Topic 4 使用MQTT.fx 测试ThingsClond 4.1 使用MQTT.fx连接ThingsClond 4.2 MQTT.fx 订阅ThingsCloud Messag…