sbz_switch/soundcore/
parameter.rs

1use std::mem::{self, MaybeUninit};
2use std::str;
3
4use slog::Logger;
5
6use winapi::shared::winerror::E_ACCESSDENIED;
7
8use crate::com::ComObject;
9use crate::ctsndcr::{ISoundCore, Param, ParamInfo, ParamValue};
10use crate::hresult::{check, Win32Error};
11
12/// Captures the value of a parameter.
13#[derive(Clone, Copy, Debug)]
14pub enum SoundCoreParamValue {
15    /// A floating point value
16    Float(f32),
17    /// A boolean value
18    Bool(bool),
19    /// An unsigned integer value
20    U32(u32),
21    /// A signed integer value
22    I32(i32),
23    /// No value
24    None,
25}
26
27/// Represents a parameter of a feature.
28#[derive(Debug)]
29pub struct SoundCoreParameter {
30    core: ComObject<ISoundCore>,
31    logger: Logger,
32    context: u32,
33    feature_id: u32,
34    feature_description: String,
35    /// A numeric ID for this value
36    pub id: u32,
37    /// The kind of the value
38    pub kind: u32,
39    /// The size of the value, or `None`
40    pub size: Option<u32>,
41    /// The minimum acceptable value, or `None`
42    pub min_value: SoundCoreParamValue,
43    /// The maximum acceptable value, or `None`
44    pub max_value: SoundCoreParamValue,
45    /// The distance between acceptable values, or `None`
46    pub step_size: SoundCoreParamValue,
47    /// Parameter attributes
48    pub attributes: u32,
49    /// A description of the parameter
50    pub description: String,
51}
52
53impl SoundCoreParameter {
54    pub(crate) fn new(
55        core: ComObject<ISoundCore>,
56        feature_description: String,
57        logger: Logger,
58        info: &ParamInfo,
59    ) -> Self {
60        let description_length = info
61            .description
62            .iter()
63            .position(|i| *i == 0)
64            .unwrap_or(info.description.len());
65        Self {
66            core,
67            context: info.param.context,
68            feature_id: info.param.feature,
69            feature_description,
70            logger,
71            id: info.param.param,
72            description: str::from_utf8(&info.description[0..description_length])
73                .unwrap()
74                .to_owned(),
75            attributes: info.param_attributes,
76            kind: info.param_type,
77            size: match info.param_type {
78                5 => Some(info.data_size),
79                _ => None,
80            },
81            min_value: convert_param_value(&info.min_value),
82            max_value: convert_param_value(&info.max_value),
83            step_size: convert_param_value(&info.step_size),
84        }
85    }
86    /// Gets the value of a parameter.
87    ///
88    /// May return `Err(Win32Error { code: E_ACCESSDENIED })` when getting a
89    /// parameter that is not currently applicable.
90    pub fn get(&self) -> Result<SoundCoreParamValue, Win32Error> {
91        // varsize -> not supported
92        if self.kind == 5 {
93            return Ok(SoundCoreParamValue::None);
94        }
95        unsafe {
96            let param = Param {
97                context: self.context,
98                feature: self.feature_id,
99                param: self.id,
100            };
101            let mut value = MaybeUninit::uninit();
102            trace!(
103                self.logger,
104                "Fetching parameter value .{}.{}.{}...",
105                self.context,
106                self.feature_id,
107                self.id
108            );
109            match check(self.core.GetParamValue(param, value.as_mut_ptr())) {
110                Ok(_) => {}
111                Err(Win32Error { code, .. }) if code == E_ACCESSDENIED => {
112                    trace!(
113                        self.logger,
114                        "Got parameter value .{}.{}.{} = {}",
115                        self.context,
116                        self.feature_id,
117                        self.id,
118                        "ACCESSDENIED"
119                    );
120                    return Ok(SoundCoreParamValue::None);
121                }
122                Err(error) => return Err(error),
123            };
124            let value = value.assume_init();
125            trace!(
126                self.logger,
127                "Got parameter value .{}.{}.{} = {:?}",
128                self.context,
129                self.feature_id,
130                self.id,
131                value
132            );
133            Ok(convert_param_value(&value))
134        }
135    }
136    /// Sets the value of a parameter.
137    ///
138    /// May return `Err(Win32Error { code: E_ACCESSDENIED })` when setting a
139    /// parameter that is not currently applicable.
140    pub fn set(&mut self, value: &SoundCoreParamValue) -> Result<(), Win32Error> {
141        unsafe {
142            let param = Param {
143                context: self.context,
144                feature: self.feature_id,
145                param: self.id,
146            };
147            let param_value = ParamValue {
148                kind: match *value {
149                    SoundCoreParamValue::Float(_) => 0,
150                    SoundCoreParamValue::Bool(_) => 1,
151                    SoundCoreParamValue::U32(_) => 2,
152                    SoundCoreParamValue::I32(_) => 3,
153                    _ => panic!("tried to set parameter with nothing"),
154                },
155                value: match *value {
156                    SoundCoreParamValue::Float(f) => f.to_bits(),
157                    SoundCoreParamValue::Bool(b) => {
158                        if b {
159                            0xffff_ffff
160                        } else {
161                            0
162                        }
163                    }
164                    SoundCoreParamValue::U32(u) => u,
165                    SoundCoreParamValue::I32(i) => mem::transmute(i),
166                    _ => panic!("tried to set parameter with nothing"),
167                },
168            };
169            info!(
170                self.logger,
171                "Setting {}.{} = {:?}", self.feature_description, self.description, value
172            );
173            check(self.core.SetParamValue(param, param_value))?;
174            Ok(())
175        }
176    }
177}
178
179fn convert_param_value(value: &ParamValue) -> SoundCoreParamValue {
180    unsafe {
181        match value.kind {
182            0 => SoundCoreParamValue::Float(f32::from_bits(value.value)),
183            1 => SoundCoreParamValue::Bool(value.value != 0),
184            2 => SoundCoreParamValue::U32(value.value),
185            3 => SoundCoreParamValue::I32(mem::transmute(value.value)),
186            _ => SoundCoreParamValue::None,
187        }
188    }
189}