【Rust GUI开发入门】编写一个本地音乐播放器(2. Rodio播放库的使用)
本系列教程对应的代码已开源在 Github zeedle
添加依赖
在Cargo.toml添加:
[dependencies]
rodio = "0.21.1"
添加/播放/暂停音频
use std::{thread, time::Duration};use rodio::Decoder;
fn main() {// create an output streamlet stream_handle = rodio::OutputStreamBuilder::from_default_device().expect("no output device available").open_stream().expect("failed to open output stream");// create a sink to play audiolet sink = rodio::Sink::connect_new(&stream_handle.mixer());// open an audio filelet file = std::fs::File::open("audios/爱情转移.flac").expect("failed to open audio file");// decode the audio filelet source = Decoder::try_from(file).expect("failed to decode audio file");// append the audio source to the sink & auto playsink.append(source);// sleep for a while to let the audio playthread::sleep(Duration::from_secs(20));// pause the audio playback explicitlysink.pause();// sleep for a whilethread::sleep(Duration::from_secs(20));// resume the audio playback explicitlysink.play();// keep the main thread alive while the audio is playingthread::sleep(Duration::from_secs(20));
}
代码及主要API解读
- stream_handle 是音频流句柄,直接跟硬件相关
- rodio::Sink::connect_new 连接到音频流,返回一个Sink对象,是输出到stream_handle对应device的“音频管理器”
- Decoder::try_from(file) 尝试解码音频文件
- sink.append 向音频管理器中添加source并立刻自动启动播放
- sink.pause 显式停止音频播放
- sink.play 显式恢复音频播放
- sink.clear 清除sink中存储的所有source,释放资源(这里并未用到)
注意
执行上述代码,会:
- 播放20秒音频
- 停止20秒
- 再播放20秒音频
- 程序退出
如果sink.append之后没有thread::sleep,程序会立刻结束,任何声音都不会被播放,这是因为,根据Rust变量的生命周期,stream_handle变量会在main函数右括号}
处立刻释放,由于stream_handle管理了计算机音频输出设备硬件资源,当它超出生命周期被释放时,与之关联的任何音频播放(也就是sink中存在的所有source)都会被强制停止,这是Rodio库为了保护硬件资源做出的一个设计,大大减小了硬件不可控事件的出现。
还有一些额外的事情需要注意:
- stream_handle直接持有了硬件资源,因此它不是线程安全的,无法在线程之间安全传递
- sink借助stream_handle操控音频播放,因此stream_handle的生命周期一定长于sink
- sink是线程安全的,可以在线程之间安全传递,后面制作音乐播放器时会大量用到这个特性,它能同时存在于UI线程与后台线程中,只要确保stream_handle的生命周期长于sink。