将音符写入wav文件

时间:2011-02-11 22:00:56

标签: c# audio wav

我对如何录制音符(例如A,B,C#等)或和弦(同时发出多个音符)感兴趣,并将它们写入wav文件。

根据我的理解,每个音符都有一个与之相关的特定频率(对于完美的音调) - 例如A4(中间C以上的A)是440 Hz(完整列表向下的2/3 {{3} })。

如果我的理解是正确的,那么这个音调是在频域中,所以需要应用反向快速傅立叶变换来生成时域等价?

我想知道的是:

  • 和弦是如何工作的?它们是球场的平均值吗?
  • 当wav文件的内容是波形时,如何指定播放每个音符的时间长度?
  • 如何将多个音符的反FFT转换为字节数组,这组成了wav文件中的数据?
  • 与此相关的任何其他相关信息。

感谢您提供任何帮助。如果给出代码示例,我使用的是C#,我目前用来创建wav文件的代码如下:

int channels = 1;
int bitsPerSample = 8;
//WaveFile is custom class to create a wav file.
WaveFile file = new WaveFile(channels, bitsPerSample, 11025);

int seconds = 60;
int samples = 11025 * seconds; //Create x seconds of audio

// Sound Data Size = Number Of Channels * Bits Per Sample * Samples

byte[] data = new byte[channels * bitsPerSample/8 * samples];

//Creates a Constant Sound
for(int i = 0; i < data.Length; i++)
{
    data[i] = (byte)(256 * Math.Sin(i));
}
file.SetData(data, samples);

这会(以某种方式)创建一个恒定的声音 - 但我不完全理解代码如何与结果相关联。

3 个答案:

答案 0 :(得分:116)

答案 1 :(得分:6)

你走在正确的轨道上。 :)

音频信号

您不需要进行逆FFT(您可以,但您需要为其找到lib或实现它,并生成信号作为输入)。直接生成我们期望的IFFT结果要容易得多,IFFT是具有给定频率的正弦信号。

正弦的参数取决于您想要生成的音符和您生成的波形文件的sampling frequency(通常等于44100Hz,在您的示例中使用的是11025Hz)。

对于1 Hz音调,您需要一个正弦信号,其周期等于一秒。对于44100 Hz,每秒有44100个样本,这意味着我们需要一个正弦信号,其中一个周期等于44100个样本。由于正弦周期等于Tau(2 * Pi),我们得到:

sin(44100*f) = sin(tau)
44100*f = tau
f = tau / 44100 = 2*pi / 44100

对于440 Hz,我们得到:

sin(44100*f) = sin(440*tau)
44100*f = 440*tau
f = 440 * tau / 44100 = 440 * 2 * pi / 44100

在C#中,这将是这样的:

double toneFreq = 440d;
double f = toneFreq * 2d * Math.PI / 44100d;
for (int i = 0; i<data.Length; i++)
    data[i] = (byte)(128 + 127*Math.Sin(f*i));

注意:我没有对此进行测试以验证代码的正确性。我会尽力做到并纠正任何错误。 更新:我已将代码更新为有效的内容。抱歉伤害了你的耳朵; - )

<强>和弦

和弦是音符组合(例如参见Minor chord on Wikipedia)。因此,信号将是具有不同频率的正弦的组合(总和)。

纯音

这些音调和和弦听起来并不自然,因为传统乐器不会播放单频音。相反,当您演奏A4时,频率分布很广,浓度约为440 Hz。请参阅示例Timbre

答案 2 :(得分:2)

还没有人提到过Karplus Strong弹拨的字符串算法。

Karplus–Strong string synthesis 这是一种非常简单的方法,用于生成逼真的弹拨弦乐声。我用这个写了复音乐器/实时MIDI播放器。

你这样做:

首先,您想要模拟的频率是多少?假设音乐会音高A = 440Hz

假设您的采样率为44.1kHz,即44100/440 =每波长100.25个样本。

让我们将它舍入到最接近的整数:100,并创建一个循环缓冲区长度100。

所以它会保持一个频率为~440Hz的驻波(注意它不准确,有很多方法)。

在-1和+1之间填充随机静态,并且:

DECAY = 0.99
while( n < 99999 )
    outbuf[n++] = buf[k]

    newVal = DECAY  *  ( buf[k] + buf_prev ) / 2

    buf_prev = buf[k]
    buf[k] = newVal

    k = (k+1) % 100

这是一个了不起的算法,因为它非常简单并产生超级声音。

理解正在发生的事情的最好方法是认识到时域中的随机静态是白噪声;频域中的随机静态。你可以把它想象成不同(随机)频率的许多波的复合。

接近440Hz(或2 * 440Hz,3 * 440Hz等)的频率将对它们自身造成建设性干扰,因为它们一次又一次地绕过环。所以它们将被保留下来。其他频率将破坏性地干扰自己。

此外,平均作为低通滤波器 - 想象你的序列是+1 -1 +1 -1 +1 -1,如果你是平均对,那么每个平均值都是0.但如果你的速度较慢波形如0 0.2 0.3 0.33 0.3 0.2 ...然后平均仍然会产生波。波浪越长,其能量保存得越多 - 即平均值会导致阻尼减少。

因此可以将平均值视为非常简单的低通滤波器。

当然存在复杂性,必须选择整数缓冲器长度迫使量化可能的频率,这对于钢琴的顶部变得明显。一切都是可以克服的,但它变得艰难!

链接:

Delicious Max/MSP Tutorial 1: Karplus-Strong

The Karplus-Strong Algorithm

只要我能看到世界上合成音产生的领先权威,所有的道路都可以回到他的网站。但要注意,它变得非常棘手,需要大学水平的数学。