@@ -9,63 +9,42 @@ use std::sync::{Arc, Mutex};
9
9
use std:: sync:: atomic:: { AtomicBool , Ordering } ;
10
10
use std:: thread:: { JoinHandle } ;
11
11
12
+ mod device;
13
+ use device:: * ;
12
14
13
15
pub mod prelude {
14
16
pub use super :: Provider ;
15
17
}
16
18
17
19
18
- #[ instrument( skip_all, name="audio init" ) ]
19
- pub fn init ( ) -> System {
20
- if false {
21
- std:: thread:: spawn ( enumerate_audio_devices) ;
22
- }
23
-
24
- let shared = Arc :: new ( SharedState {
25
- provider : Mutex :: new ( None ) ,
26
- device_lost : AtomicBool :: new ( false ) ,
27
- } ) ;
28
-
29
- System {
30
- stream_state : StreamState :: Pending ( Some ( start_stream_build ( shared. clone ( ) ) ) ) ,
31
- shared,
32
- }
20
+ pub struct System {
21
+ stream_shared : Arc < SharedStreamState > ,
22
+ stream_state : StreamState ,
33
23
}
34
24
35
- #[ instrument]
36
- fn enumerate_audio_devices ( ) -> anyhow:: Result < ( ) > {
37
- let host = cpal:: default_host ( ) ;
25
+ impl System {
26
+ #[ instrument( skip_all, name="audio init" ) ]
27
+ pub fn init ( ) -> System {
28
+ if false {
29
+ std:: thread:: spawn ( enumerate_audio_devices) ;
30
+ }
38
31
39
- log:: trace!( "vvvvvvv Available audio devices vvvvvvvv" ) ;
32
+ let stream_shared = Arc :: new ( SharedStreamState {
33
+ provider : Mutex :: new ( None ) ,
34
+ device_lost : AtomicBool :: new ( false ) ,
35
+ } ) ;
40
36
41
- for device in host. output_devices ( ) ? {
42
- log:: trace!( " => {}" , device. name( ) ?) ;
43
- log:: trace!( " => default output config: {:?}" , device. default_output_config( ) ?) ;
44
- log:: trace!( " => supported configs:" ) ;
45
- for config in device. supported_output_configs ( ) ? {
46
- log:: trace!( " => {config:?}" ) ;
37
+ System {
38
+ stream_state : StreamState :: Pending ( Some ( start_stream_build ( stream_shared. clone ( ) ) ) ) ,
39
+ stream_shared,
47
40
}
48
- log:: trace!( "" ) ;
49
41
}
50
42
51
- log:: trace!( "^^^^^^ Available audio devices ^^^^^^^" ) ;
52
-
53
- Ok ( ( ) )
54
- }
55
-
56
-
57
- pub struct System {
58
- shared : Arc < SharedState > ,
59
-
60
- stream_state : StreamState ,
61
- }
62
-
63
- impl System {
64
43
pub fn update ( & mut self ) {
65
44
match & mut self . stream_state {
66
45
StreamState :: Active ( _) => {
67
- if self . shared . device_lost . load ( Ordering :: Relaxed ) {
68
- self . stream_state = StreamState :: Pending ( Some ( start_stream_build ( self . shared . clone ( ) ) ) ) ;
46
+ if self . stream_shared . device_lost . load ( Ordering :: Relaxed ) {
47
+ self . stream_state = StreamState :: Pending ( Some ( start_stream_build ( self . stream_shared . clone ( ) ) ) ) ;
69
48
}
70
49
}
71
50
@@ -79,7 +58,7 @@ impl System {
79
58
log:: info!( "Output stream active" ) ;
80
59
81
60
self . stream_state = StreamState :: Active ( new_stream) ;
82
- self . shared . device_lost . store ( false , Ordering :: Relaxed ) ;
61
+ self . stream_shared . device_lost . store ( false , Ordering :: Relaxed ) ;
83
62
}
84
63
85
64
Ok ( Err ( error) ) => {
@@ -108,10 +87,10 @@ impl System {
108
87
pub fn set_provider < P > ( & mut self , provider : impl Into < Option < P > > ) -> anyhow:: Result < ( ) >
109
88
where P : Provider + Send
110
89
{
111
- let mut shared_provider = self . shared . provider . lock ( ) . unwrap ( ) ;
90
+ let mut shared_provider = self . stream_shared . provider . lock ( ) . unwrap ( ) ;
112
91
113
92
if let Some ( mut provider) = provider. into ( ) {
114
- let configuration = self . stream_state . as_active_stream ( ) . map ( |active_stream| active_stream . configuration ) ;
93
+ let configuration = self . stream_state . current_configuration ( ) ;
115
94
116
95
log:: info!( "Setting initial provider configuration: {configuration:?}" ) ;
117
96
provider. on_configuration_changed ( configuration) ;
@@ -126,12 +105,11 @@ impl System {
126
105
}
127
106
128
107
fn try_update_provider_config ( & mut self ) {
129
- if let Ok ( mut guard) = self . shared . provider . lock ( )
108
+ let configuration = self . stream_state . current_configuration ( ) ;
109
+
110
+ if let Ok ( mut guard) = self . stream_shared . provider . lock ( )
130
111
&& let Some ( provider) = & mut * guard
131
112
{
132
- let configuration = self . stream_state . as_active_stream ( )
133
- . map ( |active_stream| active_stream. configuration ) ;
134
-
135
113
log:: info!( "Update provider configuration: {configuration:?}" ) ;
136
114
provider. on_configuration_changed ( configuration) ;
137
115
}
@@ -148,108 +126,3 @@ pub trait Provider : Send + 'static {
148
126
fn on_configuration_changed ( & mut self , _: Option < Configuration > ) ;
149
127
fn fill_buffer ( & mut self , buffer : & mut [ f32 ] ) ;
150
128
}
151
-
152
-
153
- struct SharedState {
154
- provider : Mutex < Option < Box < dyn Provider > > > ,
155
- device_lost : AtomicBool ,
156
- }
157
-
158
-
159
- // should be able to close and reopen streams dynamically, potentially on different devices
160
- // any non-device state should be maintained
161
- // should be able to cope with different sample rates
162
-
163
-
164
- fn start_stream_build ( shared : Arc < SharedState > ) -> JoinHandle < anyhow:: Result < ActiveStream > > {
165
- std:: thread:: spawn ( move || {
166
- let host = cpal:: default_host ( ) ;
167
- build_output_stream ( & host, shared. clone ( ) )
168
- } )
169
- }
170
-
171
-
172
- #[ instrument( skip_all, name="audio build_output_stream" ) ]
173
- fn build_output_stream ( host : & cpal:: Host , shared : Arc < SharedState > ) -> anyhow:: Result < ActiveStream > {
174
- let device = host. default_output_device ( ) . context ( "no output device available" ) ?;
175
-
176
- log:: info!( "Selected audio device: {}" , device. name( ) . unwrap_or_else( |_| String :: from( "<no name>" ) ) ) ;
177
-
178
- let supported_configs_range = device. supported_output_configs ( )
179
- . context ( "error while querying configs" ) ?;
180
-
181
- // TODO(pat.m): support different sample formats
182
- let supported_config = supported_configs_range
183
- . filter ( |config| config. sample_format ( ) . is_float ( ) )
184
- . max_by ( cpal:: SupportedStreamConfigRange :: cmp_default_heuristics)
185
- . context ( "couldn't find a supported configuration" ) ?;
186
-
187
- let desired_sample_rate = 48000 . clamp ( supported_config. min_sample_rate ( ) . 0 , supported_config. max_sample_rate ( ) . 0 ) ;
188
- let supported_config = supported_config
189
- . with_sample_rate ( cpal:: SampleRate ( desired_sample_rate) ) ;
190
-
191
- let config = supported_config. into ( ) ;
192
-
193
- log:: info!( "Selected audio device config: {config:#?}" ) ;
194
-
195
- let stream = device. build_output_stream (
196
- & config,
197
- {
198
- let shared = Arc :: clone ( & shared) ;
199
-
200
- move |data : & mut [ f32 ] , _: & cpal:: OutputCallbackInfo | {
201
- let _span = tracing:: trace_span!( "audio provider callback" ) . entered ( ) ;
202
-
203
- let mut provider_maybe = shared. provider . lock ( ) . unwrap ( ) ;
204
- if let Some ( provider) = & mut * provider_maybe {
205
- provider. fill_buffer ( data) ;
206
- } else {
207
- data. fill ( 0.0 ) ;
208
- }
209
- }
210
- } ,
211
- {
212
- move |err| {
213
- // react to errors here.
214
- log:: warn!( "audio device lost! {err}" ) ;
215
- shared. device_lost . store ( true , Ordering :: Relaxed ) ;
216
- }
217
- } ,
218
- None // None=blocking, Some(Duration)=timeout
219
- ) ?;
220
-
221
- stream. play ( ) ?;
222
-
223
- let configuration = Configuration {
224
- sample_rate : config. sample_rate . 0 as u32 ,
225
- channels : config. channels as usize ,
226
- } ;
227
-
228
- Ok ( ActiveStream {
229
- // We pass around the StreamInner so that we can avoid the !Sync/!Send bounds on Stream.
230
- // Send/Sync are disabled because of android, but we don't care about that.
231
- // https://docs.rs/cpal/latest/x86_64-pc-windows-msvc/src/cpal/platform/mod.rs.html#67
232
- _stream : stream. into_inner ( ) ,
233
- configuration
234
- } )
235
- }
236
-
237
- struct ActiveStream {
238
- _stream : cpal:: platform:: StreamInner ,
239
- configuration : Configuration ,
240
- }
241
-
242
- enum StreamState {
243
- Pending ( Option < JoinHandle < anyhow:: Result < ActiveStream > > > ) ,
244
- Active ( ActiveStream ) ,
245
- InitFailure ,
246
- }
247
-
248
- impl StreamState {
249
- fn as_active_stream ( & self ) -> Option < & ActiveStream > {
250
- match self {
251
- StreamState :: Active ( active_stream) => Some ( active_stream) ,
252
- _ => None
253
- }
254
- }
255
- }
0 commit comments