《QT实用小工具·五十七》基于QT的语音识别

1、概述
源码放在文章末尾

该文章实现了简单的语音识别功能,首先,语音识别要做三件事情 :
1.记录用户的语音文件到本地
2.将用户语音编码 使用flac或者speex进行编码
3.使用第三方语音识别API或者SDK进行分析识别语音 目前做的比较简单就是使用flac文件对wav音频文件进行编码 基于Mac OSX和Win 7平台的 win 7下使用flac.exe,具体exe帮助,读者可以使用flac.exe --help > help.txt 重定向到一个help文件中,方便查阅. mac osx下面安装flac.dmg的安装包即可使用flac命令 我们先看音频的录入 Qt集成了音频模块

项目部分代码如下所示:


/*
 * Based on Qt Example
 * PCM2WAV is not mine, I found it in Google and modified it.
 */

#ifndef SPEECHINPUT
#define SPEECHINPUT

#include <QPixmap>
#include <QWidget>
#include <QObject>
#include <QPushButton>
#include <QByteArray>
//#include <Phonon/AudioOutput>
#include <QtMultimedia>
#include <QIODevice>
#include <QFile>

class WavPcmFile : public QFile {
public:
        WavPcmFile(const QString & name, const QAudioFormat & format, QObject *parent = 0);
        bool open();
        void close();

private:
        void writeHeader();
        bool hasSupportedFormat();
        QAudioFormat format;
};

class AudioInfo : public QIODevice
{
        Q_OBJECT
public:
        AudioInfo(const QAudioFormat &format, QObject *parent, const QString &filename = "./data/tmp/speechInput.wav");
        ~AudioInfo();

        void start();
        void stop();

        qreal level() const { return m_level; }

        qint64 readData(char *data, qint64 maxlen);
        qint64 writeData(const char *data, qint64 len);

private:
        const QAudioFormat m_format;
        quint16 m_maxAmplitude;
        qreal m_level; // 0.0 <= m_level <= 1.0

        WavPcmFile * m_file;

signals:
        void update();
};


class RenderArea : public QPushButton
{
        Q_OBJECT

public:
        RenderArea(QWidget *parent = 0);

        void setLevel(qreal value);

protected:
        void paintEvent(QPaintEvent *event);

private:
        qreal m_level;
        QPixmap m_pixmap;
};

#endif

/*
 * Based on Qt Example
 * PCM2WAV is not mine, I found it in Google and modified it.
 */

#include "speechInput.h"

#include <QtEndian>
#include <QDebug>
#include <QPainter>

WavPcmFile::WavPcmFile(const QString & name, const QAudioFormat & format_, QObject *parent_)
        : QFile(name, parent_), format(format_)
{
}

bool WavPcmFile::hasSupportedFormat()
{
        return (format.sampleSize() == 8
                && format.sampleType() == QAudioFormat::UnSignedInt)
                || (format.sampleSize() > 8
                && format.sampleType() == QAudioFormat::SignedInt
                && format.byteOrder() == QAudioFormat::LittleEndian);
}

bool WavPcmFile::open()
{
        if (!hasSupportedFormat()) {
                setErrorString("Wav PCM supports only 8-bit unsigned samples "
                        "or 16-bit (or more) signed samples (in little endian)");
                return false;
        } else {
                if (!QFile::open(ReadWrite | Truncate))
                        return false;
                writeHeader();
                return true;
        }
}

void WavPcmFile::writeHeader()
{
        QDataStream out(this);
        out.setByteOrder(QDataStream::LittleEndian);

        // RIFF chunk
        out.writeRawData("RIFF", 4);
        out << quint32(0); // Placeholder for the RIFF chunk size (filled by close())
        out.writeRawData("WAVE", 4);

        // Format description chunk
        out.writeRawData("fmt ", 4);
        out << quint32(16); // "fmt " chunk size (always 16 for PCM)
        out << quint16(1);  // data format (1 => PCM)
        out << quint16(format.channelCount());
        out << quint32(format.sampleRate());
        out << quint32(format.sampleRate() * format.channelCount()
                * format.sampleSize() / 8 ); // bytes per second
        out << quint16(format.channelCount() * format.sampleSize() / 8); // Block align
        out << quint16(format.sampleSize()); // Significant Bits Per Sample

        // Data chunk
        out.writeRawData("data", 4);
        out << quint32(0);  // Placeholder for the data chunk size (filled by close())

        Q_ASSERT(pos() == 44); // Must be 44 for WAV PCM
}

void WavPcmFile::close()
{
        // Fill the header size placeholders
        quint32 fileSize = size();

        QDataStream out(this);
        // RIFF chunk size
        seek(4);
        out << quint32(fileSize - 8);

        // data chunk size
        seek(40);
        out << quint32(fileSize - 44);

        QFile::close();
}

AudioInfo::AudioInfo(const QAudioFormat &format, QObject *parent, const QString &filename)
        :   QIODevice(parent)
        ,   m_format(format)
        ,   m_maxAmplitude(0)
        ,   m_level(0.0)

{
        switch (m_format.sampleSize()) {
        case 8:
                switch (m_format.sampleType()) {
                case QAudioFormat::UnSignedInt:
                        m_maxAmplitude = 255;
                        break;
                case QAudioFormat::SignedInt:
                        m_maxAmplitude = 127;
                        break;
                default:
                        break;
                }
                break;
        case 16:
                switch (m_format.sampleType()) {
                case QAudioFormat::UnSignedInt:
                        m_maxAmplitude = 65535;
                        break;
                case QAudioFormat::SignedInt:
                        m_maxAmplitude = 32767;
                        break;
                default:
                        break;
                }
                break;
        default:
                break;
        }

        m_file = new WavPcmFile(filename,format,this);

}

AudioInfo::~AudioInfo()
{
}

void AudioInfo::start()
{
        m_file->open();
        open(QIODevice::WriteOnly);
}

void AudioInfo::stop()
{
        close();
        m_file->close();
}

qint64 AudioInfo::readData(char *data, qint64 maxlen)
{
        Q_UNUSED(data)
                Q_UNUSED(maxlen)

                return 0;
}

qint64 AudioInfo::writeData(const char *data, qint64 len)
{
        if (m_maxAmplitude) {
                Q_ASSERT(m_format.sampleSize() % 8 == 0);
                const int channelBytes = m_format.sampleSize() / 8;
                const int sampleBytes = m_format.channelCount() * channelBytes;
                Q_ASSERT(len % sampleBytes == 0);
                const int numSamples = len / sampleBytes;

                quint16 maxValue = 0;
                const unsigned char *ptr = reinterpret_cast<const unsigned char *>(data);

                for (int i = 0; i < numSamples; ++i) {
                        for(int j = 0; j < m_format.channelCount(); ++j) {
                                quint16 value = 0;

                                if (m_format.sampleSize() == 8 && m_format.sampleType() == QAudioFormat::UnSignedInt) {
                                        value = *reinterpret_cast<const quint8*>(ptr);
                                } else if (m_format.sampleSize() == 8 && m_format.sampleType() == QAudioFormat::SignedInt) {
                                        value = qAbs(*reinterpret_cast<const qint8*>(ptr));
                                } else if (m_format.sampleSize() == 16 && m_format.sampleType() == QAudioFormat::UnSignedInt) {
                                        if (m_format.byteOrder() == QAudioFormat::LittleEndian)
                                                value = qFromLittleEndian<quint16>(ptr);
                                        else
                                                value = qFromBigEndian<quint16>(ptr);
                                } else if (m_format.sampleSize() == 16 && m_format.sampleType() == QAudioFormat::SignedInt) {
                                        if (m_format.byteOrder() == QAudioFormat::LittleEndian)
                                                value = qAbs(qFromLittleEndian<qint16>(ptr));
                                        else
                                                value = qAbs(qFromBigEndian<qint16>(ptr));
                                }

                                maxValue = qMax(value, maxValue);
                                ptr += channelBytes;
                        }
                }

                maxValue = qMin(maxValue, m_maxAmplitude);
                m_level = qreal(maxValue) / m_maxAmplitude;
        }

        m_file->write(data,len);

        emit update();
        return len;
}


RenderArea::RenderArea(QWidget *parent)
        : QPushButton(parent)
{
        setBackgroundRole(QPalette::Base);
        setAutoFillBackground(true);

        m_level = 0;
        setMinimumHeight(30);
        setMinimumWidth(80);

}

void RenderArea::paintEvent(QPaintEvent * /* event */)
{
        QPainter painter(this);
        QPixmap pixmap = QPixmap(":/images/button_default.png").scaled(this->size());
        painter.drawPixmap(this->rect(), pixmap);

//        painter.setPen(Qt::black);
//        painter.drawRect(QRect(painter.viewport().left(),
//                painter.viewport().top(),
//                painter.viewport().right()-20,
//                painter.viewport().bottom()-20));
        if (m_level == 0.0)
                return;
        painter.setPen(Qt::darkGray);
        int pos = ((painter.viewport().right()-20)-(painter.viewport().left()+11))*m_level;
        for (int i = 0; i < 10; ++i) {
                int x1 = painter.viewport().left()+11;
                int y1 = painter.viewport().top()+10+i;
                int x2 = painter.viewport().left()+20+pos;
                int y2 = painter.viewport().top()+10+i;
                if (x2 < painter.viewport().left()+10)
                        x2 = painter.viewport().left()+10;

                painter.drawLine(QPoint(x1+10, y1+10),QPoint(x2+10, y2+10));
        }
}

void RenderArea::setLevel(qreal value)
{
        m_level = value;
        repaint();
}

源码下载

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【微信小程序开发】程序开发(微信登录前后端流程)

简单开发 程序开发微信小程序的目录结构开发简单入门 微信登录流程小程序发布 程序开发 微信小程序的目录结构 一个小程序主体部分由三个文件组成&#xff08;必须放在项目的根目录&#xff09; 文件作用app.js小程序逻辑app.json小程序公共配置app.wxss小程序公共样式表 小…

Vue入门到关门之Vue3项目创建

一、vue3介绍 1、为什么要学习vue3&#xff1f; vue3的变化&#xff1a; 首先vue3完全兼容vue2&#xff0c;但是vue3不建议用vue2的写法&#xff1b;其次&#xff0c;vue3拥抱TypeScript&#xff0c;之前vue2使用的JavaScript&#xff0c;ts完全兼容js 最后之前学的vue2 是…

昂科烧录器支持O2Micro凹凸科技的电池组管理IC OZ7708

芯片烧录行业领导者-昂科技术近日发布最新的烧录软件更新及新增支持的芯片型号列表&#xff0c;其中O2Micro凹凸科技的电池组管理IC OZ7708已经被昂科的通用烧录平台AP8000所支持。 OZ7708是一款高度集成、低成本的电池组管理IC&#xff0c;适用于5~8s Li-Ion/Polymer电池组&a…

245 基于matlab的MEEMD信号分解及重构算法

基于matlab的MEEMD信号分解及重构算法。MEEMD方法的主要步骤包括&#xff1a;1. 定义多元信号集合&#xff0c;将多个信号进行集合&#xff1b;2. 对多元信号集合进行EEMD分解&#xff0c;得到一组IMFs&#xff1b;3. 将相同IMF进行平均&#xff0c;得到改进的IMFs&#xff1b;…

用于图生成的自回归扩散模型 笔记

1 Title Autoregressive Diffusion Model for Graph Generation&#xff08;Lingkai Kong、Jiaming Cui、Haotian Sun、Yuchen Zhuang、B. Aditya Prakash、Chao Zhang&#xff09;【PMLR 2022】 2 Conclusion This study propose an autoregressive diffusion model …

【数据结构】单链表和双链表的基操实现

文章目录 一、链表的概念及结构二、链表的分类三、无头单向非循环链表1.单链表创建2.尾插和头插3.尾删和头删4.打印5.查找6.插入7.删除8.销毁 四、带头双向循环链表1.双链表的创建2.初始化3.判断链表是否为空4.尾插和头插5.尾删和头删6.查找7.插入8.删除9.销毁 五、总结链表和顺…

深入理解回溯算法

大家好&#xff0c;我是 方圆&#xff0c;本篇我们来讲回溯。回溯相当于穷举搜索&#xff0c;它会尝试各种可能的情况直到找到一个满足约束条件的解&#xff0c;寻找解的手段一般通过 DFS 实现&#xff0c;是一个 增量构造答案 的过程。回溯法适用于解决能够将原问题拆分成子问…

OpenSSL实现AES-CBC加解密,可一次性加解密任意长度的明文字符串或字节流(QT C++环境)

本篇博文讲述如何在Qt C的环境中使用OpenSSL实现AES-CBC-Pkcs7加/解密&#xff0c;可以一次性加解密一个任意长度的明文字符串或者字节流&#xff0c;但不适合分段读取加解密的&#xff08;例如&#xff0c;一个4GB的大型文件需要加解密&#xff0c;要分段读取&#xff0c;每次…

常见通信协议

1、串口&#xff1a;&#xff08;串行异步全双工&#xff0c;先发低位&#xff09; 因为是异步的&#xff0c;所以没有时钟线&#xff0c;因为是全双工&#xff0c;所以有两条数据传输线&#xff0c;实现数据的收发。 帧格式 起始位1位&#xff0c;数据位8位&#xff0c;校验…

【C++】stack、queue和priority_queue的模拟实现

在本篇博客中&#xff0c;作者将会讲解STL中的stack、queue和priority_queue的模拟实现&#xff0c;同时还会带大家了解一下deque这个容器。 一.什么是适配器 STL中一共有6大组件&#xff1a;容器&#xff0c;适配器&#xff0c;空间配置器&#xff0c;仿函数&#xff0c;迭代器…

控制台调试 hover 后才出现的元素

调试 hover后才出现的元素 打开开发者工具&#xff0c;鼠标放在hover时才出现的元素上&#xff0c;然后点击右键&#xff1b; 不要选中任何选项&#xff0c;将鼠标移动到开发者工具的调试面板中&#xff1b; 按下N键&#xff0c;此时悬浮的元素不会消失&#xff0c;定位成功。…

Java注解介绍

注解&#xff08;Annotation&#xff09;是Java提供的一种元数据形式&#xff0c;它可以被添加到Java代码的各种元素上&#xff0c;如类、方法、变量、参数等。注解的作用主要包括&#xff1a; 1. 代码文档&#xff1a;注解可以用于生成文档&#xff0c;提高代码的可读性。 2.…

前端之深拷贝

前提&#xff1a; 就是在实际开发中&#xff0c;我有一个编辑的弹窗&#xff0c;可以查看和编辑&#xff0c;因为弹窗里面是一个步骤条&#xff0c;点击下一步就要向对应的接口发送请求&#xff0c;考虑到就比如我点击下一步&#xff0c;此次表箱信息其实不需要修改&#xff0…

大模型_DISC-MedLLM基于Baichuan-13B-Base医疗健康对话

文章目录 DISC-MedLLM介绍概述数据集部署推理流程 DISC-MedLLM 介绍 DISC-MedLLM 是一个专门针对医疗健康对话式场景而设计的医疗领域大模型&#xff0c;由复旦大学数据智能与社会计算实验室 (Fudan-DISC) 开发并开源。 该项目包含下列开源资源: DISC-Med-SFT 数据集 (不包…

智慧园区综合物业管理平台解决方案PPT(130页精品)

我们对智慧园区的理解 智慧园区&#xff0c;是通过信息技术和各类资源的整合&#xff0c;充分降低企业运营成本&#xff0c;提高工作效率&#xff0c;加强各类园区创新、服务和管理能力&#xff0c;为园区铸就一套超强的软实力。智慧园区的实现是多技术融合、多系统融合、多领域…

初识C语言——第十三天

关键字2&#xff1a; static 修饰局部变量&#xff0c;改变了局部变量的生命周期&#xff08;本质上是改变了变量的存储类型&#xff09; static修饰全局变量&#xff0c;使得这个全局变量只能在自己所在的源文件&#xff08;.c)内部可以使用&#xff0c;其他源文件不能使用 …

全方位了解 Meta Llama 3

本文将为您提供 Llama 3 的全面概览&#xff0c;从其架构、性能到未来的发展方向&#xff0c;让您一文了解这一革命性大语言模型的所有要点。 Meta Llama 发展历程 Llama 1 Llama 是由 Meta(FaceBook) AI 发布的一个开源项目&#xff0c;允许商用&#xff0c;影响力巨大。Lla…

基于springboot+vue+Mysql的在线动漫信息平台

开发语言&#xff1a;Java框架&#xff1a;springbootJDK版本&#xff1a;JDK1.8服务器&#xff1a;tomcat7数据库&#xff1a;mysql 5.7&#xff08;一定要5.7版本&#xff09;数据库工具&#xff1a;Navicat11开发软件&#xff1a;eclipse/myeclipse/ideaMaven包&#xff1a;…

Qt | QLCDNumber 类(LCD 数字),LCD 表示液晶显示屏

01、上节回顾 Qt 基础教程合集02、QLCDNumber 1、QLCDNumber 类用于显示类似于 LCD 显示屏上的字符(见右图) ​ 2、QLCDNumber 类是 QFrame 类的直接子类,因此 QLCDNumber 以使用从 QFrame 类继承而来的边框效果 3、QLCDNumber 可显示的符号有:0,1,2,3,4,5,6,7,8,…

ue引擎游戏开发笔记(33)——武器与角色的匹配,将新武器装备到角色身上

1.需求分析&#xff1a; 武器能出现在世界中&#xff0c;完成了第一步&#xff0c;下一步需要角色和武器适配&#xff0c;即不论角色跑动&#xff0c;射击等&#xff0c;武器和角色都相匹配&#xff0c;将武器装备到角色身上。 2.操作实现&#xff1a; 1.首先先把角色原有的武…