#![doc = include_str!("../README.md")]
use crate::asr::{AutomaticSpeechRecognition, AutomaticSpeechRecognitionConfig};
use crate::error::*;
use crate::input::{AudioInput, AudioInputConfig};
use crate::vad::{VoiceActivityDetection, VoiceActivityDetectionConfig};
use serde::{Deserialize, Serialize};
use tokio::sync::mpsc;
pub mod asr;
pub mod error;
pub mod input;
pub mod utils;
pub mod vad;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct NihilityListenerConfig {
pub sample_channel_size: usize,
pub speech_channel_size: usize,
pub audio_input: AudioInputConfig,
pub vad: VoiceActivityDetectionConfig,
pub asr: AutomaticSpeechRecognitionConfig,
}
pub struct NihilityListener {
input: AudioInput,
vad: VoiceActivityDetection,
asr: AutomaticSpeechRecognition,
}
impl NihilityListener {
pub fn init_from_file_config() -> Result<(Self, mpsc::UnboundedReceiver<String>)> {
Self::init(nihility_config::get_config::<NihilityListenerConfig>(
env!("CARGO_PKG_NAME"),
)?)
}
pub fn init(config: NihilityListenerConfig) -> Result<(Self, mpsc::UnboundedReceiver<String>)> {
let (sample_sender, sample_receiver) = mpsc::channel::<f32>(config.sample_channel_size);
let (speech_sender, speech_receiver) =
mpsc::channel::<Vec<f32>>(config.speech_channel_size);
let (text_sender, text_receiver) = mpsc::unbounded_channel::<String>();
let input = AudioInput::init(config.audio_input.clone(), sample_sender)?;
let vad = VoiceActivityDetection::init(config.vad.clone(), sample_receiver, speech_sender)?;
let asr =
AutomaticSpeechRecognition::init(config.asr.clone(), speech_receiver, text_sender)?;
Ok((Self { input, vad, asr }, text_receiver))
}
pub async fn start(&mut self) -> Result<()> {
tokio::try_join!(self.asr.run(), self.vad.run())?;
self.input.play()?;
Ok(())
}
}
impl Default for NihilityListenerConfig {
fn default() -> Self {
Self {
sample_channel_size: 16000 * 10,
speech_channel_size: 5,
audio_input: AudioInputConfig::default(),
vad: VoiceActivityDetectionConfig::default(),
asr: AutomaticSpeechRecognitionConfig::default(),
}
}
}