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}