use crate::mic_sim::MicrophoneSimConfig;
use serde::Serialize;
use serde::{Deserialize, Serialize as SerdeSerialize};
use std::collections::BTreeMap;
use std::fmt;
use std::path::PathBuf;
#[derive(Debug, Clone, Copy)]
pub struct AudioSpec {
pub sample_rate: u32,
pub channels: u16,
}
#[derive(Debug, Clone)]
pub struct AudioFrame {
pub samples: Vec<f32>,
#[allow(dead_code)]
pub spec: AudioSpec,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum AudioProfile {
#[default]
VoiceClean,
Raw,
VoiceNoisyRoom,
VoiceHvac,
}
pub const SUPPORTED_AUDIO_PROFILES: [AudioProfile; 4] = [
AudioProfile::Raw,
AudioProfile::VoiceClean,
AudioProfile::VoiceNoisyRoom,
AudioProfile::VoiceHvac,
];
impl AudioProfile {
pub fn from_str(value: &str) -> Self {
match value {
"raw" => Self::Raw,
"voice_hvac" => Self::VoiceHvac,
"voice_noisy_room" => Self::VoiceNoisyRoom,
_ => Self::VoiceClean,
}
}
pub fn as_str(&self) -> &'static str {
match self {
Self::Raw => "raw",
Self::VoiceClean => "voice_clean",
Self::VoiceNoisyRoom => "voice_noisy_room",
Self::VoiceHvac => "voice_hvac",
}
}
pub fn label(&self) -> &'static str {
match self {
Self::Raw => "Raw",
Self::VoiceClean => "Voice Clean",
Self::VoiceNoisyRoom => "Voice Noisy Room",
Self::VoiceHvac => "Voice HVAC",
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum DelayEffectPreset {
VoiceDoubler,
Thickener,
Slapback,
Bloom,
}
pub const SUPPORTED_DELAY_EFFECT_PRESETS: [DelayEffectPreset; 4] = [
DelayEffectPreset::VoiceDoubler,
DelayEffectPreset::Thickener,
DelayEffectPreset::Slapback,
DelayEffectPreset::Bloom,
];
impl DelayEffectPreset {
pub fn from_str(value: &str) -> Self {
match value {
"thickener" => Self::Thickener,
"slapback" => Self::Slapback,
"bloom" => Self::Bloom,
_ => Self::VoiceDoubler,
}
}
pub fn as_str(&self) -> &'static str {
match self {
Self::VoiceDoubler => "voice_doubler",
Self::Thickener => "thickener",
Self::Slapback => "slapback",
Self::Bloom => "bloom",
}
}
pub fn label(&self) -> &'static str {
match self {
Self::VoiceDoubler => "Voice Doubler",
Self::Thickener => "Thickener",
Self::Slapback => "Slapback",
Self::Bloom => "Bloom",
}
}
}
#[derive(Debug, Clone, Copy)]
pub struct DelayEffectConfig {
pub preset: DelayEffectPreset,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, SerdeSerialize, Default)]
#[serde(rename_all = "snake_case")]
pub enum ProcessorOverrideMode {
#[default]
Inherit,
Enabled,
Disabled,
}
impl ProcessorOverrideMode {
pub fn as_str(&self) -> &'static str {
match self {
Self::Inherit => "inherit",
Self::Enabled => "enabled",
Self::Disabled => "disabled",
}
}
pub fn label(&self) -> &'static str {
match self {
Self::Inherit => "Inherit",
Self::Enabled => "Force On",
Self::Disabled => "Force Off",
}
}
}
#[derive(Debug, Clone, Default)]
pub struct NativeCaptureConfig {
pub output_path: PathBuf,
pub target_sample_rate: u32,
pub target_channels: u16,
pub preferred_input_device: Option<String>,
pub profile: AudioProfile,
pub input_gain_db: f32,
pub limiter_threshold: f32,
pub high_pass_hz: f32,
pub noise_suppression_amount: f32,
pub noise_calibration_ms: u32,
pub delay_effect: Option<DelayEffectConfig>,
pub stage_overrides: BTreeMap<String, ProcessorOverrideMode>,
pub microphone_sim: MicrophoneSimConfig,
}
impl NativeCaptureConfig {
pub fn new(output_path: PathBuf, profile: AudioProfile) -> Self {
Self {
output_path,
profile,
target_sample_rate: 44_100,
target_channels: 1,
preferred_input_device: None,
input_gain_db: 0.0,
limiter_threshold: 0.95,
high_pass_hz: 80.0,
noise_suppression_amount: 0.5,
noise_calibration_ms: 300,
delay_effect: None,
stage_overrides: BTreeMap::new(),
microphone_sim: MicrophoneSimConfig::default(),
}
}
}
#[derive(Debug, Clone)]
pub struct InputDeviceInfo {
pub id: String,
pub name: String,
pub is_default: bool,
}
#[derive(Debug, Clone, Serialize, Default)]
pub struct CaptureDiagnostics {
pub backend: String,
pub profile: String,
pub profile_base: Option<String>,
pub device_name: Option<String>,
pub sample_rate: Option<u32>,
pub channels: Option<u16>,
pub duration_ms: Option<i64>,
pub frames_processed: u64,
pub analyzed_frames: u64,
pub noise_only_frames: u64,
pub transitional_frames: u64,
pub speech_like_frames: u64,
pub rms_level: f32,
pub peak_level: f32,
pub clipping_events: u64,
pub processor_names: Vec<String>,
pub processor_stage_overrides: Vec<String>,
pub resolved_delay_preset: Option<String>,
pub microphone_sim_model: Option<String>,
pub microphone_sim_processor_names: Vec<String>,
pub notes: Vec<String>,
}
#[derive(Debug, Clone)]
pub struct AudioError {
message: String,
}
impl AudioError {
pub fn new(message: impl Into<String>) -> Self {
Self {
message: message.into(),
}
}
}
impl fmt::Display for AudioError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(&self.message)
}
}
impl std::error::Error for AudioError {}
pub type AudioResult<T> = Result<T, AudioError>;
pub trait ActiveRecording: Send {
fn stop(&mut self) -> AudioResult<CaptureDiagnostics>;
}
pub trait ActiveListening: Send {
fn stop(&mut self) -> AudioResult<CaptureDiagnostics>;
fn update_config(&mut self, config: NativeCaptureConfig) -> AudioResult<()>;
}