基于 Tauri + Tokio + Rodio 的 Rust 简易音乐播放器桌面程序

导读:笔者最近跟着 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<Sender<AudioEvent>>, 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 ⭐️ ,共同进步 !

  • 9
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值