use fission_core::{
MicrophoneAvailability, MicrophoneCapture, MicrophoneCaptureRequest, MicrophoneDevice,
MicrophoneError, MicrophonePermission, MicrophonePermissionRequest, CANCEL_MICROPHONE_CAPTURE,
CAPTURE_MICROPHONE_AUDIO, GET_MICROPHONE_AVAILABILITY, REQUEST_MICROPHONE_PERMISSION,
};
use fission_shell::async_host::AsyncRegistry;
use std::sync::Arc;
pub trait MicrophoneHost: Send + Sync + 'static {
fn availability(&self) -> Result<MicrophoneAvailability, MicrophoneError>;
fn request_permission(
&self,
request: MicrophonePermissionRequest,
) -> Result<MicrophonePermission, MicrophoneError>;
fn capture_audio(
&self,
request: MicrophoneCaptureRequest,
) -> Result<MicrophoneCapture, MicrophoneError>;
fn cancel_capture(&self) -> Result<(), MicrophoneError>;
}
#[derive(Debug, Default)]
pub struct UnsupportedMicrophoneHost;
impl MicrophoneHost for UnsupportedMicrophoneHost {
fn availability(&self) -> Result<MicrophoneAvailability, MicrophoneError> {
Ok(MicrophoneAvailability {
permission: MicrophonePermission::Denied,
devices: Vec::new(),
})
}
fn request_permission(
&self,
_request: MicrophonePermissionRequest,
) -> Result<MicrophonePermission, MicrophoneError> {
Err(MicrophoneError::unsupported("request_permission"))
}
fn capture_audio(
&self,
_request: MicrophoneCaptureRequest,
) -> Result<MicrophoneCapture, MicrophoneError> {
Err(MicrophoneError::unsupported("capture_audio"))
}
fn cancel_capture(&self) -> Result<(), MicrophoneError> {
Err(MicrophoneError::unsupported("cancel_capture"))
}
}
#[derive(Debug, Clone)]
pub struct MemoryMicrophoneHost {
availability: MicrophoneAvailability,
capture: MicrophoneCapture,
}
impl MemoryMicrophoneHost {
pub fn new(availability: MicrophoneAvailability, capture: MicrophoneCapture) -> Self {
Self {
availability,
capture,
}
}
}
impl Default for MemoryMicrophoneHost {
fn default() -> Self {
Self::new(
MicrophoneAvailability {
permission: MicrophonePermission::Granted,
devices: vec![MicrophoneDevice {
id: "memory-mic".into(),
label: Some("Memory microphone".into()),
is_default: true,
}],
},
MicrophoneCapture {
bytes: vec![0, 1, 2, 3],
content_type: "audio/pcm".into(),
sample_rate_hz: 48_000,
channels: 1,
duration_ms: 1_000,
device_id: Some("memory-mic".into()),
},
)
}
}
impl MicrophoneHost for MemoryMicrophoneHost {
fn availability(&self) -> Result<MicrophoneAvailability, MicrophoneError> {
Ok(self.availability.clone())
}
fn request_permission(
&self,
_request: MicrophonePermissionRequest,
) -> Result<MicrophonePermission, MicrophoneError> {
Ok(self.availability.permission)
}
fn capture_audio(
&self,
_request: MicrophoneCaptureRequest,
) -> Result<MicrophoneCapture, MicrophoneError> {
Ok(self.capture.clone())
}
fn cancel_capture(&self) -> Result<(), MicrophoneError> {
Ok(())
}
}
pub(crate) fn register_microphone_capabilities(
async_registry: &mut AsyncRegistry,
host: Arc<dyn MicrophoneHost>,
) {
let availability_host = host.clone();
async_registry.register_operation_capability(GET_MICROPHONE_AVAILABILITY, move |(), _| {
let host = availability_host.clone();
async move { host.availability() }
});
let permission_host = host.clone();
async_registry.register_operation_capability(
REQUEST_MICROPHONE_PERMISSION,
move |request, _| {
let host = permission_host.clone();
async move { host.request_permission(request) }
},
);
let capture_host = host.clone();
async_registry.register_operation_capability(CAPTURE_MICROPHONE_AUDIO, move |request, _| {
let host = capture_host.clone();
async move { host.capture_audio(request) }
});
async_registry.register_operation_capability(CANCEL_MICROPHONE_CAPTURE, move |(), _| {
let host = host.clone();
async move { host.cancel_capture() }
});
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn unsupported_host_reports_errors() {
let host = UnsupportedMicrophoneHost;
assert!(host
.capture_audio(MicrophoneCaptureRequest::default())
.is_err());
}
#[test]
fn memory_host_returns_audio_capture() {
let host = MemoryMicrophoneHost::default();
let availability = host.availability().unwrap();
assert_eq!(availability.permission, MicrophonePermission::Granted);
let capture = host
.capture_audio(MicrophoneCaptureRequest::default())
.unwrap();
assert_eq!(capture.content_type, "audio/pcm");
assert_eq!(capture.sample_rate_hz, 48_000);
}
}