1- use crate :: backends:: alsa:: stream:: AlsaStream ;
1+ use crate :: { backends:: alsa:: stream:: AlsaStream , device :: AudioDuplexDevice , duplex :: AudioDuplexCallback , SendEverywhereButOnWeb } ;
22use crate :: backends:: alsa:: AlsaError ;
33use crate :: device:: Channel ;
44use crate :: device:: { AudioDevice , AudioInputDevice , AudioOutputDevice , DeviceType } ;
@@ -16,6 +16,23 @@ pub struct AlsaDevice {
1616 pub ( super ) direction : alsa:: Direction ,
1717}
1818
19+ impl AlsaDevice {
20+ fn channel_map ( & self , requested_direction : alsa:: Direction ) -> impl Iterator < Item = Channel > {
21+ let max_channels = if self . direction == requested_direction {
22+ self . pcm
23+ . hw_params_current ( )
24+ . and_then ( |hwp| hwp. get_channels_max ( ) )
25+ . unwrap_or ( 0 )
26+ } else {
27+ 0
28+ } ;
29+ ( 0 ..max_channels as usize ) . map ( |i| Channel {
30+ index : i,
31+ name : Cow :: Owned ( format ! ( "Channel {}" , i) ) ,
32+ } )
33+ }
34+ }
35+
1936impl fmt:: Debug for AlsaDevice {
2037 fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
2138 f. debug_struct ( "AlsaDevice" )
@@ -111,8 +128,8 @@ impl AlsaDevice {
111128 }
112129
113130 pub ( super ) fn new ( name : & str , direction : alsa:: Direction ) -> Result < Self , alsa:: Error > {
114- let pcm = PCM :: new ( name, direction, true ) ? ;
115- let pcm = Rc :: new ( pcm ) ;
131+ log :: info! ( "Opening device: { name} , direction {direction:?}" ) ;
132+ let pcm = Rc :: new ( PCM :: new ( name , direction , true ) ? ) ;
116133 Ok ( Self {
117134 name : name. to_string ( ) ,
118135 direction,
@@ -125,11 +142,12 @@ impl AlsaDevice {
125142 hwp. set_channels ( config. channels as _ ) ?;
126143 hwp. set_rate ( config. samplerate as _ , alsa:: ValueOr :: Nearest ) ?;
127144 if let Some ( min) = config. buffer_size_range . 0 {
128- hwp. set_buffer_size_min ( min as _ ) ?;
145+ hwp. set_buffer_size_min ( min as pcm :: Frames * 2 ) ?;
129146 }
130147 if let Some ( max) = config. buffer_size_range . 1 {
131- hwp. set_buffer_size_max ( max as _ ) ?;
148+ hwp. set_buffer_size_max ( max as pcm :: Frames * 2 ) ?;
132149 }
150+ hwp. set_periods ( 2 , alsa:: ValueOr :: Nearest ) ?;
133151 hwp. set_format ( pcm:: Format :: float ( ) ) ?;
134152 hwp. set_access ( pcm:: Access :: RWInterleaved ) ?;
135153 Ok ( hwp)
@@ -147,13 +165,24 @@ impl AlsaDevice {
147165
148166 log:: debug!( "Apply config: hwp {hwp:#?}" ) ;
149167
168+ swp. set_avail_min ( hwp. get_period_size ( ) ?) ?;
150169 swp. set_start_threshold ( hwp. get_buffer_size ( ) ?) ?;
151170 self . pcm . sw_params ( & swp) ?;
152171 log:: debug!( "Apply config: swp {swp:#?}" ) ;
153172
154173 Ok ( ( hwp, swp, io) )
155174 }
156175
176+ pub ( super ) fn ensure_state ( & self , hwp : & pcm:: HwParams ) -> Result < bool , AlsaError > {
177+ match self . pcm . state ( ) {
178+ pcm:: State :: Suspended if hwp. can_resume ( ) => self . pcm . resume ( ) ?,
179+ pcm:: State :: Suspended => self . pcm . prepare ( ) ?,
180+ pcm:: State :: Paused => return Ok ( true ) ,
181+ _ => { }
182+ }
183+ Ok ( false )
184+ }
185+
157186 fn default_config ( & self ) -> Result < StreamConfig , AlsaError > {
158187 let samplerate = 48e3 ; // Default ALSA sample rate
159188 let channel_count = 2 ; // Stereo stream
@@ -166,3 +195,81 @@ impl AlsaDevice {
166195 } )
167196 }
168197}
198+
199+ pub struct AlsaDuplexDevice {
200+ pub ( super ) input : AlsaDevice ,
201+ pub ( super ) output : AlsaDevice ,
202+ }
203+
204+ impl AudioDevice for AlsaDuplexDevice {
205+ type Error = AlsaError ;
206+
207+ fn name ( & self ) -> Cow < str > {
208+ Cow :: Owned ( format ! ( "{} / {}" , self . input. name( ) , self . output. name( ) ) )
209+ }
210+
211+ fn device_type ( & self ) -> DeviceType {
212+ DeviceType :: Duplex
213+ }
214+
215+ fn is_config_supported ( & self , config : & StreamConfig ) -> bool {
216+ let Ok ( ( hwp, _, _) ) = self . output . apply_config ( config) else {
217+ return false ;
218+ } ;
219+ let Ok ( period) = hwp. get_period_size ( ) else {
220+ return false ;
221+ } ;
222+ let period = period as usize ;
223+ self . input
224+ . apply_config ( & StreamConfig {
225+ buffer_size_range : ( Some ( period) , Some ( period) ) ,
226+ ..* config
227+ } )
228+ . is_ok ( )
229+ }
230+
231+ fn enumerate_configurations ( & self ) -> Option < impl IntoIterator < Item = StreamConfig > > {
232+ Some (
233+ self . output
234+ . enumerate_configurations ( ) ?
235+ . into_iter ( )
236+ . filter ( |config| self . is_config_supported ( config) ) ,
237+ )
238+ }
239+ }
240+
241+ impl AudioDuplexDevice for AlsaDuplexDevice {
242+ type StreamHandle < Callback : AudioDuplexCallback > = AlsaStream < Callback > ;
243+
244+ fn default_duplex_config ( & self ) -> Result < StreamConfig , Self :: Error > {
245+ self . output . default_output_config ( )
246+ }
247+
248+ fn create_duplex_stream < Callback : SendEverywhereButOnWeb + AudioDuplexCallback > (
249+ & self ,
250+ config : StreamConfig ,
251+ callback : Callback ,
252+ ) -> Result < <Self as AudioDuplexDevice >:: StreamHandle < Callback > , Self :: Error > {
253+ AlsaStream :: new_duplex (
254+ config,
255+ self . input . name . clone ( ) ,
256+ self . output . name . clone ( ) ,
257+ callback,
258+ )
259+ }
260+ }
261+
262+ impl AlsaDuplexDevice {
263+ /// Create a new duplex device from an input and output device.
264+ pub fn new ( input : AlsaDevice , output : AlsaDevice ) -> Self {
265+ Self { input, output }
266+ }
267+
268+ /// Create a full-duplex device from the given name.
269+ pub fn full_duplex ( name : & str ) -> Result < Self , AlsaError > {
270+ Ok ( Self :: new (
271+ AlsaDevice :: new ( name, alsa:: Direction :: Capture ) ?,
272+ AlsaDevice :: new ( name, alsa:: Direction :: Playback ) ?,
273+ ) )
274+ }
275+ }
0 commit comments