@@ -27,72 +27,64 @@ use audio::AppSounds;
2727use auth:: { AuthStore , AuthenticationInvalid , Plan } ;
2828use camera:: { CameraPreview , CameraWindowState } ;
2929use cap_displays:: { DisplayId , WindowId , bounds:: LogicalBounds } ;
30- use cap_editor:: EditorInstance ;
31- use cap_editor:: EditorState ;
32- use cap_project:: RecordingMetaInner ;
33- use cap_project:: XY ;
30+ use cap_editor:: { EditorInstance , EditorState } ;
3431use cap_project:: {
35- ProjectConfiguration , RecordingMeta , SharingMeta , StudioRecordingMeta , ZoomSegment ,
32+ ProjectConfiguration , RecordingMeta , RecordingMetaInner , SharingMeta , StudioRecordingMeta , XY ,
33+ ZoomSegment ,
3634} ;
37- use cap_recording:: feeds:: DeviceOrModelID ;
3835use cap_recording:: {
39- feeds:: CameraFeed ,
40- feeds:: RawCameraFrame ,
41- feeds:: { AudioInputFeed , AudioInputSamplesSender } ,
36+ feeds:: {
37+ self , CameraFeed , DeviceOrModelID , RawCameraFrame ,
38+ microphone:: { self , MicrophoneFeed } ,
39+ } ,
4240 sources:: ScreenCaptureTarget ,
4341} ;
44- use cap_rendering:: ProjectRecordingsMeta ;
45- use cap_rendering:: RenderedFrame ;
42+ use cap_rendering:: { ProjectRecordingsMeta , RenderedFrame } ;
4643use clipboard_rs:: common:: RustImage ;
4744use clipboard_rs:: { Clipboard , ClipboardContext } ;
48- use editor_window:: EditorInstances ;
49- use editor_window:: WindowEditorInstance ;
45+ use editor_window:: { EditorInstances , WindowEditorInstance } ;
5046use general_settings:: GeneralSettingsStore ;
47+ use kameo:: { Actor , actor:: ActorRef } ;
5148use mp4:: Mp4Reader ;
5249use notifications:: NotificationType ;
5350use png:: { ColorType , Encoder } ;
5451use recording:: InProgressRecording ;
5552use relative_path:: RelativePathBuf ;
56-
57- use scap :: capturer:: Capturer ;
58- use scap :: frame:: Frame ;
59- use scap :: frame :: VideoFrame ;
53+ use scap :: {
54+ capturer:: Capturer ,
55+ frame:: { Frame , VideoFrame } ,
56+ } ;
6057use serde:: { Deserialize , Serialize } ;
6158use serde_json:: json;
6259use specta:: Type ;
63- use std:: collections:: BTreeMap ;
64- use std:: path:: Path ;
65- use std:: time:: Duration ;
6660use std:: {
61+ collections:: BTreeMap ,
6762 fs:: File ,
6863 future:: Future ,
6964 io:: { BufReader , BufWriter } ,
7065 marker:: PhantomData ,
71- path:: PathBuf ,
66+ path:: { Path , PathBuf } ,
7267 process:: Command ,
7368 str:: FromStr ,
7469 sync:: Arc ,
70+ time:: Duration ,
7571} ;
76- use tauri:: Window ;
77- use tauri:: { AppHandle , Manager , State , WindowEvent } ;
72+ use tauri:: { AppHandle , Manager , State , Window , WindowEvent } ;
7873use tauri_plugin_deep_link:: DeepLinkExt ;
7974use tauri_plugin_dialog:: DialogExt ;
8075use tauri_plugin_global_shortcut:: GlobalShortcutExt ;
8176use tauri_plugin_notification:: { NotificationExt , PermissionState } ;
8277use tauri_plugin_opener:: OpenerExt ;
8378use tauri_plugin_shell:: ShellExt ;
8479use tauri_specta:: Event ;
85- use tokio:: sync:: mpsc;
86- use tokio:: sync:: { Mutex , RwLock } ;
87- use tokio:: time:: timeout;
88- use tracing:: debug;
89- use tracing:: error;
90- use tracing:: trace;
80+ use tokio:: {
81+ sync:: { Mutex , RwLock , mpsc} ,
82+ time:: timeout,
83+ } ;
84+ use tracing:: { error, trace} ;
9185use upload:: { S3UploadMeta , create_or_get_video, upload_image, upload_video} ;
9286use web_api:: ManagerExt as WebManagerExt ;
93- use windows:: EditorWindowIds ;
94- use windows:: set_window_transparent;
95- use windows:: { CapWindowId , ShowCapWindow } ;
87+ use windows:: { CapWindowId , EditorWindowIds , ShowCapWindow , set_window_transparent} ;
9688
9789use crate :: upload:: build_video_meta;
9890
@@ -116,15 +108,13 @@ pub struct App {
116108 #[ serde( skip) ]
117109 camera_feed_initialization : Option < mpsc:: Sender < ( ) > > ,
118110 #[ serde( skip) ]
119- mic_feed : Option < AudioInputFeed > ,
120- #[ serde( skip) ]
121- mic_samples_tx : AudioInputSamplesSender ,
122- #[ serde( skip) ]
123111 handle : AppHandle ,
124112 #[ serde( skip) ]
125113 recording_state : RecordingState ,
126114 #[ serde( skip) ]
127115 recording_logging_handle : LoggingHandle ,
116+ #[ serde( skip) ]
117+ mic_feed : ActorRef < feeds:: microphone:: MicrophoneFeed > ,
128118 server_url : String ,
129119}
130120
@@ -227,27 +217,26 @@ impl App {
227217#[ tauri:: command]
228218#[ specta:: specta]
229219async fn set_mic_input ( state : MutableState < ' _ , App > , label : Option < String > ) -> Result < ( ) , String > {
230- let mut app = state. write ( ) . await ;
220+ let mic_feed = state. read ( ) . await . mic_feed . clone ( ) ;
231221
232- match ( label, & mut app. mic_feed ) {
233- ( Some ( label) , None ) => {
234- AudioInputFeed :: init ( & label)
235- . await
236- . map_err ( |e| e. to_string ( ) )
237- . map ( async |feed| {
238- feed. add_sender ( app. mic_samples_tx . clone ( ) ) . await . unwrap ( ) ;
239- app. mic_feed = Some ( feed) ;
240- } )
241- . transpose_async ( )
222+ match label {
223+ None => {
224+ mic_feed
225+ . ask ( microphone:: RemoveInput )
242226 . await
227+ . map_err ( |e| e. to_string ( ) ) ?;
243228 }
244- ( Some ( label) , Some ( feed) ) => feed. switch_input ( & label) . await . map_err ( |e| e. to_string ( ) ) ,
245- ( None , _) => {
246- debug ! ( "removing mic in set_start_recording_options" ) ;
247- app. mic_feed . take ( ) ;
248- Ok ( ( ) )
229+ Some ( label) => {
230+ mic_feed
231+ . ask ( feeds:: microphone:: SetInput { label } )
232+ . await
233+ . map_err ( |e| e. to_string ( ) ) ?
234+ . await
235+ . map_err ( |e| e. to_string ( ) ) ?;
249236 }
250237 }
238+
239+ Ok ( ( ) )
251240}
252241
253242#[ tauri:: command]
@@ -1119,7 +1108,7 @@ async fn list_audio_devices() -> Result<Vec<String>, ()> {
11191108 return Ok ( vec ! [ ] ) ;
11201109 }
11211110
1122- Ok ( AudioInputFeed :: list_devices ( ) . keys ( ) . cloned ( ) . collect ( ) )
1111+ Ok ( MicrophoneFeed :: list ( ) . keys ( ) . cloned ( ) . collect ( ) )
11231112}
11241113
11251114#[ derive( Serialize , Type , tauri_specta:: Event , Debug , Clone ) ]
@@ -2024,7 +2013,28 @@ pub async fn run(recording_logging_handle: LoggingHandle) {
20242013
20252014 let ( camera_tx, camera_ws_port, _shutdown) = camera_legacy:: create_camera_preview_ws ( ) . await ;
20262015
2027- let ( audio_input_tx, audio_input_rx) = AudioInputFeed :: create_channel ( ) ;
2016+ let ( mic_samples_tx, mic_samples_rx) = flume:: bounded ( 8 ) ;
2017+
2018+ let mic_feed = {
2019+ let ( error_tx, error_rx) = flume:: bounded ( 1 ) ;
2020+
2021+ let mic_feed = MicrophoneFeed :: spawn ( MicrophoneFeed :: new ( error_tx) ) ;
2022+
2023+ // TODO: make this part of a global actor one day
2024+ tokio:: spawn ( async move {
2025+ let Ok ( err) = error_rx. recv_async ( ) . await else {
2026+ return ;
2027+ } ;
2028+
2029+ error ! ( "Mic feed actor error: {err}" ) ;
2030+ } ) ;
2031+
2032+ let _ = mic_feed
2033+ . ask ( feeds:: microphone:: AddSender ( mic_samples_tx) )
2034+ . await ;
2035+
2036+ mic_feed
2037+ } ;
20282038
20292039 tauri:: async_runtime:: set ( tokio:: runtime:: Handle :: current ( ) ) ;
20302040
@@ -2121,10 +2131,9 @@ pub async fn run(recording_logging_handle: LoggingHandle) {
21212131 handle : app. clone ( ) ,
21222132 camera_feed : None ,
21232133 camera_feed_initialization : None ,
2124- mic_samples_tx : audio_input_tx,
2125- mic_feed : None ,
21262134 recording_state : RecordingState :: None ,
21272135 recording_logging_handle,
2136+ mic_feed,
21282137 server_url : GeneralSettingsStore :: get ( & app)
21292138 . ok ( )
21302139 . flatten ( )
@@ -2173,7 +2182,7 @@ pub async fn run(recording_logging_handle: LoggingHandle) {
21732182 }
21742183 } ) ;
21752184
2176- audio_meter:: spawn_event_emitter ( app. clone ( ) , audio_input_rx ) ;
2185+ audio_meter:: spawn_event_emitter ( app. clone ( ) , mic_samples_rx ) ;
21772186
21782187 tray:: create_tray ( & app) . unwrap ( ) ;
21792188
@@ -2213,7 +2222,8 @@ pub async fn run(recording_logging_handle: LoggingHandle) {
22132222 let app_state = & mut * state. write ( ) . await ;
22142223
22152224 if !app_state. is_recording_active_or_pending ( ) {
2216- app_state. mic_feed . take ( ) ;
2225+ let _ =
2226+ app_state. mic_feed . ask ( microphone:: RemoveInput ) . await ;
22172227 app_state. camera_feed . take ( ) ;
22182228
22192229 if let Some ( camera) = CapWindowId :: Camera . get ( & app) {
@@ -2422,28 +2432,6 @@ trait EventExt: tauri_specta::Event {
24222432
24232433impl < T : tauri_specta:: Event > EventExt for T { }
24242434
2425- trait TransposeAsync {
2426- type Output ;
2427-
2428- fn transpose_async ( self ) -> impl Future < Output = Self :: Output >
2429- where
2430- Self : Sized ;
2431- }
2432-
2433- impl < F : Future < Output = T > , T , E > TransposeAsync for Result < F , E > {
2434- type Output = Result < T , E > ;
2435-
2436- async fn transpose_async ( self ) -> Self :: Output
2437- where
2438- Self : Sized ,
2439- {
2440- match self {
2441- Ok ( f) => Ok ( f. await ) ,
2442- Err ( e) => Err ( e) ,
2443- }
2444- }
2445- }
2446-
24472435fn open_project_from_path ( path : & Path , app : AppHandle ) -> Result < ( ) , String > {
24482436 let meta = RecordingMeta :: load_for_project ( path) . map_err ( |v| v. to_string ( ) ) ?;
24492437
0 commit comments