PCM转WAV格式 Wav数据格式的44个字节头部信息汇总

转自: https://blog.csdn.net/jeffasd/article/details/72466302

和 http://ikinglai.blog.51cto.com/6220785/1224481

 

一般通过麦克风采集的录音数据都是PCM格式的,即不包含头部信息,这样导致很多播放器都播放不了,你只能用像Audition这样的专业工具才能进行播放,显然是非常不方便的。为了得到wav格式的录音数据,我们在保存录音数据的时候就要自己手工加上44个字节的头部信息。

 

wav格式的音频数据的头部信息基本上是固定不变的,总共44个字节,包括以下内容:

 

1.    "RIFF"(4个字节)

       固定字符串

 

2.    录音数据长度 +(44 -8) (4个字节)

      类型可以是int或long,但必须保证类型占4个字节大小

 

3.    "WAVE "(4个字节)

       固定字符串

 

4.    "fmt "  (4个字节)

       固定字符串,注意最后有一个空格

 

5.     size1(4个字节)

       值为16

 

6.    format tag(2个字节)

      值为1

 

7.    channel(2个字节)

       声道数,1为单声道,2为多声道

 

8.    sampleRate(4个字节)

       采样率,值为8000,16000等

 

9.    bytePerSec(4个字节)

       每秒所需的字节数

 

10.   blockAlign(2个字节)

       每个采样需要的字节数,计算公式:声道数 * 每个采样需要的bit  / 8

 

11.    bitPerSample(2个字节)

       每个采样需要的bit数,一般为8或16

 

12.    "data"(4个字节)

         固定字符串

 

13.    size2(4个字节)

         录音数据的长度,不包括头部长度

PCM录音数据转Wav格式

 

#include <stdlib.h>
#include "WaveHeader.h"
 
// wav头部结构体
struct wave_header {
 
char riff[4];
unsignedlong fileLength;
char wavTag[4];
char fmt[4];
unsignedlong size;
unsignedshort formatTag;
unsignedshort channel;
unsignedlong sampleRate;
unsignedlong bytePerSec;
unsignedshort blockAlign;
unsignedshort bitPerSample;
char data[4];
unsignedlong dataSize;
 
};
 
 
void *createWaveHeader(int fileLength, short channel, int sampleRate, short bitPerSample)
{
 
structwave_header *header = malloc(sizeof(structwave_header));
 
if (header == NULL) {
    return  NULL;
}
 
// RIFF
   header->riff[0] = 'R';
   header->riff[1] = 'I';
   header->riff[2] = 'F';
   header->riff[3] = 'F';
 
// file length
   header->fileLength = fileLength + (44 - 8);
 
// WAVE
   header->wavTag[0] = 'W';
   header->wavTag[1] = 'A';
   header->wavTag[2] = 'V';
   header->wavTag[3] = 'E';
 
// fmt
   header->fmt[0] = 'f';
   header->fmt[1] = 'm';
   header->fmt[2] = 't';
   header->fmt[3] = ' ';
 
   header->size = 16;
   header->formatTag = 1;
   header->channel = channel;
   header->sampleRate = sampleRate;
   header->bitPerSample = bitPerSample;
   header->blockAlign = (short)(header->channel * header->bitPerSample / 8);
   header->bytePerSec = header->blockAlign * header->sampleRate;
 
// data
   header->data[0] = 'd';
   header->data[1] = 'a';
   header->data[2] = 't';
   header->data[3] = 'a';
 
// data size
   header->dataSize = fileLength;
 
return header;
}
 
使用示例:
 
// fileLength 原始录音数据长度
// 1 表示 单声道
// 16000 采样率
//  16 表示每个采样点是16个bit
void *header = createWaveHeader(fileLength, 1, 16000, 16);
 
// 使用。。。。
 
free(header);
package com.example.pcm2wave;
 
import java.io.ByteArrayOutputStream;
import java.io.IOException;
 
public class WaveHeader {
 
private char fileID[] = { 'R', 'I', 'F', 'F' };
private int fileLength;
private char wavTag[] = { 'W', 'A', 'V', 'E' };
private char fmtHdrID[] = { 'f', 'm', 't', ' ' };
private int fmtHdrLeth = 16;
private short formatTag = 1;
public short channels = 1;
public short sampleRate = 16000;
public short bitsPerSample = 16;
private short blockAlign = (short)(channels * bitsPerSample / 8);
private int avgBytesPerSec = blockAlign * sampleRate;
private char dataHdrID[] = { 'd', 'a', 't', 'a' };
private int dataHdrLeth;
public WaveHeader (int fileLength){
this.fileLength = fileLength + (44 - 8);
dataHdrLeth = fileLength;
}
public WaveHeader (int fileLength, short channels, short sampleRate, short bitsPerSample){
this.fileLength = fileLength + (44 - 8);
dataHdrLeth = fileLength;
this.channels = channels;
this.sampleRate = sampleRate;
this.bitsPerSample = bitsPerSample;
blockAlign = (short)(channels * bitsPerSample / 8);
avgBytesPerSec = blockAlign * sampleRate;
}
/**
* @return byte[] 44个字节
* @throws IOException
*/
public byte[] getHeader() throws IOException {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
WriteChar(bos, fileID);
WriteInt(bos, fileLength);
WriteChar(bos, wavTag);    
WriteChar(bos, fmtHdrID);
WriteInt(bos, fmtHdrLeth);
WriteShort(bos, formatTag);
WriteShort(bos, channels);
WriteInt(bos, sampleRate);
WriteInt(bos, avgBytesPerSec);
WriteShort(bos, blockAlign);
WriteShort(bos, bitsPerSample);
WriteChar(bos, dataHdrID);
WriteInt(bos, dataHdrLeth);
bos.flush();
byte[] r = bos.toByteArray();
bos.close();
return r;
}
 
private void WriteShort(ByteArrayOutputStream bos, int s)
throws IOException {
byte[] mybyte = new byte[2];
mybyte[1] = (byte) ((s << 16) >> 24);
mybyte[0] = (byte) ((s << 24) >> 24);
bos.write(mybyte);
}
 
private void WriteInt(ByteArrayOutputStream bos, int n) throws IOException {
byte[] buf = new byte[4];
buf[3] = (byte) (n >> 24);
buf[2] = (byte) ((n << 8) >> 24);
buf[1] = (byte) ((n << 16) >> 24);
buf[0] = (byte) ((n << 24) >> 24);
bos.write(buf);
}
 
private void WriteChar(ByteArrayOutputStream bos, char[] id) {
for (int i = 0; i < id.length; i++) {
char c = id[i];
bos.write(c);
}
}
}
 
使用方法:
 
// fileLength 录音数据的长度
WaveHeader header = new WaveHeader(fileLength);
// 返回44个字节的数组
byte[] waveHeaderBytes = header.getHeader();

 

要将实时接收的二进制PCM数据换为WAV格式,可以按照以下步骤进行操作: 1. 打开一个WAV文件并写入WAV文件头,其中包括采样率、声道数、位深等信息WAV文件头的结构可以参考WAV文件格式的定义。 2. 接收实时的PCM数据,将其写入WAV文件中。需要注意的是,PCM数据的采样位深度和声道数必须与WAV文件头中的设置一致。 3. 在完成数据写入后,关闭WAV文件即可。 以下是一个简单的C++代码示例,用于将16位、单声道的PCM数据实时换为WAV格式: ```c++ #include <iostream> #include <fstream> using namespace std; // WAV文件头结构体定义 struct WaveHeader { char chunkId[4]; // "RIFF" unsigned int chunkSize; // 文件大小 char format[4]; // "WAVE" char subchunk1Id[4]; // "fmt " unsigned int subchunk1Size; // 子块1大小 unsigned short audioFormat; // 音频格式 unsigned short numChannels; // 声道数 unsigned int sampleRate; // 采样率 unsigned int byteRate; // 每秒字节数 unsigned short blockAlign; // 数据块对齐 unsigned short bitsPerSample; // 采样位深度 char subchunk2Id[4]; // "data" unsigned int subchunk2Size; // 数据块大小 }; int main() { // 打开WAV文件 ofstream wavFile("output.wav", ios::binary); if (!wavFile) { cerr << "无法打开WAV文件" << endl; return -1; } const unsigned int sampleRate = 44100; // 采样率 const unsigned short numChannels = 1; // 声道数 const unsigned short bitsPerSample = 16; // 采样位深度 // 写入WAV文件头 WaveHeader header = {}; memcpy(header.chunkId, "RIFF", 4); header.chunkSize = 0; // 先写入0 memcpy(header.format, "WAVE", 4); memcpy(header.subchunk1Id, "fmt ", 4); header.subchunk1Size = 16; // PCM格式 header.audioFormat = 1; // PCM格式 header.numChannels = numChannels; header.sampleRate = sampleRate; header.bitsPerSample = bitsPerSample; header.byteRate = sampleRate * numChannels * bitsPerSample / 8; header.blockAlign = numChannels * bitsPerSample / 8; memcpy(header.subchunk2Id, "data", 4); header.subchunk2Size = 0; // 先写入0 wavFile.write(reinterpret_cast<const char*>(&header), sizeof(header)); // 实时接收PCM数据并写入WAV文件 while (true) { // 假设从某个设备实时接收到了PCM数据 short pcmData; // 16位PCM数据 // 这里省略接收和处理实时数据的代码 // 将PCM数据写入WAV文件中 wavFile.write(reinterpret_cast<const char*>(&pcmData), sizeof(pcmData)); if (!wavFile) { cerr << "写入WAV文件失败" << endl; return -1; } header.subchunk2Size += sizeof(pcmData); // 更新数据块大小 // 每隔一段时间检查是否需要结束录音 // 这里省略检查代码,假设按下了结束录音按钮 break; } // 更新WAV文件头中的文件大小和数据块大小 header.chunkSize = 36 + header.subchunk2Size; header.subchunk2Size /= sizeof(short); // 换为采样帧数 wavFile.seekp(0, ios::beg); wavFile.write(reinterpret_cast<const char*>(&header), sizeof(header)); // 关闭WAV文件 wavFile.close(); return 0; } ``` 需要注意的是,这只是一个简单的示例代码,实际应用中可能需要考虑更多的情况,比如错误处理、缓冲区管理等。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值