Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/394.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 停止MIDI音序器,以便我可以播放其他内容_Java_Multithreading_Swing_Midi_Javax.sound.midi - Fatal编程技术网

Java 停止MIDI音序器,以便我可以播放其他内容

Java 停止MIDI音序器,以便我可以播放其他内容,java,multithreading,swing,midi,javax.sound.midi,Java,Multithreading,Swing,Midi,Javax.sound.midi,编辑:这里有一个自包含的示例: MidiLatte midiLatte = new MidiLatte(); for (int i = 60; i <= 72; i++) { midiLatte.addNote(i, 4); } midiLatte.playAndRemove(); try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } try {

编辑:这里有一个自包含的示例:

MidiLatte midiLatte = new MidiLatte();

for (int i = 60; i <= 72; i++) {
    midiLatte.addNote(i, 4);
}
midiLatte.playAndRemove();

try {
    Thread.sleep(3000);
} catch (InterruptedException e) {
    e.printStackTrace();
}

try {
    Field f = MidiLatte.class.getDeclaredField("player");
    f.setAccessible(true);
    Sequencer s = (Sequencer) f.get(midiLatte);
    s.stop();
} catch (NoSuchFieldException | IllegalAccessException e) {
    e.printStackTrace();
}

for (int i = 48; i <= 60; i++) {
    midiLatte.addNote(i, 4);
}
midiLatte.playAndRemove();
midiatte
是一个实用类,它使发送midi消息和包装
序列器变得更容易。基本上,它的工作方式是
任务
消费者
链接锁定队列
。这允许我做的是在外部类(
MPThread
,我的自定义
Thread
,是一个内部类)中有一个方法,它将
使用者
添加到队列中。这样可以轻松安排MIDI任务,如下所示:

midiPlayer.addAction(midiLatte -> {
    // do midi stuff
});
然而,如上所述,我需要能够取消这些任务,并停止MIDI播放一旦他们已经被发送。前者很简单,我只需清除队列即可:

tasks.clear();
但是,后一种方法很困难,因为MIDI任务已经发送并且掌握在MIDIAPI手中。我尝试过使用
sequencer.stop()
,但从中得到了奇怪的结果,如本例所示:

player.addAction(midiLatte -> {
    //midi stuff
});
try {
    Thread.sleep(3000);
} catch (InterruptedException e) {
    e.printStackTrace();
}
player.cancelPending();
player.addAction(midiLatte -> {
    //Midi stuff
});
其中
cancelPending()
调用
sequencer.stop()
并清除队列。最终发生的情况如下(我从经验中知道,当用户不清楚地描述他们的bug时,这可能会让人恼火和困惑,所以我将一步一步地展示它。如果您仍然不明白发生了什么,请告诉我):

  • 第一个序列开始正确播放
  • 3000(或多或少)毫秒后,第一个序列停止
  • 第二个序列立即开始播放,但不从头开始;相反,它开始演奏,就好像它一直在演奏一样
为了说明这一点,假设第一个序列的音符是
abcdefga
,第二个序列是
12345678
。如果在第一个序列中3000密耳落在
C
D
之间,则整个播放是
abc1 2 3 4 5 6 7 8
。然而,实际发生的是
abc45678


我怎样才能达到我想要的行为?如果我设置并发的方式存在固有问题,请告诉我。只是重申一下我想要的行为,就在这里按下播放按钮时,MIDI序列播放。如果在序列仍在播放时再次按此键,则第一次播放将停止,序列将从一开始再次播放。如果我有任何不清楚的地方,请告诉我。谢谢大家!

好的,所以,正如我们所知,问题在于除了无法实际控制底层的
Sequencer
,因为它正在继续播放缓存的数据

不幸的是,您没有访问
序列器的权限。。。yippy skippy,
midiatte
也不提供任何扩展点供您修改代码

黑客解决方案是做你已经在做的事情,例如使用反射来访问私有字段

!! 这是一个黑客!! 如果您无法修改
midiatte
的原始源代码,那么您就别无选择,为了实现您似乎想要实现的目标,您必须能够访问
Sequencer

public static class StoppableMidiLatte extends MidiLatte {

    public void stop() {
        removeAll();
        try {
            Field f = MidiLatte.class.getDeclaredField("player");
            f.setAccessible(true);
            Sequencer s = (Sequencer) f.get(this);
            s.stop();
        } catch (NoSuchFieldException | IllegalAccessException e) {
            e.printStackTrace();
        }
    }

}

至于并发性的需要,我认为您不需要它,因为
Sequencer
已经在使用它自己的线程来播放音符,而并发性只会使在这种情况下更难更快地获得更好的帮助,发布一个or。我与@AndrewThompson合作——让我们了解您的代码和问题的最好方法是创建您的and/or并将其与您的问题一起发布,但这不是一件容易的事情,因为您将不得不模拟我们无法访问的库。我不确定这是否与Swing-per-say有关,所以您可能只需要从自定义库生成一个示例player@ostrichofevil我理解并尊重Swing让你沿着既定的道路工作,但是您遇到的问题与Swing有关,它更像是一个一般的并发问题。我只是建议MVCE不必有Swing组件,因为您可以在没有它的情况下复制问题。如果在生成示例的过程中,它在没有UI的情况下(但使用线程模型)能按预期工作,那么无论如何,包装一个简单的UI,我只是建议您可以生成一个没有UI的简单示例it@MadProgrammer我会试着这么做。对不起,这是我的错,我没有选择在我的例子中放什么,但我已经这么做了!这就是为什么我能够呼叫
stop
并遇到问题的原因。即使在我这样做的时候,第二个序列也不是从一开始就开始的。我想我将通过更仔细地查看
midiatte
和更多地查看守护进程线程来解决这个问题。我也可以试着获得许可,放弃Midiatte,自己使用声音API。谢谢你抽出时间!我还做了一件事,就是调用
removeAll
,不确定它是否有什么不同,但在我的测试中,我能够让它正常工作
public static class StoppableMidiLatte extends MidiLatte {

    public void stop() {
        removeAll();
        try {
            Field f = MidiLatte.class.getDeclaredField("player");
            f.setAccessible(true);
            Sequencer s = (Sequencer) f.get(this);
            s.stop();
        } catch (NoSuchFieldException | IllegalAccessException e) {
            e.printStackTrace();
        }
    }

}