龙空技术网

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

QT教程 165

前言:

眼前小伙伴们对“ubuntu录音”大体比较注重,姐妹们都需要剖析一些“ubuntu录音”的相关知识。那么小编也在网上网罗了一些关于“ubuntu录音””的相关知识,希望兄弟们能喜欢,同学们快快来学习一下吧!

一、环境介绍

QT版本: 5.12.6

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

二、功能介绍

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

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

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

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

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

对讲APP服务器端

对讲APP客户端

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

AudioInputOut.h文件代码:
#ifndef AUDIOINPUTOUT_H#define AUDIOINPUTOUT_H#include <QObject>#include <QWidget>#include <QTcpServer>#include <QHostInfo>  //获取计算机网络信息#include <QUdpSocket>#include <QtNetwork>#include <QHostInfo>#include <QDebug>#include <QTcpSocket>#include <QHostAddress>#include <QDebug>#include <QMessageBox>#include <QLineEdit>#include <QHBoxLayout>#include <QComboBox>#include <QFile>#include <QTimer>#include <QScrollBar>#include <QScrollBar>#include <QAudio>     //这五个是QT处理音频的库#include <QAudioFormat>#include <QAudioInput>#include <QAudioOutput>#include <QIODevice>#define AUDIO_SERVER_IP "192.168.1.10"#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

AudioInputOut.cpp文件代码:

#include "AudioInputOut.h"//创建客户端,连接至服务器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("连接上服务器.\n");}//客户端模式:断开服务器void AudioInputOutput::LocalTcpClientDisconnectedSlot(){    //停止音频输入输出    audio_in->stop();    audio_out->stop();    LogSend("服务器断开.\n");}//客户端模式:读取服务器发过来的数据void AudioInputOutput::LocalTcpClientReadDtatSlot(){   QByteArray array=LocalTcpClientSocket->readAll();   audio_out_streamIn->write(array);}//音频输出初始化void AudioInputOutput::Audio_Out_Init(){    QAudioFormat auido_out_format;    //设置录音的格式    auido_out_format.setSampleRate(44100); //设置采样率以对赫兹采样。 以秒为单位,每秒采集多少声音数据的频率.    auido_out_format.setChannelCount(1);   //将通道数设置为通道。    auido_out_format.setSampleSize(16);     /*将样本大小设置为指定的sampleSize(以位为单位)通常为8或16,但是某些系统可能支持更大的样本量。*/    auido_out_format.setCodec("audio/pcm"); //设置编码格式    auido_out_format.setByteOrder(QAudioFormat::LittleEndian); //样本是小端字节顺序    auido_out_format.setSampleType(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_format.setSampleRate(44100); //设置采样率以对赫兹采样。 以秒为单位,每秒采集多少声音数据的频率.    auido_input_format.setChannelCount(1);   //将通道数设置为通道。    auido_input_format.setSampleSize(16);     /*将样本大小设置为指定的sampleSize(以位为单位)通常为8或16,但是某些系统可能支持更大的样本量。*/    auido_input_format.setCodec("audio/pcm"); //设置编码格式    auido_input_format.setByteOrder(QAudioFormat::LittleEndian); //样本是小端字节顺序    auido_input_format.setSampleType(QAudioFormat::SignedInt); //样本类型    //选择设备作为输入源    QAudioDeviceInfo info =QAudioDeviceInfo::defaultInputDevice();    emit LogSend(tr("当前的录音设备的名字:%1\n").arg(info.deviceName()));    //判断输入的格式是否支持,如果不支持就使用系统支持的默认格式    if(!info.isFormatSupported(auido_input_format))    {      emit LogSend("返回与系统支持的提供的设置最接近的QAudioFormat\n");      auido_input_format=info.nearestFormat(auido_input_format);      /*       * 返回与系统支持的提供的设置最接近的QAudioFormat。         这些设置由所使用的平台/音频插件提供。         它们还取决于所使用的QAudio :: Mode。      */    }    //当前设备支持的编码    emit LogSend("当前设备支持的编码格式:\n");    QStringList list=info.supportedCodecs();    for(int i=0;i<list.size();i++)    {        text=list.at(i)+"\n";        emit LogSend(text);    }    emit LogSend(tr("当前录音的采样率=%1\n").arg(auido_input_format.sampleRate()));    emit LogSend(tr("当前录音的通道数=%1\n").arg(auido_input_format.channelCount()));    emit LogSend(tr("当前录音的样本大小=%1\n").arg(auido_input_format.sampleSize()));    emit LogSend(tr("当前录音的编码格式=%1\n").arg(auido_input_format.codec()));    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 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()<<"录音出现错误.\n";         } else {             // Finished recording             qDebug()<<"完成录音\n";         }         break;     case QAudio::ActiveState:         // Started recording - read from IO device            qDebug()<<"开始从IO设备读取PCM声音数据.\n";         break;     default:         // ... other cases as appropriate         break; }}//停止连接void AudioInputOutput::stop_connect(){    //判断服务器是否处于连接状态    if(LocalTcpClientSocket->socketDescriptor()!=-1)    {        //断开服务器连接        LocalTcpClientSocket->disconnectFromHost();        //停止音频输入输出        audio_in->stop();        audio_out->stop();    }}
RecvServerCMD.h

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

点击这里:「链接」

#ifndef RECVSERVERCMD_H#define RECVSERVERCMD_H#include <QObject>#include <QWidget>#include <QTcpServer>#include <QHostInfo>  //获取计算机网络信息#include <QUdpSocket>#include <QtNetwork>#include <QHostInfo>#include <QDebug>#include <QTcpSocket>#include <QHostAddress>#include <QDebug>#include <QMessageBox>#include <QLineEdit>#include <QHBoxLayout>#include <QComboBox>#include <QFile>#include <QTimer>#include <QScrollBar>#include <QScrollBar>#include <QAudio>     //这五个是QT处理音频的库#include <QAudioFormat>#include <QAudioInput>#include <QAudioOutput>#include <QIODevice>#include <QTimer>#define RECV_SERVER_CMD_IP "127.0.0.1"#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

RecvServerCMD.cpp

#include "RecvServerCMD.h"//创建客户端,连接至服务器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("命令长连接:连接上服务器.\n");}//客户端模式:断开服务器void RecvServerCmd::LocalTcpClientDisconnectedSlot(){    LogSend("命令长连接:服务器断开.\n");}//客户端模式:读取服务器发过来的数据void RecvServerCmd::LocalTcpClientReadDtatSlot(){   QByteArray array=LocalTcpClientSocket->readAll();   qDebug()<<"服务器下发的命令:"<<array;   LocalTcpClientSocket->write(array);}//心跳包void RecvServerCmd::check_connect(){    //判断该客户端是否已经断开    if(LocalTcpClientSocket->socketDescriptor()==-1)    {        /*4. 尝试连接服务器主机*/        LocalTcpClientSocket->connectToHost(QHostAddress(RECV_SERVER_CMD_IP),RECV_SERVER_CMD_PROT);    }    else    {        LocalTcpClientSocket->write("device connect ok.");    }}

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

码widget.cpp代码:
#include "widget.h"#include "ui_widget.h"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;    QList<QHostAddress> list = QNetworkInterface::allAddresses();    for(int i=0;i<list.count();i++)    {        QHostAddress addr=list.at(i);        if(addr.protocol() == QAbstractSocket::IPv4Protocol)        {          ui->comboBox->addItem(addr.toString());        }    }}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()));    TcpFarClientList.append(ServerSocket);//添加到列表    //显示已经连接的客户端    ui->ClientComboBoxList->clear();    for(int i=0;i<TcpFarClientList.count();i++)    {        QString info=TcpFarClientList.at(i)->peerAddress().toString();        info+=":";        info+=QString::number(TcpFarClientList.at(i)->peerPort());        ui->ClientComboBoxList->addItem(info);    }}//服务器模式:响应断开的客户端void Widget::TcpClientDisconnected(){    for(int i=0;i<TcpFarClientList.count();i++)    {        //取出地址列表中的一个客户端地址        QTcpSocket *item = TcpFarClientList.at(i);        //判断该客户端是否已经断开        if(item->socketDescriptor()==-1)        {            TcpFarClientList.removeAt(i);        }    }    //显示已经连接的客户端    ui->ClientComboBoxList->clear();    for(int i=0;i<TcpFarClientList.count();i++)    {        QString info=TcpFarClientList.at(i)->peerAddress().toString();        info+=":";        info+=QString::number(TcpFarClientList.at(i)->peerPort());        ui->ClientComboBoxList->addItem(info);    }}//服务器模式:读数据void Widget::ReadTcpClientData(){    for(int i=0;i<TcpFarClientList.count();i++)    {       if(!TcpFarClientList.at(i)->atEnd())       {          QByteArray array=TcpFarClientList.at(i)->readAll();          audio_out_streamIn->write(array);       }    }}//音频输出初始化void Widget::Audio_Out_Init(){    QAudioFormat auido_out_format;    //设置录音的格式    auido_out_format.setSampleRate(44100); //设置采样率以对赫兹采样。 以秒为单位,每秒采集多少声音数据的频率.    auido_out_format.setChannelCount(1);   //将通道数设置为通道。    auido_out_format.setSampleSize(16);     /*将样本大小设置为指定的sampleSize(以位为单位)通常为8或16,但是某些系统可能支持更大的样本量。*/    auido_out_format.setCodec("audio/pcm"); //设置编码格式    auido_out_format.setByteOrder(QAudioFormat::LittleEndian); //样本是小端字节顺序    auido_out_format.setSampleType(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_format.setSampleRate(44100); //设置采样率以对赫兹采样。 以秒为单位,每秒采集多少声音数据的频率.    auido_input_format.setChannelCount(1);   //将通道数设置为通道。    auido_input_format.setSampleSize(16);     /*将样本大小设置为指定的sampleSize(以位为单位)通常为8或16,但是某些系统可能支持更大的样本量。*/    auido_input_format.setCodec("audio/pcm"); //设置编码格式    auido_input_format.setByteOrder(QAudioFormat::LittleEndian); //样本是小端字节顺序    auido_input_format.setSampleType(QAudioFormat::SignedInt); //样本类型    //选择设备作为输入源    QAudioDeviceInfo info =QAudioDeviceInfo::defaultInputDevice();    ui->plainTextEdit->appendPlainText(tr("当前的录音设备的名字:%1\n").arg(info.deviceName()));    //判断输入的格式是否支持,如果不支持就使用系统支持的默认格式    if(!info.isFormatSupported(auido_input_format))    {      ui->plainTextEdit->appendPlainText("返回与系统支持的提供的设置最接近的QAudioFormat\n");      auido_input_format=info.nearestFormat(auido_input_format);      /*       * 返回与系统支持的提供的设置最接近的QAudioFormat。         这些设置由所使用的平台/音频插件提供。         它们还取决于所使用的QAudio :: Mode。      */    }    //当前设备支持的编码    ui->plainTextEdit->appendPlainText("当前设备支持的编码格式:\n");    QStringList list=info.supportedCodecs();    for(int i=0;i<list.size();i++)    {        text=list.at(i)+"\n";       ui->plainTextEdit->appendPlainText(text);    }    ui->plainTextEdit->appendPlainText(tr("当前录音的采样率=%1\n").arg(auido_input_format.sampleRate()));    ui->plainTextEdit->appendPlainText(tr("当前录音的通道数=%1\n").arg(auido_input_format.channelCount()));    ui->plainTextEdit->appendPlainText(tr("当前录音的样本大小=%1\n").arg(auido_input_format.sampleSize()));    ui->plainTextEdit->appendPlainText(tr("当前录音的编码格式=%1\n").arg(auido_input_format.codec()));    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<TcpFarClientList.count();i++)    {        //取出地址列表中的一个客户端地址        QTcpSocket *item = TcpFarClientList.at(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()<<"录音出现错误.\n";         } else {             // Finished recording             qDebug()<<"完成录音\n";         }         break;     case QAudio::ActiveState:         // Started recording - read from IO device            qDebug()<<"开始从IO设备读取PCM声音数据.\n";         break;     default:         // ... other cases as appropriate         break; }}void Widget::on_pushButton_clicked(){    Create_Server();    Audio_Out_Init();    Audio_in_Init();}

widget.h代码:

#ifndef WIDGET_H#define WIDGET_H#include <QWidget>#include <QWidget>#include <QTcpServer>#include <QHostInfo>  //获取计算机网络信息#include <QUdpSocket>#include <QtNetwork>#include <QHostInfo>#include <QDebug>#include <QTcpSocket>#include <QHostAddress>#include <QDebug>#include <QMessageBox>#include <QLineEdit>#include <QHBoxLayout>#include <QComboBox>#include <QFile>#include <QTimer>#include <QScrollBar>#include <QScrollBar>#include <QAudio>     //这五个是QT处理音频的库#include <QAudioFormat>#include <QAudioInput>#include <QAudioOutput>#include <QIODevice>#include <QDebug>QT_BEGIN_NAMESPACEnamespace Ui { class Widget; }QT_END_NAMESPACEclass Widget : public QWidget{    Q_OBJECTpublic:    Widget(QWidget *parent = nullptr);    ~Widget();    QTcpServer *LocalTcpServer;    QList<QTcpSocket*> TcpFarClientList;    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开发教程学习资料,点击下方链接莬费领取↓↓,先码住不迷路~】

点击这里:Qt资料领取(视频教程+文档+代码+项目实战)

作者:DS小龙哥

链接:

标签: #ubuntu录音