QTextToSpeech的使用——Qt

2024-03-15 14:04
文章标签 使用 qt qtexttospeech

本文主要是介绍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);}
}

结束语

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

这篇关于QTextToSpeech的使用——Qt的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/812220

相关文章

中文分词jieba库的使用与实景应用(一)

知识星球:https://articles.zsxq.com/id_fxvgc803qmr2.html 目录 一.定义: 精确模式(默认模式): 全模式: 搜索引擎模式: paddle 模式(基于深度学习的分词模式): 二 自定义词典 三.文本解析   调整词出现的频率 四. 关键词提取 A. 基于TF-IDF算法的关键词提取 B. 基于TextRank算法的关键词提取

使用SecondaryNameNode恢复NameNode的数据

1)需求: NameNode进程挂了并且存储的数据也丢失了,如何恢复NameNode 此种方式恢复的数据可能存在小部分数据的丢失。 2)故障模拟 (1)kill -9 NameNode进程 [lytfly@hadoop102 current]$ kill -9 19886 (2)删除NameNode存储的数据(/opt/module/hadoop-3.1.4/data/tmp/dfs/na

Hadoop数据压缩使用介绍

一、压缩原则 (1)运算密集型的Job,少用压缩 (2)IO密集型的Job,多用压缩 二、压缩算法比较 三、压缩位置选择 四、压缩参数配置 1)为了支持多种压缩/解压缩算法,Hadoop引入了编码/解码器 2)要在Hadoop中启用压缩,可以配置如下参数

Makefile简明使用教程

文章目录 规则makefile文件的基本语法:加在命令前的特殊符号:.PHONY伪目标: Makefilev1 直观写法v2 加上中间过程v3 伪目标v4 变量 make 选项-f-n-C Make 是一种流行的构建工具,常用于将源代码转换成可执行文件或者其他形式的输出文件(如库文件、文档等)。Make 可以自动化地执行编译、链接等一系列操作。 规则 makefile文件

使用opencv优化图片(画面变清晰)

文章目录 需求影响照片清晰度的因素 实现降噪测试代码 锐化空间锐化Unsharp Masking频率域锐化对比测试 对比度增强常用算法对比测试 需求 对图像进行优化,使其看起来更清晰,同时保持尺寸不变,通常涉及到图像处理技术如锐化、降噪、对比度增强等 影响照片清晰度的因素 影响照片清晰度的因素有很多,主要可以从以下几个方面来分析 1. 拍摄设备 相机传感器:相机传

嵌入式QT开发:构建高效智能的嵌入式系统

摘要: 本文深入探讨了嵌入式 QT 相关的各个方面。从 QT 框架的基础架构和核心概念出发,详细阐述了其在嵌入式环境中的优势与特点。文中分析了嵌入式 QT 的开发环境搭建过程,包括交叉编译工具链的配置等关键步骤。进一步探讨了嵌入式 QT 的界面设计与开发,涵盖了从基本控件的使用到复杂界面布局的构建。同时也深入研究了信号与槽机制在嵌入式系统中的应用,以及嵌入式 QT 与硬件设备的交互,包括输入输出设

pdfmake生成pdf的使用

实际项目中有时会有根据填写的表单数据或者其他格式的数据,将数据自动填充到pdf文件中根据固定模板生成pdf文件的需求 文章目录 利用pdfmake生成pdf文件1.下载安装pdfmake第三方包2.封装生成pdf文件的共用配置3.生成pdf文件的文件模板内容4.调用方法生成pdf 利用pdfmake生成pdf文件 1.下载安装pdfmake第三方包 npm i pdfma

零基础学习Redis(10) -- zset类型命令使用

zset是有序集合,内部除了存储元素外,还会存储一个score,存储在zset中的元素会按照score的大小升序排列,不同元素的score可以重复,score相同的元素会按照元素的字典序排列。 1. zset常用命令 1.1 zadd  zadd key [NX | XX] [GT | LT]   [CH] [INCR] score member [score member ...]

git使用的说明总结

Git使用说明 下载安装(下载地址) macOS: Git - Downloading macOS Windows: Git - Downloading Windows Linux/Unix: Git (git-scm.com) 创建新仓库 本地创建新仓库:创建新文件夹,进入文件夹目录,执行指令 git init ,用以创建新的git 克隆仓库 执行指令用以创建一个本地仓库的

【北交大信息所AI-Max2】使用方法

BJTU信息所集群AI_MAX2使用方法 使用的前提是预约到相应的算力卡,拥有登录权限的账号密码,一般为导师组共用一个。 有浏览器、ssh工具就可以。 1.新建集群Terminal 浏览器登陆10.126.62.75 (如果是1集群把75改成66) 交互式开发 执行器选Terminal 密码随便设一个(需记住) 工作空间:私有数据、全部文件 加速器选GeForce_RTX_2080_Ti