#java #midi #duration #javax.sound.midi
#java #midi #Продолжительность #javax.sound.midi
Вопрос:
Имея этот простой код, который работает (но просто никогда не заканчивается, пока я не завершу его с помощью ^-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>
помощью ). Почему это? Когда заканчивается секвенсор и, следовательно, программа?
РЕДАКТИРОВАТЬ: это может быть вызвано открытием секвенсора, но не его закрытием. Поэтому я хотел закрыть ее в конце, но она не может сразу следовать за player.start()
инструкцией, иначе она немедленно заканчивается. Поэтому должна быть некоторая задержка. Хотя это неуклюжее решение
...
player.setSequence(seq);
player.start();
TimeUnit.SECONDS.sleep(3);
player.close();
После чего возвращается приглашение. Но есть ли лучшее решение?
Ответ №1:
Midi-последовательность завершается, когда Sequencer#isRunning() равен false
. Я думаю, вам следует закрыть объект sequencer перед запуском другой последовательности. Объявление объекта 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. Извините за -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() в прослушивателе.
Обратите внимание, что если вы используете Sequencer.setLoopCount(x) с x> 0, прослушиватель конца последовательности никогда не вызывается, вам понадобится кнопка пользовательского интерфейса (или таймер) для запуска вызова Sequencer.stop() .
Есть несколько хитростей, которые нужно знать при использовании Java sequencer, если вам нужен пример кода, ознакомьтесь с моим приложением JJazzLab-X на GitHub, особенно MusicController.java
в MusicController
модуле.