2017-07-14 15:23:42 +00:00
|
|
|
#![allow(missing_docs)] // enums
|
|
|
|
|
|
|
|
//! High-level audio subsystem.
|
|
|
|
//!
|
|
|
|
//! This is the middleman between the low-level audio backend (alsa),
|
|
|
|
//! and the high-level ui code.
|
|
|
|
//! This abstraction layer allows the high-level code to be completely unaware
|
|
|
|
//! of the underlying audio implementation, may it be alsa or whatever.
|
|
|
|
|
|
|
|
|
2017-06-26 07:08:37 +00:00
|
|
|
use errors::*;
|
|
|
|
use std::cell::Ref;
|
|
|
|
use std::cell::RefCell;
|
|
|
|
use std::f64;
|
|
|
|
use std::rc::Rc;
|
2017-07-19 10:12:08 +00:00
|
|
|
use support::audio::*;
|
2017-06-26 07:08:37 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#[derive(Clone, Copy, Debug)]
|
2017-07-14 15:23:42 +00:00
|
|
|
/// The volume level of the current audio configuration.
|
2017-06-26 07:08:37 +00:00
|
|
|
pub enum VolLevel {
|
|
|
|
Muted,
|
|
|
|
Low,
|
|
|
|
Medium,
|
|
|
|
High,
|
|
|
|
Off,
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-07-14 15:23:42 +00:00
|
|
|
/// An audio user, used to determine from where a signal originated.
|
2017-06-26 07:08:37 +00:00
|
|
|
#[derive(Clone, Copy, Debug)]
|
|
|
|
pub enum AudioUser {
|
|
|
|
Unknown,
|
|
|
|
Popup,
|
|
|
|
TrayIcon,
|
|
|
|
Hotkeys,
|
|
|
|
PrefsWindow,
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-07-14 15:23:42 +00:00
|
|
|
/// An audio signal. This will be used to connect callbacks to the
|
|
|
|
/// audio system and react appropriately.
|
2017-06-26 07:08:37 +00:00
|
|
|
#[derive(Clone, Copy, Debug)]
|
|
|
|
pub enum AudioSignal {
|
|
|
|
NoCard,
|
|
|
|
CardInitialized,
|
|
|
|
CardCleanedUp,
|
|
|
|
CardDisconnected,
|
|
|
|
CardError,
|
|
|
|
ValuesChanged,
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#[derive(Clone)]
|
2017-07-14 15:23:42 +00:00
|
|
|
/// Convenience struct to make handling this madness easier.
|
2017-06-26 07:08:37 +00:00
|
|
|
pub struct Handlers {
|
|
|
|
inner: Rc<RefCell<Vec<Box<Fn(AudioSignal, AudioUser)>>>>,
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
impl Handlers {
|
2017-07-18 15:20:17 +00:00
|
|
|
pub fn new() -> Handlers {
|
2017-06-26 07:08:37 +00:00
|
|
|
return Handlers { inner: Rc::new(RefCell::new(vec![])) };
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-07-18 15:20:17 +00:00
|
|
|
pub fn borrow(&self) -> Ref<Vec<Box<Fn(AudioSignal, AudioUser)>>> {
|
2017-06-26 07:08:37 +00:00
|
|
|
return self.inner.borrow();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-07-18 15:20:17 +00:00
|
|
|
pub fn add_handler(&self, cb: Box<Fn(AudioSignal, AudioUser)>) {
|
2017-06-26 07:08:37 +00:00
|
|
|
self.inner.borrow_mut().push(cb);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-07-19 10:12:08 +00:00
|
|
|
/// This is the audio frontend, which can be implemented by different backends,
|
|
|
|
/// e.g. Alsa or PulseAudio. The high-level UI code only calls these
|
|
|
|
/// functions, never the underlying backend functions. The backend
|
|
|
|
/// implementation must ensure proper state and consistency, especially
|
|
|
|
/// wrt handlers and switching the card.
|
2017-07-18 15:20:17 +00:00
|
|
|
pub trait AudioFrontend {
|
|
|
|
/// Switches the current card. Must invoke handlers.
|
2017-07-14 15:23:42 +00:00
|
|
|
/// ## `user`
|
|
|
|
/// Where the card switch originates from.
|
2017-07-19 10:12:08 +00:00
|
|
|
fn switch_card(
|
|
|
|
&self,
|
|
|
|
card_name: Option<String>,
|
|
|
|
elem_name: Option<String>,
|
|
|
|
user: AudioUser,
|
|
|
|
) -> Result<()>;
|
2017-07-18 15:20:17 +00:00
|
|
|
|
|
|
|
/// Current volume. Between 0 and 100.
|
2017-07-19 10:12:08 +00:00
|
|
|
/// This always gets the volume of the `FrontRight` channel, because that
|
2017-07-18 15:20:17 +00:00
|
|
|
/// seems to be the safest bet.
|
|
|
|
fn get_vol(&self) -> Result<f64>;
|
|
|
|
|
|
|
|
/// Set the current volume. Must invoke handlers.
|
2017-07-14 15:23:42 +00:00
|
|
|
/// ## `new_vol`
|
2017-07-19 10:12:08 +00:00
|
|
|
/// Set the volume to this value. From 0 to 100.
|
2017-07-14 15:23:42 +00:00
|
|
|
/// ## `user`
|
|
|
|
/// Where the card switch originates from.
|
|
|
|
/// ## `dir`
|
|
|
|
/// The "direction" of the volume change, e.g. is it a decrease
|
|
|
|
/// or increase. This helps with rounding problems.
|
|
|
|
/// ## `auto_unmute`
|
|
|
|
/// Whether to automatically unmute if the volume changes.
|
2017-07-19 10:12:08 +00:00
|
|
|
fn set_vol(
|
|
|
|
&self,
|
|
|
|
new_vol: f64,
|
|
|
|
user: AudioUser,
|
|
|
|
dir: VolDir,
|
|
|
|
auto_unmute: bool,
|
|
|
|
) -> Result<()>;
|
2017-06-26 07:08:37 +00:00
|
|
|
|
2017-07-18 15:20:17 +00:00
|
|
|
/// Current volume level, nicely usable for e.g. selecting from a set
|
|
|
|
/// of images.
|
|
|
|
fn vol_level(&self) -> VolLevel;
|
2017-06-26 07:08:37 +00:00
|
|
|
|
2017-07-14 15:23:42 +00:00
|
|
|
/// Increase the volume. The step to increasy by is taken from
|
|
|
|
/// `self.scroll_step`.
|
|
|
|
/// ## `user`
|
|
|
|
/// Where the card switch originates from.
|
2017-07-18 15:20:17 +00:00
|
|
|
fn increase_vol(&self, user: AudioUser, auto_unmute: bool) -> Result<()>;
|
2017-06-26 07:08:37 +00:00
|
|
|
|
2017-07-14 15:23:42 +00:00
|
|
|
/// Decrease the volume. The step to decrease by is taken from
|
|
|
|
/// `self.scroll_step`.
|
|
|
|
/// ## `user`
|
|
|
|
/// Where the card switch originates from.
|
2017-07-18 15:20:17 +00:00
|
|
|
fn decrease_vol(&self, user: AudioUser, auto_unmute: bool) -> Result<()>;
|
2017-06-26 07:08:37 +00:00
|
|
|
|
2017-07-14 15:23:42 +00:00
|
|
|
/// Whether the current audio configuration can be muted.
|
2017-07-18 15:20:17 +00:00
|
|
|
fn has_mute(&self) -> bool;
|
2017-06-26 07:08:37 +00:00
|
|
|
|
2017-07-14 15:23:42 +00:00
|
|
|
/// Get the mute state of the current audio configuration.
|
2017-07-18 15:20:17 +00:00
|
|
|
fn get_mute(&self) -> Result<bool>;
|
2017-06-26 07:08:37 +00:00
|
|
|
|
2017-07-14 15:23:42 +00:00
|
|
|
/// Set the mute state of the current audio configuration.
|
2017-07-18 15:20:17 +00:00
|
|
|
/// Must invoke handlers.
|
|
|
|
fn set_mute(&self, mute: bool, user: AudioUser) -> Result<()>;
|
2017-06-26 07:08:37 +00:00
|
|
|
|
2017-07-14 15:23:42 +00:00
|
|
|
/// Toggle the mute state of the current audio configuration.
|
2017-07-18 15:20:17 +00:00
|
|
|
fn toggle_mute(&self, user: AudioUser) -> Result<()>;
|
2017-06-26 07:08:37 +00:00
|
|
|
|
2017-07-14 15:23:42 +00:00
|
|
|
/// Connect a signal handler to the audio subsystem. This can
|
|
|
|
/// be done from anywhere, e.g. in the UI code to react to
|
|
|
|
/// certain signals. Multiple handlers for the same signals are fine,
|
|
|
|
/// they will be executed in order.
|
2017-07-18 15:20:17 +00:00
|
|
|
fn connect_handler(&self, cb: Box<Fn(AudioSignal, AudioUser)>);
|
2017-07-14 23:19:10 +00:00
|
|
|
|
|
|
|
/// Get the current card name.
|
2017-07-18 15:20:17 +00:00
|
|
|
fn card_name(&self) -> Result<String>;
|
2017-07-14 23:19:10 +00:00
|
|
|
|
|
|
|
/// Get the currently playable channel names.
|
2017-07-18 15:20:17 +00:00
|
|
|
fn playable_chan_names(&self) -> Vec<String>;
|
2017-07-14 23:19:10 +00:00
|
|
|
|
|
|
|
/// Get the current active channel name.
|
2017-07-18 15:20:17 +00:00
|
|
|
fn chan_name(&self) -> Result<String>;
|
2017-06-26 07:08:37 +00:00
|
|
|
|
2017-07-18 15:20:17 +00:00
|
|
|
/// Set the scroll step.
|
|
|
|
fn set_scroll_step(&self, scroll_step: u32);
|
2017-06-26 07:08:37 +00:00
|
|
|
|
2017-07-18 15:20:17 +00:00
|
|
|
/// Get the scroll step.
|
|
|
|
fn get_scroll_step(&self) -> u32;
|
2017-06-26 07:08:37 +00:00
|
|
|
}
|