【发布时间】:2020-01-24 12:29:06
【问题描述】:
我现在正在编写一个基本的合成器,但遇到了一个奇怪的问题。在通过 SourceDataLine 播放代表 16 位单声道音频的字节数组时,我会听到持续不断的爆裂声。
流行音乐以恒定的速度播放,并且据我所知,音高。不过,流行音乐的频率确实略有不同(同样,从我能听到的情况来看),一些音符有低通的流行音乐,而另一些则有高通的声音。虽然爆裂声并没有压倒一切,但您仍然可以在背景中听到所需的声音。
除了采样率之外,没有什么会改变弹出频率,不是音符音高,不是 SourceDataLine 缓冲区大小,不是我一次写入它的字节数。
降低采样率会降低爆音率,反之亦然。
为了测试我的程序,我打印了大约半秒钟写入SourceDataLine的数据,并查看了播放的正弦波的大约15个周期,完全没问题;没有突然的跳跃、剪裁或其他任何事情。
我使用采样率值的唯一两件事是一些基本的数学运算,以帮助我的采样器以正确的频率进行采样,每个音符只计算一次,并且绝对可以在音高完美的情况下工作,并且创建 SourceDataLine。
以下是我启动 SourceDataLine 的方式(取自 main 方法的多个部分):
AudioFormat format = new AudioFormat(AudioEnvironment.SAMPLE_RATE, AudioEnvironment.BIT_DEPTH, 1, true, true);
SourceDataLine line = AudioSystem.getSourceDataLine(format);
line.open(format, 8000);
line.start();
我的数据在 big-endian 中是正确的,我在构造函数中更改了 endian 标志并让我的耳朵充满了白噪声。
程序设置好一切后,在这个无限循环中不断地向SourceDataLine写入数据:
while (true) {
for (Channel channel : channelSystem.getChannels()) {
if (channel.pitch != 0) {
wave.sample(channel, buffer);
line.write(buffer, 0, AudioEnvironment.SUB_BUFFER_SIZE * 2);
}
}
}
(Channel 是我创建的一个类,其中包含单个音符的所有数据(尽管显然程序目前没有正确设置复音),buffer 是一个字节数组,@987654325 @ 是我将数据采样到buffer 的地方,AudioEnvironment.SUB_BUFFER_SIZE * 2 是buffer 的大小)
我不一定需要如何在代码中解决此问题的示例,但解释为什么会发生这种情况会很好。
编辑:我还应该补充的是,我尝试在无限写入循环中放置一个打印语句以打印出 SourceDataLine 中的可用字节数,它始终保持在 500 - 2000 左右,偶尔会起床到 5000 左右,但永远不会接近 8000,因此缓冲区永远不会耗尽数据。
【问题讨论】:
-
你能在一个小的独立程序中重现这个(不依赖于单独的
Channel类)吗? -
我对你所说的流行音乐以恒定的“音高”但不同的“频率”播放感到困惑。音高不是由频率决定的吗?
-
@ruakh 在这种情况下,我认为这无关紧要;就像我上面说的,我查看了 wave.sample() 生成的数据,这很好。 Channel 目前只包含变量和构造函数。代码中唯一可能出错的部分是第一个代码 sn-p 和 line.write()。话虽如此,我会看看我是否可以编写一个小的单类程序来复制我的问题。
-
@ruakh 我想一般来说它们会有点模棱两可,但对于 midi/synths,音高通常是指音符本身(比如 C4、F#6、Gb2 等),频率会参考实际产生的声波。或者至少这是我通常看到的术语。
标签: java audio-player