1- use crate :: backends:: alsa:: stream:: AlsaStream ;
21use crate :: backends:: alsa:: AlsaError ;
32use crate :: device:: Channel ;
43use crate :: device:: { AudioDevice , AudioInputDevice , AudioOutputDevice , DeviceType } ;
54use crate :: stream:: { AudioInputCallback , AudioOutputCallback , StreamConfig } ;
5+ use crate :: {
6+ backends:: alsa:: stream:: AlsaStream , device:: AudioDuplexDevice , duplex:: AudioDuplexCallback ,
7+ SendEverywhereButOnWeb ,
8+ } ;
69use alsa:: { pcm, PCM } ;
710use std:: borrow:: Cow ;
811use std:: fmt;
@@ -16,6 +19,23 @@ pub struct AlsaDevice {
1619 pub ( super ) direction : alsa:: Direction ,
1720}
1821
22+ impl AlsaDevice {
23+ fn channel_map ( & self , requested_direction : alsa:: Direction ) -> impl Iterator < Item = Channel > {
24+ let max_channels = if self . direction == requested_direction {
25+ self . pcm
26+ . hw_params_current ( )
27+ . and_then ( |hwp| hwp. get_channels_max ( ) )
28+ . unwrap_or ( 0 )
29+ } else {
30+ 0
31+ } ;
32+ ( 0 ..max_channels as usize ) . map ( |i| Channel {
33+ index : i,
34+ name : Cow :: Owned ( format ! ( "Channel {}" , i) ) ,
35+ } )
36+ }
37+ }
38+
1939impl fmt:: Debug for AlsaDevice {
2040 fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
2141 f. debug_struct ( "AlsaDevice" )
@@ -32,13 +52,6 @@ impl AudioDevice for AlsaDevice {
3252 Cow :: Borrowed ( self . name . as_str ( ) )
3353 }
3454
35- fn device_type ( & self ) -> DeviceType {
36- match self . direction {
37- alsa:: Direction :: Playback => DeviceType :: Output ,
38- alsa:: Direction :: Capture => DeviceType :: Input ,
39- }
40- }
41-
4255 fn is_config_supported ( & self , config : & StreamConfig ) -> bool {
4356 self . get_hwp ( config)
4457 . inspect_err ( |err| {
@@ -110,8 +123,8 @@ impl AlsaDevice {
110123 }
111124
112125 pub ( super ) fn new ( name : & str , direction : alsa:: Direction ) -> Result < Self , alsa:: Error > {
113- let pcm = PCM :: new ( name, direction, true ) ? ;
114- let pcm = Rc :: new ( pcm ) ;
126+ log :: info! ( "Opening device: { name} , direction {direction:?}" ) ;
127+ let pcm = Rc :: new ( PCM :: new ( name , direction , true ) ? ) ;
115128 Ok ( Self {
116129 name : name. to_string ( ) ,
117130 direction,
@@ -124,11 +137,12 @@ impl AlsaDevice {
124137 hwp. set_channels ( config. channels as _ ) ?;
125138 hwp. set_rate ( config. samplerate as _ , alsa:: ValueOr :: Nearest ) ?;
126139 if let Some ( min) = config. buffer_size_range . 0 {
127- hwp. set_buffer_size_min ( min as _ ) ?;
140+ hwp. set_buffer_size_min ( min as pcm :: Frames * 2 ) ?;
128141 }
129142 if let Some ( max) = config. buffer_size_range . 1 {
130- hwp. set_buffer_size_max ( max as _ ) ?;
143+ hwp. set_buffer_size_max ( max as pcm :: Frames * 2 ) ?;
131144 }
145+ hwp. set_periods ( 2 , alsa:: ValueOr :: Nearest ) ?;
132146 hwp. set_format ( pcm:: Format :: float ( ) ) ?;
133147 hwp. set_access ( pcm:: Access :: RWInterleaved ) ?;
134148 Ok ( hwp)
@@ -146,13 +160,24 @@ impl AlsaDevice {
146160
147161 log:: debug!( "Apply config: hwp {hwp:#?}" ) ;
148162
163+ swp. set_avail_min ( hwp. get_period_size ( ) ?) ?;
149164 swp. set_start_threshold ( hwp. get_buffer_size ( ) ?) ?;
150165 self . pcm . sw_params ( & swp) ?;
151166 log:: debug!( "Apply config: swp {swp:#?}" ) ;
152167
153168 Ok ( ( hwp, swp, io) )
154169 }
155170
171+ pub ( super ) fn ensure_state ( & self , hwp : & pcm:: HwParams ) -> Result < bool , AlsaError > {
172+ match self . pcm . state ( ) {
173+ pcm:: State :: Suspended if hwp. can_resume ( ) => self . pcm . resume ( ) ?,
174+ pcm:: State :: Suspended => self . pcm . prepare ( ) ?,
175+ pcm:: State :: Paused => return Ok ( true ) ,
176+ _ => { }
177+ }
178+ Ok ( false )
179+ }
180+
156181 fn default_config ( & self ) -> Result < StreamConfig , AlsaError > {
157182 let samplerate = 48e3 ; // Default ALSA sample rate
158183 let channel_count = 2 ; // Stereo stream
@@ -165,3 +190,77 @@ impl AlsaDevice {
165190 } )
166191 }
167192}
193+
194+ pub struct AlsaDuplexDevice {
195+ pub ( super ) input : AlsaDevice ,
196+ pub ( super ) output : AlsaDevice ,
197+ }
198+
199+ impl AudioDevice for AlsaDuplexDevice {
200+ type Error = AlsaError ;
201+
202+ fn name ( & self ) -> Cow < str > {
203+ Cow :: Owned ( format ! ( "{} / {}" , self . input. name( ) , self . output. name( ) ) )
204+ }
205+
206+ fn is_config_supported ( & self , config : & StreamConfig ) -> bool {
207+ let Ok ( ( hwp, _, _) ) = self . output . apply_config ( config) else {
208+ return false ;
209+ } ;
210+ let Ok ( period) = hwp. get_period_size ( ) else {
211+ return false ;
212+ } ;
213+ let period = period as usize ;
214+ self . input
215+ . apply_config ( & StreamConfig {
216+ buffer_size_range : ( Some ( period) , Some ( period) ) ,
217+ ..* config
218+ } )
219+ . is_ok ( )
220+ }
221+
222+ fn enumerate_configurations ( & self ) -> Option < impl IntoIterator < Item = StreamConfig > > {
223+ Some (
224+ self . output
225+ . enumerate_configurations ( ) ?
226+ . into_iter ( )
227+ . filter ( |config| self . is_config_supported ( config) ) ,
228+ )
229+ }
230+ }
231+
232+ impl AudioDuplexDevice for AlsaDuplexDevice {
233+ type StreamHandle < Callback : AudioDuplexCallback > = AlsaStream < Callback > ;
234+
235+ fn default_duplex_config ( & self ) -> Result < StreamConfig , Self :: Error > {
236+ self . output . default_output_config ( )
237+ }
238+
239+ fn create_duplex_stream < Callback : SendEverywhereButOnWeb + AudioDuplexCallback > (
240+ & self ,
241+ config : StreamConfig ,
242+ callback : Callback ,
243+ ) -> Result < <Self as AudioDuplexDevice >:: StreamHandle < Callback > , Self :: Error > {
244+ AlsaStream :: new_duplex (
245+ config,
246+ self . input . name . clone ( ) ,
247+ self . output . name . clone ( ) ,
248+ callback,
249+ )
250+ }
251+ }
252+
253+ impl AlsaDuplexDevice {
254+ /// Create a new duplex device from an input and output device.
255+ pub fn new ( input : AlsaDevice , output : AlsaDevice ) -> Self {
256+ Self { input, output }
257+ }
258+
259+ /// Create a full-duplex device from the given name.
260+ pub fn full_duplex ( name : & str ) -> Result < Self , AlsaError > {
261+ Ok ( Self :: new (
262+ AlsaDevice :: new ( name, alsa:: Direction :: Capture ) ?,
263+ AlsaDevice :: new ( name, alsa:: Direction :: Playback ) ?,
264+ ) )
265+ }
266+ }
0 commit comments