audio_device/alsa/
configurator.rs

1use crate::alsa::{Access, Direction, Error, Format, Pcm, Result, Sample};
2use crate::libc as c;
3use std::marker;
4
5/// Default access to configure.
6const DEFAULT_ACCESS: Access = Access::ReadWriteInterleaved;
7/// Default latency of 500 us.
8const DEFAULT_LATENCY: c::c_uint = 500_000;
9/// Default number of channels to use is 2.
10const DEFAULT_CHANNELS: c::c_uint = 2;
11/// Default sample rate to use.
12const DEFAULT_RATE: c::c_uint = 44100;
13
14/// The stream configuration used after the configurator has been successfully installed.
15///
16/// See [Configurator::install].
17#[derive(Debug, Clone, Copy)]
18pub struct Config {
19    /// The number of channels being used.
20    pub channels: c::c_uint,
21    /// The configured sample rate being used.
22    pub rate: c::c_uint,
23    /// The configured buffer time.
24    pub buffer_time: c::c_uint,
25    /// The configured period time.
26    pub period_time: c::c_uint,
27    /// The configured period size in frames.
28    pub period_size: c::c_ulong,
29}
30
31/// A simple [Pcm] stream configuration.
32///
33/// # Examples
34///
35/// ```rust,no_run
36/// use audio_device::alsa;
37///
38/// # fn main() -> anyhow::Result<()> {
39/// let mut pcm = alsa::Pcm::open_default(alsa::Stream::Playback)?;
40///
41/// let config = pcm.configure::<f32>()
42///     .rate(48000)
43///     .channels(5)
44///     .install()?;
45///
46/// dbg!(config);
47/// # Ok(()) }
48/// ```
49pub struct Configurator<'a, T> {
50    pcm: &'a mut Pcm,
51    access: Access,
52    format: Format,
53    latency: c::c_uint,
54    channels: c::c_uint,
55    rate: c::c_uint,
56    _marker: marker::PhantomData<T>,
57}
58
59impl<'a, T> Configurator<'a, T>
60where
61    T: Sample,
62{
63    pub(super) fn new(pcm: &'a mut Pcm) -> Self {
64        Self {
65            pcm,
66            access: DEFAULT_ACCESS,
67            format: T::DEFAULT_FORMAT,
68            latency: DEFAULT_LATENCY,
69            channels: DEFAULT_CHANNELS,
70            rate: DEFAULT_RATE,
71            _marker: marker::PhantomData,
72        }
73    }
74
75    /// Configure the stream access to use.
76    ///
77    /// # Examples
78    ///
79    /// ```rust,no_run
80    /// use audio_device::alsa;
81    ///
82    /// # fn main() -> anyhow::Result<()> {
83    /// let mut pcm = alsa::Pcm::open_default(alsa::Stream::Playback)?;
84    ///
85    /// let config = pcm.configure::<f32>()
86    ///     .access(alsa::Access::MmapInterleaved)
87    ///     .install()?;
88    ///
89    /// dbg!(config);
90    /// # Ok(()) }
91    /// ```
92    pub fn access(self, access: Access) -> Self {
93        Self { access, ..self }
94    }
95
96    /// Configure the stream format to use.
97    ///
98    /// This will check that the format is appropriate to use by the current
99    /// sample. Inappropriate formats will be signalled with
100    /// [Error::FormatMismatch].
101    ///
102    /// # Examples
103    ///
104    /// ```rust,no_run
105    /// use audio_device::alsa;
106    ///
107    /// # fn main() -> anyhow::Result<()> {
108    /// let mut pcm = alsa::Pcm::open_default(alsa::Stream::Playback)?;
109    ///
110    /// let config = pcm.configure::<f32>()
111    ///     .format(alsa::Format::FloatLE)?
112    ///     .install()?;
113    ///
114    /// dbg!(config);
115    /// # Ok(()) }
116    /// ```
117    pub fn format(self, format: Format) -> Result<Self> {
118        if !T::test(format) {
119            return Err(Error::FormatMismatch {
120                ty: T::describe(),
121                format,
122            });
123        }
124
125        Ok(Self { format, ..self })
126    }
127
128    /// Configure the stream latency to use.
129    ///
130    /// Will never accept a latency higher than `2**32`. Anything larger will be floored to it.
131    ///
132    /// # Examples
133    ///
134    /// ```rust,no_run
135    /// use audio_device::alsa;
136    ///
137    /// # fn main() -> anyhow::Result<()> {
138    /// let mut pcm = alsa::Pcm::open_default(alsa::Stream::Playback)?;
139    ///
140    /// let config = pcm.configure::<f32>()
141    ///     .latency(std::time::Duration::from_micros(500))
142    ///     .install()?;
143    ///
144    /// dbg!(config);
145    /// # Ok(()) }
146    /// ```
147    pub fn latency(self, latency: std::time::Duration) -> Self {
148        let latency = u128::min(u32::MAX as u128, latency.as_micros()) as u32;
149        Self { latency, ..self }
150    }
151
152    /// Configure the number of channels to use.
153    ///
154    /// # Examples
155    ///
156    /// ```rust,no_run
157    /// use audio_device::alsa;
158    ///
159    /// # fn main() -> anyhow::Result<()> {
160    /// let mut pcm = alsa::Pcm::open_default(alsa::Stream::Playback)?;
161    ///
162    /// let config = pcm.configure::<f32>()
163    ///     .channels(5)
164    ///     .install()?;
165    ///
166    /// dbg!(config);
167    /// # Ok(()) }
168    /// ```
169    pub fn channels(self, channels: c::c_uint) -> Self {
170        Self { channels, ..self }
171    }
172
173    /// Configure the sample rate to use.
174    ///
175    /// # Examples
176    ///
177    /// ```rust,no_run
178    /// use audio_device::alsa;
179    ///
180    /// # fn main() -> anyhow::Result<()> {
181    /// let mut pcm = alsa::Pcm::open_default(alsa::Stream::Playback)?;
182    ///
183    /// let config = pcm.configure::<f32>()
184    ///     .rate(48000)
185    ///     .install()?;
186    ///
187    /// dbg!(config);
188    /// # Ok(()) }
189    /// ```
190    pub fn rate(self, rate: c::c_uint) -> Self {
191        Self { rate, ..self }
192    }
193
194    /// Install the current configuration and return the one which is used by
195    /// the underlying PCM.
196    ///
197    /// # Examples
198    ///
199    /// ```rust,no_run
200    /// use audio_device::alsa;
201    ///
202    /// # fn main() -> anyhow::Result<()> {
203    /// let mut pcm = alsa::Pcm::open_default(alsa::Stream::Playback)?;
204    ///
205    /// let config = pcm.configure::<f32>()
206    ///     .channels(2)
207    ///     .rate(48000)
208    ///     .install()?;
209    ///
210    /// dbg!(config);
211    /// # Ok(()) }
212    /// ```
213    pub fn install(self) -> Result<Config> {
214        let mut hw = self.pcm.hardware_parameters_any()?;
215        hw.set_rate_resample(false)?;
216        hw.set_access(self.access)?;
217        hw.set_format(self.format)?;
218        hw.set_channels(self.channels)?;
219
220        let (rate, _) = hw.set_rate_near(self.rate, Direction::Nearest)?;
221        let (buffer_time, _) = hw.set_buffer_time_near(self.latency, Direction::Nearest)?;
222        let period_time = self.latency / 4;
223        let (period_time, _) = hw.set_period_time_near(period_time, Direction::Nearest)?;
224        let buffer_size = hw.buffer_size()?;
225        let (period_size, _) = hw.period_size()?;
226
227        hw.install()?;
228
229        let mut sw = self.pcm.software_parameters_mut()?;
230        sw.set_start_threshold((buffer_size / period_size) * period_size)?;
231        sw.set_available_min(period_size)?;
232        sw.install()?;
233
234        Ok(Config {
235            channels: self.channels,
236            rate,
237            buffer_time,
238            period_time,
239            period_size,
240        })
241    }
242}