Skip to main content

proteus_lib/container/play_settings/
mod.rs

1//! Serde models for `play_settings.json` with versioned decoding.
2
3use log::{info, warn};
4use serde::{Deserialize, Deserializer, Serialize, Serializer};
5
6use crate::dsp::effects::AudioEffect;
7#[allow(deprecated)]
8#[deprecated(note = "Use DelayReverbSettings instead.")]
9pub use crate::dsp::effects::BasicReverbSettings;
10pub use crate::dsp::effects::{
11    CompressorSettings, ConvolutionReverbSettings, DelayReverbSettings, DistortionSettings,
12    EqPointSettings, HighEdgeFilterSettings, HighPassFilterSettings, LimiterSettings,
13    LowEdgeFilterSettings, LowPassFilterSettings, MultibandEqSettings,
14};
15
16pub mod legacy;
17pub mod v1;
18pub mod v2;
19pub mod v3;
20
21pub use legacy::{PlaySettingsLegacy, PlaySettingsLegacyFile, PlaySettingsTrackLegacy};
22pub use v1::{PlaySettingsV1, PlaySettingsV1File};
23pub use v2::{PlaySettingsV2, PlaySettingsV2File};
24pub use v3::{PlaySettingsV3, PlaySettingsV3File};
25
26/// Effect settings variants that can appear in the settings file.
27pub type EffectSettings = AudioEffect;
28
29/// Track-level configuration shared by newer settings versions.
30#[derive(Debug, Clone, Serialize, Deserialize)]
31pub struct SettingsTrack {
32    pub level: f32,
33    pub pan: f32,
34    pub ids: Vec<u32>,
35    pub name: String,
36    pub safe_name: String,
37    #[serde(default = "default_selections_count")]
38    pub selections_count: u32,
39    #[serde(default)]
40    pub shuffle_points: Vec<String>,
41}
42
43fn default_selections_count() -> u32 {
44    1
45}
46
47/// Wrapper allowing `play_settings` to be nested or flat.
48#[derive(Debug, Clone, Serialize, Deserialize)]
49#[serde(untagged)]
50pub enum PlaySettingsContainer<T> {
51    Nested { play_settings: T },
52    Flat(T),
53}
54
55impl<T> PlaySettingsContainer<T> {
56    /// Return the inner settings payload, regardless of nesting.
57    pub fn inner(&self) -> &T {
58        match self {
59            PlaySettingsContainer::Nested { play_settings } => play_settings,
60            PlaySettingsContainer::Flat(inner) => inner,
61        }
62    }
63
64    /// Return mutable access to the inner settings payload.
65    pub fn inner_mut(&mut self) -> &mut T {
66        match self {
67            PlaySettingsContainer::Nested { play_settings } => play_settings,
68            PlaySettingsContainer::Flat(inner) => inner,
69        }
70    }
71}
72
73/// Versioned settings file representation.
74#[derive(Debug, Clone)]
75pub enum PlaySettingsFile {
76    Legacy(PlaySettingsLegacyFile),
77    V1(PlaySettingsV1File),
78    V2(PlaySettingsV2File),
79    V3(PlaySettingsV3File),
80    Unknown {
81        encoder_version: Option<String>,
82        raw: serde_json::Value,
83    },
84}
85
86impl PlaySettingsFile {
87    /// Get the encoder version string, if known.
88    pub fn encoder_version(&self) -> Option<&str> {
89        match self {
90            PlaySettingsFile::Legacy(_) => None,
91            PlaySettingsFile::V1(_) => Some("1"),
92            PlaySettingsFile::V2(_) => Some("2"),
93            PlaySettingsFile::V3(_) => Some("3"),
94            PlaySettingsFile::Unknown {
95                encoder_version, ..
96            } => encoder_version.as_deref(),
97        }
98    }
99}
100
101impl<'de> Deserialize<'de> for PlaySettingsFile {
102    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
103    where
104        D: Deserializer<'de>,
105    {
106        let value = serde_json::Value::deserialize(deserializer)?;
107        let encoder_version = value.get("encoder_version").and_then(|raw| match raw {
108            serde_json::Value::String(version) => Some(version.clone()),
109            serde_json::Value::Number(number) => number
110                .as_f64()
111                .map(|val| {
112                    if (val - 1.0).abs() < f64::EPSILON {
113                        "1".to_string()
114                    } else if (val - 2.0).abs() < f64::EPSILON {
115                        "2".to_string()
116                    } else if (val - 3.0).abs() < f64::EPSILON {
117                        "3".to_string()
118                    } else {
119                        number.to_string()
120                    }
121                })
122                .or_else(|| Some(number.to_string())),
123            _ => None,
124        });
125
126        info!("Encoder version: {:?}", encoder_version);
127
128        let parsed = match encoder_version.as_deref() {
129            None => serde_json::from_value::<PlaySettingsLegacyFile>(value.clone())
130                .map(PlaySettingsFile::Legacy),
131            Some("1") => serde_json::from_value::<PlaySettingsV1File>(value.clone())
132                .map(PlaySettingsFile::V1),
133            Some("2") => serde_json::from_value::<PlaySettingsV2File>(value.clone())
134                .map(PlaySettingsFile::V2),
135            Some("3") => serde_json::from_value::<PlaySettingsV3File>(value.clone())
136                .map(PlaySettingsFile::V3),
137            Some(version) => {
138                warn!("Unknown encoder version: {:?}", version);
139                return Ok(PlaySettingsFile::Unknown {
140                    encoder_version,
141                    raw: value,
142                });
143            }
144        };
145
146        parsed.or_else(|_| {
147            Ok(PlaySettingsFile::Unknown {
148                encoder_version,
149                raw: value,
150            })
151        })
152    }
153}
154
155impl Serialize for PlaySettingsFile {
156    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
157    where
158        S: Serializer,
159    {
160        fn with_version<T, S>(payload: &T, version: &str, serializer: S) -> Result<S::Ok, S::Error>
161        where
162            T: Serialize,
163            S: Serializer,
164        {
165            let mut value = serde_json::to_value(payload).map_err(serde::ser::Error::custom)?;
166            match value {
167                serde_json::Value::Object(ref mut map) => {
168                    map.insert(
169                        "encoder_version".to_string(),
170                        serde_json::Value::String(version.to_string()),
171                    );
172                }
173                other => {
174                    let mut map = serde_json::Map::new();
175                    map.insert(
176                        "encoder_version".to_string(),
177                        serde_json::Value::String(version.to_string()),
178                    );
179                    map.insert("play_settings".to_string(), other);
180                    value = serde_json::Value::Object(map);
181                }
182            }
183            value.serialize(serializer)
184        }
185
186        match self {
187            PlaySettingsFile::Legacy(file) => file.serialize(serializer),
188            PlaySettingsFile::V1(file) => with_version(file, "1", serializer),
189            PlaySettingsFile::V2(file) => with_version(file, "2", serializer),
190            PlaySettingsFile::V3(file) => with_version(file, "3", serializer),
191            PlaySettingsFile::Unknown { raw, .. } => raw.serialize(serializer),
192        }
193    }
194}