播放器是无法直接播放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. P2572 [SCOI2010] 序列操作 题解

    题解:序列操作 比较综合的 ds 题,综合了线段树常见的几种操作:维护最大子段和.区间翻转.区间求和.区间覆盖 . 维护子段和常见的我们维护三类东西: 前缀最长连续段.后缀最长连续段.当前区间上的最大 ...

  2. 七款云上共享文件系统 POSIX 兼容性大比拼

    当用户在进行文件系统选型时,POSIX 语义兼容性是必不可缺的一项考察指标.JuiceFS 一直非常重视对 POSIX 标准的高度兼容,在持续完善功能.提高性能的同时,尽力保持最大程度的 POSIX ...

  3. 小知识:开启NTP服务并设置为开机启动

    我的一套测试环境发现时间慢了10分钟,影响我做各类测试. 首先就想到NTP服务,发现已安装NTP安装包,也有默认的NTP配置文件,只是没有启用. 用到的相关命令参考如下: [root@bogon ~] ...

  4. jwt 生成的token exp 的单位是秒

    public class Test { public static void main(String[] args) throws UnsupportedEncodingException { Dat ...

  5. Word-批量导出Word中的图片

    当我们需要把Word文件中的图片保存起来,你是如何导出Word图片呢?右键一张张保存图片吗?这效率太低了.如果文档中有大量的图片,这个方法会浪费很多时间. 下面给大家分享word如何批量导出图片的技巧 ...

  6. 【scikit-learn基础】--模型持久化

    模型持久化(模型保存与加载)是机器学习完成的最后一步.因为,在实际情况中,训练一个模型可能会非常耗时,如果每次需要使用模型时都要重新训练,这无疑会浪费大量的计算资源和时间. 通过将训练好的模型持久化到 ...

  7. PAC主成分分析__784手写特征案例

    from sklearn.neighbors import KNeighborsClassifier as KNN from sklearn.decomposition import PCA from ...

  8. CF131D Subway 题解

    题目传送门 前置知识 强连通分量 | 最短路 解法 考虑用 Tarjan 进行缩点,然后跑最短路. 缩点:本题的缩点有些特殊,基于有向图缩点修改而得,因为是无向图,所以在 Tarjan 过程中要额外记 ...

  9. NC51112 Stars in Your Window

    题目链接 题目 题目描述 Fleeting time does not blur my memory of you. Can it really be 4 years since I first sa ...

  10. 在Hexo中引入本地图片的实现

    实现步骤 第一步:修改项目根目录下的_config.yml文件参数post_asset_folder值为true. # 开始使用本地静态资源 post_asset_folder: true 第二步:安 ...