Создание файла WAV — базовая программа на языке C

Пытаюсь сделать небольшую программу для записи wav файла (одиночная нота 440Гц, частота дискретизации 44100 сэмплов/сек). Программа следующая:

#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
#include <math.h>
#include <string.h>

int main()
{
    FILE *fp;
    struct header
    {
        char riff[4]; 
        int fsize; 
        char wave[4];
        char fmt[4];
        int chunk_size; 
        short int format_tag; 
        short int num_channel;
        int sample_rate; 
        int bytes_per_second;
        short int bytes_per_sample; 
        short int bits_per_sample; 
        char ddh[4]; 
        int size_dc; 
    } h;
    char filename[100];
    float freq;

    printf("\n\tEnter a file name..\n\t");
    scanf("%s", filename);

    strcat (filename,".WAV");

    printf("\n\n\tEnter sampling rate...\n\t");
    scanf("%d", &h.sample_rate);

    printf("\n\n\tEnter frequency...\n\t");
    scanf("%f", &freq);

    int i, tsec = 10;
    strcpy(h.riff,"RIFF");
    strcpy(h.wave, "WAVE");
    strcpy(h.fmt, "fmt ");
    h.chunk_size = 16;
    h.format_tag = 1;
    h.num_channel = 1;

    h.bits_per_sample = 16;
    h.bytes_per_sample = h.bits_per_sample * h.num_channel / 8;

    h.bytes_per_second = h.sample_rate * h.bits_per_sample * h.num_channel/8;
    strcpy(h.ddh, "data");
    h.size_dc = h.bytes_per_second * tsec;
    h.fsize = h.size_dc + 44;

    long long int samples;
    samples = h.sample_rate * tsec;

    short int audio_data[samples];

    for (i=0; i<samples; i++)
        audio_data[i] = 32760 * sin((2 * M_PI * freq * i)/h.sample_rate);

    fp = fopen (filename, "w");
    fwrite(&h,44,1,fp);
    printf("%d", sizeof(audio_data));
    fwrite(&audio_data,sizeof(audio_data),1,fp);

    fclose (fp);

    printf ("\n\tFile creation completed ....");
    getch();
    return 0;
}

Вышеупомянутая программа хорошо работает для частоты 440 Гц и частоты дискретизации 8800 выборок в секунду или даже 44000 выборок в секунду, но все становится не так, когда я использую частоту дискретизации 44100 выборок в секунду или любую другую частоту дискретизации, которая не является кратно 440. Когда я открываю файл WAV (созданный с частотой 440 Гц и 44100 выборок в секунду) в Audacity, визуализация выглядит так, как показано на рисунке ниже.

Я не могу понять, почему синусоидальная функция внезапно дает сбой, а затем снова возвращается в нужное русло. Если я построю ту же синусоидальную функцию в Excel, на выходе получится чистая синусоидальная волна, как и ожидалось. Возможно, я выхожу за пределы некоторых используемых здесь типов данных. Пожалуйста, укажите здесь на ошибку. Спасибо всем заранее.

strcpy(h.riff,"RIFF");, strcpy(h.wave, "WAVE"); и strcpy(h.fmt, "fmt "); все получают доступ к массивам за пределами границ из-за завершающего 0. Вместо этого следует использовать memcpy. strcpy(h.ddh, "data");тоже.
Retired Ninja 04.06.2024 08:09

ОТ: не используйте магические числа, как в fwrite(&h,44,1,fp), пишите fwrite(&h, sizeof(h),1,fp)

Jabberwocky 04.06.2024 08:13

Возможно перерасход в audio_data[]. Ширина всего 16 укусов. Что произойдет, если вы поставите long int?

Mike 04.06.2024 08:15

@Майк, я тоже попробую. Но это абсолютно нормально работает при частоте frewq 440 Гц и частоте дискретизации 44000 выборок/сек. Проблема возникает только тогда, когда частота выборки не кратна 440, например, 44100.

Vinayak Deshmukh 04.06.2024 08:24

@Майк, здесь образцы 16-битные, так что проблема не в этом.

Jabberwocky 04.06.2024 08:40
fp = fopen (filename, "w"); лучше как fp = fopen (filename, "wb");
chux - Reinstate Monica 04.06.2024 09:13

Добавьте assert(sizeof(h) == 44);

chux - Reinstate Monica 04.06.2024 09:15

Почему с long long int samples; samples = h.sample_rate * tsec;samples является long long? h.sample_rate * tsec — это int * int, а его произведение — int. Возможно, вы хотите 1LL* h.sample_rate * tsec получить long long продукт.

chux - Reinstate Monica 04.06.2024 09:22

@chux-ReinstateMonica long long int samples должен быть просто int samples. Это просто количество образцов. Я сомневаюсь, что ОП хочет создать 2-гигабайтный wav-файл.

Jabberwocky 04.06.2024 09:33
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
2
9
65
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Вам необходимо открыть файл в двоичном режиме, например fopen (filename, "wb") (обратите внимание на букву b в "wb"). В противном случае на некоторых платформах файл открывается в текстовом режиме, и LF (символы перевода строки, код ASCII 10) будут заменены CRLF (возврат каретки и перевод строки, ASCII-код 13 и 10).

Я смог воспроизвести проблему под Windows с помощью Visual Studio 2022. В режиме "wb" в fopen все работало нормально с любой частотой дискретизации.

При частоте дискретизации 44000 исходный код работал только случайно, поскольку последовательность байтов на выходе не содержала никаких LF.

Вы можете проверить, содержит ли сгенерированная последовательность байтов LF-байты, добавив этот код после цикла for:

  char* b = audio_data;

  for (i = 0; i < samples * 2; i++)
  {
    assert(b[i] != 10);
  }

Вам нужно добавить #include <assert.h>, чтобы assert скомпилировалось.

Другие вопросы по теме