diff --git a/tauri/src-tauri/Entitlements.plist b/tauri/src-tauri/Entitlements.plist index 46f67566..0d041c2c 100644 --- a/tauri/src-tauri/Entitlements.plist +++ b/tauri/src-tauri/Entitlements.plist @@ -8,5 +8,7 @@ com.apple.security.cs.disable-library-validation + com.apple.security.device.audio-input + diff --git a/tauri/src-tauri/gen/Assets.car b/tauri/src-tauri/gen/Assets.car index db1bac23..7b81cf77 100644 Binary files a/tauri/src-tauri/gen/Assets.car and b/tauri/src-tauri/gen/Assets.car differ diff --git a/tauri/src-tauri/src/audio_capture/macos.rs b/tauri/src-tauri/src/audio_capture/macos.rs index d929e65a..ef5451d0 100644 --- a/tauri/src-tauri/src/audio_capture/macos.rs +++ b/tauri/src-tauri/src/audio_capture/macos.rs @@ -163,25 +163,67 @@ fn extract_audio_samples(sample_buffer: CMSampleBuffer) -> Result, Stri .audio_buffer_list() .ok_or_else(|| "Failed to get audio buffer list".to_string())?; - let mut samples = Vec::new(); + let buffers: Vec<_> = audio_buffer_list.iter().collect(); + let num_buffers = buffers.len(); + + if num_buffers == 0 { + return Ok(Vec::new()); + } - // Iterate through audio buffers - for buffer in audio_buffer_list.iter() { - // Get raw bytes and interpret as f32 samples + // ScreenCaptureKit on macOS provides audio in Float32 format + // The audio can be either: + // - Interleaved (1 buffer with L,R,L,R,... samples) + // - Planar (2 buffers, one for L channel, one for R channel) + + if num_buffers == 1 { + // Interleaved stereo or mono in a single buffer + let buffer = &buffers[0]; let data_bytes = buffer.data(); let num_samples = data_bytes.len() / std::mem::size_of::(); if num_samples > 0 { unsafe { - // Interpret bytes as f32 samples let data_ptr = data_bytes.as_ptr() as *const f32; let data = std::slice::from_raw_parts(data_ptr, num_samples); - samples.extend_from_slice(data); + return Ok(data.to_vec()); } } + } else { + // Planar format - separate buffer for each channel + // We need to interleave them: L0, R0, L1, R1, ... + let mut channel_data: Vec> = Vec::new(); + let mut max_samples = 0; + + for buffer in &buffers { + let data_bytes = buffer.data(); + let num_samples = data_bytes.len() / std::mem::size_of::(); + + if num_samples > 0 { + unsafe { + let data_ptr = data_bytes.as_ptr() as *const f32; + let data = std::slice::from_raw_parts(data_ptr, num_samples); + channel_data.push(data.to_vec()); + max_samples = max_samples.max(num_samples); + } + } + } + + // Interleave the channels + let mut interleaved = Vec::with_capacity(max_samples * num_buffers); + for i in 0..max_samples { + for channel in &channel_data { + if i < channel.len() { + interleaved.push(channel[i]); + } else { + interleaved.push(0.0); // Pad with silence if needed + } + } + } + + return Ok(interleaved); } - Ok(samples) + Ok(Vec::new()) } fn samples_to_wav(samples: &[f32], sample_rate: u32, channels: u16) -> Result, String> {