Wave音频格式解析

来源:互联网 发布:广东网络干部培训学院 编辑:程序博客网 时间:2024/05/30 05:23

PCM脉码调制数字音频格式是70年代末发展起来的,记录媒体之一的CD80年代初由飞利浦和索尼公司共同推出。PCM的音频格式也被DVD-A所采用,它支持立体声和5.1环绕声,1999年由DVD讨论会发布和推出的。 
PCM的比特率,从14-bit发展到16-bit18-bit20-bit直到24-bit;采样频率从44.1kHz发展到192kHz。到目前 为止PCM这项技术可以改善和提高的方面则越来越来小。只是简单的增加PCM比特率和采样率,不能根本的改善它的根本问题。

PCM的主要问题在于: 
1)任何PCM数字音频系统需要在其输入端设置急剧升降的滤波器,仅让20 Hz - 22.05 kHz的频率通过(高端22.05 kHz是由于CD 44.1 kHz的一半频率而确定),这是一项非常困难的任务。
2)在录音时采用多级或者串联抽选的数字滤波器(减低采样率),在重放时采用多级的内插的数字滤波器 (提高采样率),为了控制小信号在编码时的失真,两者又都需要加入重复定量噪声。这样就限制了PCM技术在音频还原时的保真度

为了全面改善PCM 数字音频技术,获得更好的声音质量,就需要有新的技术来替换。近年来飞利浦和索尼公司再次联手,共同推出一种称为直接流数字编码技术DSD的格式其记录媒体为超级音频CDSACD,支持立体声和5.1环绕声。 

WAVE文件作为多媒体中使用的声波文件格式之一,它是以RIFF格式为标准的。 RIFF是英文Resource Interchange File Format的缩写,每个WAVE文件的头四个字节便是“RIFF”WAVE文件由文件头和数据体两大部分组成。其中文件头又分为RIFFWAV文件标识段和声音数据格式说明段两部分。WAVE文件各部分内容及格式见附表。 
常见的声音文件主要有两种,分别对应于单声道(11.025KHz采样率、8Bit 的采样值)和双声道(44.1KHz采样率、16Bit的采样值)。采样率是指:声音信号在转换过程中单位时间内采样的次数。采样值是指每一次采样周期内声音模拟信号的积分值。对于单声道声音文件,采样数据为八位的短整数(short int 00H-FFH); 而对于双声道立体声声音文件,每次采样数据为一个16位的整数(int),高八位和低八位分别代表左右两个声道。 
WAVE文件数据块包含以脉冲编码调制(PCM)格式表示的样本。WAVE文件是由样本组织而成的。在单声道WAVE文件中,声道0代表左声道,声道1代表右声道。在多声道WAVE文件中,样本是交替出现的。

 

wave文件 = wave文件头部 音频数据(PCM格式)
//wave文件头部格式(共计44字节)

偏移地址

字节数

数据类型

内容

说明

0x00

4

unsigned long

“RIFF”

文件头标识,一般就是“RIFF”四个字母。

0x04

4

unsigned long

int

整个文件的大小。不包括0x00~0x07的空间大小。

0x08

4

unsigned long

“WAVE”

一般就是“WAVE”四个字母。

0x0c

4

unsigned long

“fmt”

格式说明块,一般就是“fmt”。

0x10

4

unsigned long

int

本数据块的大小。

0x14

2

unsigned short

格式类别

音频格式说明(1PCM形式的声音数据)。

0x16

2

unsigned short

声道数

单声道为1,双声道为2

0x18

4

unsigned long

采样率

每秒样本数,表示每个通道的播放速度。

0x1c

4

unsigned long

数据速率,以字节为单位计算

波形音频数据传送速率,其值为通道数×采样率×每样本的数据位数/8

0x20

2

unsigned short

数据块的调整块

其值为通道数×每样本的数据位值/8

0x22

2

unsigned short

每个样本的数据位数

表示每个声道中各个样本的数据位数。

0x24

4

char

“DATA”

一般就是“DATA”。

0x28

4

long

int

音频数据的长度。


PCM数据包格式

Sample1

Sample2

Sample3

Smaple4

Channel0

Channel0

Channel0

Channel0

8 bit单声道PCM数据包格式

Sample1

Sample2

Chanell0(left)

Channel1(right)

Channel0(left)

Channel0(right)

8bit立体音PCM数据包格式

Sample1

Sample2

Channel0

Low-order byte

Channel0

Hight-order byte

Channel0

Low-order byte

Channel0

Hight-order byte

16bit单声道PCM数据包格式

Smaple1

Channel0(left)

Low-order byte

Channel0(left)

High-order byte

Channel1(right)

Low-order byte

Channel1(right)

High-order byte

16bit立体音PCM数据包格式

 
 
//以下为一个简单的转换代码
//By6000WaveFile.h

#ifndef  __By6000WaveFile_H__
#define  __By6000WaveFile_H__

#include <windows.h>
#include <fstream>
#include <string>
#include <memory>
using namespace std;

struct WaveFile_Header

 unsigned long  ulChunkID;    //equal to "RIFF"
 unsigned long  ulChunkSize;   //size of chunk,not contain ulChunkID and ulChunkSize
 unsigned long  ulFormat;    //equal to "WAVE"
 unsigned long  ulSubChunk01ID;   //equal to "fmt"
 unsigned long  ulSubChunk01Size;  //size of SubChunk01,equal to 16 when audio format is pcm
 unsigned short  usAudioFormat;   //1,PCM; ...
 unsigned short  usChannelCount;   //1,only one audio channel; 2,have two audio channels
 unsigned long  ulSampleRate;   //sample count per second
 unsigned long  ulByteRate;    //byte count per sample
 unsigned short  usBlockAlign;   //
 unsigned short  usBitsPerSample;  //bit count a sampe have
 unsigned long  ulSubChunk02ID;   //equal to "DATA"
 unsigned long  ulSubChunk02Size;  //length of valid data
};

class CBy6000WaveFile;
struct   THREAD_PARAMS_By6000WaveFile
{
 CBy6000WaveFile* lpRunClass;
 bool    (CBy6000WaveFile::*lpRunFun)(void* lpParam);
};

#define   SINGLE_CONVERT_LENGTH_MAX    32*1024

class CBy6000WaveFile
{
public:
 CBy6000WaveFile(void);
 virtual ~CBy6000WaveFile(void);

 bool  parse_wave_file(WaveFile_Header& varWaveFileHeader, const wchar_t* lpszFilePath);

 bool  data_convert_wave16_to_by6000pcm128(const wchar_t* lpszBy6000pcm128_FilePath, const wchar_t* lpszWave16_FilePath);

private:
 bool  single_data_convert_wave16_to_by6000pcm128(BYTE* lpszBy6000pcm128_DataBuffer, const BYTE* lpszWave16_DataBuffer, long lConverLen);

 bool  start_thread_convert();
 void  end_thread_convert();
 bool  thread_fun_convert(void* lpParam);

 static  DWORD  __stdcall  thread_run(LPVOID lpParam);

private:
 fstream       m_fsReader;
 fstream       m_fsWriter;
 wstring       m_wstrBy6000pcm128_FilePath;
 wstring       m_wstrWave16_FilePath;

 HANDLE       m_hThread_Convert;
 bool       m_bThreadRunning_Convert;
 THREAD_PARAMS_By6000WaveFile m_varThreadParams_By6000WaveFile;

 bool       m_bConverSucceed;

 auto_ptr<BYTE>     m_ptrWave16_DataBuffer;
 auto_ptr<BYTE>     m_ptrBy6000pcm128_DataBuffer;
};

#endif

 

//By6000WaveFile.cpp

#include "StdAfx.h"
#include "By6000WaveFile.h"

CBy6000WaveFile::CBy6000WaveFile(void)
{
 m_bThreadRunning_Convert = false;
 m_hThread_Convert = NULL;

 m_bConverSucceed = false;

 m_ptrWave16_DataBuffer = auto_ptr<BYTE>(new BYTE[SINGLE_CONVERT_LENGTH_MAX + 0x100]);
 m_ptrBy6000pcm128_DataBuffer = auto_ptr<BYTE>(new BYTE[4*SINGLE_CONVERT_LENGTH_MAX + 0x100]);
}

CBy6000WaveFile::~CBy6000WaveFile(void)
{
}

bool CBy6000WaveFile::parse_wave_file(WaveFile_Header& varWaveFileHeader, const wchar_t* lpszFilePath)
{
 m_fsReader.open(lpszFilePath, ios::in | ios::binary);

 if(!m_fsReader.is_open() )
 {
  return false;
 }

 m_fsReader.read((char*)&varWaveFileHeader, sizeof(varWaveFileHeader) );

 m_fsReader.close();

 return true;
}

bool CBy6000WaveFile::data_convert_wave16_to_by6000pcm128(const wchar_t* lpszBy6000pcm128_FilePath, const wchar_t* lpszWave16_FilePath)
{
 m_wstrBy6000pcm128_FilePath = lpszBy6000pcm128_FilePath;
 m_wstrWave16_FilePath = lpszWave16_FilePath;

 start_thread_convert();

 if(WAIT_OBJECT_0 !=::WaitForSingleObject(m_hThread_Convert, 24*3600*1000) )
 {
  end_thread_convert();

  return false;
 }

 end_thread_convert();

 if(!m_bConverSucceed)
 {
  return false;
 }

 return true;
}

bool CBy6000WaveFile::single_data_convert_wave16_to_by6000pcm128(BYTE* lpszBy6000pcm128_DataBuffer, const BYTE* lpszWave16_DataBuffer, long lConverLen)
{
 if(NULL == lpszBy6000pcm128_DataBuffer || NULL == lpszWave16_DataBuffer)
 {
  return false;
 }

 if(0 != lConverLen % 4)
 {
  return false;
 }

 for(int i = 0; i < lConverLen; i += 4)
 {
  /*
  lpszBy6000pcm128_DataBuffer[4*i + 0] = lpszWave16_DataBuffer[i];
  lpszBy6000pcm128_DataBuffer[4*i + 1] = lpszWave16_DataBuffer[i + 1];  
  lpszBy6000pcm128_DataBuffer[4*i + 4] = lpszWave16_DataBuffer[i + 2];
  lpszBy6000pcm128_DataBuffer[4*i + 5] = lpszWave16_DataBuffer[i + 3];
   */
  
  lpszBy6000pcm128_DataBuffer[4*i + 0] = lpszWave16_DataBuffer[i + 1];
  lpszBy6000pcm128_DataBuffer[4*i + 1] = lpszWave16_DataBuffer[i + 0];
  lpszBy6000pcm128_DataBuffer[4*i + 4] = lpszWave16_DataBuffer[i + 3];
  lpszBy6000pcm128_DataBuffer[4*i + 5] = lpszWave16_DataBuffer[i + 2];
 }

 return true;
}


bool CBy6000WaveFile::start_thread_convert()
{
 //if the thread is running, end it first
 end_thread_convert();

 memset(&m_varThreadParams_By6000WaveFile, 0, sizeof(m_varThreadParams_By6000WaveFile) );
 m_varThreadParams_By6000WaveFile.lpRunClass = this;
 m_varThreadParams_By6000WaveFile.lpRunFun = &CBy6000WaveFile::thread_fun_convert;

 m_bThreadRunning_Convert = true;

 m_bConverSucceed = false;

 m_hThread_Convert = ::CreateThread(NULL, 0, &CBy6000WaveFile::thread_run, &m_varThreadParams_By6000WaveFile, FALSE, NULL);

 if(NULL == m_hThread_Convert)
 {
  return false;
 }

 return true;
}

void CBy6000WaveFile::end_thread_convert()
{
 if(m_bThreadRunning_Convert)
 {
  m_bThreadRunning_Convert = false;

  ::WaitForSingleObject(m_hThread_Convert, 200);
  ::CloseHandle(m_hThread_Convert);
  m_hThread_Convert = NULL;
 }
}

bool CBy6000WaveFile::thread_fun_convert(void* lpParam)
{
 WaveFile_Header varWaveFile_Header = {0};

 if(!parse_wave_file(varWaveFile_Header, m_wstrWave16_FilePath.c_str() ) )
 {
  return false;
 }

 m_fsReader.open(m_wstrWave16_FilePath.c_str(), ios::in|ios::binary);
 
 if(!m_fsReader.is_open() )
 {
  return false;
 }

 m_fsWriter.open(m_wstrBy6000pcm128_FilePath.c_str(), ios::out|ios::binary);
 
 if(!m_fsWriter.is_open() )
 {
  m_fsReader.close();

  return false;
 }

 m_fsWriter.clear();

 long lConvertTotalLength = varWaveFile_Header.ulSubChunk02Size - (varWaveFile_Header.ulSubChunk02Size % 4);
 long lReadPos = sizeof(WaveFile_Header);
 long lReadLen = (SINGLE_CONVERT_LENGTH_MAX < lConvertTotalLength) ? SINGLE_CONVERT_LENGTH_MAX : lConvertTotalLength;

 while(true)
 {
  memset(m_ptrWave16_DataBuffer.get(), 0, SINGLE_CONVERT_LENGTH_MAX + 0x100);
  memset(m_ptrBy6000pcm128_DataBuffer.get(), 0, 4*SINGLE_CONVERT_LENGTH_MAX + 0x100);

  m_fsReader.seekg(lReadPos);
  m_fsReader.read((char*)m_ptrWave16_DataBuffer.get(), lReadLen);

  single_data_convert_wave16_to_by6000pcm128(m_ptrBy6000pcm128_DataBuffer.get(), m_ptrWave16_DataBuffer.get(), lReadLen);

  m_fsWriter.write((char*)m_ptrBy6000pcm128_DataBuffer.get(), 4*lReadLen);
  m_fsWriter.flush();

  lReadPos += lReadLen;

  //finish the convert
  if(long(lReadPos - sizeof(WaveFile_Header) ) >= lConvertTotalLength)
  {
   break;
  }
  else
  {
   long lLengthRemain = lConvertTotalLength + sizeof(WaveFile_Header) - lReadPos;
   lReadLen = (SINGLE_CONVERT_LENGTH_MAX < lLengthRemain) ? SINGLE_CONVERT_LENGTH_MAX : lLengthRemain;
  }
 }

 m_fsReader.close();
 m_fsWriter.close();

 m_bConverSucceed = true;

 return true;
}

DWORD CBy6000WaveFile::thread_run(LPVOID lpParam)
{
 THREAD_PARAMS_By6000WaveFile* lpThreadParams = reinterpret_cast<THREAD_PARAMS_By6000WaveFile*>(lpParam);
 CBy6000WaveFile* lpRunClass = lpThreadParams->lpRunClass;
 bool (CBy6000WaveFile::*lpRunFun)(void*) = lpThreadParams->lpRunFun;

 (lpRunClass->*lpRunFun)(NULL);

 return 0;
}

 

//main.cpp

// Console.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include <windows.h>

#include <iostream>
using namespace std;

// #define BOOST_ALL_NO_LIB
// #include <boost/thread.hpp>
// #pragma comment(lib, "boost_thread-vc90-mt-1_48.lib")

#include "By6000WaveFile.h"

int main(int argc, char* argv[])
{
 CBy6000WaveFile varBy6000WaveFile;

 WaveFile_Header varWaveFileHeader = {0};

 varBy6000WaveFile.parse_wave_file(varWaveFileHeader, L"D:\\Wave.wav");

 varBy6000WaveFile.data_convert_wave16_to_by6000pcm128(L"D:\\Wave.pcm", L"D:\\Wave.wav");

 return 0;
}

 

原创粉丝点击