Java sound api - 扫描 MIDI 设备

Posted

技术标签:

【中文标题】Java sound api - 扫描 MIDI 设备【英文标题】:Java sound api - Scanning for midi devices 【发布时间】:2010-09-20 14:21:37 【问题描述】:

我正在开发一个使用 javax.sound.midi 库从 midi 硬件接收 midi 事件的 java 项目。在文档中,它说MidiSystem.getMidiDeviceInfo() 返回所有连接的 MIDI 硬件的列表。它对我有用,但问题是,它只能工作一次。第一次实际扫描设备需要一点时间,但此后每次都会立即返回相同的列表,即使已连接新设备也是如此。有没有办法强制它重新扫描?如果应用程序重新启动,它将重新扫描,但我不希望我的用户在连接新的 midi 设备时必须重新启动。

顺便说一句,我使用的是 Mac OS X...有人指出,不同操作系统的行为可能会有所不同。

【问题讨论】:

我不想问这个问题,但是这个问题发生在 Mac OSX、Linux 或 Windows 上吗?我知道 Java 是一次写入,随处运行,但事实是这些操作系统具有完全不同的 MIDI 系统,Java 运行时本身可能无法正确与其交互。 感谢您以 Google 找到的方式提出此问题。 【参考方案1】:

MidiSystem.getMidiDeviceInfo() 获取完整的供应商列表,并从每个供应商处提取设备信息。

通过静态方法getProviders()从JDK底层类com.sun.media.sound.JDK13Services恢复MIDIs提供者列表

public static synchronized List getProviders(Class serviceClass) 获取包含已安装的列表 提供者的实例 请求的服务。名单 提供者被缓存一段时间 cachePeriod 给出的时间。中 这期间,同一个List实例是 返回相同类型的 提供者。在这段时间之后,一个新的 实例被构造并返回。 返回的 List 是不可变的。

所以,这个类似乎将你的 Providers 列表保存在缓存中,一段时间后会重新加载。您可以使用setCachingPeriod(int seconds) 方法将此时间段设置为自定义值。据我所知,默认缓存时间设置为 60 秒。

例如,要每秒刷新一次缓存,您可以在代码中添加这一行:

com.sun.media.sound.JDK13Services.setCachingPeriod(1);

请注意,此解决方案使用 Sun 专有类,因此它不能 100% 可移植。

【讨论】:

com.sun.media.sound.JDK13Services.setCachingPeriod(..) 似乎已在 1.7_51 和 1.7_67 之间的某个位置被删除。如果有人找到加快缓存周期的替代方法,我很想听听。 1) 这段代码仍然可以编译并运行com.sun.media.sound.JDK13Services.setCachingPeriod(1); 2) 在 OSX 上我很确定它根本没有帮助 我可以确认这 DOES NOT WORK 在 OS X 10.5.5 上。没有尝试过其他任何东西。另外,似乎缓存期是无限期的?我等了整整一分钟,但它仍然无法启动新设备。【参考方案2】:

在我的工作 PC 或任何类型的 Mac 上缺少任何 MIDI 设备,我怀疑我能否正确测试它,但是...

MidiSystem 类似乎使用com.sun.media.sound.JDK13Services.getProviders(Class providerClass) 来查找系统上的设备列表。 API docs for this class 表示列表是在cachingPeriod 之外的连续调用中重新创建的,可以通过调用setCachingPeriod(int seconds) 方便地设置。

如果运气好的话,您只需在应用程序开始时调用一次并将其设置为 5 秒或其他时间,它就会神奇地工作。但是,文档还声明“此方法仅用于测试。”,因此我不确定这种方法的效果如何。

希望这足以让您开始,同时我会继续四处寻找,看看是否能找到更清洁的方法。

【讨论】:

所以我们做到了,几乎在同一时间 :)【参考方案3】:

我也回答了这是Update list of Midi Devices in Java,但是对于最终来到这里的人来说,现在有一个正确支持这个的库:https://github.com/DerekCook/CoreMidi4J

该库充当 MIDI 子系统的设备提供程序,因此它基本上是一个插件,您现有的所有代码都可以使用。

我不是作者,但它可以很好地满足我的需求,并且需要进行相当多的搜索才能找到,因此我将其发布在这里以供遇到问题的其他人使用。

【讨论】:

【参考方案4】:

我找到了一个使用 JavaFX 线程的解决方案。出于某种原因,这至少在 MacOSX 上有效。在普通线程上它不起作用。

import fx.FX_Platform;
import javafx.embed.swing.JFXPanel;
import javax.sound.midi.MidiDevice;
import javax.sound.midi.MidiSystem;


public class miditest 
  
  
  static public void main( String[] args ) 
    // **** Just to init FX platform
    new JFXPanel();    
    new Thread( () -> 
      for( int ix=0; ix<1000; ix++ ) 
        try 
          Thread.sleep(2000);
         catch( InterruptedException e )           
        
        FX_Platform.runLater( () -> 
          MidiDevice.Info[] infos = MidiSystem.getMidiDeviceInfo();
          System.out.println("FOUND: " + infos.length);
          for( MidiDevice.Info midiinfo : infos ) 
            System.out.println(midiinfo);
          
        );
      
    ).start();
    

  
  

【讨论】:

【参考方案5】:

这听起来可能是操作系统特定的错误,但我可以想办法解决。

在 java 中,您可以运行操作系统的外部命令。 (一个快速的谷歌,给了我这个例子http://www.javafaq.nu/java-example-code-186.html它看起来不错并且给了你这个想法)。

在检查新设备时,您可以发送一个外部命令来运行一个简单的 java 程序,该程序使用 MidiSystem.getMidiDeviceInfo() 快速查询 midi 设备并将结果输出到文本文件,或者您可以从 BufferedReader 对象获取输出.

还要记住,用于查询 midi 设备的外部程序不必用 Java 编写,以防 Java 给您带来更多问题。或者,您可以只在操作系统中查询已连接的设备,然后使用 grep 过滤结果。

希望这会有所帮助。

【讨论】:

我需要 java 才能真正连接到设备。我只能从 MidiSystem.getMidiDeviceInfo() 的结果中连接到它们。 如果您使返回的对象可序列化,这仍然是可能的。如果您希望不断调用和使用更多低级操作系统信息,我建议使用 Java 以外的其他东西,它不是最好的语言。特别是考虑到 Mac 和 Java 并不总是相处融洽。你在不同的操作系统上试过了吗? 我还没有尝试过其他操作系统。我需要它在 OS X 和 Java 上工作。 您尝试过序列化技术吗,应该可以解决问题。

以上是关于Java sound api - 扫描 MIDI 设备的主要内容,如果未能解决你的问题,请参考以下文章

java sound初探

使用 javax.sound.midi 接收 midi 输入

java学习笔记_MIDI

从 Java 中的 Midi 文件中读取 Midi 消息 [重复]

Java 中用于播放的硬件 MIDI 输出?

java学习笔记_MIDI_GUI