Skip to main content

fission_shell_winit/
microphone.rs

1use fission_core::{
2    MicrophoneAvailability, MicrophoneCapture, MicrophoneCaptureRequest, MicrophoneDevice,
3    MicrophoneError, MicrophonePermission, MicrophonePermissionRequest, CANCEL_MICROPHONE_CAPTURE,
4    CAPTURE_MICROPHONE_AUDIO, GET_MICROPHONE_AVAILABILITY, REQUEST_MICROPHONE_PERMISSION,
5};
6use fission_shell::async_host::AsyncRegistry;
7use std::sync::Arc;
8
9/// Host-side microphone provider.
10pub trait MicrophoneHost: Send + Sync + 'static {
11    /// Returns microphone permission state and available input devices.
12    fn availability(&self) -> Result<MicrophoneAvailability, MicrophoneError>;
13    /// Requests microphone permission and returns the resulting state.
14    fn request_permission(
15        &self,
16        request: MicrophonePermissionRequest,
17    ) -> Result<MicrophonePermission, MicrophoneError>;
18    /// Captures bounded audio using the requested device and audio format preferences.
19    fn capture_audio(
20        &self,
21        request: MicrophoneCaptureRequest,
22    ) -> Result<MicrophoneCapture, MicrophoneError>;
23    /// Cancels an active microphone capture flow.
24    fn cancel_capture(&self) -> Result<(), MicrophoneError>;
25}
26
27#[derive(Debug, Default)]
28pub struct UnsupportedMicrophoneHost;
29
30impl MicrophoneHost for UnsupportedMicrophoneHost {
31    fn availability(&self) -> Result<MicrophoneAvailability, MicrophoneError> {
32        Ok(MicrophoneAvailability {
33            permission: MicrophonePermission::Denied,
34            devices: Vec::new(),
35        })
36    }
37
38    fn request_permission(
39        &self,
40        _request: MicrophonePermissionRequest,
41    ) -> Result<MicrophonePermission, MicrophoneError> {
42        Err(MicrophoneError::unsupported("request_permission"))
43    }
44
45    fn capture_audio(
46        &self,
47        _request: MicrophoneCaptureRequest,
48    ) -> Result<MicrophoneCapture, MicrophoneError> {
49        Err(MicrophoneError::unsupported("capture_audio"))
50    }
51
52    fn cancel_capture(&self) -> Result<(), MicrophoneError> {
53        Err(MicrophoneError::unsupported("cancel_capture"))
54    }
55}
56
57#[derive(Debug, Clone)]
58pub struct MemoryMicrophoneHost {
59    availability: MicrophoneAvailability,
60    capture: MicrophoneCapture,
61}
62
63impl MemoryMicrophoneHost {
64    pub fn new(availability: MicrophoneAvailability, capture: MicrophoneCapture) -> Self {
65        Self {
66            availability,
67            capture,
68        }
69    }
70}
71
72impl Default for MemoryMicrophoneHost {
73    fn default() -> Self {
74        Self::new(
75            MicrophoneAvailability {
76                permission: MicrophonePermission::Granted,
77                devices: vec![MicrophoneDevice {
78                    id: "memory-mic".into(),
79                    label: Some("Memory microphone".into()),
80                    is_default: true,
81                }],
82            },
83            MicrophoneCapture {
84                bytes: vec![0, 1, 2, 3],
85                content_type: "audio/pcm".into(),
86                sample_rate_hz: 48_000,
87                channels: 1,
88                duration_ms: 1_000,
89                device_id: Some("memory-mic".into()),
90            },
91        )
92    }
93}
94
95impl MicrophoneHost for MemoryMicrophoneHost {
96    fn availability(&self) -> Result<MicrophoneAvailability, MicrophoneError> {
97        Ok(self.availability.clone())
98    }
99
100    fn request_permission(
101        &self,
102        _request: MicrophonePermissionRequest,
103    ) -> Result<MicrophonePermission, MicrophoneError> {
104        Ok(self.availability.permission)
105    }
106
107    fn capture_audio(
108        &self,
109        _request: MicrophoneCaptureRequest,
110    ) -> Result<MicrophoneCapture, MicrophoneError> {
111        Ok(self.capture.clone())
112    }
113
114    fn cancel_capture(&self) -> Result<(), MicrophoneError> {
115        Ok(())
116    }
117}
118
119pub(crate) fn register_microphone_capabilities(
120    async_registry: &mut AsyncRegistry,
121    host: Arc<dyn MicrophoneHost>,
122) {
123    let availability_host = host.clone();
124    async_registry.register_operation_capability(GET_MICROPHONE_AVAILABILITY, move |(), _| {
125        let host = availability_host.clone();
126        async move { host.availability() }
127    });
128
129    let permission_host = host.clone();
130    async_registry.register_operation_capability(
131        REQUEST_MICROPHONE_PERMISSION,
132        move |request, _| {
133            let host = permission_host.clone();
134            async move { host.request_permission(request) }
135        },
136    );
137
138    let capture_host = host.clone();
139    async_registry.register_operation_capability(CAPTURE_MICROPHONE_AUDIO, move |request, _| {
140        let host = capture_host.clone();
141        async move { host.capture_audio(request) }
142    });
143
144    async_registry.register_operation_capability(CANCEL_MICROPHONE_CAPTURE, move |(), _| {
145        let host = host.clone();
146        async move { host.cancel_capture() }
147    });
148}
149
150#[cfg(test)]
151mod tests {
152    use super::*;
153
154    #[test]
155    fn unsupported_host_reports_errors() {
156        let host = UnsupportedMicrophoneHost;
157        assert!(host
158            .capture_audio(MicrophoneCaptureRequest::default())
159            .is_err());
160    }
161
162    #[test]
163    fn memory_host_returns_audio_capture() {
164        let host = MemoryMicrophoneHost::default();
165        let availability = host.availability().unwrap();
166        assert_eq!(availability.permission, MicrophonePermission::Granted);
167
168        let capture = host
169            .capture_audio(MicrophoneCaptureRequest::default())
170            .unwrap();
171        assert_eq!(capture.content_type, "audio/pcm");
172        assert_eq!(capture.sample_rate_hz, 48_000);
173    }
174}