QT使用websocket实现语音对讲

news/2024/10/9 5:22:42 标签: qt, websocket, 开发语言

简介:

    本文所描述的功能和代码,是基于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.niftyadmin.cn/n/5695330.html

相关文章

动手学深度学习9.3. 深度循环神经网络-笔记练习(PyTorch)

本节课程地址&#xff1a;58 深层循环神经网络【动手学深度学习v2】_哔哩哔哩_bilibili 本节教材地址&#xff1a;9.3. 深度循环神经网络 — 动手学深度学习 2.0.0 documentation (d2l.ai) 本节开源代码&#xff1a;...>d2l-zh>pytorch>chapter_multilayer-perceptr…

硬件开发笔记(三十一):TPS54331电源设计(四):PCB布板12V转5V电路、12V转3.0V和12V转4V电路

若该文为原创文章&#xff0c;转载请注明原文出处 本文章博客地址&#xff1a;https://hpzwl.blog.csdn.net/article/details/142757509 长沙红胖子Qt&#xff08;长沙创微智科&#xff09;博文大全&#xff1a;开发技术集合&#xff08;包含Qt实用技术、树莓派、三维、OpenCV…

AIGC的底层技术:生成对抗网络(GAN)、变分自编码器(VAE)、预训练模型(如GPT、BERT等)

引言 随着人工智能生成内容(AIGC)技术的快速发展,我们看到它在文本、图像、音频和视频生成等领域的广泛应用。AIGC的核心在于底层技术的支持,本文将深入探讨AIGC的底层技术,包括生成对抗网络(GAN)、变分自编码器(VAE)、预训练模型(如GPT、BERT等),以及相关的深度学…

昆虫分类与检测系统源码分享

昆虫分类与检测系统源码分享 [一条龙教学YOLOV8标注好的数据集一键训练_70全套改进创新点发刊_Web前端展示] 1.研究背景与意义 项目参考AAAI Association for the Advancement of Artificial Intelligence 项目来源AACV Association for the Advancement of Computer Visio…

前端开发中的高级技巧与最佳实践

在前端开发的广阔领域中,不断探索和掌握新的技巧与实践方法是提升开发水平和项目质量的关键。本文将深入探讨一些前端开发中的高级技巧,希望能为广大前端开发者提供有价值的参考和启示。 一、高效的组件化开发 组件化是前端开发中的核心概念之一。通过将页面拆分成独立的、…

设计模式、系统设计 record part04

结构型模式 结构型模式分为&#xff1a; 1.类结构型模式 2.对象结构型模式 3。类结构型&#xff0c;使用继承机制&#xff0c;耦合度高&#xff0c;不灵活 4.对象结构型&#xff0c;使用组合、聚合关系&#xff0c;耦合低&#xff0c;灵活 代理模式 1.代理就是中介 2.静态代理&…

原生小程序 extendClasses 如何使用

官方文档, 文档其实写的比较清楚了。需要注意的点是&#xff0c;外部样式类是不支持嵌套选择器的。只能一对一的修改。 因为我写 uniapp 多一些&#xff0c;可能是因为习惯遇到了下面的问题。 子组件 /* 组件 custom-component.js */ Component({externalClasses: ["my…

R语言绘制饼图

饼图是一种圆形统计图。它将一个圆分割成若干扇形&#xff0c;每个扇形代表一个数据类别&#xff0c;扇形面积大小对应该类别在总体中所占比例。饼图直观展示各类别数据占比关系&#xff0c;但不适合过多类别及比较绝对数值大小。常用于市场份额分析、预算分配及调查结果展示等…