播放器是无法直接播放PCM的,因为播放器并不知道PCM的采样率、声道数、位深度等参数。当PCM转成某种特定的音频文件格式后(比如转成WAV),就能够被播放器识别播放了。

本文通过2种方式(命令行、编程)演示一下:如何将PCM转成WAV。

WAV文件格式

在进行PCM转WAV之前,先再来认识一下WAV的文件格式

  • WAV、AVI文件都是基于RIFF标准的文件格式
  • RIFF(Resource Interchange File Format,资源交换文件格式)由Microsoft和IBM提出
  • 所以WAV、AVI文件的最前面4个字节都是RIFF四个字符

WAV 文件标准格式如下:**

我们用一个数据格式使用十六进制展示的大小为 72 字节 WAVE 格式文件举例:

下面来一张通俗易懂的图:

每一个chunk(数据块)都由3部分组成:

  • id:chunk的标识
  • data size:chunk的数据部分大小,字节为单位
  • data,chunk的数据部分

整个WAV文件是一个RIFF chunk,它的data由3部分组成:

  • format:文件类型
  • fmt chunk
    • 音频参数相关的chunk
    • 它的data里面有采样率、声道数、位深度等参数信息
  • data chunk
    • 音频数据相关的chunk
    • 它的data就是真正的音频数据(比如PCM数据)

RIFF chunk除去data chunk的data(音频数据)后,剩下的内容可以称为:WAV文件头,一般是44字节。

命令行

通过下面的命令可以将PCM转成WAV。

// ffmpeg 输入文件参数 -i 输入文件 输出文件参数 输出文件

ffmpeg -ar 44100 -ac 2 -f s16le -i out.pcm out.wav

需要注意的是:上面命令生成的WAV文件头有78字节。对比44字节的文件头,它多增加了一个34字节大小的LIST chunk。

关于LIST chunk的参考资料:

加上一个输出文件参数-bitexact可以去掉LIST Chunk。

ffmpeg -ar 44100 -ac 2 -f s16le -i out.pcm -bitexact out2.wav

编程

在PCM数据的前面插入一个44字节的WAV文件头,就可以将PCM转成WAV。

WAV的文件头结构

WAV的文件头结构大概如下所示:

#define AUDIO_FORMAT_PCM 1
#define AUDIO_FORMAT_FLOAT 3 // WAV文件头(44字节)
typedef struct {
// RIFF chunk的id
uint8_t riffChunkId[4] = {'R', 'I', 'F', 'F'};
// RIFF chunk的data大小,即文件总长度减去8字节
uint32_t riffChunkDataSize; // "WAVE"
uint8_t format[4] = {'W', 'A', 'V', 'E'}; /* fmt chunk */
// fmt chunk的id
uint8_t fmtChunkId[4] = {'f', 'm', 't', ' '};
// fmt chunk的data大小:存储PCM数据时,是16
uint32_t fmtChunkDataSize = 16;
// 音频编码,1表示PCM,3表示Floating Point
uint16_t audioFormat = AUDIO_FORMAT_PCM;
// 声道数
uint16_t numChannels;
// 采样率
uint32_t sampleRate;
// 字节率 = sampleRate * blockAlign
uint32_t byteRate;
// 一个样本的字节数 = bitsPerSample * numChannels >> 3
uint16_t blockAlign;
// 位深度
uint16_t bitsPerSample; /* data chunk */
// data chunk的id
uint8_t dataChunkId[4] = {'d', 'a', 't', 'a'};
// data chunk的data大小:音频数据的总长度,即文件总长度减去文件头的长度(一般是44)
uint32_t dataChunkDataSize;
} WAVHeader;

PCM转WAV核心实现

封装到了FFmpegUtil类的pcm2wav方法中。

#include <QFile>
#include <QDebug> class FFmpegUtil {
public:
FFmpegUtil();
static void pcm2wav(WAVHeader &header,
const char *pcmFilename,
const char *wavFilename);
}; void FFmpegUtil::pcm2wav(WAVHeader &header,
const char *pcmFilename,
const char *wavFilename) {
header.blockAlign = header.bitsPerSample * header.numChannels >> 3;
header.byteRate = header.sampleRate * header.blockAlign; // 打开pcm文件
QFile pcmFile(pcmFilename);
if (!pcmFile.open(QFile::ReadOnly)) {
qDebug() << "文件打开失败" << pcmFilename;
return;
}
header.dataChunkDataSize = pcmFile.size();
header.riffChunkDataSize = header.dataChunkDataSize
+ sizeof (WAVHeader)
- sizeof (header.riffChunkId)
- sizeof (header.riffChunkDataSize); // 打开wav文件
QFile wavFile(wavFilename);
if (!wavFile.open(QFile::WriteOnly)) {
qDebug() << "文件打开失败" << wavFilename; pcmFile.close();
return;
} // 写入头部
wavFile.write((const char *) &header, sizeof (WAVHeader)); // 写入pcm数据
char buf[1024];
int size;
while ((size = pcmFile.read(buf, sizeof (buf))) > 0) {
wavFile.write(buf, size);
} // 关闭文件
pcmFile.close();
wavFile.close();
}

调用函数

// 封装WAV的头部
WAVHeader header;
header.numChannels = 2;
header.sampleRate = 44100;
header.bitsPerSample = 16;
header.audioFormat = 1; // 调用函数
FFmpegUtil::pcm2wav(header, "F:/in.pcm", "F:/out.wav");

代码链接

10_PCM转WAV的更多相关文章

  1. C++标准库实现WAV文件读写

    在上一篇文章RIFF和WAVE音频文件格式中对WAV的文件格式做了介绍,本文将使用标准C++库实现对数据为PCM格式的WAV文件的读写操作,只使用标准C++库函数,不依赖于其他的库. WAV文件结构 ...

  2. iOS 使用EZAudio库生成wav出错的情况

    使用EZAudio库 录M4A格式可以参考该库例子中的代码. 录wav格式得改下源码.看下面的代码 AVAudioSession *session = [AVAudioSession sharedIn ...

  3. C#播放wav文件

    C#使用HWQPlayer类播放wav文件 类的代码: using System.IO; using System.Runtime.InteropServices; namespace HoverTr ...

  4. WIN32下使用DirectSound接口的简单音频播放器(支持wav和mp3)

    刚好最近接触了一些DirectSound,就写了一个小程序练练手,可以用来添加播放基本的wav和mp3音频文件的播放器.界面只是简单的GDI,dxsdk只使用了DirectSound8相关的接口. D ...

  5. ffmpeg常用转换命令,支持WAV转AMR

    音频转换: 1.转换amr到mp3: ffmpeg -i shenhuxi.amr amr2mp3.mp3 2.转换amr到wav: ffmpeg -acodec libamr_nb -i shenh ...

  6. wince mobile环境下播放WAV声音

     [DllImport("coredll", EntryPoint = "PlaySound")]         public static extern i ...

  7. 音频文件解析(二):WAV格式文件波形绘制

    解析WAV头部信息后,接下来就可以根据相关参数和DATA块数据绘制波形. 1.重新编码(转换为8bits,单声道数据) Public Function GetFormatData(ByVal pDat ...

  8. 音频文件解析(一):WAV格式文件头部解析

    WAV为微软公司(Microsoft)开发的一种声音文件格式,它符合RIFF(Resource Interchange File Format)文件规范,用于保存Windows平台的音频信息资源. 文 ...

  9. python 播放 wav 文件

    未使用其他库, 只是使用 pywin32 调用系统底层 API 播放 wav 文件. # Our raison d'etre - playing sounds import pywintypes im ...

  10. FreeSWITCH无法读取wav文件

    错误日志如下: -- :: Invalid file format [wav] /suite-espanola-op--leyenda.wav]! -- :: Can't open /usr/loca ...

随机推荐

  1. P7167 [eJOI2020 Day1] Fountain 题解

    题目链接:Fountain 很不错的基础算法组合题:单调栈+倍增 首先考虑到一个事实,就是下面第一个比当前半径大的位置会成为移动的第一次落脚点,抽象下就是下面出现的第一次比自身大的半径,这个问题显然可 ...

  2. 痞子衡嵌入式:我入选了2023年度与非网(eefocus)最佳创作者Top15

    最近收到了「与非网」发来的 2023 年度最佳创作者 证书,证书做得一如既往地有质感,这是与非网第二次给痞子衡发证书了,足见与非网对痞子衡的认可. 与非网自 2021 年起,每年都会评选一次年度创作者 ...

  3. yapi 个人空间 这个分组的问题

    总结:yapi个人空间分组的问题,我暂时不用理睬 他自己自由,但是 不允许他 创建非个人空间的分组.这点留意 避免不统一.所有的分组都必须我自己来创建,不允许他们私自创建.

  4. 【译】.NET 8 网络改进(二)

    原文 | Máňa,Natalia Kondratyeva 翻译 | 郑子铭 修改 HttpClient 日志记录 自定义(甚至简单地关闭)HttpClientFactory 日志记录是长期请求的功能 ...

  5. Java判断一个字符串中是否包含数字

    知识点 本例考察以下Java知识点: 正则表达式 关于正则表达式: https://www.runoob.com/java/java-regular-expressions.html Characte ...

  6. nodejs+express4实现文件上传下载删除和列表展示功能

    0.效果展示 1.创建项目 创建文件夹:express_file_upload npm init # 入口文件选择server.js 安装插件 npm install express npm inst ...

  7. jar not loaded. See Servlet Spec 3.0, section 10.7.2 Offending class: javax/servlet/Servlet

    说明: 今天在整合activemq功能时启动应用模块报错: jar not loaded. See Servlet Spec 3.0, section 10.7.2 Offending class: ...

  8. sliver生成木马.sh

    生成sliver木马多个步骤合成一个sh #!/bin/bash # date: 20230222 host_ip=$1 WORK_DIR=/opt/work rm -rf /root/.sliver ...

  9. 01-Redis系列之-Redis介绍安装配置

    Redis初识 Redis主要内容 1 redis介绍,特性,安装和配置,典型应用场景 2 单线程架构,5大数据类型操作,通用指令和高级API的使用 3 高级用法(慢查询,管道,shu发布订阅,bit ...

  10. zookeeper运行时dos窗口一闪而过

    错误:从官网下载zookeeper解压到本地之后,鼠标双击运行zkServer.cmd文件,dos窗口一闪而过,看不到错误原因: 解决方法:通过dos窗口执行zkServer.cmd文件,对应的错误信 ...