生成WAV文件音调

本文关键字:文件 WAV 生成 | 更新日期: 2023-09-27 18:27:16

我正在尝试以编程方式构建一个音频WAV文件,该文件包含您拨打电话号码时听到的标准英国铃声的n秒。

我找到了一个记录音调频率和持续时间的来源,它是两个频率的正弦波:400Hz和450Hz。

我编写的代码生成了一个正确格式的PCM WAV文件,我可以使用Windows Media player等音频播放器播放,但它生成的音调听起来比应该的频率低得多,所以我认为我的forumla不太正确。

这就是我目前正在做的:

var duration = 10;
var bitsPerSample = 8;
var samplesPerSec = 8000;
var f1 = 400;
var f2 = 450;
var pattern = new[] {
            TimeSpan.FromMilliseconds(400),
            TimeSpan.FromMilliseconds(200),
            TimeSpan.FromMilliseconds(400),
            TimeSpan.FromMilliseconds(2000)
        };
var wavdata = new byte[duration * samplesPerSec]; // 10 seconds of wav data @ 8000 samples per sec, 8 bits per sample, 1 channel 
// Loop through each sample
for (var i = 0; i < wavdata.Length; i = i + (bitsPerSample / 8)) {
    // Get time in seconds of the current sample
    var time = Convert.ToDouble(i) / (Convert.ToDouble(bitsPerSample) / 8) / samplesPerSec;
    // Calculate the on off pattern
    var onoff = 0;
    var timeMilliseconds = time * 1000;
    var p = 0;
    while (timeMilliseconds >= 0) {
        timeMilliseconds = timeMilliseconds - pattern[p].TotalMilliseconds;
        onoff = onoff == 1 ? 0 : 1;
        if (++p >= pattern.Length) p = 0;
    }
    // Calculate the sample: (sin(time * 400) * 128 + sin(time * 450) * 128)) / 2
    var sample = onoff * (((Math.Sin(time * f1) * 128) + (Math.Sin(time * f2) * 128)) / 2);
    // Store sample
    wavdata[i] = Convert.ToByte(sample + 128);
}

正如你所看到的,我使用的公式是:

sin(time-of-sample * frequency) * amplitude

我在两个组合频率中使用了两次:

sin(time * 400) * 128
sin(time * 450) * 128

然后我把它们加在一起,除以2得到平均值。然后我乘以1或0,得到音调之间的静音,得到铃声。最后,当我将值存储在数据数组中时,我将样本偏移128,因为WAV文件数据表示为加号或减号数据。

我做错了什么?为什么这产生的音调比预期的要低得多?

生成WAV文件音调

你忘了Pi。正弦周期从0…2π,而不是0..1:

var sample = onoff * (((Math.Sin(2 * Math.Pi * time * f1) * 127) + (Math.Sin(2 * Math.Pi * time * f2) * 127)) / 2);

另外,请注意,我将128秒改为127秒,因为如果Sin太接近1或-1,则浮点到字节的转换将溢出。也许有一种更好的方法可以做到这一点,它不会牺牲范围,但这可能与你正在做的事情无关。

如果我可以评论的话:代码做了太多的单位转换。同时以秒和毫秒表示时间是令人困惑的。"sample"的范围应该是-1到1,然后将转换为字节作为一个单独的步骤。通过在该行内乘以128,它将字节的概念与音频计算混合在一起,这有点令人困惑。