midi.Sequencer 的序列何时结束?
Posted
技术标签:
【中文标题】midi.Sequencer 的序列何时结束?【英文标题】:When does the Sequence from midi.Sequencer ends? 【发布时间】:2020-12-06 23:55:15 【问题描述】:拥有这个有效的简单代码(但在我用 ^-c 终止它之前永远不会结束):
import javax.sound.midi.*;
public class Foo
public void play(int instrument, int note)
try
Sequencer player = MidiSystem.getSequencer();
player.open();
Sequence seq = new Sequence(Sequence.PPQ, 4);
Track track = seq.createTrack();
ShortMessage first = new ShortMessage();
first.setMessage(192, 1, instrument, 0);
MidiEvent changeInstrument = new MidiEvent(first, 1);
track.add(changeInstrument);
ShortMessage a = new ShortMessage();
a.setMessage(144, 1, note, 100);
MidiEvent noteOn = new MidiEvent(a, 1); //fired on "tick 1"
track.add(noteOn);
ShortMessage b = new ShortMessage();
b.setMessage(128, 1, note, 100);
MidiEvent noteOff = new MidiEvent(b, 8); //fired on "tick 8"
track.add(noteOff);
player.setSequence(seq);
player.start();
catch (Exception e)
e.printStackTrace();
public static void main(String[] args)
Foo foo = new Foo();
foo.play(0, 42);
noteOn
MidiEvent
在 1 tick
上触发(MidiEvent
构造函数的第二个参数)。 noteOff
MidiEvent
在8 tick
上触发,这意味着。应该有 7 个刻度跨度。但我永远看不到结束(程序结束)。当从 cmd 触发时,提示永远不会返回,除非我强制终止进程(使用<ctrl-c>
)。这是为什么? Sequencer 何时结束,程序何时结束?
编辑:
可能是打开了 Sequencer,但没有关闭它。所以我想在最后关闭它,但它不能立即跟随player.start()
语句,否则它会立即结束。所以肯定有一些延迟。这是一个笨拙的解决方案
...
player.setSequence(seq);
player.start();
TimeUnit.SECONDS.sleep(3);
player.close();
之后,提示返回。但是有更好的解决方案吗?
【问题讨论】:
【参考方案1】:当 Sequencer#isRunning() 等于 false
时,Midi 序列完成。我认为您应该在开始另一个序列之前关闭序列器对象。将 Sequence 对象声明为类成员可以提供更多的灵活性:
// Declare Sequencer as a class member variable and initialize to null.
javax.sound.midi.Sequencer sequencer = null;
public void playMidi(String midiFilePath)
// Prevent a new Midi audio file is one is already playing.
if (sequencer != null)
// If sequncer is running get outta here.
if (sequencer.isRunning())
return;
// Otherwise close the sequencer.
else
sequencer.close();
// Play the Midi file in its own thread.
Thread newThread = new Thread(() ->
try
// Obtains the default Sequencer connected to the System's default device.
sequencer = javax.sound.midi.MidiSystem.getSequencer();
// Opens the device, indicating that it should now acquire any
// system resources it needs and becomes operational.
if (sequencer.isRunning())
return;
sequencer.open();
// Stream in a Midi file
java.io.InputStream is = new java.io.BufferedInputStream(new java.io.FileInputStream(new File(midiFilePath)));
// Sets the current sequence on which the sequencer operates.
// The stream must point to MIDI file data.
sequencer.setSequence(is);
is.close();
// Starts playback of the MIDI data in the currently loaded sequence.
sequencer.start();
catch (MidiUnavailableException | IOException | InvalidMidiDataException ex)
Logger.getLogger("playMidi() Method Error!").log(Level.SEVERE, null, ex);
);
newThread.start();
【讨论】:
对不起 -1 但这确实不能回答问题。也不需要启动另一个线程,sequencer.start() 会在内部处理它。【参考方案2】:您需要添加一个 MetaEventListener 来捕获“序列结束”事件:
player.addMetaEventListener(metaEvent ->
if (metaEvent.getType() == 47) // end of sequence
player.stop(); // Not mandatory according to API doc, but better to have it
// This is called from the Sequencer thread, NOT from the EDT
// So we need SwingUtilities if we want to update the UI
Runnable doRun= new Runnable()
@Override
public void run()
doSomethingAfterStop();
;
SwingUtilities.invokeLater(doRun);
);
如果您只想在序列结束时退出程序,只需在侦听器中直接调用 System.exit() 即可。
请注意,如果您在 x>0 的情况下使用 Sequencer.setLoopCount(x),则永远不会调用序列结束侦听器,您需要一个 UI 按钮(或计时器)来触发对 Sequencer.stop 的调用()。
使用 Java 定序器时有一些技巧需要了解,如果您需要示例代码,请查看我在 GitHub 上的应用程序 JJazzLab-X,尤其是 MusicController
模块中的 MusicController.java
。
【讨论】:
以上是关于midi.Sequencer 的序列何时结束?的主要内容,如果未能解决你的问题,请参考以下文章
使用 jQuery 确定 HTML5 音频何时暂停或曲目何时结束