导读:笔者最近跟着 Rust Course 过了一遍 Rust 的知识点后写了一个练手项目加深一下理解。项目主要依赖 Tauri、 Tokio、Rodio 库。项目中涉及到了结构体、多线程与 Arc/Mutex、异步编程与channel 通道、生命周期与所有权等知识点。
项目概述
一个用Rust编写的简单的桌面音乐播放器应用程序。前端基于Tauri (Vue + Typescript),后端主要使用 Tokio(异步库) 、 Rodio(音频库)。
当前已实现的功能:
- 音频基础控制
- 音量调节
- 播放模式可选
项目核心代码解析
启动函数准备好 tokio runtime、初始化 AudioService 、并通过 Rodio manage 共享部分属性。
#[tokio::main]
async fn main() {
let audio_service = AudioService::new();
tauri::Builder::default()
.invoke_handler(tauri::generate_handler![
handle_event,
scan_files_in_directory,
is_sink_empty
])
.manage(audio_service.event_sender) // share
.manage(audio_service.sink)
.run(tauri::generate_context!())
.expect("error while running tauri application");
}
前端发生操作行为后会调用后端 handle_event 函数,该函数根据前端传过来的事件类型并进行相应处理。主要为通过
tokio::sync::broadcast::Sender; 发送事件到 tokio::sync::broadcast: 的 channel 实现异步处理。
/// Receive events sent by the front end, encapsulate them as [`AudioEvent`] and send them to the channel.
#[tauri::command]
fn handle_event(sender: tauri::State>, event: String) {
let event: serde_json::Value = serde_json::from_str(&event).unwrap();
if let Some(action) = event["action"].as_str() {
match action {
"play" => event["file_path"]
.as_str()
.map(|file_path| sender.send(AudioEvent::Play(file_path.to_owned()))),
"pause" => Some(sender.send(AudioEvent::Pause)),
"recovery" => Some(sender.send(AudioEvent::Recovery)),
"volume" => event["volume"]
.as_f64()
.map(|volume| sender.send(AudioEvent::Volume(volume as f32))),
_ => None, // other actions
};
}
}
AudioService 在初始化时会创建一个线程用于接收 channel 的事件,并根据事件类型调用 Rodio 相应的接口完成对音频控制。
impl AudioService {
pub fn new() -> Self {
// Create a tokio broadcast channel to transmit events.
let (event_sender, mut event_receiver) = broadcast::channel(100);
// Create a Rodio sink and use Arc and Mutex to share data. If not. The ownership of the sink will be Moved and the sink will not be able to be used in the future.
let (_stream, handle) = OutputStream::try_default().unwrap();
let sink = Arc::new(Mutex::new(Sink::try_new(&handle).unwrap()));
let sink_clone = Arc::clone(&sink);
tokio::spawn(async move {
while let Ok(event) = event_receiver.recv().await {
let sink = sink_clone.lock().await;
match event {
AudioEvent::Play(file_path) => {
let file = BufReader::new(File::open(file_path).unwrap());
let source = Decoder::new(file).unwrap();
sink.clear();
if sink.is_paused() {
sink.append(source);
sink.play();
}
}
AudioEvent::Recovery => {
sink.play();
}
AudioEvent::Pause => {
sink.pause();
}
AudioEvent::Volume(volume) => {
sink.set_volume(volume / 50.0);
}
}
}
});
Self {
event_sender,
_stream,
sink,
}
}
}
最后
项目地址(
https://github.com/senlizishi/music-player),各位如果觉得项目有帮助的话麻烦给个 Star ?? ,共同进步 !
感谢您的阅读,如果喜欢本文欢迎关注和转发,转载需注明出处,本头条号将持续分享IT技术知识。对于文章内容有其他想法或意见建议等,欢迎提出共同讨论共同进步。
本文暂时没有评论,来添加一个吧(●'◡'●)