Qt之实现录音播放及raw(pcm)转wav格式

您所在的位置:网站首页 怎么存录音文件 Qt之实现录音播放及raw(pcm)转wav格式

Qt之实现录音播放及raw(pcm)转wav格式

2024-07-08 05:15:29| 来源: 网络整理| 查看: 265

简述

在上一篇 Qt 之 WAV文件解析 中详细地分析了wav格式文件的文件头信息。通过QAudioInput实现录音功能,但是录音生成的文件并不能用播放器打开,就算更改后缀名也无法识别(有时候下载的一些音频文件通过修改文件名可以播放)。在Qt助手中将录音生成的文件保存为.raw格式,那么这个raw到底是什么格式呢?

其实看raw字面的意思是原始的、未处理的、未加工的,从此看来QAudioInput 生成的音频文件未经过处理,即文件保存的数据全部为音频数据,没有文件头,播放器识别不了。好了,既然知道这个原因导致播放器播不了,那么我们就手动给.raw文件添加上头信息,转为wav格式,这样不仅可以通过QAudioOutput来播放,同时播放器也能够播放该音频文件。

代码之路 代码思路

这里主要是通过QAudioInput来生成音频文件,录音结束后,将.raw音频文件转为.wav格式文件,在 Qt 之 WAV文件解析 中介绍了wav文件头的数据结构,这里我们只要将这个结构的数据加在.raw文件的头部即可,代码中通过addWavHeader方法将.raw文件转成.wav文件。播放音频文件利用QAudioOutput类即可,既可播放.raw文件也可以播放.wav文件。我们这里就直接播放重新生成的.wav格式的音频文件。

仔细看代码有点多,其实实现很简单,这里我添加的一些代码是用来实现一个简单完整的小录音机功能。

MyAudioInput.cpp #include "myaudioinput.h" #include #include #include #define RAW_RECORD_FILENAME "F:/audio/test.raw" // 录音文件名; #define WAV_RECORD_FILENAME "F:/audio/test.wav" // 录音文件转wav格式文件名; const qint64 TIME_TRANSFORM = 1000 * 1000; // 微妙转秒; struct WAVFILEHEADER { // RIFF 头 char RiffName[4]; unsigned long nRiffLength; // 数据类型标识符 char WavName[4]; // 格式块中的块头 char FmtName[4]; unsigned long nFmtLength; // 格式块中的块数据 unsigned short nAudioFormat; unsigned short nChannleNumber; unsigned long nSampleRate; unsigned long nBytesPerSecond; unsigned short nBytesPerSample; unsigned short nBitsPerSample; // 数据块中的块头 char DATANAME[4]; unsigned long nDataLength; }; MyAudioInput::MyAudioInput(QWidget *parent) : QWidget(parent) , m_isRecord(false) , m_isPlay(false) , m_RecordTimerId(0) , m_RecordTime(0) { ui.setupUi(this); // 录音,播放 等按钮 信号槽; connect(ui.pButtonRecord, SIGNAL(clicked()), this, SLOT(onStartRecord())); connect(ui.pButtonStopRecord, SIGNAL(clicked()), this, SLOT(onStopRecording())); connect(ui.pButtonPlay, SIGNAL(clicked()), this, SLOT(onPlay())); connect(ui.pButtonStopPlay, SIGNAL(clicked()), this, SLOT(onStopPlay())); // 输出当前设备支持的音频编码格式; QAudioDeviceInfo info(QAudioDeviceInfo::defaultOutputDevice()); qDebug()stop(); destinationFile.close(); delete m_audioInput; m_audioInput = NULL; } // 将生成的.raw文件转成.wav格式文件; if (addWavHeader(RAW_RECORD_FILENAME, WAV_RECORD_FILENAME) > 0) QMessageBox::information(NULL, tr("Save"), tr("RecordFile Save Success")); } } void MyAudioInput::onPlay() { // 当前没有录音才播放; if (!m_isRecord) { // 如果正在播放则关闭播放,准备重新播放; if (m_isPlay) { onStopPlay(); } m_isPlay = true; sourceFile.setFileName(WAV_RECORD_FILENAME); sourceFile.open(QIODevice::ReadOnly); // 设置播放音频格式; QAudioFormat format; format.setSampleRate(8000); format.setChannelCount(1); format.setSampleSize(16); format.setCodec("audio/pcm"); format.setByteOrder(QAudioFormat::LittleEndian); format.setSampleType(QAudioFormat::UnSignedInt); QAudioDeviceInfo info(QAudioDeviceInfo::defaultOutputDevice()); qDebug() setText(QString("Idle : %1/S").arg(m_RecordTime)); if (m_audioOutput != NULL) { m_audioOutput->stop(); sourceFile.close(); delete m_audioOutput; m_audioOutput = NULL; } } } // 播放状态更新; void MyAudioInput::handleStateChanged(QAudio::State state) { switch (state) { case QAudio::IdleState: // Finished playing (no more data) onStopPlay(); break; case QAudio::StoppedState: // Stopped for other reasons if (m_audioOutput->error() != QAudio::NoError) { // Error handling } break; default: // ... other cases as appropriate break; } } // 时钟事件; void MyAudioInput::timerEvent(QTimerEvent *event) { if (event->timerId() == m_RecordTimerId) { QString strState; if (m_isRecord) { strState = "Recording"; m_RecordTime = m_audioInput->elapsedUSecs() / TIME_TRANSFORM; } else if (m_isPlay) { strState = "Playing"; m_RecordTime = m_audioOutput->elapsedUSecs() / TIME_TRANSFORM; } ui.labelTime->setText(QString("%1 : %2/S").arg(strState).arg(m_RecordTime)); } } // 将生成的.raw文件转成.wav格式文件; qint64 MyAudioInput::addWavHeader(QString catheFileName , QString wavFileName) { // 开始设置WAV的文件头 // 这里具体的数据代表什么含义请看上一篇文章(Qt之WAV文件解析)中对wav文件头的介绍 WAVFILEHEADER WavFileHeader; qstrcpy(WavFileHeader.RiffName, "RIFF"); qstrcpy(WavFileHeader.WavName, "WAVE"); qstrcpy(WavFileHeader.FmtName, "fmt "); qstrcpy(WavFileHeader.DATANAME, "data"); // 表示 FMT块 的长度 WavFileHeader.nFmtLength = 16; // 表示 按照PCM 编码; WavFileHeader.nAudioFormat = 1; // 声道数目; WavFileHeader.nChannleNumber = 1; // 采样频率; WavFileHeader.nSampleRate = 8000; // nBytesPerSample 和 nBytesPerSecond这两个值通过设置的参数计算得到; // 数据块对齐单位(每个采样需要的字节数 = 通道数 × 每次采样得到的样本数据位数 / 8 ) WavFileHeader.nBytesPerSample = 2; // 波形数据传输速率 // (每秒平均字节数 = 采样频率 × 通道数 × 每次采样得到的样本数据位数 / 8 = 采样频率 × 每个采样需要的字节数 ) WavFileHeader.nBytesPerSecond = 16000; // 每次采样得到的样本数据位数; WavFileHeader.nBitsPerSample = 16; QFile cacheFile(catheFileName); QFile wavFile(wavFileName); if (!cacheFile.open(QIODevice::ReadWrite)) { return -1; } if (!wavFile.open(QIODevice::WriteOnly)) { return -2; } int nSize = sizeof(WavFileHeader); qint64 nFileLen = cacheFile.bytesAvailable(); WavFileHeader.nRiffLength = nFileLen - 8 + nSize; WavFileHeader.nDataLength = nFileLen; // 先将wav文件头信息写入,再将音频数据写入; wavFile.write((char *)&WavFileHeader, nSize); wavFile.write(cacheFile.readAll()); cacheFile.close(); wavFile.close(); return nFileLen; } 程序截图

这里写图片描述

点击录音开始录音,再次点击会弹出提示,当前正在录音,这样防止多次点击录音,导致重复录音。点击停止录音,结束录音,并将生成的.raw文件转成.wav文件,点击播放,则播放.wav音频文件。多次点击播放会重新播放,点击停止播放,则停止当前播放。 这里做了一些简单的逻辑判断,避免多次点击按钮导致生成的文件错误,同时在时钟事件中不断更新当前状态。

生成的文件截图

这里写图片描述

这里写图片描述这里写图片描述

仔细对比两个文件,二者大小相差44个字节,这44个字节即为手动添加的wav文件头信息,而前者是不能用播放器打开,后者可以直接用播放器播放。

这里写图片描述 从上图中可以通过文件头来判断是否是一个wav文件,该文件的前4位为 “52 49 46 46” ,即为 “RIFF”。后面的数据也是按照wav文件头数据结构依次存储。有兴趣可以对照上一篇文章对wav文件头的介绍,将生成的wav文件头信息解析出来。

以下是对wav文件解析后的数据: 这里写图片描述 具体如何解析可以参考 Qt 之 解析wav文件的头信息(详细分析、对比不同wav文件的数据)。

特别注意

在利用QAudioInput生成音频时需要设置音频的格式(通过QAudioFormat来设置),这里设置的格式要与 转为wav文件时设置的一系列参数 以及 在用 QAudioOutput 进行播放时设置的格式要完全一致,否则会导致声音文件识别不了,或者播放声音不清楚或者就只能听见嗡嗡的声音,所以一定要保持格式的一致性。至于在格式中参数的取值,到底对生成的音频文件有什么影响,将在下篇中进行解答。

下一篇将继续介绍用Qt直接生成wav格式的文件,不需要手动来添加wav文件头,同时也会用代码来解析一个wav文件的头信息,以及在生成时设置的一些格式参数对音频文件的影响等,下次见 。这里写图片描述

代码下载

Qt之实现录音播放及raw(pcm)转wav格式

其他文章:

Qt 之 WAV文件解析 Qt 之 解析wav文件的头信息(详细分析、对比不同wav文件的数据)。



【本文地址】

公司简介

联系我们

今日新闻


点击排行

实验室常用的仪器、试剂和
说到实验室常用到的东西,主要就分为仪器、试剂和耗
不用再找了,全球10大实验
01、赛默飞世尔科技(热电)Thermo Fisher Scientif
三代水柜的量产巅峰T-72坦
作者:寞寒最近,西边闹腾挺大,本来小寞以为忙完这
通风柜跟实验室通风系统有
说到通风柜跟实验室通风,不少人都纠结二者到底是不
集消毒杀菌、烘干收纳为一
厨房是家里细菌较多的地方,潮湿的环境、没有完全密
实验室设备之全钢实验台如
全钢实验台是实验室家具中较为重要的家具之一,很多

推荐新闻


图片新闻

实验室药品柜的特性有哪些
实验室药品柜是实验室家具的重要组成部分之一,主要
小学科学实验中有哪些教学
计算机 计算器 一般 打孔器 打气筒 仪器车 显微镜
实验室各种仪器原理动图讲
1.紫外分光光谱UV分析原理:吸收紫外光能量,引起分
高中化学常见仪器及实验装
1、可加热仪器:2、计量仪器:(1)仪器A的名称:量
微生物操作主要设备和器具
今天盘点一下微生物操作主要设备和器具,别嫌我啰嗦
浅谈通风柜使用基本常识
 众所周知,通风柜功能中最主要的就是排气功能。在

专题文章

    CopyRight 2018-2019 实验室设备网 版权所有 win10的实时保护怎么永久关闭