'Writing to WAV file C++

I have a homework about WAV files and FIR filters for a Digital Signal Processing class.

My program must read a WAV file, apply a filter to the data and write the output data to another WAV file again.

I have completed reading and applying filters but I can't write the WAV file. The program doesn't give any errors while compiling but the WAV file doesn't play.

If I write "temp" to the WAV, it runs properly. But if I write "data", it doesn't.

How can I write a WAV file properly?

#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);
}




Solution 1:[1]

This works for me:

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);
}

My changes:

  • The major change is to use the full buffer, with extremely misleading name: temp, instead of your out that does not compile, as the argument of write_wav.
  • I applied "my" filter coefficients (the sound from the output file is really distorted),
  • I applied my favorite indentation

If the code is to be portable, you need to check the endiannes and act accordingly.

I would expect the input and output files to be of the same length, but they're not. Please check it yourself why this is not the case. Example:

-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

It looks like 130 bytes are missing in the output file.

Your float filter[N] with N not known at compile time is a C++ extension: please use std::vector in your final code instead.

Next time please provide also a link for any input files. For my tests, I used https://freewavesamples.com/alesis-fusion-clean-guitar-c3 , but all these little things, like finding an input file (WAV format has several flavors, I could have missed the correct one), guessing filter parameters etc. take time and effort.

Your condition if ((i - j) >= 0) can be written in a way easier to understand; preferably by changing the inner loop "header".

Solution 2:[2]

Had the same problem, solved without IE fallback.
This will make it behave more like just typing it in the 'Run' window:

Process.Start(new ProcessStartInfo("https://www.example.com") { UseShellExecute = true });

Note that I'm setting UseShellExecute = true

The default is supposed to be true on .Net Framework, and false on .Net Core
and UWP apps should not use this flag. see docs
(I was running on .Net Core)

Solution 3:[3]

You can open the URL using InternetExplorer which comes along with Windows OS.

Try This:

Process.Start("IEXPLORE",url);

Solution 4:[4]

For some use cases in Core, even if ProcessInfo.WorkingDirectory is set, Environment.CurrentDirectory also needs to be set: an unintuitive difference in APIs that will undoubtedly result in a lot of broken ported code and confusion.

Solution 5:[5]

I have this code in a windows forms application and it works fine:

var info = new ProcessStartInfo(url);
Process.Start(info);

Solution 6:[6]

try
{
  Process.Start(url);
}
catch (Win32Exception)
{
  Process.Start("IExplore.exe", url);
}

Specially since your dealing with XP, this is more than likely machine specific issue.

Solution 7:[7]

I get

System.ComponentModel.Win32Exception

For WPF Framework project (host=win7, x64).

Try this:

filename="https://www.example.com"; 
Process.Start(filename) 

If the browser is not started add Process.Start("chrome.exe", filename) in catch block;

It will start the chrome browser with and tab with "https://www.example.com".

Here the complete example:

try
{
    var runningProcess = Process.GetProcessesByName("chrome");
    if (runningProcess.Length != 0)
    {
        Process.Start("chrome", filename);
        return;
    }
    runningProcess = Process.GetProcessesByName("firefox");
    if (runningProcess.Length != 0)
    {
        Process.Start("firefox", filename);
        return;
    }
    runningProcess = Process.GetProcessesByName("iexplore");
    if (runningProcess.Length != 0)
    {
        Process.Start("iexplore", filename);
        return;
    }
    Process.Start(filename);
}
catch (System.ComponentModel.Win32Exception)
{
    Process.Start("chrome.exe", filename);
}

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 zkoza
Solution 2
Solution 3 Sudhakar Tillapudi
Solution 4 fartwhif
Solution 5 James R.
Solution 6 Dayan
Solution 7 juagicre