如何在Java中以(MIDI)序列播放音频剪辑?

时间:2019-12-02 16:32:33

标签: java audio midi javasound

我试图用Java编写一个非常简单的DAW,但是无法按顺序播放音频剪辑。我已经研究了Java Sound中的采样类和MIDI类,但是我真正需要的是两者的混合体。

例如,对于MIDI类,您似乎无法使用音序器来播放自己的音频剪辑。 我试图使用调度来编写自己的音序器,以在序列中播放javax.sound.sampled.Clip,但是时间相差太大。这确实不是一个可行的选择,因为它不能节省时间。

有人对我如何解决这个问题有任何建议吗?

2 个答案:

答案 0 :(得分:0)

否,您不能使用音序器直接播放自己的剪辑。 在MIDI世界中,您必须处理样本,乐器和音库。

很快,样本就是音频数据+信息,例如循环点,样本所覆盖的音符范围,基本音量和包络等。 乐器是一组样本,而音库包含一组乐器。 如果要使用自己的声音来播放音乐,则必须用它们来制作音库。

您还需要使用不同于Java提供的默认设置的其他实现,因为该默认设置只能读取专有格式的音库,这种格式至少已经存在15年,甚至20年了。 早在2008-2009年,就存在Gervill。它能够读取SF2和DLS音库。 SF2和DLS是两种流行的音库格式,市场上有几种免费或付费的程序可以对其进行编辑。

如果您想从采样开始,这也是完全正确的,那么您就不能依赖计时器,任务计划,Thread.sleep等来获得足够的精度。 通过使用这些精度,您可以达到的最佳精度约为10毫秒,当然,对于音乐来说,这太少了。

通常的做法是通过将自己的音频片段混合到最终片段中来生成音乐音频。这样就可以达到帧精度。 实际上,这正是MIDI合成器的作用。

答案 1 :(得分:0)

我可以证明,结合MIDI和样本方面的音频混合系统可以用Java编写,就像我自己编写的那样,它目前可以与样本和我也编写的几个实时合成器一起使用。

关键是使样本的音频数据每帧可用,并且帧计数命令处理器/音频混合器既管理“命令”的执行,又收集和混合音频帧数据。达到44100 fps时,精度约为0.02毫秒。如果需要,我可以详细描述。

尽管我个人没有这样做,但另一种可行的方法可能更明智,那就是对Java bridge之类的系统使用Jack


编辑:在评论中回答问题(19/12/8)。

Java中的音频样本数据通常保存在内存中(Java使用Clip)或从.wav文件中读取。因为Clip不会暴露各个帧,所以我编写了一个备用帧,并使用它来保存数据,其范围为-1到1的有符号浮点数。有符号浮点数是保存音频数据的常用方法。进行多种操作。

对于播放.wav音频,Java结合了用AudioInputStream读取数据和用SourceDataLine输出。您的系统将必须坐在中间,拦截AudioInputStream,转换为PCM浮动帧,并在运行时对帧进行计数。

可以同时处理多个源或轨道,并将其合并(简单添加归一化浮点数)到单个信号。该信号可以转换回字节,并通过单个SourceDataLine发送出去进行回放。

从单个SourceDataLine的任意第0个帧开始计数输出帧将有助于保持组成的传入轨道协调一致,并将提供帧号参考,用于计划要在该帧之前执行的任何其他命令输出(例如,更改信号源的音量/声相或合成器上的设置)。

我个人剪辑的替代品与AudioCue非常相似,欢迎您检查和使用。主要区别在于,无论好坏,我一次都在系统中处理所有帧,并且AudioCue及其“ Mixer”进程缓冲区加载。我有几个非常可信的人批评我的个人每帧系统效率低下,因此当我为AudioCue制作公共API时,我就屈服于这种先入为主的观念。 [有一些方法可以向每帧系统添加缓冲以重新获得该效率,并且每帧使调度更加简单。所以我坚持使用每帧逻辑方案。]