@@ -50,6 +50,7 @@ use cap_recording::{
5050use cap_rendering:: { ProjectRecordingsMeta , RenderedFrame } ;
5151use clipboard_rs:: common:: RustImage ;
5252use clipboard_rs:: { Clipboard , ClipboardContext } ;
53+ use cpal:: StreamError ;
5354use editor_window:: { EditorInstances , WindowEditorInstance } ;
5455use ffmpeg:: ffi:: AV_TIME_BASE ;
5556use general_settings:: GeneralSettingsStore ;
@@ -109,6 +110,8 @@ pub struct App {
109110 recording_state : RecordingState ,
110111 recording_logging_handle : LoggingHandle ,
111112 mic_feed : ActorRef < feeds:: microphone:: MicrophoneFeed > ,
113+ mic_meter_sender : flume:: Sender < microphone:: MicrophoneSamples > ,
114+ selected_mic_label : Option < String > ,
112115 camera_feed : ActorRef < feeds:: camera:: CameraFeed > ,
113116 server_url : String ,
114117 logs_dir : PathBuf ,
@@ -168,6 +171,32 @@ impl App {
168171 }
169172 }
170173
174+ async fn restart_mic_feed ( & mut self ) -> Result < ( ) , String > {
175+ info ! ( "Restarting microphone feed after actor shutdown" ) ;
176+
177+ let ( error_tx, error_rx) = flume:: bounded ( 1 ) ;
178+ let mic_feed = MicrophoneFeed :: spawn ( MicrophoneFeed :: new ( error_tx) ) ;
179+
180+ spawn_mic_error_logger ( error_rx) ;
181+
182+ mic_feed
183+ . ask ( microphone:: AddSender ( self . mic_meter_sender . clone ( ) ) )
184+ . await
185+ . map_err ( |e| e. to_string ( ) ) ?;
186+
187+ if let Some ( label) = self . selected_mic_label . clone ( ) {
188+ let ready = mic_feed
189+ . ask ( microphone:: SetInput { label } )
190+ . await
191+ . map_err ( |e| e. to_string ( ) ) ?;
192+ ready. await . map_err ( |e| e. to_string ( ) ) ?;
193+ }
194+
195+ self . mic_feed = mic_feed;
196+
197+ Ok ( ( ) )
198+ }
199+
171200 async fn add_recording_logging_handle ( & mut self , path : & PathBuf ) -> Result < ( ) , String > {
172201 let logfile =
173202 std:: fs:: File :: create ( path) . map_err ( |e| format ! ( "Failed to create logfile: {e}" ) ) ?;
@@ -208,8 +237,9 @@ impl App {
208237#[ instrument( skip( state) ) ]
209238async fn set_mic_input ( state : MutableState < ' _ , App > , label : Option < String > ) -> Result < ( ) , String > {
210239 let mic_feed = state. read ( ) . await . mic_feed . clone ( ) ;
240+ let desired_label = label. clone ( ) ;
211241
212- match label {
242+ match desired_label . as_ref ( ) {
213243 None => {
214244 mic_feed
215245 . ask ( microphone:: RemoveInput )
@@ -218,14 +248,21 @@ async fn set_mic_input(state: MutableState<'_, App>, label: Option<String>) -> R
218248 }
219249 Some ( label) => {
220250 mic_feed
221- . ask ( feeds:: microphone:: SetInput { label } )
251+ . ask ( feeds:: microphone:: SetInput {
252+ label : label. clone ( ) ,
253+ } )
222254 . await
223255 . map_err ( |e| e. to_string ( ) ) ?
224256 . await
225257 . map_err ( |e| e. to_string ( ) ) ?;
226258 }
227259 }
228260
261+ {
262+ let mut app = state. write ( ) . await ;
263+ app. selected_mic_label = desired_label;
264+ }
265+
229266 Ok ( ( ) )
230267}
231268
@@ -271,6 +308,16 @@ async fn set_camera_input(
271308 Ok ( ( ) )
272309}
273310
311+ fn spawn_mic_error_logger ( error_rx : flume:: Receiver < StreamError > ) {
312+ tokio:: spawn ( async move {
313+ let Ok ( err) = error_rx. recv_async ( ) . await else {
314+ return ;
315+ } ;
316+
317+ error ! ( "Mic feed actor error: {err}" ) ;
318+ } ) ;
319+ }
320+
274321#[ derive( specta:: Type , Serialize , tauri_specta:: Event , Clone ) ]
275322pub struct RecordingOptionsChanged ;
276323
@@ -1946,6 +1993,7 @@ pub async fn run(recording_logging_handle: LoggingHandle, logs_dir: PathBuf) {
19461993 let ( camera_tx, camera_ws_port, _shutdown) = camera_legacy:: create_camera_preview_ws ( ) . await ;
19471994
19481995 let ( mic_samples_tx, mic_samples_rx) = flume:: bounded ( 8 ) ;
1996+ let mic_meter_sender = mic_samples_tx. clone ( ) ;
19491997
19501998 let camera_feed = CameraFeed :: spawn ( CameraFeed :: default ( ) ) ;
19511999 let _ = camera_feed. ask ( feeds:: camera:: AddSender ( camera_tx) ) . await ;
@@ -1955,18 +2003,14 @@ pub async fn run(recording_logging_handle: LoggingHandle, logs_dir: PathBuf) {
19552003
19562004 let mic_feed = MicrophoneFeed :: spawn ( MicrophoneFeed :: new ( error_tx) ) ;
19572005
1958- // TODO: make this part of a global actor one day
1959- tokio:: spawn ( async move {
1960- let Ok ( err) = error_rx. recv_async ( ) . await else {
1961- return ;
1962- } ;
1963-
1964- error ! ( "Mic feed actor error: {err}" ) ;
1965- } ) ;
2006+ spawn_mic_error_logger ( error_rx) ;
19662007
1967- let _ = mic_feed
2008+ if let Err ( err ) = mic_feed
19682009 . ask ( feeds:: microphone:: AddSender ( mic_samples_tx) )
1969- . await ;
2010+ . await
2011+ {
2012+ error ! ( "Failed to attach audio meter sender: {err}" ) ;
2013+ }
19702014
19712015 mic_feed
19722016 } ;
@@ -2136,6 +2180,8 @@ pub async fn run(recording_logging_handle: LoggingHandle, logs_dir: PathBuf) {
21362180 recording_state : RecordingState :: None ,
21372181 recording_logging_handle,
21382182 mic_feed,
2183+ mic_meter_sender,
2184+ selected_mic_label : None ,
21392185 camera_feed,
21402186 server_url,
21412187 logs_dir : logs_dir. clone ( ) ,
0 commit comments