settings/audio/
types.rs

1// Copyright 2021 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use crate::audio::audio_default_settings::{create_default_audio_stream, AudioInfoLoader};
6use crate::audio::{create_default_modified_counters, ModifiedCounters};
7use crate::base::SettingType;
8use anyhow::{anyhow, Error};
9use serde::{Deserialize, Serialize};
10use settings_storage::device_storage::DeviceStorageCompatible;
11use std::collections::HashMap;
12use std::ops::RangeInclusive;
13
14const RANGE: RangeInclusive<f32> = 0.0..=1.0;
15
16#[derive(PartialEq, Eq, Debug, Clone, Copy, Serialize, Deserialize)]
17pub enum AudioSettingSource {
18    User,
19    System,
20    SystemWithFeedback,
21}
22
23#[derive(PartialEq, Debug, Clone, Copy, Serialize, Deserialize, Hash, Eq)]
24pub enum AudioStreamType {
25    Background,
26    Media,
27    Interruption,
28    SystemAgent,
29    Communication,
30    Accessibility,
31}
32
33pub(crate) const AUDIO_STREAM_TYPE_COUNT: usize = 6;
34pub const LEGACY_AUDIO_STREAM_TYPE_COUNT: usize = 5;
35
36impl AudioStreamType {
37    /// Legacy stream types are the subset of AudioStreamType values which correspond to values in
38    /// |fuchsia.media.AudioRenderUsage|. FIDL tables |AudioSettings| and |AudioStreamSettings|,
39    /// and FIDL methods |Set| and |Watch|, are limited to these stream types.
40    ///
41    /// |fuchsia.media.AudioRenderUsage2| contains all the |AudioRenderUsage| values, and more.
42    /// |AudioStreamType| is based on |AudioRenderUsage2|, and |AudioRenderUsage2| is used with
43    /// tables |AudioSettings2| and |AudioStreamSettings2|, and with methods |Set2| and |Watch2|.
44    pub fn is_legacy(&self) -> bool {
45        match *self {
46            AudioStreamType::Background
47            | AudioStreamType::Communication
48            | AudioStreamType::Interruption
49            | AudioStreamType::Media
50            | AudioStreamType::SystemAgent => true,
51            _ => false,
52        }
53    }
54}
55
56#[derive(PartialEq, Debug, Clone, Copy, Serialize, Deserialize)]
57pub struct AudioStream {
58    pub stream_type: AudioStreamType,
59    pub source: AudioSettingSource,
60    pub user_volume_level: f32,
61    pub user_volume_muted: bool,
62}
63
64impl AudioStream {
65    pub(crate) fn has_valid_volume_level(&self) -> bool {
66        RANGE.contains(&self.user_volume_level)
67    }
68}
69
70#[derive(PartialEq, Debug, Clone, Copy)]
71pub struct SetAudioStream {
72    pub stream_type: AudioStreamType,
73    pub source: AudioSettingSource,
74    pub user_volume_level: Option<f32>,
75    pub user_volume_muted: Option<bool>,
76}
77
78impl SetAudioStream {
79    pub(crate) fn has_valid_volume_level(&self) -> bool {
80        self.user_volume_level.map(|v| RANGE.contains(&v)).unwrap_or(true)
81    }
82
83    pub(crate) fn is_valid_payload(&self) -> bool {
84        self.user_volume_level.is_some() || self.user_volume_muted.is_some()
85    }
86}
87
88impl From<AudioStream> for SetAudioStream {
89    fn from(stream: AudioStream) -> Self {
90        Self {
91            stream_type: stream.stream_type,
92            source: stream.source,
93            user_volume_level: Some(stream.user_volume_level),
94            user_volume_muted: Some(stream.user_volume_muted),
95        }
96    }
97}
98
99#[derive(PartialEq, Debug, Clone, Serialize, Deserialize)]
100pub struct AudioInfo {
101    pub streams: [AudioStream; AUDIO_STREAM_TYPE_COUNT],
102    pub modified_counters: Option<ModifiedCounters>,
103}
104
105impl From<&AudioInfo> for SettingType {
106    fn from(_: &AudioInfo) -> SettingType {
107        SettingType::Audio
108    }
109}
110
111impl DeviceStorageCompatible for AudioInfo {
112    type Loader = AudioInfoLoader;
113    const KEY: &'static str = "audio_info";
114
115    fn try_deserialize_from(value: &str) -> Result<Self, Error> {
116        Self::extract(value).or_else(|_| AudioInfoV3::try_deserialize_from(value).map(Self::from))
117    }
118}
119
120////////////////////////////////////////////////////////////////
121/// Past versions of AudioInfo.
122////////////////////////////////////////////////////////////////
123
124/// The following struct should never be modified. It represents an old
125/// version of the audio settings.
126#[derive(PartialEq, Debug, Clone, Serialize, Deserialize)]
127pub struct AudioInfoV3 {
128    pub streams: [AudioStream; LEGACY_AUDIO_STREAM_TYPE_COUNT],
129    pub modified_counters: Option<ModifiedCounters>,
130}
131
132impl AudioInfoV3 {
133    pub(super) fn try_deserialize_from(value: &str) -> Result<Self, Error> {
134        serde_json::from_str(value)
135            .map_err(|e| anyhow!("could not deserialize: {e:?}"))
136            .or_else(|_| AudioInfoV2::try_deserialize_from(value).map(Self::from))
137    }
138
139    #[cfg(test)]
140    pub(super) fn default_value(default_setting: AudioInfo) -> Self {
141        AudioInfoV3 {
142            streams: default_setting.streams[0..LEGACY_AUDIO_STREAM_TYPE_COUNT].try_into().unwrap(),
143            modified_counters: None,
144        }
145    }
146}
147
148impl From<AudioInfoV3> for AudioInfo {
149    fn from(v3: AudioInfoV3) -> AudioInfo {
150        let mut stream_vec = v3.streams.to_vec();
151        stream_vec.push(create_default_audio_stream(AudioStreamType::Accessibility));
152
153        AudioInfo {
154            streams: stream_vec.try_into().unwrap(),
155            modified_counters: v3.modified_counters,
156        }
157    }
158}
159
160#[derive(PartialEq, Eq, Debug, Clone, Copy, Serialize, Deserialize)]
161pub struct AudioInputInfo {
162    pub mic_mute: bool,
163}
164
165/// The following struct should never be modified. It represents an old
166/// version of the audio settings.
167#[derive(PartialEq, Debug, Clone, Serialize, Deserialize)]
168pub struct AudioInfoV2 {
169    pub streams: [AudioStream; LEGACY_AUDIO_STREAM_TYPE_COUNT],
170    pub input: AudioInputInfo,
171    pub modified_counters: Option<ModifiedCounters>,
172}
173
174impl AudioInfoV2 {
175    pub(super) fn try_deserialize_from(value: &str) -> Result<Self, Error> {
176        serde_json::from_str(value)
177            .map_err(|e| anyhow!("could not deserialize: {e:?}"))
178            .or_else(|_| AudioInfoV1::try_deserialize_from(value).map(Self::from))
179    }
180
181    #[cfg(test)]
182    pub(super) fn default_value(default_setting: AudioInfo) -> Self {
183        AudioInfoV2 {
184            streams: default_setting.streams[0..LEGACY_AUDIO_STREAM_TYPE_COUNT].try_into().unwrap(),
185            input: AudioInputInfo { mic_mute: false },
186            modified_counters: None,
187        }
188    }
189}
190
191impl From<AudioInfoV2> for AudioInfoV3 {
192    fn from(v2: AudioInfoV2) -> AudioInfoV3 {
193        AudioInfoV3 { streams: v2.streams, modified_counters: v2.modified_counters }
194    }
195}
196
197/// The following struct should never be modified. It represents an old
198/// version of the audio settings.
199#[derive(PartialEq, Debug, Clone, Serialize, Deserialize)]
200pub struct AudioInfoV1 {
201    pub streams: [AudioStream; LEGACY_AUDIO_STREAM_TYPE_COUNT],
202    pub input: AudioInputInfo,
203    pub modified_timestamps: Option<HashMap<AudioStreamType, String>>,
204}
205
206impl AudioInfoV1 {
207    pub(super) fn try_deserialize_from(value: &str) -> Result<Self, Error> {
208        serde_json::from_str(value)
209            .map_err(|e| anyhow!("could not deserialize: {e:?}"))
210            .or_else(|_| AudioInfoV1::try_deserialize_from(value).map(Self::from))
211    }
212
213    #[cfg(test)]
214    pub(super) fn default_value(default_setting: AudioInfo) -> Self {
215        AudioInfoV1 {
216            streams: default_setting.streams[0..LEGACY_AUDIO_STREAM_TYPE_COUNT].try_into().unwrap(),
217            input: AudioInputInfo { mic_mute: false },
218            modified_timestamps: None,
219        }
220    }
221}
222
223impl From<AudioInfoV1> for AudioInfoV2 {
224    fn from(v1: AudioInfoV1) -> Self {
225        AudioInfoV2 {
226            streams: v1.streams,
227            input: v1.input,
228            modified_counters: Some(create_default_modified_counters()),
229        }
230    }
231}
232
233#[cfg(test)]
234mod tests {
235    use super::*;
236
237    const VALID_AUDIO_STREAM: AudioStream = AudioStream {
238        stream_type: AudioStreamType::Background,
239        source: AudioSettingSource::User,
240        user_volume_level: 0.5,
241        user_volume_muted: false,
242    };
243    const INVALID_NEGATIVE_AUDIO_STREAM: AudioStream =
244        AudioStream { user_volume_level: -0.1, ..VALID_AUDIO_STREAM };
245    const INVALID_GREATER_THAN_ONE_AUDIO_STREAM: AudioStream =
246        AudioStream { user_volume_level: 1.1, ..VALID_AUDIO_STREAM };
247
248    #[fuchsia::test]
249    fn test_volume_level_validation() {
250        assert!(VALID_AUDIO_STREAM.has_valid_volume_level());
251        assert!(!INVALID_NEGATIVE_AUDIO_STREAM.has_valid_volume_level());
252        assert!(!INVALID_GREATER_THAN_ONE_AUDIO_STREAM.has_valid_volume_level());
253    }
254
255    #[fuchsia::test]
256    fn test_set_audio_stream_validation() {
257        let valid_set_audio_stream = SetAudioStream::from(VALID_AUDIO_STREAM);
258        assert!(valid_set_audio_stream.has_valid_volume_level());
259        let invalid_set_audio_stream = SetAudioStream::from(INVALID_NEGATIVE_AUDIO_STREAM);
260        assert!(!invalid_set_audio_stream.has_valid_volume_level());
261        let invalid_set_audio_stream = SetAudioStream::from(INVALID_GREATER_THAN_ONE_AUDIO_STREAM);
262        assert!(!invalid_set_audio_stream.has_valid_volume_level());
263    }
264}