对于wav的采样格式讨论较多的是定浮点采样
基于上一节我们对采样点的理解
车载音频开发(二):对音频数据作音量调节_音频数据的音量控制代码-CSDN博客
定点常见的有16bit,24bit,和32bit
浮点一般用float (32bit) IEEE 754浮点数
不同位深度的取值范围:
16bit 定点数: -32,768 ~ 32,767
24bit 定点数: -8,388,608 ~ 8,388,607
32bit 定点数: -2,147,483,648 ~ 2,147,483,647
IEEE 浮点数: [-3.4*10^38, -1.18*10^-38] ∪ [1.18*10^-38, 3.4 * 10^38]
对于不论多少位的定点采样,通常都要将其转换为浮点数(归一化)对其进行处理,且在wav定点采样存储时,数值超过1或小于-1的值通常无意义,即不论多少位的定点采样,在转换至浮点时,其值不能超过1,且不能小于-1;
以下是定点转浮点的转换关系表:
转化前 | 16bit | -32,768 | …… | 32,767 |
24bit | -8,388,608 | …… | 8,388,607 | |
32bit | -2,147,483,648 | …… | 2,147,483,647 | |
…… | ||||
转化后 | 浮点数 | -1 | …… | 1 |
当浮点转定点时,若数值在-1到1的范围内,乘上所要转换的位深度的最大取值;当数值超过1或小于-1时,通常会把大于1或者小于-1的数直接赋值为1或-1;
知道这些我就可以开始创作我们的代码了:
wav.h
//wav.h
#pragma once
#ifndef __Wav_h__
#define __Wav_h__
#include <vector>
#include <string>
#include <iostream>
using namespace std;
const double PI = 3.14159265358979323846;
#ifndef M_PI
#define M_PI 3.141592653589793238462643383279502884197169399375105820974944592307816406
#endif
#ifndef WAV___
#define WAV___
class wav {
vector<char> buffer;
vector<char> Peak_buffer; // Peak
vector<float> data_ford;
vector<size_t> channel_sizes;
struct WavHeader {
char chunkId[4] = { 'R','I','F','F' }; //"RIFF"
uint32_t chunkSize = 0; //totalsize - 8
char format[4] = { 'R','I','F','F' }; //"WAVE"
char subchunk1Id[4] = { 'f','m','t',' ' }; //"fmt"
uint32_t subchunk1Size = 16; //16:Normal; 18:Non_PCM; 40:Extensible;
}headera;
struct type40_header {
uint16_t audioFormat = 1; //wav 格式 1;int型 3:float型 65534:未知
uint16_t numChannels = 1; //声道数
uint32_t sampleRate = 48000; //采样率
uint32_t byteRate = 48000; //比特率:采样率 * 采样位宽
uint16_t blockAlign = 2; //采样深度:
uint16_t bitsPerSample = 16; //采样位宽:采样深度 * 8
uint16_t cbSize;
uint16_t wValidBitsPerSample;
uint32_t dwChannelMask;
char SubFormat[4];
char ckID[4];
uint32_t cksize;
uint32_t dwSampleLength;
}headerb;
struct data_header {
char subchunk2Id[4] = { 'd','a','t','a' }; //"data"
uint32_t subchunk2Size = 0; //datasize
}headerc;
// 定义fact块结构
struct FactChunk {
char fact_id[4] = { 'f','a','c','t' }; // "fact"
uint32_t fact_size = 4; // fact块大小
uint32_t sample_length; // 未压缩的样本数
}headerd;
// 定义fact块结构
struct PeakChunk {
char Peak_id[4] = { 'P','E','A','K' }; // "fact"
uint32_t Peak_size = 4; // fact块大小
}headere;
int fact_flag = 0;
int Peak_flag = 0;
public:
string filename;
string filepath;
int flag = 0;
wav(const string& filename);
//查看头部数据
int header_check();
int data_check();
void free();
};
#endif // ! WAV
struct WavHeader {
char chunkId[4]; //"RIFF"
uint32_t chunkSize; //totalsize - 8
char format[4]; //"WAVE"
char subchunk1Id[4]; //"fmt"
uint32_t subchunk1Size; //16:Normal; 18:Non_PCM; 40:Extensible;
};
struct type40_header {
uint16_t audioFormat; //wav 格式 1;int型 3:float型 65534:未知
uint16_t numChannels; //声道数
uint32_t sampleRate; //采样率
uint32_t byteRate; //比特率:采样率 * 采样位宽
uint16_t blockAlign; //采样深度:
uint16_t bitsPerSample; //采样位宽:采样深度 * 8
uint16_t cbSize;
uint16_t wValidBitsPerSample;
uint32_t dwChannelMask;
char SubFormat[4];
char ckID[4];
uint32_t cksize;
uint32_t dwSampleLength;
};
struct data_header {
char subchunk2Id[4]; //"data"
uint32_t subchunk2Size; //datasize
};
int switch_blockAlign(const char* inputfile, int cmd, const string& outputfile);
#endif
wav.cpp
//wav.c
#include <fstream>
#include "wav.h"
#include "Filter.h"
wav::wav(const string& filename) {
ifstream inputFile(filename, ios::binary);
if (!inputFile.is_open()) {
cerr << "无法打开文件" << endl;
this->flag = -1;
return;
}
inputFile.read(reinterpret_cast<char*>(&this->headera), sizeof(this->headera));
inputFile.read(reinterpret_cast<char*>(&this->headerb), this->headera.subchunk1Size);
inputFile.read(reinterpret_cast<char*>(&this->headerc), sizeof(this->headerc));
string data = this->headerc.subchunk2Id;
data.resize(4);
if (data != "data")
{
this->fact_flag = 1;
memcpy(this->headerd.fact_id, this->headerc.subchunk2Id, 4);
//printf("fact_id : ,%c%c%c%c\n", this->headerd.fact_id[0], this->headerd.fact_id[1], this->headerd.fact_id[2], this->headerd.fact_id[3]);
this->headerd.fact_size = this->headerc.subchunk2Size;
//cout << "fact_size : ," << this->headerd.fact_size << endl;
inputFile.read(reinterpret_cast<char*>(&this->headerd.sample_length), this->headerc.subchunk2Size);
//cout << "sample_length : ," << this->headerd.sample_length << endl;
inputFile.read(reinterpret_cast<char*>(&this->headerc), sizeof(this->headerc));
data = this->headerc.subchunk2Id;
data.resize(4);
}
if (data != "data")
{
this->Peak_flag = 1;
memcpy(this->headere.Peak_id, this->headerc.subchunk2Id, 4);
// printf("PEAK_id : ,%c%c%c%c\n", this->headere.Peak_id[0], this->headere.Peak_id[1], this->headere.Peak_id[2], this->headere.Peak_id[3]);
this->headere.Peak_size = this->headerc.subchunk2Size;
//cout << "PEAK_size : ," << this->headere.Peak_size << endl;
this->Peak_buffer.resize(this->headerc.subchunk2Size);
inputFile.read(this->Peak_buffer.data(), this->headere.Peak_size);
inputFile.read(reinterpret_cast<char*>(&this->headerc), sizeof(this->headerc));
data = this->headerc.subchunk2Id;
}
this->filepath = filename;
this->filename = filename.substr(filename.rfind("\\") + 1, filename.size());
this->buffer.resize(this->headerc.subchunk2Size);
inputFile.read(this->buffer.data(), this->headerc.subchunk2Size);
this->buffer.resize(this->headerc.subchunk2Size);
fflush(stdout);
inputFile.close();
}
void wav::free()
{
this->buffer.clear();
this->data_ford.clear();
this->Peak_buffer.clear();
this->filename.clear();
this->filepath.clear();
}
//查看头部数据
int wav::header_check() {
//printf("\ntotalsize : \t%u \n", header.chunkSize + 8);
printf("chunkId : ,%c%c%c%c\n", this->headera.chunkId[0], this->headera.chunkId[1], this->headera.chunkId[2], this->headera.chunkId[3]);
cout << "chunkSize : ," << this->headera.chunkSize << endl;
printf("format : ,%c%c%c%c\n", this->headera.format[0], this->headera.format[1], this->headera.format[2], this->headera.format[3]);
printf("subchunk1Id : ,%c%c%c%c\n", this->headera.subchunk1Id[0], this->headera.subchunk1Id[1], this->headera.subchunk1Id[2], this->headera.subchunk1Id[3]);
cout << "subchunk1Size : ," << this->headera.subchunk1Size << endl;
cout << "audioFormat : ," << this->headerb.audioFormat << endl;
cout << "numChannels : ," << this->headerb.numChannels << endl;
cout << "sampleRate : ," << this->headerb.sampleRate << endl;
cout << "byteRate : ," << this->headerb.byteRate << endl;
cout << "blockAlign : ," << this->headerb.blockAlign << endl;
cout << "bitsPerSample : ," << this->headerb.bitsPerSample << endl;
if (65534 == this->headerb.audioFormat)
{
cout << "cbSize : ," << this->headerb.cbSize << endl;
cout << "wValidBitsPerSample: ," << this->headerb.wValidBitsPerSample << endl;
cout << "dwChannelMask : ," << this->headerb.dwChannelMask << endl;
printf("SubFormat : ,%02X %02X %02X %02X\n", this->headerb.SubFormat[0], this->headerb.SubFormat[1], this->headerb.SubFormat[2], this->headerb.SubFormat[3]);
printf("ckID : ,%02X %02X %02X %02X\n", this->headerb.ckID[0], this->headerb.ckID[1], this->headerb.ckID[2], this->headerb.ckID[3]);
cout << "cksize : ," << this->headerb.cksize << endl;
cout << "dwSampleLength: ," << this->headerb.dwSampleLength << endl;
}
if (fact_flag) {
printf("fact_id : ,%c%c%c%c\n", this->headerd.fact_id[0], this->headerd.fact_id[1], this->headerd.fact_id[2], this->headerd.fact_id[3]);
cout << "fact_size : ," << this->headerd.fact_size << endl;
cout << "sample_length : ," << this->headerd.sample_length << endl;
if (Peak_flag)
{
//memcpy(this->headere.Peak_id, this->headerc.subchunk2Id, 4);
printf("PEAK_id : ,%c%c%c%c\n", this->headere.Peak_id[0], this->headere.Peak_id[1], this->headere.Peak_id[2], this->headere.Peak_id[3]);
//this->headere.Peak_size = this->headerc.subchunk2Size;
cout << "PEAK_size : ," << this->headere.Peak_size << endl;
cout << "PEAK_DATA : ," << endl;
for (int i = 0; i < this->headere.Peak_size; i++) {
printf(" %02X ,", (uint8_t)this->Peak_buffer[i]);
}
cout << endl;
}
}
printf("subchunk2Id : ,%c%c%c%c\n", this->headerc.subchunk2Id[0], this->headerc.subchunk2Id[1], this->headerc.subchunk2Id[2], this->headerc.subchunk2Id[3]);
cout << "subchunk2Size : ," << this->headerc.subchunk2Size << endl;
return 0;
}
#define __PRINTF
int wav::data_check() {
const size_t dataSize = this->headerc.subchunk2Size;
uint16_t perdatasize = this->headerb.bitsPerSample / 8;
int channels = this->headerb.numChannels;
this->data_ford.resize(dataSize);
#ifdef __MAXDATA
this->maxdata.resize(this->headerb.numChannels);
#endif
float wavdata = 0.0;
int32_t Wavdata = 0;
int16_t Wavdata16 = 0;
#ifdef __SPRINTF_S
string csv;
char sam[1024] = {};
csv += ",";
for (int j = 0; j < headera.numChannels; j++) {
sprintf_s(sam, "channel[%d] , ,", j + 1);
csv += sam;
}
csv += ("\ndata,");
for (int j = 0; j < headera.numChannels; j++) {
csv += ("Frequency , Response ,");
}
csv += "\n";
#endif
size_t each_size = dataSize / perdatasize / channels;
for (size_t i = 0; i < each_size; i++) {
#ifdef __SPRINTF_S
sprintf_s(sam, "data[%8d] : ,", (unsigned int)i);
csv += sam;
#endif
for (size_t j = 0; j < channels; j++) {
if (16 == this->headerb.bitsPerSample)
{
memcpy(&Wavdata16, (this->buffer).data() + i * channels * perdatasize + j * perdatasize, perdatasize);
wavdata = (float)Wavdata16 / 32768;
#ifdef __PRINTF
printf(" %10d , %.2f db ,", Wavdata16, 20 * log10f(abs((float)Wavdata16) / 32768));
#endif
#ifdef __SPRINTF_S
sprintf_s(sam, " %10d , %.2f db ,", Wavdata16, 20 * log10f(abs((float)Wavdata16) / 32768));
#endif
}
else if (24 == this->headerb.bitsPerSample)
{
memcpy(&Wavdata, (this->buffer).data() + i * channels * perdatasize + j * perdatasize, perdatasize);
wavdata = (float)(Wavdata << 8) / 256 / 8388608;
#ifdef __PRINTF
printf(" %10d , %.2f db ,", (Wavdata << 8) / 256, 20 * log10f(abs((float)(Wavdata << 8)) / 256 / 8388608));
#endif
#ifdef __SPRINTF_S
sprintf_s(sam, " %10d , %.2f db ,", (Wavdata << 8) / 256, 20 * log10f(abs((float)(Wavdata << 8)) / 256 / 8388608));
#endif
}
else if (32 == this->headerb.bitsPerSample)
{
if (3 == this->headerb.audioFormat || (65534 == this->headerb.audioFormat && 0x03 == this->headerb.SubFormat[0])) {
memcpy(&wavdata, (this->buffer).data() + i * channels * perdatasize + j * perdatasize, perdatasize);
#ifdef __PRINTF
printf(" %.6f , %.2f db ,", wavdata, 20 * log10f(abs(wavdata)));
#endif
#ifdef __SPRINTF_S
sprintf_s(sam, " %.8f , %.2f db ,", wavdata, 20 * log10f(abs(wavdata)));
#endif
}
else {
memcpy(&Wavdata, (this->buffer).data() + i * channels * perdatasize + j * perdatasize, perdatasize);
wavdata = (float)Wavdata / 2147483648;
#ifdef __PRINTF
printf(" %16d , %.2f db ,", Wavdata, 20 * log10f(abs((float)Wavdata) / 2147483648));
#endif
#ifdef __SPRINTF_S
sprintf_s(sam, " %16d , %.2f db ,", Wavdata, 20 * log10f(abs((float)Wavdata) / 2147483648));
#endif
}
}
if ((uint32_t)wavdata | 0) {
this->data_ford[j * each_size + i] = (double)wavdata;
}
else
{
this->data_ford[j * each_size + i] = (double)wavdata;
//this->data_ford[j * each_size + i] = (double)0.00000001;
}
#ifdef __MAXDATA
this->maxdata[j] = this->maxdata[j] >= wavdata ? this->maxdata[j] : wavdata;
#endif
#ifdef __SPRINTF_S
csv += sam;
#endif
}
#ifdef __PRINTF
cout << endl;
#endif
#ifdef __SPRINTF_S
csv += "\n";
#endif
}
//this->buffer.clear();
//inputFile.close();
#ifdef __SPRINTF_S
ofstream out;
string outfile = this->filename.substr(0, filename.rfind(".") + 1) + "csv";
out.open(outfile);
out.flush();
out << csv;
out.close();
cout << "\ndatas save at " << outfile << endl;
#endif
return 0;
}
static inline int32_t clamp32_from_float(float f)
{
static const float scale = (float)(1UL << 31);
static const float limpos = 1.;
static const float limneg = -1.;
if (f <= limneg) {
return INT32_MIN;
}
else if (f >= limpos) {
return INT32_MAX;
}
f *= scale;
/* integer conversion is through truncation (though int to float is not).
* ensure that we round to nearest, ties away from 0.
*/
return f > 0 ? f + 0.5 : f - 0.5;
}
static inline float float_from_i32(int32_t ival)
{
static const float scale = 1. / (float)(1UL << 31);
return ival * scale;
}
int switch_blockAlign(const char* inputfile, int cmd, const string& outputfile)
{
WavHeader header = {};
type40_header headera = {};
data_header headerb = {};
//header_check(inputfile);
ifstream input(inputfile, ios::binary);
if (!input.is_open()) {
cerr << "无法打开文件" << endl;
return -1;
}
// 读取WAV文件头部信息并写入
ofstream output(outputfile, ios::binary);
if (!output.is_open()) {
cerr << "无法打开文件" << endl;
return -1;
}
uint32_t rearsize = 0;
input.read(reinterpret_cast<char*>(&header), sizeof(header));
input.read(reinterpret_cast<char*>(&headera), header.subchunk1Size);
input.read(reinterpret_cast<char*>(&headerb), sizeof(headerb));
uint16_t oldperdata = headera.bitsPerSample;
uint16_t Format = headera.audioFormat;
if (5 == cmd) {
headera.bitsPerSample = 8 * 4;
headera.audioFormat = 3;
}
else {
headera.bitsPerSample = 8 * cmd;
headera.audioFormat = 1;
}
const size_t dataSize = headerb.subchunk2Size;
headerb.subchunk2Size = headerb.subchunk2Size / oldperdata * headera.bitsPerSample;
headera.blockAlign = headera.bitsPerSample / 8 * headera.numChannels;
headera.byteRate = headera.blockAlign * headera.sampleRate;
rearsize = header.chunkSize - (uint32_t)dataSize - header.subchunk1Size - sizeof(headerb) - 12;
header.chunkSize = header.chunkSize + headerb.subchunk2Size - (uint32_t)dataSize;
output.write(reinterpret_cast<char*>(&header), sizeof(header));
output.write(reinterpret_cast<char*>(&headera), header.subchunk1Size);
output.write(reinterpret_cast<char*>(&headerb), sizeof(headerb));
vector<char> outputBuffer(headerb.subchunk2Size);
vector<char> buffer(dataSize);
input.read(buffer.data(), dataSize);
vector<char> rear(rearsize);
input.read(rear.data(), rear.size());
input.close();
// 读取WAV文件头部信息并写入
uint16_t perdatasize = oldperdata / 8;
uint16_t perdatasize1 = headera.bitsPerSample / 8;
float wavdata = 0;
int32_t WavData32 = 0;
int32_t WavData24 = 0;
int16_t WavData16 = 0;
// 复制数据到20个通道
for (size_t i = 0; i < dataSize / perdatasize; i++) {
switch (oldperdata) {
case 16:
memcpy(&WavData16, buffer.data() + i * perdatasize, perdatasize);
//printf(" data[%d]:\t%10d \t %f db \n", (int)i, WavDate16, 20 * log10f(abs((float)WavDate16) / 32768));
WavData32 = WavData16 * 256 * 256;
wavdata = (float)WavData16 / 32768;
break;
case 24:
memcpy(&WavData24, buffer.data() + i * perdatasize, perdatasize);
//printf(" data[%d]:\t%10d \t %f db \n", (int)i, (WavDate32 << 8) / 256, 20 * log10f(abs((float)(WavDate32 << 8)) / 256 / 8388608));
wavdata = (float)(WavData24 << 8) / 256 / 8388608;
WavData32 = WavData24 << 8;
break;
case 32:
if (1 == Format) {
memcpy(&WavData32, buffer.data() + i * perdatasize, perdatasize);
//printf(" data[%d]:\t%.16f \t %f db \n", (int)i, wavdate, 20 * log10f(abs(wavdate)));
wavdata = float_from_i32(WavData32);
}
else {
memcpy(&wavdata, buffer.data() + i * perdatasize, perdatasize);
//printf(" data[%d]:\t%16d \t %f db \n", (int)i, WavDate32, 20 * log10f(abs((float)WavDate32) / 2147483648));
WavData32 = clamp32_from_float(wavdata);
}
break;
}
switch (cmd)
{
case 2:
WavData16 = WavData32 / 256 / 256;
memcpy(&outputBuffer.data()[i * perdatasize1], &WavData16, perdatasize1);
break;
case 3:
WavData24 = WavData32 / 256;
memcpy(&outputBuffer.data()[i * perdatasize1], &WavData24, perdatasize1);
break;
case 4:
memcpy(&outputBuffer.data()[i * perdatasize1], &WavData32, perdatasize1);
break;
case 5:
memcpy(&outputBuffer.data()[i * perdatasize1], &wavdata, perdatasize1);
}
}
// 写入数据到输出文件的各通道
input.close();
output.write(outputBuffer.data(), outputBuffer.size());
output.write(rear.data(), rear.size());
output.close();
cout << "****************************************************" << endl;
wav outputwav(outputfile);
outputwav.header_check();
return 0;
}
wavBitConversion.cpp
//wavBitConversion.cpp
#include <fstream>
#include "wav.h"
void user_usage()
{
//printf("/*********************************************************************************\n");
printf("\nuser usage : \n");
printf(" Convert WAV file bit depth:\n wavBitConversion.exe [options] input.wav \n");
printf("options : \n");
printf(" -int16 : Convert WAV file bit depth to int16 type\n");
printf(" -int24 : Convert WAV file bit depth to int24 type\n");
printf(" -int32 : Convert WAV file bit depth to int32 type\n");
printf(" -f32 : Convert WAV file bit depth to float32 type\n");
//printf("*********************************************************************************/\n");
}
int work(const char* op, const char* file) {
int flag = 0;
string num_str;
string cmd = op;
if (cmd == "-int16")
{
cout << " options : -int16 " << endl << endl;
flag = 2;
num_str = "bitint16_from_";
}
else if (cmd == "-int24")
{
cout << " options : -int24 " << endl << endl;
flag = 3;
num_str = "bitint24_from_";
}
else if (cmd == "-int32")
{
cout << " options : -int32 " << endl << endl;
flag = 4;
num_str = "bitint32_from_";
}
else if (cmd == "-f32")
{
cout << " options : -f32 " << endl << endl;
flag = 5;
num_str = "bitf32_from_";
}
else {
cerr << "Error : option is observe" << endl;
return -1;
}
string filename = file;
if (filename.size() < 4 || filename.rfind(".") == std::string::npos || filename.substr(filename.rfind("."), filename.size())!= ".wav") {
cerr << "Error : input.wav is observe >>>>>>> " << filename << endl;
return -1;
}
wav wav1(filename);
if( wav1.flag ) {
cerr << "Error : input.wav is observe >>>>>>> " << filename << endl;
return -1;
}
wav1.header_check();
if (1 < flag && 6 > flag) {
switch_blockAlign(file, flag, num_str + wav1.filename);
cout << endl << "转换成功 :" << num_str + wav1.filename << endl << endl;
}
return 0;
}
int main(int argc, const char* argv[]) {
if (argc == 3 && !work(argv[1], argv[2])) {
cout << endl;
}
else {
user_usage();
}
return 0;
}