我正在寻找使用Javasound编写一个简单的MIDI驱动音频音序器。
我有多个样本(每个MIDI音高一个)作为(全局可访问的)Map<MidiPitch,AudioInputStream>
加载到内存中。
javax.sound.midi.Receiver
的自定义子类响应传入的MIDI事件,如下所示:
如果事件是音符开启,则获取剪辑并按如下方式播放:
Clip clip = AudioSystem.getClip();
clip.open(lookupAIS(pitch));
clip.start();
然后将剪辑添加到全局可访问的Map<MidiPitch,List<Clip>>
,代表已启动的剪辑,即上面已调用start()
的剪辑,但其中有一个注释 - 尚未收到关闭事件。
如果事件是笔记关闭,则从上面的地图中获取开始剪辑的相应列表。列表顶部的剪辑将被删除,并在其上调用stop()
和close()
。
上述Receiver
以通常的方式与MidiSystem.getSequencer()
相关联,然后调用以下内容:
sequencer.setSequence(MidiSystem.getSequence(new File(myMidFile)))
sequencer.open()
sequencer.start()
Thread.sleep(aLongTime())
sequencer.stop()
sequencer.close()
当驱动MIDI序列的速度较慢时,上述情况有效,但在较高的速度下,音符只会挂起(即使是包含极少数音符的序列)。
我的理解是clip.start()
在Javasound API后面的一个单独的线程中运行。
有谁能说明为什么会这样?它可能是同步问题吗?
编辑:'挂',我的意思是说有些音符卡住,尽管日志输出报告已经调用了相应的'stop'方法。
EDIT2:当第二次播放给定音符时,看起来好像首先发生悬挂。即使MIDI序列是单声道的,即前一个音符已停止,也会发生这种情况。
答案 0 :(得分:1)
为每次播放加载Clip的方法将成为可变延迟的重要来源。每次调用此文件时,文件都会重新读取,直到整个文件加载完毕后才会开始播放。
我建议预加载所有剪辑并将其保存在内存中。调用音符时,将剪辑光标设置为零,然后播放:
clip[mapIndex].setFramePosition(0);
clip[mapIndex].start();
这些剪辑应该已经打开。我将它们放在一个数组中并使用“mapIndex”作为一种合理的方式来选择可能与您已经设置的映射一起使用的正确剪辑。
您可能不需要“停止”或“关闭”剪辑,直到整个序列完成,除非剪辑相当长并且设计为在进行中停止,或者它们是作为循环播放
这应该会大大改善。我不能说它是否能解决所有问题。 cpu可能正在进行一些线程多路复用,有时候在你当前的代码中,剪辑在另一个线程上完成加载之前,会在一个线程上调用clip.close。