cog-task 1.2.0

A general-purpose low-latency application to run cognitive tasks
Documentation
use crate::server::Config;
use eyre::{eyre, Result};
use serde::{Deserialize, Serialize};
use std::fmt::{Debug, Formatter};
use std::path::Path;
use std::time::Duration;

#[cfg(feature = "rodio")]
mod rodio;

#[derive(Debug, Copy, Clone, Deserialize, Serialize)]
#[serde(rename_all = "snake_case")]
pub enum AudioBackend {
    None,
    Inherit,
    #[cfg(feature = "rodio")]
    Rodio,
}

#[derive(Clone)]
pub enum AudioBuffer {
    None,
    #[cfg(feature = "rodio")]
    Rodio(rodio::Buffer),
}

pub enum AudioSink {
    None,
    #[cfg(feature = "rodio")]
    Rodio(rodio::Sink),
}

pub enum AudioDevice {
    None,
    #[cfg(feature = "rodio")]
    Rodio(rodio::Device),
}

impl AudioDevice {
    pub fn try_clone(&self) -> Result<Self> {
        match self {
            AudioDevice::None => Ok(AudioDevice::None),
            #[cfg(feature = "rodio")]
            AudioDevice::Rodio(_) => rodio::Device::new().map(AudioDevice::Rodio),
        }
    }
}

impl Default for AudioBackend {
    #[inline(always)]
    fn default() -> Self {
        AudioBackend::Inherit
    }
}

impl AudioBackend {
    pub fn or(&self, other: &Self) -> Self {
        if let Self::Inherit = self {
            *other
        } else {
            *self
        }
    }
}

#[derive(Copy, Clone, Deserialize, Serialize)]
#[serde(untagged)]
pub enum Volume {
    Inherit,
    Value(f32),
}

impl Default for Volume {
    #[inline(always)]
    fn default() -> Self {
        Volume::Inherit
    }
}

impl Debug for Volume {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        if let Volume::Value(vol) = self {
            write!(f, "{vol}")
        } else {
            write!(f, "inherit")
        }
    }
}

impl Volume {
    pub fn and(&self, other: &Self) -> Self {
        match (self, other) {
            (&Self::Value(x), &Self::Value(y)) => Self::Value(x * y),
            (Self::Value(_), _) => *self,
            (Self::Inherit, _) => *other,
        }
    }

    pub fn or(&self, other: &Self) -> Self {
        if let Self::Inherit = self {
            *other
        } else {
            *self
        }
    }

    pub fn value(&self) -> f32 {
        if let &Self::Value(x) = self {
            x
        } else {
            1.0
        }
    }
}

impl From<f32> for Volume {
    fn from(v: f32) -> Self {
        Self::Value(v.max(0.0))
    }
}

#[derive(Debug, Copy, Clone, Deserialize, Serialize)]
#[serde(rename_all = "snake_case")]
pub enum UseTrigger {
    Inherit,
    Yes,
    No,
}

impl Default for UseTrigger {
    #[inline(always)]
    fn default() -> Self {
        UseTrigger::Inherit
    }
}

impl UseTrigger {
    pub fn or(&self, other: &Self) -> Self {
        if let Self::Inherit = self {
            *other
        } else {
            *self
        }
    }

    pub fn value(&self) -> bool {
        !matches!(self, UseTrigger::No)
    }
}

#[derive(Debug, Copy, Clone, Deserialize, Serialize)]
#[serde(rename_all = "snake_case")]
pub enum TimePrecision {
    Inherit,
    RespectIntervals,
    RespectBoundaries,
}

impl Default for TimePrecision {
    #[inline(always)]
    fn default() -> Self {
        TimePrecision::Inherit
    }
}

impl TimePrecision {
    pub fn or(&self, other: &Self) -> Self {
        if let Self::Inherit = self {
            *other
        } else {
            *self
        }
    }
}

#[allow(unused_variables)]
pub fn audio_from_file(path: &Path, config: &Config) -> Result<AudioBuffer> {
    match config.audio_backend() {
        AudioBackend::None => Err(eyre!("Cannot load audio file with backend=None.")),
        AudioBackend::Inherit => Err(eyre!("Cannot load audio file with backend=Inherit.")),
        #[cfg(feature = "rodio")]
        AudioBackend::Rodio => rodio::Buffer::new(path, config).map(AudioBuffer::Rodio),
    }
}

impl AudioBuffer {
    pub fn duration(&self) -> Duration {
        match self {
            AudioBuffer::None => Duration::default(),
            #[cfg(feature = "rodio")]
            AudioBuffer::Rodio(x) => x.duration(),
        }
    }

    pub fn sample_rate(&self) -> u32 {
        match self {
            AudioBuffer::None => 0,
            #[cfg(feature = "rodio")]
            AudioBuffer::Rodio(x) => x.sample_rate(),
        }
    }

    pub fn channels(&self) -> u16 {
        match self {
            AudioBuffer::None => 0,
            #[cfg(feature = "rodio")]
            AudioBuffer::Rodio(x) => x.channels(),
        }
    }

    pub fn interlace(self, other: AudioBuffer) -> Result<AudioBuffer> {
        match (self, other) {
            #[cfg(feature = "rodio")]
            (AudioBuffer::Rodio(x), AudioBuffer::Rodio(y)) => {
                x.interlace(y).map(AudioBuffer::Rodio)
            }
            (_, _) => Err(eyre!("Cannot interlace audio buffers of different types.")),
        }
    }

    pub fn drop_last(self) -> Result<AudioBuffer> {
        match self {
            AudioBuffer::None => Err(eyre!("Cannot interlace audio buffers of different types.")),
            #[cfg(feature = "rodio")]
            AudioBuffer::Rodio(x) => x.drop_last().map(AudioBuffer::Rodio),
        }
    }
}

impl AudioSink {
    pub fn pause(&mut self) -> Result<()> {
        match self {
            AudioSink::None => Err(eyre!("Cannot pause audio sink with backend=None.")),
            #[cfg(feature = "rodio")]
            AudioSink::Rodio(sink) => {
                sink.pause();
                Ok(())
            }
        }
    }

    #[allow(unused_variables)]
    pub fn set_volume(&mut self, volume: f32) -> Result<()> {
        match self {
            AudioSink::None => Err(eyre!("Cannot pause audio sink with backend=None.")),
            #[cfg(feature = "rodio")]
            AudioSink::Rodio(sink) => {
                sink.set_volume(volume);
                Ok(())
            }
        }
    }

    pub fn queue(&mut self, buffer: AudioBuffer) -> Result<()> {
        match (self, buffer) {
            (AudioSink::None, _) => Err(eyre!("Cannot queue audio on sink=None.")),
            #[cfg(feature = "rodio")]
            (AudioSink::Rodio(sink), AudioBuffer::Rodio(buffer)) => {
                sink.queue(buffer);
                Ok(())
            }
            #[allow(unreachable_patterns)]
            (_, _) => Err(eyre!("Cannot queue audio on incompatible sink.")),
        }
    }

    pub fn repeat(&mut self, buffer: AudioBuffer) -> Result<()> {
        match (self, buffer) {
            (AudioSink::None, _) => Err(eyre!("Cannot repeat audio on sink=None.")),
            #[cfg(feature = "rodio")]
            (AudioSink::Rodio(sink), AudioBuffer::Rodio(buffer)) => {
                sink.repeat(buffer);
                Ok(())
            }
            #[allow(unreachable_patterns)]
            (_, _) => Err(eyre!("Cannot repeat audio on incompatible sink.")),
        }
    }

    pub fn play(&mut self) -> Result<()> {
        match self {
            AudioSink::None => Err(eyre!("Cannot play audio sink with backend=None.")),
            #[cfg(feature = "rodio")]
            AudioSink::Rodio(sink) => {
                sink.play();
                Ok(())
            }
        }
    }

    pub fn stop(&mut self) -> Result<()> {
        match self {
            AudioSink::None => Err(eyre!("Cannot stop audio sink with backend=None.")),
            #[cfg(feature = "rodio")]
            AudioSink::Rodio(sink) => {
                sink.stop();
                Ok(())
            }
        }
    }

    pub fn empty(&self) -> Result<bool> {
        match self {
            AudioSink::None => Ok(true),
            #[cfg(feature = "rodio")]
            AudioSink::Rodio(sink) => Ok(sink.empty()),
        }
    }

    pub fn detach(self) -> Result<()> {
        match self {
            AudioSink::None => Ok(()),
            #[cfg(feature = "rodio")]
            AudioSink::Rodio(sink) => {
                sink.detach();
                Ok(())
            }
        }
    }
}

impl AudioDevice {
    pub fn new(config: &Config) -> Result<Self> {
        match config.audio_backend() {
            AudioBackend::None => Err(eyre!("Cannot obtain audio device with backend=None.")),
            AudioBackend::Inherit => Err(eyre!("Cannot obtain audio device with backend=None.")),
            #[cfg(feature = "rodio")]
            AudioBackend::Rodio => rodio::Device::new().map(Self::Rodio),
        }
    }

    pub fn sink(&self) -> Result<AudioSink> {
        match self {
            AudioDevice::None => Err(eyre!("Cannot create audio sink with backend=None.")),
            #[cfg(feature = "rodio")]
            AudioDevice::Rodio(device) => device.sink().map(AudioSink::Rodio),
        }
    }
}