我正在使用glutin,所以我的程序的主循环有一个移动闭包,我试图用rodio机箱播放一个音频文件。使用下面的代码,一切正常工作,每次程序循环时,我都会听到一声嘟嘟声:
...
let sink: rodio::Sink = ...;
event_loop.run(move |event, _, control_flow | {
let sound_file = File::open("beep.ogg").unwrap();
let buf_wrap = BufReader::new(sound_file);
let source = rodio::Decoder::new(buf_wrap).unwrap();
sink.append(source);
...
});
...但是,这非常慢,因为每次打开同一个文件循环时都会打开它,所以我尝试用以下方法修复它:
...
let sound_file = File::open("beep.ogg").unwrap();
event_loop.run(move |event, _, control_flow | {
let buf_wrap = BufReader::new(sound_file);
...
});
...但是现在编译器提供了以下错误消息:
error[E0507]: cannot move out of `sound_file`, a captured variable in an `FnMut` closure
--> src/lib.rs:86:33
|
83 | let sound_file = File::open("beep.ogg").unwrap();
| ---------- captured outer variable
...
86 | let buf_wrap = BufReader::new(sound_file);
| ^^^^^^^^^^ move occurs because `sound_file` has type `File`, which does not implement the `Copy` trait我试着修复这个问题已经有一段时间了,但没有成功,任何洞察力都会受到极大的赞赏。
发布于 2021-06-15 16:27:05
问题
基本上,当前的问题是rodio::Decoder::new使用它读取的值(实际上,它已经被BufReader::new使用了)。因此,如果您有一个循环或一个可以多次调用的闭包,那么每次都必须得到一个新的值。这是File::open在您的第一个代码片段中所做的事情。
在第二个代码片段中,您只创建一次File,然后尝试多次使用它,这是Rust的所有权概念阻止您这样做的。
还请注意,遗憾的是,在rodio中使用引用并不是一种选择,因为解码器必须是'static (例如,请参见绑定在S上的Sink::append特性)。
解决方案
如果您认为您的文件系统有点慢,并且希望对其进行优化,那么您可能实际上希望预先读取整个文件( File::open不需要这样做)。这样做还应该为您提供一个可以克隆的缓冲区(例如Vec<u8>),从而允许重复创建可由Decoder使用的新值。下面是一个这样做的示例:
use std::io::Read;
let sink: rodio::Sink = /*...*/;
// Read file up-front
let mut data = Vec::new();
let mut sound_file = File::open("beep.ogg").unwrap();
sound_file.read_to_end(&mut data).unwrap();
event_loop.run(move |event, _, control_flow | {
// Copies the now in-memory file content
let cloned_data = data.clone();
// Give the data a `std::io::Read` impl via the `std::io::Cursor` wrapper
let buffered_file = Cursor::new(cloned_data);
let source = rodio::Decoder::new(buffered_file).unwrap();
sink.append(source);
});然而,在我个人使用rodio的经验中,在Decoder中仍有相当多的处理工作,所以我也会预先进行解码,并使用rodio::source::Buffered包装器,如下所示:
use std::io::Read;
let sink: rodio::Sink = /*...*/;
// Read & decode file
let sound_file = File::open("beep.ogg").unwrap();
let source = rodio::Decoder::new(file).unwrap();
// store the decoded audio in a buffer
let source_buffered = source.buffered();
// At least in my version, this buffer is lazyly initialized,
// So, maybe, you also want to initialize it here buffer, e.g. via:
//source_buffered.clone().for_each(|_| {})
event_loop.run(move |event, _, control_flow | {
// Just copy the in-memory decoded buffer and play it
sink.append(source_buffered.clone());
});如果在多线程环境中使用这种方法,或者就像静力学一样,也可以使用lazy_static机箱使这些rodio::source::Buffered实例在整个程序中可用,同时只进行一次初始化。
https://stackoverflow.com/questions/67988070
复制相似问题