我在使用此音频库理解 Rust 生命周期时遇到问题

问题描述 投票:0回答:1

我有多年的编程经验,但我对 Rust 很陌生。在理解如何在特定场景中使用引用时遇到问题,而编译器却没有告诉我我做错了。

就上下文而言,我正在通过构建一个小型软件合成器来学习 Rust。我使用 egui 作为 UI,使用 rodio 作为音频。

所以,我有一个可以发出声音的振荡器。我将其传递给 rodio 库来播放声音。它通过迭代器(和其他一些东西)来做到这一点,但最重要的部分在底部

return Some(self.get_sample());

pub struct GeneralOscillator {
    ...
    envelope: EnvelopeADSR,
}


impl GeneralOscillator {

pub fn new(...stuff...) -> GeneralOscillator {
    let mut oscillator = GeneralOscillator{
        ...
        envelope: EnvelopeADSR::new(),
    };

    ...
    return oscillator;
}

fn note_pressed(&mut self){
    self.envelope.note_on(time::get_time());
}

pub fn note_released(&mut self){
    self.envelope.note_off(time::get_time());
}

pub fn get_amplitude(&self) -> f32 {
    return self.envelope.get_amplitude(time::get_time());
}

pub fn get_sample(&mut self) -> f32 {
    return self.get_amplitude() * self.note_oscillator.get_sample();
}

}

impl Iterator for GeneralOscillator {
type Item = f32;

fn next(&mut self) -> Option<f32>{
    return Some(self.get_sample());
}
}

我需要与该振荡器沟通某个键已被按下或释放。当按下一个音符时,我创建一个振荡器并将其传递给 rodio

Sink
,如下所示:

let osc = GeneralOscillator::new(freq, 44100, wave_table);
self.sink.append(osc);
self.sink.play();

这会导致音符正确播放,并且包络可以正确处理起音/衰减/延音。

我的问题是让振荡器知道我已经释放了按键。从概念上来说,我看到两个选择。我的第一个想法是我想在某处保留对振荡器的引用,以便在释放按键时我可以执行

oscillator.note_released()
。不过,我将振荡器移至
sink.append(osc)
,所以我相信 Rust 不会允许我做类似的事情。

我的第二个想法是我想在振荡器上保留对

something
的引用。每次调用
next()
然后我都可以检查该键是否被释放,类似于(比这更聪明,但概念上有一个对 GeneralOscillator 上其他对象的引用,它可以检查该键是否被释放):

fn next(&mut self) -> Option<f32>{
    if self.key_event_handler.key_released() {
        self.envelope.note_released();
    }
    return Some(self.get_sample());
}

我的理解是,由于所有权规则,选项 1 几乎是不可能的。对于 2,我一直遇到生命周期的问题。

我的应用程序有一个带有更新循环的顶级结构。在该应用程序结构上,我可以存储一些了解按键按下/释放状态的信息(为了示例,此处仅称为

KeyChecker
)。我知道这个结构将在程序的整个存在期间存在,因为它包含应用程序循环和所有数据。

struct OxidizerApp {
    ... stuff ...

    key_checker: KeyChecker
}

在更新循环中我尝试执行以下操作

impl App for OxidizerApp {
    fn update(&mut self, ... ) {
        ... stuff

        self.start_stop_playing();

    }

    fn start_stop_playing (&mut self) {
        ... stuff
        let osc = GeneralOscillator::new(freq, 44100, wave_table, &self.key_checker);

        self.sink.append(osc);
        self.sink.play();
    }
}

最后 - 错误:

error[E0521]: borrowed data escapes outside of method
 --> src\main.rs:153:13
|
82  |     fn start_stop_playing(&mut self){
|                           ---------
|                           |
|                           `self` is a reference that is only valid in the method body
|                           let's call the lifetime of this reference `'1`
...
153 |             self.sink.append(osc);
|             ^^^^^^^^^^^^^^^^^^^^^
|             |
|             `self` escapes the method body here
|             argument requires that `'1` must outlive `'static`

我的理解是,由于某种原因,当

self.sink()
取得振荡器的所有权时,它强制其上存在的任何引用都是“静态生命周期”。我知道
&self.key_checker
的生命周期对于所有意图和目的来说都是静态的,但我想编译器无法确定这一点。

对于我在这里做错了什么或误解有什么建议吗?我对此消息的天真解释是,因为更新循环位于

OxidizerApp
上,并且我必须将所有数据存储在该结构上,所以每当我使用像
self
这样对
&self.key_checker
的引用时,它都会抱怨因为
self is a reference that is only valid in the method body
错误?

我的主要功能本质上就是这个(以防有什么不同),其中

run_native
等用于 egui UI 框架。

fn main() -> Result<(), eframe::Error> {
    let (_stream, stream_handle) = OutputStream::try_default().expect("Failed to create output stream");
    let sink = Sink::try_new(&stream_handle).unwrap();

    return run_native(
        "Test App", 
        options, 
        Box::new(|_cc| {
            let app = OxidizerApp::default(sink, &WAVE_TABLES);
            return Box::<OxidizerApp>::new(app);
        }));
}

任何指导或帮助将不胜感激!

rust lifetime borrow-checker egui rodio
1个回答
0
投票

我从 Reddit 上的帖子中收到了答案。我正在寻找的是 std::sync::mpsc。

它让我建立一个通道,通过发送器和接收器从主应用程序到振荡器进行通信。

© www.soinside.com 2019 - 2024. All rights reserved.