写入 WAV 文件 C++

Posted

技术标签:

【中文标题】写入 WAV 文件 C++【英文标题】:Writing to WAV file C++ 【发布时间】:2021-09-06 22:26:26 【问题描述】:

我有一个关于数字信号处理课程的 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);



【问题讨论】:

如果您在没有应用任何过滤器的情况下读取并写入文件,您是否可以读取(和收听)新文件? WAV 文件需要在前面加上标题,请参阅:docs.fileformat.com/audio/wav write_wav("test.wav", out, csample * ssample + WAV_HEADER_LENGTH); 使用了一个不存在的变量out - 这显然不是您正在运行的实际代码,因为它不会编译。 为什么不使用库来处理 WAV 文件格式?如github.com/mhroth/tinywav @mmc055 所以你必须通过编辑问题来修复错误。我们无法回答有关我们看不到的代码或我们可以看到的代码的问题,但不是您要询问的代码!具体来说,我们看不到您在写什么。 【参考方案1】:

这对我有用:

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,而不是您无法编译的out,作为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]N 在编译时未知是一个C++ 扩展:请在您的最终代码中使用std::vector

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

您的条件if ((i - j) &gt;= 0) 可以写成更容易理解的方式;最好通过更改内部循环“标题”。

【讨论】:

我知道“temp”可以正常工作,因为程序读取 wav 并返回“temp”。您将“temp”设置为参数,那么为什么我们有“output”?我应该将“输出”设置为参数以查看输入和输出之间的差异。 当然不是!输出只是对 temp 的参考。是同一个内存缓冲区! 您应该为输出分配一个单独的缓冲区。我建议它应该包含标准 WAV 标头的额外 44 个字节。 memcopy 这些字节来自temp 或手动设置它们。然后根据您的过滤器设置“声音”字节。目前你使用“输出”作为输入和输出缓冲区,这本质上是错误的。 dataoutputtemp 指的是相同的内存(只是初始偏移量略有不同)。从算法上讲,这看起来并不“犹太”,但据我所知,您问为什么您的输出文件根本没有播放。这是一个不同的问题。根据我们的对话,我推断您可能忘记在输出文件中添加 WAV 标头。 @mmc055 因为那样你只会编写没有元数据标头的示例数据。那么它就不是一个有效的 WAV。由于您不厌其烦地阅读和显示标题数据,因此不清楚为什么这不明显,而且问题中的代码既不写临时也不写输出(直到您按要求编辑)不清楚您的问题是,除了你的暴露评论。

以上是关于写入 WAV 文件 C++的主要内容,如果未能解决你的问题,请参考以下文章

wav文件多次写入和播放

NAudio - 读取和写入 Wav 文件

写入 wav 文件时定期创建静音

写入立体声后 WAV 写入功能无法同步

Python:将 wav 文件写入 numpy 浮点数组

无需在 Python 中写入磁盘即可加入 .wav 文件