QT采用QAudioInput、QAudioOutput实现远程语音对讲功能

一、环境介绍


【资料图】

QT版本:

操作系统: Android、ubuntu、windows 都测试OK。

二、功能介绍

两台设备之间实现语音对讲功能。类似于QQ、微信语音电话效果。

采用QAudioInput获取声卡输出,QAudioOutpu用于输出数据到声卡。

音频数据采用TCP网络方式双向传输。

(1)对讲APP服务器端: 服务器收到客户端的数据就播放,同时采集声卡数据发送给客户端。

(2) 对讲APP客户端:客户端收到服务器的数据就播放,同时采集声卡数据发送给服务器。

对讲APP服务器端

对讲APP客户端

三、音频客户端: 核心代码

文件代码:
#ifndef AUDIOINPUTOUT_H#define AUDIOINPUTOUT_H#include #include #include #include //获取计算机网络信息#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include //这五个是QT处理音频的库#include #include #include #include #define AUDIO_SERVER_IP ""#define AUDIO_SERVER_PROT 8888//用于连接服务器,实现语音对讲功能class AudioInputOutput : public QObject{    Q_OBJECTpublic:    QTcpSocket *LocalTcpClientSocket;    AudioInputOutput(QObject* parent=nullptr):QObject(parent)    {        LocalTcpClientSocket=nullptr;        audio_in=nullptr;        audio_out=nullptr;    }    void TCP_ConnectServer(QString Ipaddr, quint16 prot);    void Audio_in_Init();    void Audio_Out_Init();    QAudioInput *audio_in;    QAudioOutput *audio_out;    QIODevice* audio_streamIn;    QIODevice* audio_out_streamIn;public slots:    void LocalTcpClientConnectedSlot();    void LocalTcpClientDisconnectedSlot();    void LocalTcpClientReadDtatSlot();    void run();    void handleStateChanged_input(QAudio::State newState);    void audio_ReadyRead();    void stop_connect();signals:    void LogSend(QString text);};#endif // AUDIOINPUTOUT_H

文件代码:

#include ""//创建客户端,连接至服务器void AudioInputOutput::TCP_ConnectServer(QString Ipaddr,quint16  prot){      /* 设置服务器IP地址*/    QHostAddress FarServerAddr(Ipaddr);    if(LocalTcpClientSocket==nullptr)    {        /*1. 创建本地客户端TCP套接字*/        LocalTcpClientSocket = new QTcpSocket;        /*2. 连接客户端的信号槽*/        connect(LocalTcpClientSocket,SIGNAL(connected()),this,SLOT(LocalTcpClientConnectedSlot()));        connect(LocalTcpClientSocket,SIGNAL(disconnected()),this,SLOT(LocalTcpClientDisconnectedSlot()));        connect(LocalTcpClientSocket,SIGNAL(readyRead()),this,SLOT(LocalTcpClientReadDtatSlot()));    }    /*3. 尝试连接服务器主机*/    LocalTcpClientSocket->connectToHost(FarServerAddr,prot);}//开始执行程序void AudioInputOutput::run(){    TCP_ConnectServer(AUDIO_SERVER_IP,AUDIO_SERVER_PROT);    Audio_in_Init();    Audio_Out_Init();}//客户端模式:响应连接上服务器之后的操作void AudioInputOutput::LocalTcpClientConnectedSlot(){    LocalTcpClientSocket->write("device connect server.");    LogSend("连接上服务器.
");}//客户端模式:断开服务器void AudioInputOutput::LocalTcpClientDisconnectedSlot(){    //停止音频输入输出    audio_in->stop();    audio_out->stop();    LogSend("服务器断开.
");}//客户端模式:读取服务器发过来的数据void AudioInputOutput::LocalTcpClientReadDtatSlot(){   QByteArray array=LocalTcpClientSocket->readAll();   audio_out_streamIn->write(array);}//音频输出初始化void AudioInputOutput::Audio_Out_Init(){    QAudioFormat auido_out_format;    //设置录音的格式    auido_out_(44100); //设置采样率以对赫兹采样。 以秒为单位,每秒采集多少声音数据的频率.    auido_out_(1);   //将通道数设置为通道。    auido_out_(16);     /*将样本大小设置为指定的sampleSize(以位为单位)通常为8或16,但是某些系统可能支持更大的样本量。*/    auido_out_("audio/pcm"); //设置编码格式    auido_out_(QAudioFormat::LittleEndian); //样本是小端字节顺序    auido_out_(QAudioFormat::SignedInt); //样本类型    QAudioDeviceInfo info(QAudioDeviceInfo::defaultOutputDevice());    if(audio_out)    {        delete audio_out;        audio_out=nullptr;    }    audio_out = new QAudioOutput(auido_out_format);    audio_out_streamIn=audio_out->start();}// 音频输入初始化void AudioInputOutput::Audio_in_Init(){    QString text;    QAudioFormat auido_input_format;    //设置录音的格式    auido_input_(44100); //设置采样率以对赫兹采样。 以秒为单位,每秒采集多少声音数据的频率.    auido_input_(1);   //将通道数设置为通道。    auido_input_(16);     /*将样本大小设置为指定的sampleSize(以位为单位)通常为8或16,但是某些系统可能支持更大的样本量。*/    auido_input_("audio/pcm"); //设置编码格式    auido_input_(QAudioFormat::LittleEndian); //样本是小端字节顺序    auido_input_(QAudioFormat::SignedInt); //样本类型    //选择设备作为输入源    QAudioDeviceInfo info =QAudioDeviceInfo::defaultInputDevice();    emit LogSend(tr("当前的录音设备的名字:%1
").arg(()));    //判断输入的格式是否支持,如果不支持就使用系统支持的默认格式    if(!(auido_input_format))    {      emit LogSend("返回与系统支持的提供的设置最接近的QAudioFormat
");      auido_input_format=(auido_input_format);      /*       * 返回与系统支持的提供的设置最接近的QAudioFormat。         这些设置由所使用的平台/音频插件提供。         它们还取决于所使用的QAudio :: Mode。      */    }    //当前设备支持的编码    emit LogSend("当前设备支持的编码格式:
");    QStringList list=();    for(int i=0;istart(); //开始音频采集    //关联音频读数据信号    connect(audio_streamIn,SIGNAL(readyRead()),this,SLOT(audio_ReadyRead()),Qt::QueuedConnection);}//有音频信号可以读void AudioInputOutput::audio_ReadyRead(){    QByteArray byte=audio_streamIn->readAll();    //判断服务器是否处于连接状态    if(LocalTcpClientSocket->socketDescriptor()!=-1)    {        LocalTcpClientSocket->write(byte);    }}//录音状态void AudioInputOutput::handleStateChanged_input(QAudio::State newState){ switch (newState) {     case QAudio::StoppedState:         if (audio_in->error() != QAudio::NoError) {             // Error handling             qDebug()<<"录音出现错误.
";         } else {             // Finished recording             qDebug()<<"完成录音
";         }         break;     case QAudio::ActiveState:         // Started recording - read from IO device            qDebug()<<"开始从IO设备读取PCM声音数据.
";         break;     default:         // ... other cases as appropriate         break; }}//停止连接void AudioInputOutput::stop_connect(){    //判断服务器是否处于连接状态    if(LocalTcpClientSocket->socketDescriptor()!=-1)    {        //断开服务器连接        LocalTcpClientSocket->disconnectFromHost();        //停止音频输入输出        audio_in->stop();        audio_out->stop();    }}

【领 QT开发教程 学习资料, 点击下方链接莬费领取↓↓ ,先码住不迷路~】

点击这里:

#ifndef RECVSERVERCMD_H#define RECVSERVERCMD_H#include #include #include #include //获取计算机网络信息#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include //这五个是QT处理音频的库#include #include #include #include #include #define RECV_SERVER_CMD_IP ""#define RECV_SERVER_CMD_PROT 6666//用于连接服务器,接收服务器下发的命令class RecvServerCmd : public QObject{    Q_OBJECTpublic:    QTcpSocket *LocalTcpClientSocket;    QTimer *timer_check_connect;    RecvServerCmd(QObject* parent=nullptr):QObject(parent){LocalTcpClientSocket=nullptr;}    void TCP_ConnectServer(QString Ipaddr, quint16 prot);public slots:    void LocalTcpClientConnectedSlot();    void LocalTcpClientDisconnectedSlot();    void LocalTcpClientReadDtatSlot();    void run();    void check_connect();signals:    void LogSend(QString text);};#endif // RECVSERVERCMD_H

#include ""//创建客户端,连接至服务器void RecvServerCmd::TCP_ConnectServer(QString Ipaddr,quint16  prot){    if(LocalTcpClientSocket==nullptr)    {        /*1. 创建本地客户端TCP套接字*/        LocalTcpClientSocket = new QTcpSocket;        /*2. 设置服务器IP地址*/        QHostAddress FarServerAddr(Ipaddr);        /*3. 连接客户端的信号槽*/        connect(LocalTcpClientSocket,SIGNAL(connected()),this,SLOT(LocalTcpClientConnectedSlot()));        connect(LocalTcpClientSocket,SIGNAL(disconnected()),this,SLOT(LocalTcpClientDisconnectedSlot()));        connect(LocalTcpClientSocket,SIGNAL(readyRead()),this,SLOT(LocalTcpClientReadDtatSlot()));        /*4. 尝试连接服务器主机*/        LocalTcpClientSocket->connectToHost(FarServerAddr,prot);        /*5. 检测服务器是否断开,并发送心跳包*/        timer_check_connect=new QTimer;        connect(timer_check_connect,SIGNAL(timeout()),this,SLOT(check_connect()));        timer_check_connect->start(1000*30);    }}//开始执行程序void RecvServerCmd::run(){    TCP_ConnectServer(RECV_SERVER_CMD_IP,RECV_SERVER_CMD_PROT);}//客户端模式:响应连接上服务器之后的操作void RecvServerCmd::LocalTcpClientConnectedSlot(){    LocalTcpClientSocket->write("device connect server.");    LogSend("命令长连接:连接上服务器.
");}//客户端模式:断开服务器void RecvServerCmd::LocalTcpClientDisconnectedSlot(){    LogSend("命令长连接:服务器断开.
");}//客户端模式:读取服务器发过来的数据void RecvServerCmd::LocalTcpClientReadDtatSlot(){   QByteArray array=LocalTcpClientSocket->readAll();   qDebug()<<"服务器下发的命令:"socketDescriptor()==-1)    {        /*4. 尝试连接服务器主机*/        LocalTcpClientSocket->connectToHost(QHostAddress(RECV_SERVER_CMD_IP),RECV_SERVER_CMD_PROT);    }    else    {        LocalTcpClientSocket->write("device connect ok.");    }}

四、音频服务器端: 核心代码

码代码:
#include ""#include "ui_"Widget::Widget(QWidget *parent)    : QWidget(parent)    , ui(new Ui::Widget){    ui->setupUi(this);    audio_in=nullptr;    audio_out=nullptr;    audio_streamIn=nullptr;    audio_out_streamIn=nullptr;    QListlist = QNetworkInterface::allAddresses();    for(int i=0;icomboBox->addItem(());        }    }}Widget::~Widget(){    delete ui;}//服务器模式:创建服务器void Widget::Create_Server(){    /*1. 实例化服务器*/    LocalTcpServer= new QTcpServer;    /*2. 设置监听的端口和IP地址*/    QHostAddress addr(ui->comboBox->currentText());    LocalTcpServer->listen(addr,8888);    /*3. 关联连接信号,检测是否有新的客户端连接*/    connect(LocalTcpServer,SIGNAL(newConnection()),this,SLOT(NewTcpConnection()));}//服务器模式:响应新连接的客户端void Widget::NewTcpConnection(){    /*创建本地服务器套接字*/    QTcpSocket *ServerSocket=LocalTcpServer->nextPendingConnection();    /*关联可读信号*/    connect(ServerSocket,SIGNAL(readyRead()),this,SLOT(ReadTcpClientData()));    /*关联断开信号*/    connect(ServerSocket,SIGNAL(disconnected()),this,SLOT(TcpClientDisconnected()));    (ServerSocket);//添加到列表    //显示已经连接的客户端    ui->ClientComboBoxList->clear();    for(int i=0;i<();i++)    {        QString info=(i)->peerAddress().toString();        info+=":";        info+=QString::number((i)->peerPort());        ui->ClientComboBoxList->addItem(info);    }}//服务器模式:响应断开的客户端void Widget::TcpClientDisconnected(){    for(int i=0;i<();i++)    {        //取出地址列表中的一个客户端地址        QTcpSocket *item = (i);        //判断该客户端是否已经断开        if(item->socketDescriptor()==-1)        {            (i);        }    }    //显示已经连接的客户端    ui->ClientComboBoxList->clear();    for(int i=0;i<();i++)    {        QString info=(i)->peerAddress().toString();        info+=":";        info+=QString::number((i)->peerPort());        ui->ClientComboBoxList->addItem(info);    }}//服务器模式:读数据void Widget::ReadTcpClientData(){    for(int i=0;i<();i++)    {       if(!(i)->atEnd())       {          QByteArray array=(i)->readAll();          audio_out_streamIn->write(array);       }    }}//音频输出初始化void Widget::Audio_Out_Init(){    QAudioFormat auido_out_format;    //设置录音的格式    auido_out_(44100); //设置采样率以对赫兹采样。 以秒为单位,每秒采集多少声音数据的频率.    auido_out_(1);   //将通道数设置为通道。    auido_out_(16);     /*将样本大小设置为指定的sampleSize(以位为单位)通常为8或16,但是某些系统可能支持更大的样本量。*/    auido_out_("audio/pcm"); //设置编码格式    auido_out_(QAudioFormat::LittleEndian); //样本是小端字节顺序    auido_out_(QAudioFormat::SignedInt); //样本类型    QAudioDeviceInfo info(QAudioDeviceInfo::defaultOutputDevice());    if(audio_out)    {        delete audio_out;        audio_out=nullptr;    }    audio_out = new QAudioOutput(auido_out_format);    audio_out_streamIn=audio_out->start();}// 音频输入初始化void Widget::Audio_in_Init(){    QString text;    QAudioFormat auido_input_format;    //设置录音的格式    auido_input_(44100); //设置采样率以对赫兹采样。 以秒为单位,每秒采集多少声音数据的频率.    auido_input_(1);   //将通道数设置为通道。    auido_input_(16);     /*将样本大小设置为指定的sampleSize(以位为单位)通常为8或16,但是某些系统可能支持更大的样本量。*/    auido_input_("audio/pcm"); //设置编码格式    auido_input_(QAudioFormat::LittleEndian); //样本是小端字节顺序    auido_input_(QAudioFormat::SignedInt); //样本类型    //选择设备作为输入源    QAudioDeviceInfo info =QAudioDeviceInfo::defaultInputDevice();    ui->plainTextEdit->appendPlainText(tr("当前的录音设备的名字:%1
").arg(()));    //判断输入的格式是否支持,如果不支持就使用系统支持的默认格式    if(!(auido_input_format))    {      ui->plainTextEdit->appendPlainText("返回与系统支持的提供的设置最接近的QAudioFormat
");      auido_input_format=(auido_input_format);      /*       * 返回与系统支持的提供的设置最接近的QAudioFormat。         这些设置由所使用的平台/音频插件提供。         它们还取决于所使用的QAudio :: Mode。      */    }    //当前设备支持的编码    ui->plainTextEdit->appendPlainText("当前设备支持的编码格式:
");    QStringList list=();    for(int i=0;iplainTextEdit->appendPlainText(text);    }    ui->plainTextEdit->appendPlainText(tr("当前录音的采样率=%1
").arg(auido_input_()));    ui->plainTextEdit->appendPlainText(tr("当前录音的通道数=%1
").arg(auido_input_()));    ui->plainTextEdit->appendPlainText(tr("当前录音的样本大小=%1
").arg(auido_input_()));    ui->plainTextEdit->appendPlainText(tr("当前录音的编码格式=%1
").arg(auido_input_()));    if(audio_in)    {        delete audio_in;        audio_in=nullptr;    }    audio_in = new QAudioInput(auido_input_format);    connect(audio_in,SIGNAL(stateChanged(QAudio::State)), this, SLOT(handleStateChanged_input(QAudio::State)),Qt::QueuedConnection);    audio_streamIn=audio_in->start(); //开始音频采集    //关联音频读数据信号    connect(audio_streamIn,SIGNAL(readyRead()),this,SLOT(audio_ReadyRead()),Qt::QueuedConnection);}//有音频信号可以读void Widget::audio_ReadyRead(){    QByteArray byte=audio_streamIn->readAll();    //判断是否处于连接状态    for(int i=0;i<();i++)    {        //取出地址列表中的一个客户端地址        QTcpSocket *item = (i);        //判断该客户端是否已经断开        if(item->socketDescriptor()!=-1)        {            item->write(byte);        }    }}//录音状态void Widget::handleStateChanged_input(QAudio::State newState){ switch (newState) {     case QAudio::StoppedState:         if (audio_in->error() != QAudio::NoError) {             // Error handling             qDebug()<<"录音出现错误.
";         } else {             // Finished recording             qDebug()<<"完成录音
";         }         break;     case QAudio::ActiveState:         // Started recording - read from IO device            qDebug()<<"开始从IO设备读取PCM声音数据.
";         break;     default:         // ... other cases as appropriate         break; }}void Widget::on_pushButton_clicked(){    Create_Server();    Audio_Out_Init();    Audio_in_Init();}

代码:

#ifndef WIDGET_H#define WIDGET_H#include #include #include #include //获取计算机网络信息#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include //这五个是QT处理音频的库#include #include #include #include #include QT_BEGIN_NAMESPACEnamespace Ui { class Widget; }QT_END_NAMESPACEclass Widget : public QWidget{    Q_OBJECTpublic:    Widget(QWidget *parent = nullptr);    ~Widget();    QTcpServer *LocalTcpServer;    QListTcpFarClientList;    void Create_Server();    //对讲功能    QThread *audioInputOutput_thread;    void Audio_in_Init();    void Audio_Out_Init();    QAudioInput *audio_in;    QAudioOutput *audio_out;    QIODevice* audio_streamIn;    QIODevice* audio_out_streamIn;private slots:    void TcpClientDisconnected();    void ReadTcpClientData();    void NewTcpConnection();    void handleStateChanged_input(QAudio::State newState);    void audio_ReadyRead();    void on_pushButton_clicked();private:    Ui::Widget *ui;};#endif // WIDGET_H

【领 QT开发教程 学习资料, 点击下方链接莬费领取↓↓ ,先码住不迷路~】

点击这里:

作者:DS小龙哥 链接:/post/6985337188107943967

关键词:

推荐DIY文章
主机存在磨损或划痕风险 PICO4便携包宣布召回
穿越湖海!特斯拉Cybertruck电动皮卡可以当“船”用
vivoXFold+折叠旗舰开售 配备蔡司全焦段旗舰四摄
飞凡R7正式上市 全系标配换电架构
中兴Axon30S开售 拥有黑色蓝色两款配色
荣耀MagicBookV14 2022正式开售 搭载TOF传感器
it