车载音频开发(三):对wav音频做定浮点转换(采样深度转换)

发布于:2025-02-20 ⋅ 阅读:(124) ⋅ 点赞:(0)

 对于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;
}