如何在Rust中实现多个MP3文件的无缝播放?
问题:Rodio顺序播放MP3文件出现间隙
我从SoundCloud下载了一些MP3文件,希望按顺序播放它们,但使用Rodio库尝试时,音频之间总会出现微小的间隙。
使用的代码
use cpal::{traits::HostTrait, SampleRate, SupportedStreamConfig}; use rodio::{Decoder, Source}; use std::{fs::File, u64}; fn main() -> Result<(), Box<dyn std::error::Error>> { let default_device = cpal::default_host() .default_output_device() .ok_or("No default audio output device is found.")?; let (_stream, stream_handle) = rodio::OutputStream::try_from_device_config(&default_device, SupportedStreamConfig::new(1, SampleRate(44100), cpal::SupportedBufferSize::Range { min: 5000, max: 35000 }, cpal::SampleFormat::U64) )?; let sink = rodio::Sink::try_new(&stream_handle).unwrap(); let data1 = Decoder::new(File::open("1.mp3").unwrap()).unwrap().buffered(); let data2 = Decoder::new(File::open("2.mp3").unwrap()).unwrap().buffered(); let data3 = Decoder::new(File::open("3.mp3").unwrap()).unwrap().buffered(); sink.append(data1); sink.append(data2); sink.append(data3); std::thread::sleep(std::time::Duration::from_secs(u64::MAX)); Ok(()) }
依赖配置
rodio = {version="0.17",features=["symphonia-all"]}
我曾尝试使用minimp3后端,还试过Kira库,但由于不知道如何顺序播放文件,效果更差。请问该如何解决这个问题?
解决方案
1. 使用设备默认流配置而非硬编码
你手动创建的SupportedStreamConfig可能和设备实际支持的参数不匹配,引发缓冲异常。应该优先获取设备的默认配置:
let default_config = default_device.default_output_config()?; let (_stream, stream_handle) = rodio::OutputStream::try_from_device_config(&default_device, &default_config.into())?;
2. 预解码合并音频源
间隙的核心原因是Rodio切换音频源时,需要时间初始化新文件的解码流程。可以提前将所有音频解码为统一格式,合并成单个连续的Source:
use rodio::{Decoder, Source, OutputStream, Sink}; use std::{fs::File, io::BufReader}; fn load_audio(path: &str) -> impl Source<Item = f32> { let file = BufReader::new(File::open(path).unwrap()); Decoder::new(file).unwrap() .convert_samples() // 统一转换为f32样本格式 .buffered(1024 * 64) // 增大预缓冲容量 } fn main() -> Result<(), Box<dyn std::error::Error>> { let (_stream, stream_handle) = OutputStream::try_default()?; let sink = Sink::try_new(&stream_handle)?; // 合并所有音频源为连续流 let combined_source = load_audio("1.mp3") .chain(load_audio("2.mp3")) .chain(load_audio("3.mp3")); sink.append(combined_source); sink.sleep_until_end(); // 等待播放完成,替代无限sleep Ok(()) }
3. Kira顺序播放实现
如果想尝试Kira,可以通过播放回调实现无缝顺序播放:
use kira::{ manager::{AudioManager, AudioManagerSettings, backend::cpal::CpalBackend}, sound::SoundSettings, }; fn main() -> Result<(), Box<dyn std::error::Error>> { let mut manager = AudioManager::<CpalBackend>::new(AudioManagerSettings::default())?; // 链式回调实现顺序播放 let sound1 = manager.load_sound("1.mp3", SoundSettings::default())?; manager.play(sound1, |_| { let sound2 = manager.load_sound("2.mp3", SoundSettings::default()).unwrap(); manager.play(sound2, |_| { let sound3 = manager.load_sound("3.mp3", SoundSettings::default()).unwrap(); manager.play(sound3, |_| {}).unwrap(); }).unwrap(); })?; loop { manager.update()?; std::thread::sleep(std::time::Duration::from_millis(10)); } }
内容的提问来源于stack exchange,提问作者Bence
相关产品推荐
相关产品推荐

