OS のデフォルトではない特定のオーディオデバイス(スピーカーやヘッドホン)を選択して音声を再生する方法です。Rust 側の cpal や rodio を使用します。
1. 依存関係の追加
Rust プロジェクトのディレクトリ(src-tauri)で以下のコマンドを実行します。
cargo add rodio
※ rodio は内部で cpal を re-export しているため、別途 cpal を追加する必要はありません。別途追加すると名前の競合エラーが発生する可能性があります。
2. デバイス一覧の取得
rodio 0.21.x は内部で cpal を re-export しているため、別途 cpal をインポートする必要はありません。
use rodio::cpal::traits::HostTrait;
use rodio::DeviceTrait;
#[tauri::command]
fn list_output_devices() -> Result<Vec<String>, String> {
let host = rodio::cpal::default_host();
let devices = host.output_devices().map_err(|e| e.to_string())?;
let names: Vec<String> = devices.into_iter()
.filter_map(|d| d.name().ok())
.collect();
Ok(names)
}
3. 特定デバイスでの再生
rodio クレートは cpal の上に構築されており、高レベルな再生 API を提供します。rodio 0.21.x では OutputStreamBuilder パターンを使用します。
use std::fs::File;
use std::io::BufReader;
use rodio::cpal::traits::HostTrait;
use rodio::{Decoder, OutputStreamBuilder, Sink, DeviceTrait};
#[tauri::command]
fn play_sound_on_device(device_name: String, file_path: String) -> Result<(), String> {
// デバイスを探す
let host = rodio::cpal::default_host();
let device = host.output_devices().map_err(|e| e.to_string())?
.find(|d| d.name().map(|n| n == device_name).unwrap_or(false))
.ok_or("Device not found")?;
// ストリームの作成(OutputStreamBuilder パターン)
let stream = OutputStreamBuilder::from_device(device)
.map_err(|e| e.to_string())?
.open_stream()
.map_err(|e| e.to_string())?;
// Sink を Mixer に接続
let sink = Sink::connect_new(stream.mixer());
let file = File::open(file_path).map_err(|e| e.to_string())?;
let source = Decoder::new(BufReader::new(file)).map_err(|e| e.to_string())?;
sink.append(source);
// 再生完了までブロック(非同期にする場合は State で Sink を保持するなど)
sink.sleep_until_end();
Ok(())
}
※ フロントエンドの <audio> タグの出力先を変更するには setSinkId API がありますが、ブラウザ実装や権限に依存するため、Rust 側で制御するほうが確実な場合があります。