基于DWC2的USB驱动开发-UAC之WAV-PCM音频文件格式详解

描述

一. 前言

在做UAC,PWM音频播放的项目,需要解析WAV格式文件,通过UAC发送,或接收PCM数据,驱动喇叭播放。这里对WAV文件格式相关内容进行整理备忘。

仅介绍使用非压缩的PCM(Puls Code Modulation)脉冲编码调制格式,其他压缩格式这里不描述。

二.参考

http://tiny.systems/software/soundProgrammer/WavFormatDocs.pdf

http://soundfile.sapp.org/doc/WaveFormat/

http://www.lightlink.com/tjweber/StripWav/WAVE.html

三.格式解析

WAVE文件格式是微软RIFF多媒体文件存储规范的一个子集。RIFF文件从一个文件头开始,后面跟着一系列数据块。WAVE文件通常只是一个RIFF文件,其中包含一个由两个子块组成的“WAVE”块——一个“fmt”块指定数据格式,一个“data”块包含实际的示例数据。我们称这种形式为“规范形式”。

如下所示由RIFF fmt data三个chunk组成。

音频

Offset Size Name Description

规范的WAVE格式以RIFF报头开始:

0 4 ChunkID ASCII的"RIFF" 0x52494646 大端

4 4 ChunkSize 36+SubChunk2Size即 4+(8+SubChunk1Size)+(8+SubChunk2Size)

ChunkSize之后所有内容的大小即

整个文件-8即不包括ChunkID和ChunkSize的大小.

8 4 Format ASCII的"WAVE"(0x57415645 大端).

"WAVE" 包括以下两个subchunks: "fmt " 和 "data":

"fmt " subchunk描述声音数据的格式:

12 4 Subchunk1ID ASCII的 "fmt "(0x666d7420 大端).

16 4 Subchunk1Size 对于PCM为16.Subchunk1Size后本chunk剩余部分大小.

20 2 AudioFormat 格式:PCM = 1(即线性量化值),其他值为压缩格式.

22 2 NumChannels 通道数:Mono = 1, Stereo = 2, etc.

24 4 SampleRate 采样率:8000, 44100, etc.

28 4 ByteRate 字节速率: SampleRate * NumChannels * BitsPerSample/8

32 2 BlockAlign 块大小,即一个采样所有通道的数据量:NumChannels * BitsPerSample/8(是不是应该向上取整?)

34 2 BitsPerSample 8 bits = 8, 16 bits = 16, etc.

2 ExtraParamSize 其他参数大小:对于PCM没有

X ExtraParams 其他参数

"data" subchunk 包括数据的大小和实际的数据:

36 4 Subchunk2ID ASCII的"data"(0x64617461 大端).

40 4 Subchunk2Size 后续数据大小:NumSamples * NumChannels * BitsPerSample/8.

44 * Data 实际的数据.

以上注意所有整数是小端格式,字符ID和字符format都是大端(按照字符顺序,从低地址开始按顺序依次存放)。WAVE数据文件的默认字节顺序是小端序。使用大端字节排序方案编写的文件具有标识符RIFX而不是RIFF。

样本数据必须在偶数字节边界上结束

8位采样被存储为无符号字节,范围从0到255。16位采样被存储为2补码有符号整数,范围从-32768到32767。

在Wave数据流中可能有额外的子块。

RIFF代表资源交换文件格式。

多媒体应用需要存储和管理各种各样的数据,包括位图、音频数据、视频数据和外围设备控制信息。RIFF提供了一种存储所有这些不同类型数据的方法。RIFF文件包含的数据类型由文件扩展名表示。可能存储在RIFF文件中的数据示例如下:

·Audio/visual interleaved data (.AVI)

·Waveform data (.WAV)

·Bitmapped data (.RDI)

·MIDI information (.RMI)

·Color palette (.PAL)

·Multimedia movie (.RMN)

·Animated cursor (.ANI)

·A bundle of other RIFF files (.BND)

四.举例说明

一个WAVE文件的前面72字节如下

52 49 46 46** 24 08 00 00 57 41 56 45 66 6d 74 20 10 00 00 00 01 00** 02 00** 22 56 00 00 88 58 01 00 04 00 10 00 64 61 74 61 **00 08 00 00 00 00 00 00 24 17 1e f3 3c 13 3c 14 16 f9 18 f9 34 e7 23 a6 3c f2 24 f2 11 ce 1a 0d

音频

另外一个文件格式解析如下

音频

音频

音频

音频

音频

字节速率: SampleRate * NumChannels * BitsPerSample/8 = 44100216/2=176,400=0x0002B110

音频

五.音频处理工具

二进制编辑查看010Editor

Sox:https://sox.sourceforge.net/Main/HomePage

六.WAV文件曲线显示

# -*- coding: utf-8 -*-
import wave
import pylab as pl
import numpy as np
# 打开WAV文档
f = wave.open(r"1.wav", "rb")
# 读取格式信息
# (nchannels, sampwidth, framerate, nframes, comptype, compname)
params = f.getparams()
nchannels, sampwidth, framerate, nframes = params[:4]
print(params)
# 读取波形数据
str_data = f.readframes(nframes)
f.close()
#将波形数据转换为数组
wave_data = np.fromstring(str_data, dtype=np.short)
wave_data.shape = -1, 2
wave_data = wave_data.T
time = np.arange(0, nframes) * (1.0 / framerate)
# 绘制波形
pl.subplot(211) 
pl.plot(time, wave_data[0])
pl.subplot(212) 
pl.plot(time, wave_data[1], c="g")
pl.xlabel("time (seconds)")
pl.show()

音频

七.解析C代码

#define CHUNK_RIFF "RIFF"
#define CHUNK_WAVE "WAVE"
#define CHUNK_FMT "fmt "
#define CHUNK_DATA "data"#define AUDIO_FORMAT_PCM 0x01typedef struct
{
    uint32_t off;
    uint32_t chunksize;
    uint16_t audioformat;
    uint16_t numchannels;
    uint32_t samplerate;
    uint32_t byterate;
    uint16_t blockalign;
    uint16_t bitspersample;
    uint32_t datasize;
}wav_t;
​
int wav_decode(uint8_t* addr, wav_t* wav);
​
int wav_decode(uint8_t* addr, wav_t* wav)
{
    uint8_t* p = addr;
    uint32_t chunksize;
    uint32_t subchunksize;
    if(0 != memcmp(p,CHUNK_RIFF,4))
    {
        return -1;
    }
    p += 4;
    chunksize = (uint32_t)p[0] | ((uint32_t)p[1]< < 8) | ((uint32_t)p[2]< < 16) | ((uint32_t)p[3]< < 24);
    wav- >chunksize = chunksize;
    p += 4;
    if(0 != memcmp(p,CHUNK_WAVE,4))
    {
        return -2;
    }
    p += 4;
​
    do
    {
        if(0 == memcmp(p,CHUNK_FMT,4))
        {
            p += 4;
            subchunksize = (uint32_t)p[0] | ((uint32_t)p[1]< < 8) | ((uint32_t)p[2]< < 16) | ((uint32_t)p[3]< < 24);
            p += 4;
            /* 解析参数 */
            wav- >audioformat = (uint16_t)p[0] | ((uint16_t)p[1]< < 8);
            if(wav- >audioformat == 0x0001)
            {
                p += 2;
                wav- >numchannels = (uint16_t)p[0] | ((uint16_t)p[1]< < 8);
                p += 2;
                wav- >samplerate = (uint32_t)p[0] | ((uint32_t)p[1]< < 8) | ((uint32_t)p[2]< < 16) | ((uint32_t)p[3]< < 24);
                p += 4;
                wav- >byterate = (uint32_t)p[0] | ((uint32_t)p[1]< < 8) | ((uint32_t)p[2]< < 16) | ((uint32_t)p[3]< < 24);
                p += 4;
                wav- >blockalign = (uint16_t)p[0] | ((uint16_t)p[1]< < 8);
                p += 2;
                wav - >bitspersample = (uint16_t)p[0] | ((uint16_t)p[1]< < 8);
                p += 2;
            }
            else
            {
                p += subchunksize;
            }
        }
        else if(0 == memcmp(p,CHUNK_DATA,4))
        {
            p += 4;
            subchunksize = (uint32_t)p[0] | ((uint32_t)p[1]< < 8) | ((uint32_t)p[2]< < 16) | ((uint32_t)p[3]< < 24);
            wav- >datasize = subchunksize;
            p += 4;
            wav- >off = (uint32_t)(p- addr);
            return 0;
        }
        else
        {
            p += 4;
            subchunksize = (uint32_t)p[0] | ((uint32_t)p[1]< < 8) | ((uint32_t)p[2]< < 16) | ((uint32_t)p[3]< < 24);
            p += 4;
            p += subchunksize;
        }
    }while((uint32_t)(p - addr) < (chunksize + 8));
    return -3;
}

八.测试文件下载

https://samplelib.com/zh/sample-wav.html

审核编辑:汤梓红

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分