WAV文件合成从零开始 - Ç [英] WAV File Synthesis From Scratch - C

查看:271
本文介绍了WAV文件合成从零开始 - Ç的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

最近,我看到了我的CS 101级的视频讲座,启发了我开始用我的C.项目中使用一个简单的数学正弦函数今天已经创建的声音WAV文件格式播放。尽管一对夫妇的障碍,我的程序现在可以接受多个输入(波的频率,波的振幅,采样率等),并创建包含指定球场WAV文件。

不过,在我的电脑扬声器播放这些铃声的时候,有一个奇怪的,有节奏的爆裂声,这与采样率变化而变化。在更高的采样率,的爆裂声的频率增加并变成一个恼人的呜呜声。

但奇怪的是,该爆裂声是相同的文件在不同的计算机上是一致的。

下面我将张贴code,我用它来生成WAV文件。任何见解什么可能导致这种现象将AP preciated。这可能只是我的一个愚蠢的错误的地方。 :)

 的#include<&stdio.h中GT;
#包括LT&; SYS / types.h中>
#包括LT&; SYS / ioctl.h>
#包括LT&;&fcntl.h GT;
#包括LT&;&string.h中GT;
#包括LT&;&math.h中GT;结构WAVHeader {
    炭ChunkID [4];
    uint32_t的块大小;
    炭RIFFType [4];
};结构FormatHeader {
    炭ChunkID [4];
    uint32_t的块大小;
    uint16_t的COM pression code;
    uint16_t渠道;
    uint32_t的采样率;
    uint32_t的AvgBytesPerSec;
    uint16_t BlockAlign;
    uint16_t SigBitsPerSamp;
};结构DataHeader {
    炭ChunkID [4];
    uint32_t的块大小;};
无效的主要(INT ARGC,CHAR *的argv []){//检查参数或显示帮助有效号码
如果(的argc和下; 8){
    的printf(用法:\\ n./Tone -l [长度] -s [频] [幅] -o [输出文件] -r [采样率] \\ n);
    的printf( - 音L的长度以生产秒\\ n);
    的printf( - :S创建正弦波可多次使用频率(Hz)和幅度(0 - 每个音32767)\\ n。);
    的printf( - o文件写入到\\ n);
    的printf( - 每秒(kHz)的注r个采样:必须是双音频率最高\\ n);
    返回;
}//参数整理
INT长,SINF [10],新浪[10],采样率;
memset的(SINF,0,sizeof的(INT)* 10);
memset的(新浪,0,sizeof的(INT)* 10);
字符*输出= NULL;
INT I = 0;
诠释计数;
为(计数= 1;计数< ARGC;计数++){
    首先烧焦= *的argv [统计]
    INT秒= *(的argv [统计] + 1);
    如果(第一==' - '){
        开关(第二){
            案件的:
                SINF [I] =的atoi(argv的[计数+ 1]);
                新浪[我] =的atoi(argv的[计数+ 2]);
                我++;
                打破;
            案例'L':
                长度=的atoi(argv的[计数+ 1]);
                打破;
            案例'O':
                输出=的argv [计数+ 1];
                打破;
            案例'R':
                采样率=的atoi(argv的[计数+ 1])* 1000;
                打破;
        }
    }
}//为分配wav文件存储
为size_t大小= sizeof的(结构WAVHeader)+的sizeof(结构FormatHeader)+的sizeof(结构DataHeader)+(长*采样率* 2);
无效*缓冲区=的malloc(大小);//装满头缓冲区
结构WAVHeader * WAV =(结构WAVHeader *)缓冲区;
结构FormatHeader *格式=(结构FormatHeader *)(WAV + 1);
结构DataHeader *数据=(结构DataHeader *)(格式+ 1);的strcpy(WAV的> ChunkIDRIFF);
&WAV的GT; CHUNKSIZE =(uint32_t的)大小 - 8;
的strcpy(&WAV的GT; RIFFType,WAVE);的strcpy(格式 - > ChunkID,FMT);
格式 - > CHUNKSIZE = 16;
格式 - >的COM pression code = 1;
格式化>频道= 1;
格式 - >采样率=(uint32_t的)采样率;
格式 - > SigBitsPerSamp = 16;
格式化> BlockAlign = 2;
格式 - > AvgBytesPerSec =格式化> BlockAlign *采样率;的strcpy(数据 - > ChunkID,数据);
数据 - > CHUNKSIZE =长*采样率* 2;//生成声音
的printf(生成的声音... ... \\ n);
*短声=(短*)(数据+ 1);
总短;
时差;
浮增量= 1.0 /(浮点)采样率;
对(时间= 0;时间和LT,长度,时间+ =增量){
    总= 0;
    对于(I = 0; I&小于10;我++){
        总+ =功略[I] *罪((浮点)SINF [我] *时间*(2 * 3.1415926));
    }
    *(音+(INT)(时间*采样率))=总;
    //输出(时间:%F值:%HD \\ n,时间,累计);
}//写缓冲到文件
FILE * OUT = FOPEN(输出,W);
的fwrite(缓冲液,大小,1,下);
的printf(写为%s \\ n,输出);返回;}


解决方案

我觉得这是你的核心问题:

  *(音+(INT)(时间*采样率))=总;

我怀疑(时间*采样率)上,由于浮点舍入误差整数界限并不总是增加。因此,一些样本位置,由于舍入误差忽略和/或覆盖。这只是一个猜测。

但同时,作为时间的增加,时间*频率* 2PI的乘法会浮内溢出。所以,你应该正常化时间,这样它不会永远上升。

在任何情况下,我验证了这种修饰的环工程(和声音)就好了:

 浮动TWOPI = 6.28318531f;
unsigned int类型sample_count =长×采样率;为(unsigned int类型I = 0; I< sample_count;我++)
{
    无符号整型J =%采样率; //正常化样本位置,使得我们不会在随后的乘法炸毁
    浮动F = 0.0;
    INT结果;    为(中间体X = 0; X小于10; X ++)
    {
        F + =功略[X] *罪((SINF [X] *Ĵ* TWOPI)/采样率);
    }    结果=(长)F;    //钳16位
    如果(结果> 32767)
    {
        结果= 32767;
    }
    否则,如果(结果< -32768)
    {
        结果= -32768;
    }    声音由[i] =(短)的结果;    //的printf(%d个\\ N,音[I]);}

Recently I saw a video lecture in my CS 101 class that inspired me to start playing with the WAV File Format in C. My project today has been creating sounds using a simple mathematical sine function. Despite a couple obstacles, my program can now accept several inputs(frequencies of waves, amplitudes of waves, sampling rate, etc.) and create a wav file containing the specified pitches.

However, when playing these tones on my computer speakers, there is a strange, rhythmic popping sound, which varies with the sampling rate. At higher sampling rates, the frequency of the popping sound increases and turns into an annoying whining sound.

The strange part is that the popping sound is consistent across different computers with the same file.

Below I will post the code that I use to generate the WAV file. Any insights into what might be causing this phenomenon will be appreciated. It's probably just a stupid mistake on my part somewhere. :)

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

}

解决方案

I think this is your core problem:

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

I suspect that (time*samplerate) doesn't always increase on integer boundaries due to floating point rounding errors. Hence, some sample positions are skipped and/or overwritten due to rounding errors. That's just a guess.

But also, as "time" increases, the multiplication of "time * frequency * 2PI" will overflow within a float. So you should normalize "time" such that it doesn't increase forever.

In any case, I validated this modified loop works (and sounds) just fine:

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]);

}

这篇关于WAV文件合成从零开始 - Ç的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆