QT使用websocket实现语音对讲

news2024/12/27 3:52:24

简介:

    本文所描述的功能和代码,是基于QT的开发环境。在QT上使用websocket,接受和发送pcm音频,实现了语音对讲功能。经自测,该功能可以正常使用,以下是相关代码的分享。

在这里插入图片描述

void MainWindow::on_pushButton_OpenTalk_clicked()
{
    QString sUrl = ui->lineEdit_ws->text();
    ctWsTalk::getInstance().startWsTalk(sUrl);
}
void MainWindow::on_pushButton_CloseTalk_clicked()
{
    ctWsTalk::getInstance().stopWsTalk();
}

ctWsTalk.h

#ifndef CTWSTALK_H
#define CTWSTALK_H

#include <QAudio>
#include <QFile>
#include <QElapsedTimer>
#include <list>
#include <QWebSocket>
#include <QObject>

class ctWsTalk : public QObject
{
    Q_OBJECT
public:
    ctWsTalk();
    ~ctWsTalk();

    static ctWsTalk& getInstance();

    void startWsTalk(QString url);
    void stopWsTalk();

public slots:
    void onConnected();
    void onDisconnected();
    void onBinaryMessageReceived(const QByteArray& data);

private slots:
    void handleStateChanged_input(QAudio::State newState);
    void handleAudioNotify();
    void handleAudioData();

private:
    void wsClientStart();
    void wsClientClose();
    void wsSendBinary(const QByteArray& binaryData);
    void initMicrophoneInput();
    void initSpeakerOutput();
    void startIntercom();
    void stopIntercom();

private:
    bool m_bWsConnect = false;
    int m_nAudioSize = 0;
    QFile m_fileIn;
    QFile m_fileOut;
    QElapsedTimer m_timer;
    QString m_sUrl;

    class QAudioInput* m_pAudioInput = nullptr;
    class QAudioOutput* m_pAudioOutput = nullptr;
    class QIODevice* m_pInputDevice = nullptr;
    QIODevice* m_pOutputDevice = nullptr;
    QWebSocket m_websocket;
    std::list<QByteArray> m_audioinDataList;

};

#endif // CTWSTALK_H

ctWsTalk.cpp

#include "ctwstalk.h"
#include <QAudioFormat>
#include <QAudioDeviceInfo>
#include <QAudioInput>
#include <QAudioOutput>
#include <QBuffer>
#include <QDebug>
#include <QDateTime>

#define WRITE_INTO_PCM 1

ctWsTalk::ctWsTalk()
{
    m_timer.start();
}

ctWsTalk::~ctWsTalk()
{
   stopWsTalk();
}

ctWsTalk &ctWsTalk::getInstance()
{
    static ctWsTalk s_obj;
    return s_obj;
}

void ctWsTalk::startWsTalk(QString url)
{
    qDebug() << "ctWsTalk::startWsTalk url:" << url;
    m_sUrl = url;

#ifdef WRITE_INTO_PCM
    if (!m_fileOut.isOpen())
    {
        m_fileOut.setFileName("AudioOut.pcm");
        m_fileOut.open(QIODevice::WriteOnly);
        m_timer.restart();
    }
#endif

#ifdef WRITE_INTO_PCM
    if (!m_fileIn.isOpen())
    {

        m_fileIn.setFileName("AudioIn.pcm");
        m_fileIn.open(QIODevice::WriteOnly);
    }
#endif

    wsClientStart();
    startIntercom();
}

void ctWsTalk::stopWsTalk()
{
#ifdef WRITE_INTO_PCM
    m_fileOut.close();
    m_fileIn.close();
#endif
    stopIntercom();
    wsClientClose();
}

void ctWsTalk::handleStateChanged_input(QAudio::State newState)
{
    switch (newState)
    {
        case QAudio::StoppedState:
            if (m_pAudioInput->error() != QAudio::NoError)
            {
                qDebug() << "AudioInput Error.";
            }
            break;
        default:
            break;
    }
}

void ctWsTalk::handleAudioNotify()
{
    if (m_audioinDataList.size())
    {
        auto data = m_audioinDataList.front();
        m_pOutputDevice->write(data);
        m_audioinDataList.pop_front();
    }
    if (m_nAudioSize)
        qDebug() << "Audio Recv, list size=" << m_audioinDataList.size() << ", recv audio interval(ms)=" << m_timer.elapsed() / m_nAudioSize;
}

void ctWsTalk::handleAudioData()
{
    // 读取音频数据
    QByteArray audioData = m_pInputDevice->readAll();
    wsSendBinary(audioData);

    qDebug() << "Audio Send, audioData.size():" << audioData.size();

#ifdef WRITE_INTO_PCM
    m_fileOut.write(audioData);
#endif
}

void ctWsTalk::onConnected()
{
    qDebug() << "hello world! Connected.";
    m_bWsConnect = true;
}

void ctWsTalk::onDisconnected()
{
    qDebug() << "Disconnected.";
    m_bWsConnect = false;
}

void ctWsTalk::onBinaryMessageReceived(const QByteArray &binaryData)
{
#ifdef WRITE_INTO_PCM
    m_fileIn.write(binaryData);
#endif
    if (!m_pOutputDevice)
        return;
    m_audioinDataList.push_back(binaryData);
    m_nAudioSize++;
}

void ctWsTalk::wsClientStart()
{
    QNetworkProxy proxy;
    proxy.setType(QNetworkProxy::NoProxy);
    m_websocket.setParent(this);
    m_websocket.setProxy(proxy);

    connect(&m_websocket, SIGNAL(connected()), this, SLOT(onConnected()));
    connect(&m_websocket, SIGNAL(disconnected()), this, SLOT(onDisconnected()));
    connect(&m_websocket, SIGNAL(binaryMessageReceived(const QByteArray&)), this, SLOT(onBinaryMessageReceived(const QByteArray&)), Qt::QueuedConnection);

    m_websocket.open(QUrl(m_sUrl));
    if (m_websocket.error())
    {
        qWarning() << m_websocket.error() << m_websocket.errorString();
    }
}

void ctWsTalk::wsClientClose()
{
    m_websocket.close();
}

void ctWsTalk::wsSendBinary(const QByteArray &binaryData)
{
    if (!m_bWsConnect)
        return;
    m_websocket.sendBinaryMessage(binaryData);
}

// 初始化麦克风输入
void ctWsTalk::initMicrophoneInput()
{
    // 配置音频输入参数
    QAudioFormat format;
    format.setSampleRate(8000);     // 设置采样率
    format.setChannelCount(1);      // 设置通道数
    format.setSampleSize(16);       // 设置样本大小
    format.setCodec("audio/pcm");   // 设置编解码器
    format.setByteOrder(QAudioFormat::LittleEndian);
    format.setSampleType(QAudioFormat::SignedInt);

    //选择设备作为输入源
    QAudioDeviceInfo info = QAudioDeviceInfo::defaultInputDevice();
    qDebug() << "The name of the inputDeviceInfo: " << info.deviceName();

    //判断输入的格式是否支持,如果不支持就使用系统支持的默认格式
    if (!info.isFormatSupported(format))
    {
        format = info.nearestFormat(format);
    }
    //当前设备支持的编码
    qDebug() << "当前设备支持的编码格式:";
    QStringList list = info.supportedCodecs();
    for (int i = 0; i < list.size(); i++)
    {
        auto text = list.at(i) + " ";
        qDebug() << text;
    }
    qDebug() << "输入音频采样率=" << format.sampleRate();
    qDebug() << "输入音频通道数=" << format.channelCount();
    qDebug() << "输入音频样本大小=" << format.sampleSize();
    qDebug() << "输入音频编码格式=" << format.codec();

    // 创建音频输入对象
    if (!m_pAudioInput)
        m_pAudioInput = new QAudioInput(info, format);
    if (m_pAudioInput)
    {
        connect(m_pAudioInput, &QAudioInput::stateChanged, this, &ctWsTalk::handleStateChanged_input);
        m_pAudioInput->setNotifyInterval(17);
        connect(m_pAudioInput, &QAudioInput::notify, this, &ctWsTalk::handleAudioNotify);
        m_pInputDevice = m_pAudioInput->start();
        connect(m_pInputDevice, &QIODevice::readyRead, this, &ctWsTalk::handleAudioData);
    }
    m_nAudioSize = 0;
}

// 初始化扬声器输出
void ctWsTalk::initSpeakerOutput()
{
    // 获取默认音频输出设备
    QAudioDeviceInfo outputDeviceInfo = QAudioDeviceInfo::defaultOutputDevice();
    qDebug() << "The name of output device: " << outputDeviceInfo.deviceName();

    if (m_pAudioInput)
    {
        QAudioFormat format = m_pAudioInput->format();
        // 创建音频输出对象
        m_pAudioOutput = new QAudioOutput(outputDeviceInfo, format);
        if (m_pAudioOutput)
            m_pOutputDevice = m_pAudioOutput->start();
    }
}

void ctWsTalk::startIntercom()
{
    initMicrophoneInput();
    initSpeakerOutput();
}

void ctWsTalk::stopIntercom()
{
    if (m_pAudioInput) {
        m_pAudioInput->stop();
        m_pAudioInput->deleteLater();
        m_pAudioInput = nullptr;
    }
    if (m_pAudioOutput) {
        m_pAudioOutput->stop();
        m_pAudioOutput->deleteLater();
        m_pAudioOutput = nullptr;
    }
    if (m_pInputDevice)
        m_pInputDevice->close();
}

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

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

相关文章

Linux学习笔记(七):磁盘的挂载与扩展

Linux学习笔记&#xff08;七&#xff09;&#xff1a;磁盘的挂载与扩展 在虚拟机环境中&#xff0c;当我们的存储空间不足时&#xff0c;添加一块新的硬盘显得尤为重要。 1. 新增磁盘 首先&#xff0c;你需要确保有一块物理磁盘或虚拟磁盘。在虚拟机管理器中&#xff0c;你可以…

1.4TB! 全台湾2024年三维建筑模型3DTiles数据

在今年1月13日&#xff0c;我写了一篇文章&#xff0c;详细介绍了了全台湾2023年三维建筑模型数据以及数据背景。隔了8个月之后&#xff0c;我对全岛建筑模型数据进行了更新,不仅在数量上有增长&#xff0c;而且数据显示性能也进行了优化&#xff0c;下面我针对对2024年数据进行…

探索Python文本处理的新境界:textwrap库揭秘

文章目录 **探索Python文本处理的新境界&#xff1a;textwrap库揭秘**一、背景介绍二、textwrap库是什么&#xff1f;三、如何安装textwrap库&#xff1f;四、简单函数使用方法4.1 wrap()4.2 fill()4.3 shorten()4.4 dedent()4.5 indent() 五、实际应用场景5.1 格式化日志输出5…

黑龙江等保测评详细指南

一、什么是等保测评&#xff1f; 等保&#xff08;信息安全等级保护&#xff09;是指根据信息系统的重要性和安全需求&#xff0c;对其进行分级保护的制度。黑龙江省的等保测评旨在评估信息系统的安全性&#xff0c;确保其符合国家和地方的安全标准。 二、等保测评的必要性 1…

OpenAI重磅发布Canvas:跟ChatGPT一起写作编程

现在是大半夜1点56&#xff0c;国庆第三天&#xff0c;我想睡觉&#xff0c;真的。 但是&#xff0c;ChatGPT更新了&#xff0c;虽然不是那种王炸级的新模型模型更新&#xff0c;但是更新了一个极度优雅&#xff0c;对普通人极度友好的功能。 而且&#xff0c;顺带&#xff0…

ASB:LLM智能体应用攻防测试数据集

ABS&#xff1a;LLM智能体应用攻防测试数据集 Agent应用 Agent Security Bench (ASB): Formalizing and Benchmarking Attacks and Defenses in LLM-based Agents 尽管基于 LLM 的代理能够通过外部工具和记忆机制解决复杂任务&#xff0c;但也可能带来严重安全风险。现有文献对…

地图可视化的艺术:深入比较Mapbox、OpenLayers、Leaflet和Cesium,不同场景下应如何选择地图库

目录 地图可视化的艺术&#xff1a;深入比较Mapbox、OpenLayers、Leaflet和Cesium 一、总览 二、定制地图美学的先行者——Mapbox 1、主要功能特点 2、开源情况 3、市场与应用人群 4、安装与基础使用代码 三、开源GIS地图库的全能王——OpenLayers 1、主要功能特点 2…

重要的事情说两遍!Prompt「复读机」,显著提高LLM推理能力

【导读】 尽管大模型能力非凡&#xff0c;但干细活的时候还是比不上人类。为了提高LLM的理解和推理能力&#xff0c;Prompt「复读机」诞生了。 众所周知&#xff0c;人类的本质是复读机。 我们遵循复读机的自我修养&#xff1a;敲黑板&#xff0c;划重点&#xff0c;重要的事…

原生input实现时间选择器用法

2024.10.08今天我学习了如何用原生的input&#xff0c;实现时间选择器用法&#xff0c;效果如下&#xff1a; 代码如下&#xff1a; <div><input id"yf_start" type"text"> </div><script>$(#yf_start).datepicker({language: zh…

ELK中L的filebeat配置及使用(超详细)

上一次讲解了如何在linux服务器上使用docker配置ELK中的E和K&#xff0c;这期着重讲解一下L怎么配置。 首先L在elk中指的是一个数据处理管道&#xff0c;可以从多种来源收集数据&#xff0c;进行处理和转换&#xff0c;然后将数据发送到 Elasticsearch。L的全称就是&#xff1…

国外电商系统开发-运维系统文件下载

文件下载&#xff0c;作者设计的比较先进&#xff0c;如果下载顺利&#xff0c;真的还需要点两次鼠标&#xff0c;所有的远程文件就自动的下载到了您的PC电脑上了。 现在&#xff0c;请您首选选择要在哪些服务器上下载文件&#xff1a; 选择好了服务器以后&#xff0c;现在选择…

【智能算法应用】人工水母搜索算法求解二维路径规划问题

摘要 本文应用人工水母搜索算法&#xff08;Jellyfish Search, JFS&#xff09;求解二维空间中的路径规划问题。水母搜索算法是一种新型的智能优化算法&#xff0c;灵感来源于水母的群体运动行为&#xff0c;通过模仿水母的觅食、漂浮等行为&#xff0c;实现全局最优路径的搜索…

51单片机基本知识

51单片机的基本知识 一、单片机介绍 单片机是单片微型计算机的简称&#xff0c;把各种功能部件包括中央处理器&#xff08;CPU&#xff09;、只读存储器&#xff08;ROM&#xff09;、随机读写存储器&#xff08;RAM&#xff09;、输入输出&#xff08;I/O&#xff09;单元、…

算法闭关修炼百题计划(四)

仅供个人复习 1.两数相加2.寻找峰值3.寻找旋转排序数组中的最小值4.寻找旋转排序数组中的最小值II5.搜索旋转排序数组6.岛屿的最大面积7.最大数8.会议室9.最长连续序列 1.两数相加 给你两个 非空 的链表&#xff0c;表示两个非负的整数。它们每位数字都是按照 逆序 的方式存储…

【微服务】网关 - Gateway(上)(day7)

概述 引入 在前几个系列中&#xff0c;使用Eureka、Consul、Nacos解决了服务注册、服务发现的问题&#xff1b;使用SpringCloudLoadBalancer解决了负载均衡的问题&#xff1b;使用OpenFeign解决了远程调用的问题。 但是现在所有的微服务接口都是直接对外暴露的&#xff0c;可…

【优选算法】(第二十八篇)

目录 K个⼀组翻转链表&#xff08;hard&#xff09; 题目解析 讲解算法原理 编写代码 两数之和&#xff08;easy&#xff09; 题目解析 讲解算法原理 编写代码 K个⼀组翻转链表&#xff08;hard&#xff09; 题目解析 1.题目链接&#xff1a;. - 力扣&#xff08;Leet…

win11 24H2怎么安装_u盘安装win11 24H2详细步骤【支持新旧机型安装】

10月1日&#xff0c;微软正式发布了Windows 11 24H2正式版。对于win11 24h2新机器安装肯定是可以的&#xff0c;对于旧电脑在硬件配置上可能无法满足Windows 11 24h2的最低系统要求&#xff0c;如果按官方要求是无法安装win11的。但是如果采用第三方pe方式安装的话&#xff0c;…

Android Studio实现安卓图书管理系统

获取源码请点击文章末尾QQ名片联系&#xff0c;源码不免费&#xff0c;尊重创作&#xff0c;尊重劳动 171安卓小说 1.开发环境 android stuido3.6 jak1.8 2.功能介绍 安卓端&#xff1a; 1.注册登录 2.图书列表 3.图书借阅 4.借阅列表 3.系统截图

VSCode使用Code Runner插件运行时,路径错误问题

1. 问题介绍 由于Code Runner插件的工作目录与文件执行目录不同&#xff0c;而导致路径错误&#xff01; 示例演示&#xff1a; 创建根目录test-dir&#xff0c;然后在里面分别创建两个目录code和data&#xff0c;分别存放Python程序read_file.py和输入数据input.txt read_fi…

PCIe配置篇(2)——如何进行配置操作(二)

一、配置机制 我们之前提到过&#xff0c;配置空间存在于PCIe设备上&#xff0c;而处理器通常无法直接执行配置读写请求&#xff0c;因为它只能生成内存和I/O请求。这意味着RC&#xff08;Root Complex&#xff09;需要将某些访问请求转换为配置请求&#xff0c;以支持配置空间…