66//!
77//! Note: Requires screen recording permission and audio output device.
88
9- use screencapturekit:: cpal_adapter:: { AudioFormat , CpalAudioExt } ;
9+ use screencapturekit:: cpal_adapter:: { create_output_callback , SckAudioInputStream } ;
1010use screencapturekit:: prelude:: * ;
11- use std:: sync:: { Arc , Mutex } ;
1211
1312use cpal:: traits:: { DeviceTrait , HostTrait , StreamTrait } ;
1413
15- /// Ring buffer for audio samples
16- struct AudioRingBuffer {
17- buffer : Vec < f32 > ,
18- write_pos : usize ,
19- read_pos : usize ,
20- capacity : usize ,
21- }
22-
23- impl AudioRingBuffer {
24- fn new ( capacity : usize ) -> Self {
25- Self {
26- buffer : vec ! [ 0.0 ; capacity] ,
27- write_pos : 0 ,
28- read_pos : 0 ,
29- capacity,
30- }
31- }
32-
33- fn write ( & mut self , samples : & [ f32 ] ) {
34- for & sample in samples {
35- self . buffer [ self . write_pos ] = sample;
36- self . write_pos = ( self . write_pos + 1 ) % self . capacity ;
37- }
38- }
39-
40- fn read ( & mut self , output : & mut [ f32 ] ) {
41- for sample in output. iter_mut ( ) {
42- * sample = self . buffer [ self . read_pos ] ;
43- self . read_pos = ( self . read_pos + 1 ) % self . capacity ;
44- }
45- }
46- }
47-
48- struct AudioHandler {
49- ring_buffer : Arc < Mutex < AudioRingBuffer > > ,
50- format_detected : Arc < Mutex < Option < AudioFormat > > > ,
51- }
52-
53- impl SCStreamOutputTrait for AudioHandler {
54- fn did_output_sample_buffer ( & self , sample : CMSampleBuffer , of_type : SCStreamOutputType ) {
55- if of_type != SCStreamOutputType :: Audio {
56- return ;
57- }
58-
59- // Detect audio format on first sample
60- {
61- let mut format = self . format_detected . lock ( ) . unwrap ( ) ;
62- if format. is_none ( ) {
63- if let Some ( f) = AudioFormat :: from_sample_buffer ( & sample) {
64- println ! (
65- "🔊 Audio format detected: {}Hz, {} channels, {} bits, float={}" ,
66- f. sample_rate, f. channels, f. bits_per_sample, f. is_float
67- ) ;
68- * format = Some ( f) ;
69- }
70- }
71- }
72-
73- // Copy audio samples to ring buffer
74- if let Some ( samples) = sample. audio_f32_samples ( ) {
75- let slice = samples. as_f32_slice ( ) ;
76- if !slice. is_empty ( ) {
77- let mut rb = self . ring_buffer . lock ( ) . unwrap ( ) ;
78- rb. write ( slice) ;
79- }
80- }
81- }
82- }
83-
8414fn main ( ) -> Result < ( ) , Box < dyn std:: error:: Error > > {
8515 println ! ( "🎵 cpal Audio Capture Example" ) ;
8616 println ! ( "━━━━━━━━━━━━━━━━━━━━━━━━━━━━" ) ;
@@ -103,37 +33,31 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
10333 . exclude_windows ( & [ ] )
10434 . build ( ) ;
10535
106- // Configure stream with audio capture
107- let config = SCStreamConfiguration :: new ( )
108- . with_width ( 1920 )
109- . with_height ( 1080 )
110- . with_captures_audio ( true )
111- . with_sample_rate ( 48000 )
112- . with_channel_count ( 2 ) ;
113-
114- // Setup ring buffer for audio transfer
115- let ring_buffer = Arc :: new ( Mutex :: new ( AudioRingBuffer :: new ( 48000 * 2 * 2 ) ) ) ; // 2 seconds buffer
116- let format_detected = Arc :: new ( Mutex :: new ( None ) ) ;
117-
118- let handler = AudioHandler {
119- ring_buffer : Arc :: clone ( & ring_buffer) ,
120- format_detected : Arc :: clone ( & format_detected) ,
121- } ;
122-
123- // Start capture
124- let mut stream = SCStream :: new ( & filter, & config) ;
125- stream. add_output_handler ( handler, SCStreamOutputType :: Audio ) ;
126- stream. start_capture ( ) ?;
127-
128- println ! ( "🎬 Capture started, waiting for audio format detection..." ) ;
129-
130- // Wait for audio format detection
131- let audio_format = loop {
132- std:: thread:: sleep ( std:: time:: Duration :: from_millis ( 100 ) ) ;
133- if let Some ( format) = format_detected. lock ( ) . unwrap ( ) . clone ( ) {
134- break format;
36+ // Create SCK audio input stream
37+ let mut input = SckAudioInputStream :: new ( & filter) ?;
38+ let buffer = input. ring_buffer ( ) . clone ( ) ;
39+
40+ println ! (
41+ "🔊 Audio config: {}Hz, {} channels" ,
42+ input. sample_rate( ) ,
43+ input. channels( )
44+ ) ;
45+
46+ // Start capture - the callback receives samples (we also log)
47+ input. start ( |samples, info| {
48+ static mut SAMPLE_COUNT : usize = 0 ;
49+ unsafe {
50+ SAMPLE_COUNT += samples. len ( ) ;
51+ if SAMPLE_COUNT % ( info. sample_rate as usize * 2 ) < samples. len ( ) {
52+ println ! (
53+ "📥 Captured {} samples total" ,
54+ SAMPLE_COUNT / info. channels as usize
55+ ) ;
56+ }
13557 }
136- } ;
58+ } ) ?;
59+
60+ println ! ( "🎬 Capture started" ) ;
13761
13862 // Setup cpal output
13963 let host = cpal:: default_host ( ) ;
@@ -143,15 +67,10 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
14367
14468 println ! ( "🔈 Output device: {}" , device. name( ) ?) ;
14569
146- let stream_config = audio_format. to_stream_config ( ) ;
147- let rb_clone = Arc :: clone ( & ring_buffer) ;
148-
70+ let stream_config = input. cpal_config ( ) ;
14971 let output_stream = device. build_output_stream (
15072 & stream_config,
151- move |data : & mut [ f32 ] , _: & cpal:: OutputCallbackInfo | {
152- let mut rb = rb_clone. lock ( ) . unwrap ( ) ;
153- rb. read ( data) ;
154- } ,
73+ create_output_callback ( buffer) ,
15574 |err| eprintln ! ( "Audio output error: {}" , err) ,
15675 None ,
15776 ) ?;
@@ -166,7 +85,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
16685
16786 // Cleanup
16887 drop ( output_stream) ;
169- stream . stop_capture ( ) ?;
88+ input . stop ( ) ?;
17089
17190 println ! ( ) ;
17291 println ! ( "✅ Done!" ) ;
0 commit comments