#midi #音乐 #声音

rtmidi

RtMidi的封装库,用于实时MIDI输入/输出

2个不稳定版本

0.2.0 2021年4月18日
0.1.0 2021年3月2日

#743 in 音频

MIT许可证

32KB
563

RtMidi crates.io 测试

一个围绕RtMidi的封装库,为Linux(ALSA & JACK)、macOS(CoreMIDI & JACK)和Windows(多媒体库)操作系统提供通用的API(应用程序编程接口)用于实时MIDI输入/输出。


lib.rs:

RtMidi

一个围绕RtMidi的封装库,为Linux(ALSA & JACK)、macOS(CoreMIDI & JACK)和Windows(多媒体库)操作系统提供通用的API(应用程序编程接口)用于实时MIDI输入/输出。

在适用的情况下,可以编译多个API支持,并在创建RtMidi实例时指定特定的API。

MIDI输入和输出功能分别包含在两个结构体中,RtMidiInRtMidiOut。每个实例仅支持一个MIDI连接。RtMidi不提供定时功能(即,输出消息立即发送)。输入消息使用秒(通过f64类型)的增量时间戳。MIDI数据通过使用&[u8]将原始字节传递给用户。

探测端口/设备

客户端通常必须在决定使用哪个端口之前查询可用的MIDI端口。以下示例说明了如何进行此操作。

use rtmidi::{RtMidiIn, RtMidiOut, RtMidiError};

fn main() -> Result<(), RtMidiError> {
    // Initialise MIDI input
    let input = RtMidiIn::new(Default::default())?;

    // Get number of input ports
    let input_ports = input.port_count()?;
    println!("There are {} MIDI input sources available.", input_ports);

    // List input ports
    for port in 0..input_ports {
        println!("\tInput Port #{}: {}", port+1, input.port_name(port)?);
    }

    // Initialise MIDI output
    let output = RtMidiOut::new(Default::default())?;

    // Get number of output ports
    let output_ports = output.port_count()?;
    println!("There are {} MIDI output ports available.", output_ports);

    // List output ports
    for port in 0..output_ports {
        println!("\tOutput Port #{}: {}", port+1, output.port_name(port)?);
    }

    Ok(())
}

请注意,端口枚举是系统特定的,如果用户拔除或连接(或打开或关闭新虚拟端口)任何设备,它将发生变化。因此,应在打开端口之前立即验证端口编号。此外,如果用户在端口连接到该设备/端口时拔除设备(或关闭虚拟端口),将生成MIDI系统错误。

MIDI输出

RtMidiOut提供立即通过MIDI连接发送消息的简单功能。不提供定时功能。

use std::thread::sleep;
use std::time::Duration;
use rtmidi::{RtMidiOut, RtMidiError};

fn main() -> Result<(), RtMidiError> {
    // Initialise MIDI output
    let output = RtMidiOut::new(Default::default())?;

    // Check available ports
    let ports = output.port_count()?;
    if ports < 1 {
        eprintln!("No ports available!");
        return Ok(());
    }

    // Open first available port
    output.open_port(0, "RtMidi Output")?;

    // Program change: 192, 5
    output.message(&[192, 5])?;

    // Control Change: 176, 7, 100 (volume)
    output.message(&[176, 7, 100])?;

    // Note On: 144, 64, 90
    output.message(&[144, 64, 90])?;

    sleep(Duration::from_millis(500));

    // Note Off: 128, 64, 40
    output.message(&[128, 64, 40])?;

    Ok(())
}

MIDI输入

RtMidiIn 使用内部回调函数或线程从端口或设备接收传入的 MIDI 消息。然后,这些消息要么通过调用 RtMidiIn::message 由用户排队并读取,要么立即传递给用户指定的回调函数(必须使用 RtMidiIn::set_callback 进行“注册”)。请注意,如果您有多个 RtMidiIn 实例,每个实例可能都有自己的线程。

RtMidiIn 提供了 RtMidiIn::ignore_types 以指定某些 MIDI 消息类型被忽略。默认情况下,系统专用、定时和活动感知消息被忽略。

在打开端口后立即设置回调是必要的,以避免传入的消息写入队列(当设置回调函数时,队列不会被清空)。如果您担心这种情况发生,可以使用 RtMidiIn::message 检查队列是否为空(在设置回调后)。

use std::io::{stdin, Read};
use rtmidi::{RtMidiIn, RtMidiError};

fn main() -> Result<(), RtMidiError> {
    // Initialise MIDI input
    let input = RtMidiIn::new(Default::default())?;

    // Check available ports
    let ports = input.port_count()?;
    if ports < 1 {
        eprintln!("No ports available!");
        return Ok(());
    }

    // Open first available port
    input.open_port(0, "RtMidi Input")?;

    // Set our callback function.  This should be done immediately after
    // opening the port to avoid having incoming messages written to the
    // queue.
    input.set_callback(|timestamp, message| {
        for (index, byte) in message.iter().enumerate() {
            println!("Byte {} = 0x{:02x}, ", index, byte);
        }
    })?;

    // Don't ignore sysex, timing, or active sensing messages.
    input.ignore_types(false, false, false)?;

    println!("Reading MIDI input ...");
    stdin().read(&mut [0]).unwrap();

    Ok(())
}

无运行时依赖

~0–1.8MB
~34K SLoC