QTextToSpeech的使用——Qt

news2024/11/15 21:23:15

前言

之前随便看了几眼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.0
                if(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.0
    if(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.0
    if(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.0
                if(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.0
                if(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.coloradmin.cn/o/1518203.html

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

相关文章

Day34:安全开发-JavaEE应用反射机制攻击链类对象成员变量方法构造方法

目录 Java-反射-Class对象类获取 Java-反射-Field成员变量类获取 Java-反射-Method成员方法类获取 Java-反射-Constructor构造方法类获取 Java-反射-不安全命令执行&反序列化链构造 思维导图 Java知识点 功能&#xff1a;数据库操作&#xff0c;文件操作&#xff0c;…

Redis:使用redis-dump导出、导入、还原数据实例

redis的备份和还原&#xff0c;借助了第三方的工具&#xff0c;redis-dump 1、安装必要环境 yum -y install zlib-devel openssl-devel2、安装redis-dump 安装ruby&#xff1a; ruby下载地址&#xff1a;https://www.ruby-lang.org/zh_cn/downloads/ 我下载的是 2.5.0 版本…

GPT-SoVITS开源音色克隆框架的训练与调试

GPT-SoVITS开源框架的报错与调试 遇到的问题解决办法 GPT-SoVITS是一款创新的跨语言音色克隆工具&#xff0c;同时也是一个非常棒的少样本中文声音克隆项目。 它是是一个开源的TTS项目&#xff0c;只需要1分钟的音频文件就可以克隆声音&#xff0c;支持将汉语、英语、日语三种…

Linux ping可以上内网,不能上外网(系统重启DNS不生效)(直接更改/etc/resolv.conf修改nameserver重启被重置)

在Linux上可以连内网&#xff0c;不能连外网 ping内网可以&#xff0c;外网不行 可能的问题&#xff1a; 1.可能设置了网络防火墙&#xff0c;阻止了服务器访问外网的请求 2.DNS解析问题&#xff1a;检查服务器的DNS设置是否正确&#xff0c;能够正确解析外部域名。如果DNS解析…

案例分析篇15:软件开发方法考点(2024年软考高级系统架构设计师冲刺知识点总结系列文章)

专栏系列文章推荐: 2024高级系统架构设计师备考资料(高频考点&真题&经验)https://blog.csdn.net/seeker1994/category_12593400.html 【历年案例分析真题考点汇总】与【专栏文章案例分析高频考点目录】(2024年软考高级系统架构设计师冲刺知识点总结-案例分析篇-…

Vue首屏优化方案

在Vue项目中&#xff0c;引入到工程中的所有js、css文件&#xff0c;编译时都会被打包进vendor.js&#xff0c;浏览器在加载该文件之后才能开始显示首屏。若是引入的库众多&#xff0c;那么vendor.js文件体积将会相当的大&#xff0c;影响首屏的体验。可以看个例子&#xff1a;…

Linux搭建我的世界(MC)整合包服务器,All the Mods 9(ATM9)整合包开服教程

Linux使用MCSM面板搭建我的世界(Minecraft)整合包服务器&#xff0c;MC开服教程&#xff0c;All the Mods 9(ATM9)整合包搭建服务器的教程。 本教程使用Docker来运行mc服&#xff0c;可以方便切换不同Java版本&#xff0c;方便安装多个mc服版本。 视频教程&#xff1a;https:…

基于SpringBoot+Vue的电商应用系统的设计与实现

1 绪论 1.1研究背景 当前社会各行业领域竞争压力非常大&#xff0c;随着当前时代的信息化&#xff0c;科学化发展&#xff0c;让社会各行业领域都争相使用新的信息技术&#xff0c;对行业内的各种相关数据进行科学化&#xff0c;规范化管理。这样的大环境让那些止步不前&…

工具篇--从零开始学Git

一、git概述 1.1版本控制方式 集中式版本控制工具 集中式版本控制工具&#xff0c;版本库是集中存放在中央服务器的&#xff0c; team 里每个人 work 时从中央服务器下载代码&#xff0c;是必须联网才能工作&#xff0c; 局域网或互联网&#xff0c;个人修改之…

NB-IoT模块

目录 一. NB-IoT模块实物图 二. BC20/NB-IoT模块产品规格 三. 指令顺序 1. AT判断BC20模组是否正常 2. ATE0返回OK&#xff0c;已经返回回显 3. ATCSQ 4. AT_CEREG? 5. ATCGATT? 6. ATCGATT? 四. OneNet 连接 1. AT 查看 NB(当前NB)&#xff0c;云平台根据这两个…

Three 材质纹理 (总结三)

THREE.MeshLambertMaterial&#xff08;网格 Lambert 材质&#xff09; 该材质使用基于非物理的Lambertian模型来计算反射率。可以用来创建暗淡的并不光亮的表面&#xff0c;该材质非常易用&#xff0c;而且会与场景中的光源产生反应。 MeshLambertMaterial属性 # .color : …

OpenCASCADE开发指南:专栏内容介绍及目录

1、专栏介绍 1.1 几何内核 在三维建模软件和仿真软件中&#xff0c;我们经常能听到到“几何内核”一词。那么几何内核是究竟什么是几何建模内核&#xff1f;先说结论&#xff1a;几何没有内核&#xff0c;就没有CAD软件&#xff01; 从软件的历史中可以清楚地看到&#xff0c;…

win10txt文件丢失怎么找回?六大方法,总有一款适合你

在日常工作和生活中&#xff0c;TXT文件作为一种常见的纯文本格式&#xff0c;经常用于记录笔记、保存代码或存储其他文本信息。然而&#xff0c;有时我们可能会因为各种原因&#xff0c;导致Win10系统中的TXT文件丢失。面对这种情况&#xff0c;许多用户可能会感到焦虑和无助。…

【读论文】【精读】3D Gaussian Splatting for Real-Time Radiance Field Rendering

文章目录 1. What&#xff1a;2. Why&#xff1a;3. How&#xff1a;3.1 Real-time rendering3.2 Adaptive Control of Gaussians3.3 Differentiable 3D Gaussian splatting 4. Self-thoughts 1. What&#xff1a; What kind of thing is this article going to do (from the a…

FL Studio21水果软件2024最新中文版功能介绍及下载

FL Studio21&#xff0c;也被众多用户亲切地称为“水果”&#xff0c;是一款功能强大的数字音乐工作站软件。它为用户提供了一个完整的音乐制作环境&#xff0c;从编曲、录音、编辑到混音&#xff0c;几乎涵盖了音乐制作的所有环节。 FL Studio 21 Win-安装包下载如下: https:…

蓝桥杯深度优先搜索|剪枝|N皇后问题|路径之谜(C++)

搜索&#xff1a;暴力法算法思想的具体实现 搜索&#xff1a;通用的方法&#xff0c;一个问题如果比较难&#xff0c;那么先尝试一下搜索&#xff0c;或许能启发出更好的算法 技巧&#xff1a;竞赛时遇到不会的难题&#xff0c;用搜索提交一下&#xff0c;说不定部分判题数据很…

30-Java数据访问对象模式 ( Data Access Object )

Java数据访问对象模式 实现范例 数据访问对象模式&#xff08;Data Access Object Pattern&#xff09;或 DAO 模式用于把低级的数据访问 API 或操作从高级的业务服务中分离出来数据访问模式涉及到的参与者有&#xff1a; 数据访问对象接口&#xff08;Data Access Object Inte…

计算机视觉——目标检测(R-CNN、Fast R-CNN、Faster R-CNN )

前言、相关知识 1.闭集和开集 开集&#xff1a;识别训练集不存在的样本类别。闭集&#xff1a;识别训练集已知的样本类别。 2.多模态信息融合 文本和图像&#xff0c;文本的语义信息映射成词向量&#xff0c;形成词典&#xff0c;嵌入到n维空间。 图片内容信息提取特征&…

五星门店小程序性能优化实践

一、背景介绍 1.1 业务介绍 五星门店小程序主要服务于五星线下门店交易场景&#xff0c;目前已有79个城市267家门店&#xff08;包括超级体验店、城旗店、京东Mall等&#xff09;在使用&#xff0c;用户可以通过小程序便捷地查看和购买门店的商品。五星门店小程序已实现基于T…

用Stable Diffusion生成同角色不同pose的人脸

随着技术的不断发展&#xff0c;我们现在可以使用稳定扩散技术&#xff08;Stable Diffusion&#xff09;来生成同一角色但不同姿势的人脸图片。本文将介绍这一方法的具体步骤&#xff0c;以及如何通过合理的提示语和模型选择来生成出更加真实和多样化的人脸图像。 博客首发地…