11extern crate alsa;
22extern crate libc;
3+ extern crate parking_lot;
34
45use self :: alsa:: poll:: Descriptors ;
6+ use self :: parking_lot:: Mutex ;
57use crate :: {
68 BackendSpecificError , BufferSize , BuildStreamError , ChannelCount , Data ,
79 DefaultStreamConfigError , DeviceNameError , DevicesError , InputCallbackInfo , OutputCallbackInfo ,
@@ -163,8 +165,68 @@ impl Drop for TriggerReceiver {
163165 }
164166}
165167
166- #[ derive( Clone , Debug , PartialEq , Eq ) ]
167- pub struct Device ( String ) ;
168+ #[ derive( Default ) ]
169+ struct DeviceHandles {
170+ playback : Option < alsa:: PCM > ,
171+ capture : Option < alsa:: PCM > ,
172+ }
173+
174+ impl DeviceHandles {
175+ /// Create `DeviceHandles` for `name` and try to open a handle for both
176+ /// directions. Returns `Ok` if either direction is opened successfully.
177+ fn open ( name : & str ) -> Result < Self , alsa:: Error > {
178+ let mut handles = Self :: default ( ) ;
179+ let playback_err = handles. try_open ( name, alsa:: Direction :: Playback ) . err ( ) ;
180+ let capture_err = handles. try_open ( name, alsa:: Direction :: Capture ) . err ( ) ;
181+ if let Some ( err) = capture_err. and ( playback_err) {
182+ Err ( err)
183+ } else {
184+ Ok ( handles)
185+ }
186+ }
187+
188+ /// Get a mutable reference to the `Option` for a specific `stream_type`.
189+ /// If the `Option` is `None`, the `alsa::PCM` will be opened and placed in
190+ /// the `Option` before returning. If `handle_mut()` returns `Ok` the contained
191+ /// `Option` is guaranteed to be `Some(..)`.
192+ fn try_open (
193+ & mut self ,
194+ name : & str ,
195+ stream_type : alsa:: Direction ,
196+ ) -> Result < & mut Option < alsa:: PCM > , alsa:: Error > {
197+ let handle = match stream_type {
198+ alsa:: Direction :: Playback => & mut self . playback ,
199+ alsa:: Direction :: Capture => & mut self . capture ,
200+ } ;
201+
202+ if handle. is_none ( ) {
203+ * handle = Some ( alsa:: pcm:: PCM :: new ( name, stream_type, true ) ?) ;
204+ }
205+
206+ Ok ( handle)
207+ }
208+
209+ /// Get a mutable reference to the `alsa::PCM` handle for a specific `stream_type`.
210+ /// If the handle is not yet opened, it will be opened and stored in `self`.
211+ fn get_mut (
212+ & mut self ,
213+ name : & str ,
214+ stream_type : alsa:: Direction ,
215+ ) -> Result < & mut alsa:: PCM , alsa:: Error > {
216+ Ok ( self . try_open ( name, stream_type) ?. as_mut ( ) . unwrap ( ) )
217+ }
218+
219+ /// Take ownership of the `alsa::PCM` handle for a specific `stream_type`.
220+ /// If the handle is not yet opened, it will be opened and returned.
221+ fn take ( & mut self , name : & str , stream_type : alsa:: Direction ) -> Result < alsa:: PCM , alsa:: Error > {
222+ Ok ( self . try_open ( name, stream_type) ?. take ( ) . unwrap ( ) )
223+ }
224+ }
225+
226+ pub struct Device {
227+ name : String ,
228+ handles : Mutex < DeviceHandles > ,
229+ }
168230
169231impl Device {
170232 fn build_stream_inner (
@@ -173,10 +235,13 @@ impl Device {
173235 sample_format : SampleFormat ,
174236 stream_type : alsa:: Direction ,
175237 ) -> Result < StreamInner , BuildStreamError > {
176- let name = & self . 0 ;
238+ let handle_result = self
239+ . handles
240+ . lock ( )
241+ . take ( & self . name , stream_type)
242+ . map_err ( |e| ( e, e. errno ( ) ) ) ;
177243
178- let handle = match alsa:: pcm:: PCM :: new ( name, stream_type, true ) . map_err ( |e| ( e, e. errno ( ) ) )
179- {
244+ let handle = match handle_result {
180245 Err ( ( _, Some ( nix:: errno:: Errno :: EBUSY ) ) ) => {
181246 return Err ( BuildStreamError :: DeviceNotAvailable )
182247 }
@@ -229,16 +294,19 @@ impl Device {
229294
230295 #[ inline]
231296 fn name ( & self ) -> Result < String , DeviceNameError > {
232- Ok ( self . 0 . clone ( ) )
297+ Ok ( self . name . clone ( ) )
233298 }
234299
235300 fn supported_configs (
236301 & self ,
237302 stream_t : alsa:: Direction ,
238303 ) -> Result < VecIntoIter < SupportedStreamConfigRange > , SupportedStreamConfigsError > {
239- let name = & self . 0 ;
304+ let mut guard = self . handles . lock ( ) ;
305+ let handle_result = guard
306+ . get_mut ( & self . name , stream_t)
307+ . map_err ( |e| ( e, e. errno ( ) ) ) ;
240308
241- let handle = match alsa :: pcm :: PCM :: new ( name , stream_t , true ) . map_err ( |e| ( e , e . errno ( ) ) ) {
309+ let handle = match handle_result {
242310 Err ( ( _, Some ( nix:: errno:: Errno :: ENOENT ) ) )
243311 | Err ( ( _, Some ( nix:: errno:: Errno :: EBUSY ) ) ) => {
244312 return Err ( SupportedStreamConfigsError :: DeviceNotAvailable )
0 commit comments