電子ピアノや MIDI コントローラーからの入力を取得します。midir クレートを使用します。
1. 依存関係の追加
src-tauri ディレクトリで以下のコマンドを実行してください。
cd src-tauri
cargo add midir
2. Rust 側の実装 (lib.rs)
MIDI 入力をコールバック形式で受け取り、フロントエンドにイベントとして送信します。
// src-tauri/src/lib.rs
use midir::{Ignore, MidiInput};
use std::sync::Mutex;
use tauri::{AppHandle, Emitter, State};
// MIDI 接続を維持するための状態
struct MidiState {
conn: Mutex<Option<midir::MidiInputConnection<()>>>,
}
// 利用可能な MIDI 入力ポートの一覧を取得
#[tauri::command]
fn list_midi_ports() -> Result<Vec<String>, String> {
let midi_in = MidiInput::new("Port Scanner").map_err(|e| e.to_string())?;
let ports = midi_in.ports();
let names: Vec<String> = ports
.iter()
.filter_map(|p| midi_in.port_name(p).ok())
.collect();
Ok(names)
}
// 指定したポートに接続して MIDI 入力の監視を開始
#[tauri::command]
fn start_midi_listener(
app: AppHandle,
state: State<MidiState>,
port_index: usize,
) -> Result<String, String> {
let mut midi_in = MidiInput::new("My MIDI Input").map_err(|e| e.to_string())?;
midi_in.ignore(Ignore::None);
let ports = midi_in.ports();
let port = ports.get(port_index).ok_or("Invalid port index")?;
let port_name = midi_in.port_name(port).unwrap_or_default();
println!("Connecting to {}", port_name);
let app_handle = app.clone();
// 接続とコールバック定義
let conn_in = midi_in
.connect(
port,
"tauri-midi-input",
move |_stamp, message, _| {
// メッセージ (例: [144, 60, 100] -> Note On, Middle C, Velocity 100)
let _ = app_handle.emit("midi-message", message.to_vec());
},
(),
)
.map_err(|e| e.to_string())?;
// State に保存して接続を維持
*state.conn.lock().unwrap() = Some(conn_in);
Ok(format!("Connected to {}", port_name))
}
#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
tauri::Builder::default()
.manage(MidiState {
conn: Mutex::new(None),
})
.invoke_handler(tauri::generate_handler![
list_midi_ports,
start_midi_listener
])
.run(tauri::generate_context!())
.expect("error while running tauri application");
}
3. フロントエンドの実装 (TypeScript)
Rust 側で定義したコマンドを呼び出し、イベントを購読します。
import { invoke } from '@tauri-apps/api/core';
import { listen } from '@tauri-apps/api/event';
// 利用可能なポートの一覧を取得
async function listPorts() {
const ports = await invoke<string[]>('list_midi_ports');
console.log('Available ports:', ports);
return ports;
}
// 指定したインデックスのポートで受信を開始
async function startListening(index: number) {
try {
const result = await invoke<string>('start_midi_listener', { portIndex: index });
console.log(result);
} catch (error) {
console.error('Failed to start listener:', error);
}
}
// イベントリスナーの設定
async function setupMidiListener() {
await listen<number[]>('midi-message', (event) => {
// ペイロードは number[] (例: [144, 60, 100])
console.log('Received MIDI:', event.payload);
});
}
4. Web MIDI API との比較
フロントエンドの Web MIDI API (navigator.requestMIDIAccess) も強力で、Tauri から問題なく使用できます。特別な理由(バックエンドで信号加工したい、OS固有の挙動が必要など)がない限り、Web MIDI API を使うほうが簡単な場合が多いです。
ただし、Linux では Web MIDI API がうまく動作しないケース(権限周り)があるため、Rust 側での実装はフォールバックとして有用です。