Пытаюсь сделать небольшую программу для записи 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, на выходе получится чистая синусоидальная волна, как и ожидалось. Возможно, я выхожу за пределы некоторых используемых здесь типов данных. Пожалуйста, укажите здесь на ошибку. Спасибо всем заранее.
ОТ: не используйте магические числа, как в fwrite(&h,44,1,fp)
, пишите fwrite(&h, sizeof(h),1,fp)
Возможно перерасход в audio_data[]
. Ширина всего 16 укусов. Что произойдет, если вы поставите long int
?
@Майк, я тоже попробую. Но это абсолютно нормально работает при частоте frewq 440 Гц и частоте дискретизации 44000 выборок/сек. Проблема возникает только тогда, когда частота выборки не кратна 440, например, 44100.
@Майк, здесь образцы 16-битные, так что проблема не в этом.
fp = fopen (filename, "w");
лучше как fp = fopen (filename, "wb");
Добавьте assert(sizeof(h) == 44);
Почему с 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-ReinstateMonica long long int samples
должен быть просто int samples
. Это просто количество образцов. Я сомневаюсь, что ОП хочет создать 2-гигабайтный wav-файл.
Вам необходимо открыть файл в двоичном режиме, например 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
скомпилировалось.
strcpy(h.riff,"RIFF");
,strcpy(h.wave, "WAVE");
иstrcpy(h.fmt, "fmt ");
все получают доступ к массивам за пределами границ из-за завершающего 0. Вместо этого следует использоватьmemcpy
.strcpy(h.ddh, "data");
тоже.