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

Posted

技术标签:

【中文标题】如何在 Java 中播放(MIDI)序列中的音频剪辑?【英文标题】:How can I play an audio clip in a (MIDI) sequence in Java? 【发布时间】:2019-12-02 16:32:33 【问题描述】:

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

对于 MIDI 类,您似乎无法使用音序器来播放您自己的音频片段。 我试图编写自己的音序器,使用调度来按顺序播放 javax.sound.sampled.Clip,但时间差异太大。这不是一个真正可行的选择,因为它没有时间。

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

【问题讨论】:

【参考方案1】:

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

关键是使样本的音频数据在每帧的基础上可用,以及一个帧计数命令处理器/音频混合器,既管理“命令”的执行,又收集和混合音频帧数据.在 44100 fps 的情况下,精度接近 0.02 毫秒。如果需要,我可以更详细地描述。

另一种可能更明智的方法是使用Java bridge 到Jack 等系统。


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

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

对于 .wav 音频的播放,Java 结合使用 AudioInputStream 读取数据和使用 SourceDataLine 输出。您的系统必须位于中间,拦截AudioInputStream,转换为 PCM 浮点帧,并在执行过程中计算帧数。

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

从单个SourceDataLine 中的任意第 0 帧计算输出帧将有助于保持组成的传入轨道协调,并将提供帧编号参考,用于安排您希望在输出该帧之前执行的任何其他命令(例如,更改音源的音量/声像,或合成器的设置)。

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

【讨论】:

感谢您的评论菲尔。我理解将样本拆分为帧并根据收到的“命令”组装成一个新流的概念,并且有兴趣探索它。您是如何拆分 wav 文件的?您是否将它们读入字节数组并这样做?另外,您是如何尝试播放新的“流”的?我想这不是写出一个全新的 .wav 文件。【参考方案2】:

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

很快,一个样本就是音频数据+循环点、样本覆盖的音符范围、基本音量和包络等信息。 乐器是一组样本,而音库包含一组乐器。 如果你想用自己的声音来播放一些音乐,你必须把它们做成一个音库。

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

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

通常的方法是通过自己将音频剪辑混合到最终剪辑中来生成音乐的音频。这样就可以实现帧精度。 事实上,这就是 MIDI 合成器的大致用途。

【讨论】:

感谢您的回复。你知道 Gervill 是不是现在最好用的东西吗? 我完全不知道。我什至不知道 Gervill 是否仍然存在并使用 Java 8 之后的最新版本的 Java。

以上是关于如何在 Java 中播放(MIDI)序列中的音频剪辑?的主要内容,如果未能解决你的问题,请参考以下文章

你如何在 java 中访问 MIDI IN 流中的数据?

核心音频:不推荐使用 AUGraph 时如何通过 MusicPlayer 播放 MIDI MusicSequence?

HTML5 中的 MIDI 音乐支持

HTML5 中的 MIDI 音乐支持

在 Java 中同时播放 .mp3 和一系列音符 (MIDI)

笔记本电脑退出休眠后 Java MIDI 音频延迟