首页 > 解决方案 > 写入 WAV 文件 C++

问题描述

我有一个关于数字信号处理类的 WAV 文件和 FIR 滤波器的作业。

我的程序必须读取 WAV 文件,对数据应用过滤器并将输出数据再次写入另一个 WAV 文件。

我已完成阅读和应用过滤器,但我无法编写 WAV 文件。程序在编译时不会出错,但 WAV 文件无法播放。

如果我将“temp”写入 WAV,它会正常运行。但如果我写“数据”,它不会。

如何正确编写 WAV 文件?


#define _CRT_SECURE_NO_WARNINGS
#define PI 3.14f
#define WAV_HEADER_LENGTH 44

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <iostream>
#include <fstream>

char* read_wav(const char* filename, short*, short*, int*);
void write_wav(const char* filename, const char*, int);

using namespace std;

int main()
{
short nchannel, ssample;
int csample;

//Reading WAV file and returning the data.
char* temp = read_wav("sum.wav", &nchannel, &ssample, &csample);
short* data = (short*)&temp[WAV_HEADER_LENGTH];

cout << "How many coefficients are there in filter ?" << endl;
int N;
cin >> N ;

float filter[N];
cout << "Type coefficients in filter." << endl;
for(int i=0; i<N;i++){
    cin >> filter[i];
}

short* output = (short*)&temp[WAV_HEADER_LENGTH];

for(int i=0; i < csample; i++){

    double sum = 0;
    for(int j=0; j < N; j++){
        if((i - j) >= 0)
           sum += filter[j] * data[i-j];
    }
    output[i] = (short) sum;
}

write_wav("test.wav", out, csample * ssample + WAV_HEADER_LENGTH);

}

char* read_wav(const char* filename, short* nchannel, short* ssample, int* csample) {

    //Reading the file.
    FILE* fp = fopen(filename, "rb");

    if (!fp) {
        fprintf(stderr, "Couldn't open the file \"%s\"\n", filename);
        exit(0);
    }

    fseek(fp, 0, SEEK_END);
    int file_size = ftell(fp);
    fseek(fp, 0, SEEK_SET);

    printf("The file \"%s\" has %d bytes\n\n", filename, file_size);

    char* buffer = (char*)malloc(sizeof(char) * file_size);
    fread(buffer, file_size, 1, fp);

    // Dump the buffer info.
    *nchannel = *(short*)&buffer[22];
    *ssample = *(short*)&buffer[34] / 8;
    *csample = *(int*)&buffer[40] / *ssample;

    printf("ChunkSize :\t %u\n", *(int*)&buffer[4]);
    printf("Format :\t %u\n", *(short*)&buffer[20]);
    printf("NumChannels :\t %u\n", *(short*)&buffer[22]);
    printf("SampleRate :\t %u\n", *(int*)&buffer[24]);  // number of samples per second
    printf("ByteRate :\t %u\n", *(int*)&buffer[28]);        // number of bytes per second
    printf("BitsPerSample :\t %u\n", *(short*)&buffer[34]);
    printf("Subchunk2ID :\t \"%c%c%c%c\"\n", buffer[36], buffer[37], buffer[38], buffer[39]);   // marks beginning of the data section
    printf("Subchunk2Size :\t %u\n", *(int*)&buffer[40]);       // size of data (byte)
    printf("Duration :\t %fs\n\n", (float)(*(int*)&buffer[40]) / *(int*)&buffer[28]);

    fclose(fp);
    return buffer;
}

void write_wav(const char* filename, const char* data, int len) {
    FILE* fp = fopen(filename, "wb");

    if (!fp) {
        fprintf(stderr, "Couldn't open the file \"%s\"\n", filename);
        exit(0);
    }

    fwrite(data, len, 1, fp);
    fclose(fp);
}


标签: c++wavwrite

解决方案


这对我有用:

int main()
{
    short nchannel, ssample;
    int csample;

    // Reading WAV file and returning the data.
    char* temp = read_wav("sum.wav", &nchannel, &ssample, &csample);
    short* data = (short*)&temp[WAV_HEADER_LENGTH];

//    cout << "How many coefficients are there in filter ?" << endl;
    const int N = 2;
//    cin >> N;

    float filter[N] = {0.5, 0.75};
//    cout << "Type coefficients in filter." << endl;
//    for (int i = 0; i < N; i++)
//    {
//        cin >> filter[i];
//    }

    short* output = (short*)&temp[WAV_HEADER_LENGTH];

    for (int i = 0; i < csample; i++)
    {
        double sum = 0;
        for (int j = 0; j < N; j++)
        {
            if ((i - j) >= 0) sum += filter[j] * data[i - j];
        }
        output[i] = (short)sum;
    }

    write_wav("test.wav", (char*)temp, csample * ssample + WAV_HEADER_LENGTH);
}

我的改变:

  • 主要的变化是使用完整的缓冲区,名称极具误导性:temp,而不是你的outthat 不编译,作为write_wav.
  • 我应用了“我的”滤波器系数(输出文件中的声音真的很失真),
  • 我应用了我最喜欢的缩进

如果代码是可移植的,您需要检查字节序并采取相应措施。

我希望输入和输出文件的长度相同,但事实并非如此。请自行检查为什么不是这种情况。例子:

-rw-r--r-- 1 zkoza zkoza 787306 06-23 14:09 sum.wav
-rw-r--r-- 1 zkoza zkoza 787176 06-23 14:16 test.wav

输出文件中似乎缺少 130 个字节。

您的float filter[N]with Nnot known at compile time 是 C++ 扩展:请std::vector在您的最终代码中使用。

下次请提供任何输入文件的链接。对于我的测试,我使用了https://freewavesamples.com/alesis-fusion-clean-guitar-c3 ,但是所有这些小东西,比如查找输入文件(WAV 格式有多种风格,我可能错过了正确的一种) ,猜测过滤器参数等需要时间和精力。

你的情况if ((i - j) >= 0)可以用更容易理解的方式写出来;最好通过更改内部循环“标题”。


推荐阅读