Qt6.2.3播放PCM文件,使用QAudioSink替代老版本的QAudioOutput
作者 | 将狼才鲸 |
---|---|
创建日期 | 2022-03-29 |
Gitee源码工程目录:qt_gui_simple2complex/ source / 004_MultiMedia_VideoAudio / 006_qt6-2-3_pcm_play
CSDN文章阅读地址:
B站视频讲解地址(待完成):才鲸嵌入式
Qt5版本的PCM播放工程Gitee目录: qt_gui_simple2complex/ source / 004_MultiMedia_VideoAudio / 002_pcm_audio_play
一、工程说明
-
因为网上能搜到的都是Qt老版本QAudioFormat+QAudioOutput播放pcm的示例代码,在Qt6中用不了,所以自己写一个Qt6版本的示例。
-
Qt6.2.3中需要使用QAudioSink+QIODevice+QMediaDevices替代Qt5的QAudioOutput。
-
使用QAudioSink进行输出时需要注册QMediaDevices中的默认设备,QAudioSink开始播放时需要传入一个QIODevice虚拟输入设备,在QIODevice这个虚拟设备中进行pcm数据的出入。
-
我电脑扬声器默认输出的是采样率44.1k,双通道,采样深度16bit,代码中有参数打印,你可以根据自己电脑的声音输出配置生成同样格式的pcm。
-
生成pcm文件我使用的是ffmpeg软件,测试时我直接使用本工程同级目录另一个工程里的文件。
-
本地pcm文件路径:…/002_pcm_audio_play/002_pcm_audio_play.pcm
-
ffmpeg生成pcm文件的命令:
ffmpeg -i roar.wav -acodec pcm_s16le -f s16le -ac 2 -ar 44100 001_pcm_audio_play.pcm
- 生成并截取片段:
ffmpeg -i roar.wav -ss 00:00:00 -t 00:00:03 -acodec pcm_s16le -f s16le -ac 2 -ar 44100 002_pcm_audio_play.pcm
- 播放pcm文件测试
ffplay -ar 44100 -ac 2 -f s16le -i output.pcm
- 参考了官方的源码。
- Qt6软件安装目录本地播放音频的示例:Qt\Examples\Qt-6.2.3\multimedia\audiooutput
- 官方示例讲解地址:Audio Output Example
二、工程源码展示
- mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
PcmInputDevice::PcmInputDevice()
{
m_inputFile.setFileName("../002_pcm_audio_play/002_pcm_audio_play.pcm");
m_inputFile.open(QIODevice::ReadOnly);
}
void PcmInputDevice::start()
{
open(QIODevice::ReadOnly);
}
void PcmInputDevice::stop()
{
close();
}
PcmInputDevice::~PcmInputDevice()
{
m_inputFile.close();
}
/**
* \brief 扬声器缺少数据时会自动调用这个方法
*/
qint64 PcmInputDevice::readData(char *data, qint64 len)
{
m_inputFile.read(data, len);
return len;
}
qint64 PcmInputDevice::writeData(const char *data, qint64 len)
{
Q_UNUSED(data);
Q_UNUSED(len);
return 0;
}
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
m_outputDevices = new QMediaDevices(this);
m_outputDevice = m_outputDevices->defaultAudioOutput();
QAudioFormat format = m_outputDevice.preferredFormat();
// ChannelConfigStereo is 2, Int16 is 2
qDebug("sampleRate: %d, channelCount: %d, sampleFormat: %d",
format.sampleRate(), format.channelCount(), format.sampleFormat()
);
m_audioSinkOutput = new QAudioSink(m_outputDevice, format);
m_pcmInputDevice = new PcmInputDevice;
m_pcmInputDevice->start();
m_audioSinkOutput->start(m_pcmInputDevice);
}
MainWindow::~MainWindow()
{
m_pcmInputDevice->stop();
m_audioSinkOutput->stop();
delete ui;
}
- mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QFile>
#include <QDebug>
#include <QIODevice>
#include <QMediaDevices>
#include <QAudioFormat>
#include <QAudioSink>
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE
/**
* \brief 播放的输入数据必须从QIODevice设备中送往QAudioSink
*/
class PcmInputDevice : public QIODevice
{
Q_OBJECT
public:
PcmInputDevice();
~PcmInputDevice();
void start();
void stop();
qint64 readData(char *data, qint64 maxlen) override;
qint64 writeData(const char *data, qint64 len) override;
private:
QFile m_inputFile;
};
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
private:
Ui::MainWindow *ui;
QAudioSink *m_audioSinkOutput;
QMediaDevices *m_outputDevices;
QAudioDevice m_outputDevice;
PcmInputDevice *m_pcmInputDevice;
};
#endif // MAINWINDOW_H
- main.cpp
/**
* \brief 使用Qt6.2.3播放pcm音频
* \details 使用QAudioSink+QIODevice+QMediaDevices替代Qt5的QAudioOutput。
* 使用QAudioSink进行输出时需要注册QMediaDevices中的默认设备,
* QAudioSink开始播放时需要传入一个QIODevice虚拟输入设备,在QIODevice
* 这个虚拟设备中进行pcm数据的出入。
* 我电脑扬声器默认输出的是采样率44.1k,双通道,采样深度16bit,
* 生成pcm文件我使用的是ffmpeg软件,测试时我直接使用另一个工程里的文件,
* 本地pcm文件路径:../002_pcm_audio_play/002_pcm_audio_play.pcm
* ffmpeg生成pcm文件的命令:
* ffmpeg -i roar.wav -acodec pcm_s16le -f s16le -ac 2 -ar 44100 001_pcm_audio_play.pcm
* 生成并截取片段:
* ffmpeg -i roar.wav -ss 00:00:00 -t 00:00:03 -acodec pcm_s16le -f s16le -ac 2 -ar 44100 002_pcm_audio_play.pcm
* 播放pcm文件测试
* ffplay -ar 44100 -ac 2 -f s16le -i output.pcm
* \author 将狼才鲸
* \date 2022-03-29
* \note 因为网上能搜到的都是Qt老版本QAudioFormat+QAudioOutput播放pcm的示例代码,在Qt6中用不了,
* 所以自己写一个Qt6版本的示例。
* Qt6软件安装目录本地播放音频的示例:Qt\Examples\Qt-6.2.3\multimedia\audiooutput
* 官方示例讲解地址:[Audio Output Example](https://doc.qt.io/qt-6/qtmultimedia-multimedia-audiooutput-example.html)
*/
#include "mainwindow.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
- pcm_play.pro
QT += core gui
QT += multimedia
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
CONFIG += c++17
# You can make your code fail to compile if it uses deprecated APIs.
# In order to do so, uncomment the following line.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
SOURCES += \
main.cpp \
mainwindow.cpp
HEADERS += \
mainwindow.h
FORMS += \
mainwindow.ui
# Default rules for deployment.
qnx: target.path = /tmp/$${TARGET}/bin
else: unix:!android: target.path = /opt/$${TARGET}/bin
!isEmpty(target.path): INSTALLS += target
- mainwindow.ui
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MainWindow</class>
<widget class="QMainWindow" name="MainWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>800</width>
<height>600</height>
</rect>
</property>
<property name="windowTitle">
<string>MainWindow</string>
</property>
<widget class="QWidget" name="centralwidget">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QPushButton" name="pushButton">
<property name="text">
<string>你会听到5秒的音乐播放,然后是重复的杂音(事先保证你的电脑没有设置为静音)</string>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QMenuBar" name="menubar">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>800</width>
<height>22</height>
</rect>
</property>
</widget>
<widget class="QStatusBar" name="statusbar"/>
</widget>
<resources/>
<connections/>
</ui>