33use leptos:: prelude:: * ;
44use leptos_use:: { use_user_media_with_options, UseUserMediaOptions , UseUserMediaReturn } ;
55use thaw:: * ;
6+ use web_sys:: wasm_bindgen:: { closure:: Closure , JsCast , JsValue } ;
7+ use web_sys:: MediaRecorder ;
68
79use crate :: css:: styles;
810
11+ // Record audio
12+ //
13+ // Thanks you <https://github.com/wayeast/mediarecorder/blob/master/src/lib.rs> and chatgpt sucked
14+ // lemons.
915#[ component]
1016pub fn AudioStream ( ) -> impl IntoView {
1117 let node = NodeRef :: < leptos:: html:: Audio > :: new ( ) ;
18+ let canvas_node = NodeRef :: < leptos:: html:: Canvas > :: new ( ) ;
1219
13- let start_rec = RwSignal :: new ( true ) ;
14- let result = RwSignal :: new_local ( "" . to_string ( ) ) ;
20+ let start_rec = RwSignal :: new ( false ) ;
21+ let async_blob = RwSignal :: new ( None ) ;
1522
1623 // Create options to fetch only audio stream.
1724 let options = UseUserMediaOptions :: default ( ) . video ( false ) . audio ( true ) ;
@@ -23,7 +30,35 @@ pub fn AudioStream() -> impl IntoView {
2330 ..
2431 } = use_user_media_with_options ( options) ;
2532
26- // start/stop recording
33+ let on_data_available = Closure :: wrap ( Box :: new ( move |data : JsValue | {
34+ if let Ok ( blob) = data. dyn_into :: < web_sys:: BlobEvent > ( ) {
35+ if let Some ( data) = blob. data ( ) {
36+ // convert to gloo_file Blog.
37+ let blob = gloo_file:: Blob :: from ( data) ;
38+ * async_blob. write ( ) = Some ( LocalResource :: new ( move || {
39+ gloo_file:: futures:: read_as_bytes ( & blob)
40+ } ) ) ;
41+ }
42+ } else {
43+ tracing:: info!( " bad data" ) ;
44+ }
45+ } ) as Box < dyn FnMut ( JsValue ) > ) ;
46+
47+ Effect :: new ( move |_| {
48+ node. get ( ) . map ( |v| match stream. get ( ) {
49+ Some ( Ok ( s) ) => {
50+ tracing:: info!( "Setting stream {s:?} to src..." ) ;
51+ v. set_src_object ( Some ( & s) ) ;
52+ let recorder = MediaRecorder :: new_with_media_stream ( & s) . unwrap ( ) ;
53+ recorder. set_ondataavailable ( Some ( on_data_available. as_ref ( ) . unchecked_ref ( ) ) ) ;
54+ recorder. start_with_time_slice ( 500 ) . unwrap ( ) ;
55+ }
56+ Some ( Err ( e) ) => tracing:: error!( "Failed to get media stream: {e:?}" ) ,
57+ None => tracing:: debug!( "No stream yet" ) ,
58+ } ) ;
59+ } ) ;
60+
61+ // start/stop recording
2762 let _effect = Effect :: watch (
2863 move || start_rec. get ( ) ,
2964 move |val, _prev, _| {
@@ -38,24 +73,42 @@ pub fn AudioStream() -> impl IntoView {
3873 true , /* Trigger it as soon as possible */
3974 ) ;
4075
76+ // Watch async_blob
4177 Effect :: new ( move |_| {
42- tracing:: debug!( "State of the recording: {}." , start_rec. get_untracked( ) ) ;
43- node. get ( ) . map ( |v| match stream. get ( ) {
44- Some ( Ok ( s) ) => {
45- tracing:: debug!( "Setting stream {s:?} to src..." ) ;
46- v. set_src_object ( Some ( & s) ) ;
47- } ,
48- Some ( Err ( e) ) => tracing:: error!( "Failed to get media stream: {e:?}" ) ,
49- None => tracing:: debug!( "No stream yet" ) ,
50- } ) ;
78+ let ctx = canvas_node
79+ . get ( )
80+ . unwrap ( )
81+ . get_context ( "2d" )
82+ . unwrap ( )
83+ . unwrap ( )
84+ . dyn_into :: < web_sys:: CanvasRenderingContext2d > ( )
85+ . unwrap ( ) ;
86+
87+ if let Some ( blob_resource) = async_blob. get ( ) {
88+ if let Some ( Ok ( data) ) = blob_resource. read ( ) . as_deref ( ) {
89+ tracing:: info!( "Got blob of buffer {data:?}" ) ;
90+ ctx. reset ( ) ;
91+ ctx. begin_path ( ) ;
92+ for ( i, v) in data. iter ( ) . enumerate ( ) {
93+ ctx. line_to (
94+ i as f64 ,
95+ * v as f64 / 5.0 + 50.0 , /* 50 is half of height of canvas */
96+ )
97+ }
98+ ctx. stroke ( ) ; // render
99+ }
100+ }
51101 } ) ;
52102
53103 view ! {
54104 <Space vertical=true >
55105 // Eventually I was to draw something related to audio stream here.
56- <canvas />
57- <audio node_ref=node controls />
106+ <canvas node_ref=canvas_node class=styles :: canvas />
107+ <audio node_ref=node controls />
58108 <Switch checked=start_rec label="Start Record" />
109+ <div>
110+ "Record and plot every half second of data"
111+ </div>
59112 </Space >
60113 }
61114}
0 commit comments