从头开始的WAV文件合成 – C.

最近我在CS 101课堂上看到了一个video讲座,它启发我开始使用C语言中的WAV文件格式。我今天的项目一直是使用简单的数学正弦函数创建声音。 尽管有几个障碍,我的程序现在可以接受几个输入(波的频率,波的幅度,采样率等)并创建包含指定音高的wav文件。

但是,在我的电脑扬声器上播放这些音调时,会发出一种奇怪的,有节奏的砰砰声,这种声音会随着采样率的变化而变化。 在较高的采样率下,弹出声音的频率增加并变成恼人的呜呜声。

奇怪的是,弹出的声音在具有相同文件的不同计算机上是一致的。

下面我将发布用于生成WAV文件的代码。 任何可能导致这种现象的见解都将受到赞赏。 这可能只是我某处的一个愚蠢的错误。 🙂

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

我认为这是你的核心问题:

 *(sound + (int)(time * samplerate)) = total; 

我怀疑由于浮点舍入错误,(time * samplerate)并不总是在整数边界上增加。 因此,由于舍入误差,一些样本位置被跳过和/或覆盖。 这只是猜测。

但是,随着“时间”的增加,“time * frequency * 2PI”的乘法将在浮点数内溢出。 因此,您应该将“时间”标准化,使其不会永远增加。

无论如何,我validation了这个修改过的循环工作(和声音)就好了:

 float TWOPI = 6.28318531f; unsigned int sample_count = length * samplerate; for (unsigned int i = 0; i < sample_count; i++) { unsigned int j = i % samplerate; // normalize the sample position so that we don't blow up in the subsequent multiplication float f = 0.0f; int result; for (int x = 0; x < 10; x++) { f += sina[x] * sin((sinf[x] * j * TWOPI) / samplerate); } result = (long)f; //clamp to 16-bit if (result > 32767) { result = 32767; } else if (result < -32768) { result = -32768; } sound[i] = (short)result; //printf("%d\n", sound[i]); }