Запись в WAV-файл C

#c #wav #write

Вопрос:

У меня есть домашнее задание по 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", amp;nchannel, amp;ssample, amp;csample);
short* data = (short*)amp;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*)amp;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 bytesnn", filename, file_size);

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

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

    printf("ChunkSize :t %un", *(int*)amp;buffer[4]);
    printf("Format :t %un", *(short*)amp;buffer[20]);
    printf("NumChannels :t %un", *(short*)amp;buffer[22]);
    printf("SampleRate :t %un", *(int*)amp;buffer[24]);  // number of samples per second
    printf("ByteRate :t %un", *(int*)amp;buffer[28]);        // number of bytes per second
    printf("BitsPerSample :t %un", *(short*)amp;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 %un", *(int*)amp;buffer[40]);       // size of data (byte)
    printf("Duration :t %fsnn", (float)(*(int*)amp;buffer[40]) / *(int*)amp;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);
}


 

Комментарии:

1. Если вы прочитаете файл и запишете его без применения какого-либо фильтра, сможете ли вы прочитать (и прослушать) новый файл?

2. Файлы WAV нуждаются в предварительном заголовке, см.: документы. fileformat.com/audio/wav

3. В строке write_wav("test.wav", out, csample * ssample WAV_HEADER_LENGTH); используется переменная out , которой не существует — это явно не тот код, который вы запускаете, поскольку он не будет компилироваться.

4. Почему бы вам не использовать библиотеку для обработки форматирования файлов WAV? Такие, как github.com/mhroth/tinywav

5. @mmc055, поэтому вы должны исправить ошибку, отредактировав вопрос. Мы не можем ответить на вопрос о коде, который мы не видим, или о коде, который мы видим, но это не тот код, о котором вы спрашиваете! В частности, мы не можем видеть, что вы пишете.


Ответ №1:

Это работает для меня:

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

    // Reading WAV file and returning the data.
    char* temp = read_wav("sum.wav", amp;nchannel, amp;ssample, amp;csample);
    short* data = (short*)amp;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*)amp;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) >= 0) может быть записано более понятным способом; предпочтительно путем изменения «заголовка»внутреннего цикла.

Комментарии:

1. Я знаю, что «temp» работает правильно, потому что программа считывает wav и возвращает «temp». Вы установили «temp» в качестве параметра, тогда почему у нас есть «выход» ? Я должен установить «выход» в качестве параметра, чтобы увидеть разницу между входом и выходом.

2. Конечно, нет! Вывод-это просто ссылка на temp. Это тот же буфер памяти!

3. Вы должны выделить отдельный буфер для вывода. Я предлагаю, чтобы он содержал дополнительные 44 байта стандартного заголовка WAV. memcopy эти байты из temp или установите их вручную. Затем установите «звуковые» байты в соответствии с вашим фильтром. На данный момент вы используете «вывод» как входной, так и выходной буфер, что, по сути, неправильно.

4. data , output и temp ссылаются на ту же память (с немного разными начальными смещениями). Алгоритмически это не выглядит «кошерным», но, насколько я мог понять, вы спросили, почему ваши выходные файлы вообще не воспроизводились. Это другой вопрос. Из нашего разговора я сделал вывод, что вы, возможно, забыли добавить заголовок WAV в выходной файл.

5. @mmc055, потому что тогда вы бы писали только образцы данных без заголовка метаданных. Тогда это не будет действительным WAV. Поскольку вы потрудились прочитать и отобразить данные заголовка, непонятно, почему это не очевидно, и тот факт, что код в вопросе не записывает ни временные, ни выходные данные (пока вы не отредактируете по запросу), не ясно, в чем ваша проблема, кроме вашего раскрывающего комментария.