最近,我在我的 CS 101 课程中看到一个视频讲座,启发我开始使用 C 语言中的 WAV 文件格式。我今天的项目一直在使用简单的数学正弦函数创建声音。尽管有一些障碍,我的程序现在可以接受多个输入(波的频率、波的幅度、采样率等)并创建一个包含指定音高的 wav 文件。
但是,在我的电脑扬声器上播放这些音调时,会发出一种奇怪的、有节奏的爆裂声,它会随着采样率的不同而变化。在更高的采样率下,爆裂声的频率增加并变成令人讨厌的呜呜声。
奇怪的是,具有相同文件的不同计算机之间的爆裂声是一致的。
下面我将发布用于生成 WAV 文件的代码。任何有关可能导致这种现象的原因的见解都将受到赞赏。这可能只是我在某个地方犯的一个愚蠢的错误。:)
#include <stdio.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <string.h>
#include <math.h>
struct WAVHeader {
char ChunkID[4];
uint32_t ChunkSize;
char RIFFType[4];
};
struct FormatHeader {
char ChunkID[4];
uint32_t ChunkSize;
uint16_t CompressionCode;
uint16_t Channels;
uint32_t SampleRate;
uint32_t AvgBytesPerSec;
uint16_t BlockAlign;
uint16_t SigBitsPerSamp;
};
struct DataHeader {
char ChunkID[4];
uint32_t ChunkSize;
};
void main(int argc, char * argv[]) {
//Check for valid number of arguments or display help
if(argc < 8) {
printf("Usage:\n./Tone -l [length] -s [frequency] [amplitude] -o [output-file] -r [sample-rate]\n");
printf("-l length of tone to produce in seconds\n");
printf("-s Creates sine wave. Can be used multiple times. Frequency (Hz) and amplitude (0 - 32767) of each tone. \n");
printf("-o File to write to\n");
printf("-r samples per second (kHz). Note: Must be double highest frequency in tone.\n");
return;
}
//Organize arguments
int length, sinf[10], sina[10], samplerate;
memset(sinf, 0, sizeof(int) * 10);
memset(sina, 0, sizeof(int) * 10);
char * output = NULL;
int i = 0;
int count;
for(count = 1; count < argc; count++){
char first = *argv[count];
int second = *(argv[count] + 1);
if (first == '-') {
switch (second) {
case 's':
sinf[i] = atoi(argv[count+1]);
sina[i] = atoi(argv[count+2]);
i++;
break;
case 'l':
length = atoi(argv[count+1]);
break;
case 'o':
output = argv[count+1];
break;
case 'r':
samplerate = atoi(argv[count+1]) * 1000;
break;
}
}
}
//Allocate memory for wav file
size_t size = sizeof(struct WAVHeader) + sizeof(struct FormatHeader) + sizeof(struct DataHeader) + (length * samplerate * 2);
void * buffer = malloc(size);
//Fill buffer with headers
struct WAVHeader * WAV = (struct WAVHeader *)buffer;
struct FormatHeader * Format = (struct FormatHeader *)(WAV + 1);
struct DataHeader * Data = (struct DataHeader *)(Format + 1);
strcpy(WAV->ChunkID, "RIFF");
WAV->ChunkSize = (uint32_t)size - 8;
strcpy(WAV->RIFFType, "WAVE");
strcpy(Format->ChunkID, "fmt ");
Format->ChunkSize = 16;
Format->CompressionCode = 1;
Format->Channels = 1;
Format->SampleRate = (uint32_t)samplerate;
Format->SigBitsPerSamp = 16;
Format->BlockAlign = 2;
Format->AvgBytesPerSec = Format->BlockAlign * samplerate;
strcpy(Data->ChunkID, "data");
Data->ChunkSize = length * samplerate * 2;
//Generate Sound
printf("Generating sound...\n");
short * sound = (short *)(Data + 1);
short total;
float time;
float increment = 1.0/(float)samplerate;
for (time = 0; time < length; time += increment){
total = 0;
for (i = 0; i < 10; i++) {
total += sina[i] * sin((float)sinf[i] * time * (2 * 3.1415926));
}
*(sound + (int)(time * samplerate)) = total;
//printf("Time: %f Value: %hd\n", time, total);
}
//Write buffer to file
FILE * out = fopen(output, "w");
fwrite(buffer, size, 1, out);
printf("Wrote to %s\n", output);
return;
}