proteus_lib/dsp/effects/convolution_reverb/
spec.rs1use crate::container::play_settings::{
4 ConvolutionReverbSettings, EffectSettings, PlaySettingsFile,
5};
6
7#[derive(Debug, Clone, PartialEq, Eq)]
9pub enum ImpulseResponseSpec {
10 Attachment(String),
11 FilePath(String),
12}
13
14pub(crate) fn parse_impulse_response_spec(
16 play_settings: &PlaySettingsFile,
17) -> Option<ImpulseResponseSpec> {
18 if let Some(settings) = parse_convolution_settings(play_settings) {
19 if let Some(spec) = parse_impulse_response_string_or_struct(&settings) {
20 return Some(spec);
21 }
22 }
23
24 None
25}
26
27pub(crate) fn parse_impulse_response_tail_db(play_settings: &PlaySettingsFile) -> Option<f32> {
29 if let Some(settings) = parse_convolution_settings(play_settings) {
30 if let Some(value) = settings.impulse_response_tail_db {
31 return Some(value);
32 }
33 if let Some(value) = settings.impulse_response_tail {
34 return Some(value);
35 }
36 }
37
38 None
39}
40
41fn parse_convolution_settings(
42 play_settings: &PlaySettingsFile,
43) -> Option<ConvolutionReverbSettings> {
44 let effects = match play_settings {
45 PlaySettingsFile::V1(file) => &file.settings.inner().effects,
46 PlaySettingsFile::V2(file) => &file.settings.inner().effects,
47 _ => return None,
48 };
49
50 for effect in effects {
51 if let EffectSettings::ConvolutionReverb(effect) = effect {
52 return Some(effect.settings.clone());
53 }
54 }
55
56 None
57}
58
59pub(crate) fn parse_impulse_response_string_or_struct(
60 settings: &ConvolutionReverbSettings,
61) -> Option<ImpulseResponseSpec> {
62 if let Some(value) = settings.impulse_response.as_deref() {
63 return parse_impulse_response_string(value);
64 }
65 if let Some(value) = settings.impulse_response_attachment.as_deref() {
66 return parse_impulse_response_string(value);
67 }
68 if let Some(value) = settings.impulse_response_path.as_deref() {
69 return parse_impulse_response_string(value);
70 }
71 None
72}
73
74pub fn parse_impulse_response_string(value: &str) -> Option<ImpulseResponseSpec> {
80 if let Some(attachment) = value.strip_prefix("attachment:") {
81 return Some(ImpulseResponseSpec::Attachment(
82 attachment.trim().to_string(),
83 ));
84 }
85
86 if let Some(path) = value.strip_prefix("file:") {
87 return Some(ImpulseResponseSpec::FilePath(path.trim().to_string()));
88 }
89
90 Some(ImpulseResponseSpec::FilePath(value.trim().to_string()))
91}
92
93#[cfg(test)]
94mod tests {
95 use super::*;
96 use crate::container::play_settings::{
97 PlaySettingsContainer, PlaySettingsFile, PlaySettingsV2, PlaySettingsV2File,
98 };
99 use crate::dsp::effects::{AudioEffect, ConvolutionReverbEffect};
100
101 #[test]
102 fn parse_impulse_response_string_variants() {
103 assert_eq!(
104 parse_impulse_response_string("attachment:foo.wav"),
105 Some(ImpulseResponseSpec::Attachment("foo.wav".to_string()))
106 );
107 assert_eq!(
108 parse_impulse_response_string("file:/tmp/bar.wav"),
109 Some(ImpulseResponseSpec::FilePath("/tmp/bar.wav".to_string()))
110 );
111 assert_eq!(
112 parse_impulse_response_string("plain.wav"),
113 Some(ImpulseResponseSpec::FilePath("plain.wav".to_string()))
114 );
115 }
116
117 #[test]
118 fn parse_impulse_response_from_play_settings() {
119 let mut effect = ConvolutionReverbEffect::default();
120 effect.settings.impulse_response = Some("attachment:ir.wav".to_string());
121 effect.settings.impulse_response_tail_db = Some(-42.0);
122
123 let settings = PlaySettingsV2 {
124 tracks: Vec::new(),
125 effects: vec![AudioEffect::ConvolutionReverb(effect)],
126 };
127 let file = PlaySettingsV2File {
128 settings: PlaySettingsContainer::Flat(settings),
129 };
130
131 let play_settings = PlaySettingsFile::V2(file);
132 let spec = parse_impulse_response_spec(&play_settings);
133 assert_eq!(
134 spec,
135 Some(ImpulseResponseSpec::Attachment("ir.wav".to_string()))
136 );
137 let tail_db = parse_impulse_response_tail_db(&play_settings);
138 assert_eq!(tail_db, Some(-42.0));
139 }
140}