Skip to content

Conversation

@ComputelessComputer
Copy link
Collaborator

@ComputelessComputer ComputelessComputer commented Jul 16, 2025

implemented UI for #1062
please take on the Rust B/E from here :) @yujonglee


This is part 1 of 2 in a stack made with GitButler:

@coderabbitai
Copy link

coderabbitai bot commented Jul 16, 2025

📝 Walkthrough

Walkthrough

The changes refactor the audio control UI in the listen button component by introducing separate MicrophoneSelector and SpeakerButton components, enhancing device selection and mute toggling. Layout and icon updates are included. Localization files are updated to reflect new source code line numbers, with no changes to translation content. Additionally, internal microphone input handling was replaced with a new async device management implementation, and dependencies and logging configurations were updated.

Changes

File(s) Change Summary
apps/desktop/src/components/editor-area/note-header/listen-button.tsx Replaced AudioControlButton with MicrophoneSelector and SpeakerButton, added microphone device popover, updated icons and layout.
apps/desktop/src/locales/en/messages.po, apps/desktop/src/locales/ko/messages.po Adjusted source code line numbers in translation comments for listen button messages.
crates/audio/src/mic.rs, crates/audio/src/lib.rs, crates/audio/src/errors.rs, crates/audio/Cargo.toml Replaced external mic input re-export with internal async microphone input implementation supporting device enumeration, switching, and streaming; updated error handling and initialization. Added dasp dependency.
crates/onnx/src/lib.rs, crates/vad/src/lib.rs, crates/vad/src/error.rs, crates/vad/Cargo.toml Changed ONNX session logging level to error; updated vad crate imports and error variants to use hypr_onnx crate instead of direct ort and ndarray dependencies.
apps/desktop/src-tauri/src/lib.rs Modified logging environment filter to set "ort::logging" level to error specifically.
Cargo.toml Updated libsql dependency version from 0.9.8 to 0.9.17.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant ListenButton
    participant MicrophoneSelector
    participant SpeakerButton
    participant DeviceQuery

    User->>ListenButton: Interacts with audio controls
    ListenButton->>MicrophoneSelector: Render with mute toggle and device selection
    MicrophoneSelector->>DeviceQuery: Fetch available microphones
    DeviceQuery-->>MicrophoneSelector: Return device list
    User->>MicrophoneSelector: Selects device or toggles mute
    MicrophoneSelector-->>ListenButton: Notify of mute/device change
    ListenButton->>SpeakerButton: Render with mute toggle
    User->>SpeakerButton: Toggles speaker mute
    SpeakerButton-->>ListenButton: Notify of speaker mute state
Loading

Possibly related PRs

✨ Finishing Touches
  • 📝 Generate Docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

‼️ IMPORTANT
Auto-reply has been disabled for this repository in the CodeRabbit settings. The CodeRabbit bot will not respond to your replies unless it is explicitly tagged.

  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🔭 Outside diff range comments (1)
apps/desktop/src/locales/en/messages.po (1)

583-583: Fix the merge conflict marker.

There's an unresolved git merge conflict marker in the translation string. This will cause issues in the application.

Apply this fix:

-msgstr "Enable Integration<<<<<<< HEAD"
+msgstr "Enable Integration"
🧹 Nitpick comments (1)
apps/desktop/src/components/editor-area/note-header/listen-button.tsx (1)

335-335: Consider adding error handling for audio mutations.

The mutation calls don't handle potential errors, which could leave the UI in an inconsistent state if device operations fail.

Add error handling for better UX:

-onToggleMuted={() => toggleMicMuted.mutate()}
+onToggleMuted={() => {
+  toggleMicMuted.mutate(undefined, {
+    onError: (error) => {
+      sonnerToast.error("Failed to toggle microphone");
+    }
+  });
+}}

Also applies to: 339-339

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3a47d28 and a65b0fe.

📒 Files selected for processing (3)
  • apps/desktop/src/components/editor-area/note-header/listen-button.tsx (3 hunks)
  • apps/desktop/src/locales/en/messages.po (6 hunks)
  • apps/desktop/src/locales/ko/messages.po (6 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{js,ts,tsx,rs}

Instructions used from:

Sources:
⚙️ CodeRabbit Configuration File

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: ci (macos, macos-latest)
  • GitHub Check: ci (windows, windows-latest)
🔇 Additional comments (7)
apps/desktop/src/locales/ko/messages.po (1)

260-261: LGTM! Source reference updates are correct.

The line number updates properly reflect the code changes in listen-button.tsx after the UI refactoring. These maintenance updates ensure accurate linkage between translation entries and source code.

Also applies to: 875-875, 944-944, 956-956, 1000-1000, 1098-1098, 1102-1102

apps/desktop/src/locales/en/messages.po (1)

260-261: LGTM! Source reference updates are correct.

The line number updates properly reflect the code changes in listen-button.tsx after the UI refactoring.

Also applies to: 875-875, 944-944, 956-956, 1000-1000, 1098-1098, 1102-1102

apps/desktop/src/components/editor-area/note-header/listen-button.tsx (5)

3-13: LGTM! Import updates are clean and used.

All new icon imports are properly utilized throughout the component for the UI refactoring.


332-341: LGTM! Layout refactoring improves component separation.

The change from justify-between to gap-2 with flex-1 components provides better visual balance and separates microphone and speaker controls appropriately.


372-372: LGTM! Icon updates are consistent.

The updated icons (Pause and Square) provide better visual consistency with the new UI design.

Also applies to: 380-380


496-523: LGTM! SpeakerButton implementation is clean.

The simplified speaker button component follows the same pattern as the microphone button and provides consistent styling.


406-410: Fix potential infinite loop in useEffect.

The selectedDevice dependency in the useEffect could cause an infinite loop since selectedDevice is set within the effect.

Apply this fix to prevent potential infinite loops:

 useEffect(() => {
   if (!selectedDevice && devices.length > 0) {
     setSelectedDevice(devices[0]);
   }
-}, [devices, selectedDevice]);
+}, [devices]);

Likely an incorrect or invalid review comment.

Comment on lines +388 to +494
function MicrophoneSelector({
isMuted,
onToggleMuted,
disabled,
}: {
isMuted?: boolean;
onToggleMuted: () => void;
disabled?: boolean;
}) {
const [isOpen, setIsOpen] = useState(false);
const [selectedDevice, setSelectedDevice] = useState<string>("");

const { data: devices = [], isLoading } = useQuery({
queryKey: ["microphone-devices"],
queryFn: () => listenerCommands.listMicrophoneDevices(),
refetchOnWindowFocus: false,
});

useEffect(() => {
if (!selectedDevice && devices.length > 0) {
setSelectedDevice(devices[0]);
}
}, [devices, selectedDevice]);

const Icon = isMuted ? MicOffIcon : MicIcon;

return (
<div className="flex-1 min-w-0">
<Popover open={isOpen} onOpenChange={setIsOpen}>
<div className="flex -space-x-px">
<Button
variant="outline"
className="rounded-r-none flex-1 min-w-0 h-10"
disabled={disabled}
onClick={onToggleMuted}
>
<Icon
className={cn(
"w-4 h-4 flex-shrink-0",
isMuted ? "text-neutral-500" : "",
disabled && "text-neutral-300",
)}
/>
{!disabled && <SoundIndicator input="mic" size="long" />}
</Button>

<PopoverTrigger asChild>
<Button
variant="outline"
className="rounded-l-none px-0.5 flex-shrink-0"
disabled={disabled}
>
<ChevronDown className="w-4 h-4" />
</Button>
</PopoverTrigger>
</div>

<PopoverContent className="w-64 p-0" align="end">
<div className="p-2">
<div className="mb-2 px-2">
<span className="text-sm font-medium">Microphone</span>
</div>

{isLoading
? (
<div className="p-4 text-center">
<div className="animate-spin rounded-full h-4 w-4 border-b-2 border-neutral-600 mx-auto"></div>
<p className="text-sm text-neutral-500 mt-2">Loading devices...</p>
</div>
)
: devices.length === 0
? (
<div className="p-4 text-center">
<p className="text-sm text-neutral-500">No microphones found</p>
</div>
)
: (
<div className="space-y-1">
{devices.map((device) => {
const isSelected = device === selectedDevice;
return (
<Button
key={device}
variant="ghost"
className={cn(
"w-full justify-start text-left h-8 px-2",
isSelected && "bg-neutral-100",
)}
onClick={() => {
setSelectedDevice(device);
setIsOpen(false);
}}
>
<Icon className="w-3 h-3 mr-2 flex-shrink-0" />
<span className="text-sm truncate flex-1">{device}</span>
{isSelected && <Check className="w-3 h-3 ml-auto flex-shrink-0 text-green-600" />}
</Button>
);
})}
</div>
)}
</div>
</PopoverContent>
</Popover>
</div>
);
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Device selection logic is incomplete.

The selectedDevice state is managed but never used to actually change the microphone device. This creates a misleading UI where users can select devices but the selection has no effect.

Add device switching functionality:

 const handleStopWithTemplate = () => {
   const actualTemplateId = selectedTemplate === "auto" ? null : selectedTemplate;
   onStop(actualTemplateId);
 };
+
+const switchMicrophone = useMutation({
+  mutationFn: (deviceId: string) => listenerCommands.setMicrophoneDevice(deviceId),
+});

 onClick={() => {
   setSelectedDevice(device);
+  switchMicrophone.mutate(device);
   setIsOpen(false);
 }}

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In apps/desktop/src/components/editor-area/note-header/listen-button.tsx around
lines 388 to 494, the selectedDevice state is updated on user selection but not
used to switch the actual microphone input device. To fix this, implement logic
that applies the selectedDevice to change the active microphone input. This
likely involves calling a function or command to switch the microphone device
whenever selectedDevice changes, ensuring the UI selection affects the actual
audio input source.

This commit introduces several improvements to the microphone and speaker controls in the listen-button component:

- Adds a `MicrophoneSelector` component that allows users to select the active microphone device and toggle the microphone mute state.
- Replaces the previous `AudioControlButton` component with a `SpeakerButton` that handles the speaker mute state.
- Updates the button icons to use the more appropriate `Pause` and `Square` icons for the pause and stop actions, respectively.
- Ensures consistent styling and layout for the microphone and speaker controls.

These changes aim to provide a more intuitive and user-friendly experience for managing audio input and output during the recording process.
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🔭 Outside diff range comments (2)
crates/audio/src/lib.rs (2)

21-39: Add error handling for audio output operations

The to_speaker method silently ignores errors from OutputStream::try_default() and Decoder::new().

Consider returning a Result to propagate errors:

-pub fn to_speaker(bytes: &'static [u8]) -> std::sync::mpsc::Sender<()> {
+pub fn to_speaker(bytes: &'static [u8]) -> Result<std::sync::mpsc::Sender<()>, crate::Error> {
     use rodio::{Decoder, OutputStream, Sink};
     let (tx, rx) = std::sync::mpsc::channel();

     std::thread::spawn(move || {
-        if let Ok((_, stream)) = OutputStream::try_default() {
+        let (_, stream) = OutputStream::try_default()?;
             let file = std::io::Cursor::new(bytes);
-            if let Ok(source) = Decoder::new(file) {
+            let source = Decoder::new(file)?;
                 let sink = Sink::try_new(&stream).unwrap();
                 sink.append(source);

                 let _ = rx.recv_timeout(std::time::Duration::from_secs(3600));
                 sink.stop();
-            }
-        }
     });

-    tx
+    Ok(tx)
 }

41-56: Add error handling for silence method

Similar to to_speaker, the silence method ignores errors from OutputStream::try_default().

Consider propagating errors:

-pub fn silence() -> std::sync::mpsc::Sender<()> {
+pub fn silence() -> Result<std::sync::mpsc::Sender<()>, crate::Error> {
     use rodio::{source::Zero, OutputStream, Sink};
     let (tx, rx) = std::sync::mpsc::channel();

     std::thread::spawn(move || {
-        if let Ok((_, stream)) = OutputStream::try_default() {
+        let (_, stream) = OutputStream::try_default()?;
             let sink = Sink::try_new(&stream).unwrap();
             sink.append(Zero::<f32>::new(1, 16000));

             let _ = rx.recv();
             sink.stop();
-        }
     });

-    tx
+    Ok(tx)
 }
🧹 Nitpick comments (2)
crates/audio/src/mic.rs (2)

14-19: Remove unnecessary comment

The comment "Information about an audio input device" is redundant - it describes "what" the struct is, which is already clear from the name.

Remove the comment:

-/// Information about an audio input device
 #[derive(Debug, Clone)]
 pub struct AudioDeviceInfo {

348-365: Potential busy-loop in read_all method

The read_all method polls in a tight loop without yielding, which could consume excessive CPU.

Consider adding a yield or using a different approach:

 pub fn read_all(&mut self) -> rodio::buffer::SamplesBuffer<f32> {
     let mut samples = Vec::new();
     let mut cx = std::task::Context::from_waker(futures_util::task::noop_waker_ref());
+    let mut consecutive_empty = 0;

     // Drain all available samples
     while let std::task::Poll::Ready(Some(sample)) = self.receiver.poll_next_unpin(&mut cx) {
         samples.push(sample);
+        consecutive_empty = 0;
     }
+    
+    // Avoid tight loop if no samples available
+    if consecutive_empty > 0 {
+        std::thread::yield_now();
+    }

     rodio::buffer::SamplesBuffer::new(
         self.config.channels(),
         self.config.sample_rate().0,
         samples,
     )
 }
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 88619df and 7557d82.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (10)
  • Cargo.toml (1 hunks)
  • apps/desktop/src-tauri/src/lib.rs (1 hunks)
  • crates/audio/Cargo.toml (1 hunks)
  • crates/audio/src/errors.rs (1 hunks)
  • crates/audio/src/lib.rs (1 hunks)
  • crates/audio/src/mic.rs (1 hunks)
  • crates/onnx/src/lib.rs (2 hunks)
  • crates/vad/Cargo.toml (1 hunks)
  • crates/vad/src/error.rs (1 hunks)
  • crates/vad/src/lib.rs (2 hunks)
✅ Files skipped from review due to trivial changes (4)
  • Cargo.toml
  • crates/audio/Cargo.toml
  • crates/vad/src/lib.rs
  • crates/vad/src/error.rs
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{js,ts,tsx,rs}

Instructions used from:

Sources:
⚙️ CodeRabbit Configuration File

🪛 GitHub Actions: .github/workflows/fmt.yaml
apps/desktop/src-tauri/src/lib.rs

[error] 18-24: dprint formatting check failed. File is not formatted according to dprint rules.

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: ci (windows, windows-latest)
  • GitHub Check: ci (macos, macos-latest)
  • GitHub Check: ci
🔇 Additional comments (6)
crates/onnx/src/lib.rs (2)

2-2: LGTM - Import is properly used.

The LogLevel import is correctly utilized in the session builder configuration.


12-12: LGTM - Logging configuration is appropriate.

Setting the ONNX runtime log level to Error effectively reduces verbosity by suppressing info, warning, and debug messages, which aligns with the coordinated logging changes across the application.

crates/vad/Cargo.toml (2)

8-8: Confirm hypr-onnx exposes load-dynamic feature

In crates/vad/Cargo.toml (around line 8) you’ve added:

[features]
load-dynamic = ["hypr-onnx/load-dynamic"]

Since there’s no local hypr-onnx crate in this workspace, please verify that the external hypr-onnx version you depend on actually defines a load-dynamic feature in its Cargo.toml (e.g. on crates.io or in its upstream repo). Without that, enabling load-dynamic will break CI builds run with --all-features.


10-12: All direct ort/ndarray references now go through hypr-onnx re-exports
A search for \bort:: and \bndarray:: in crates/vad/src only turned up:

  • Nested imports in
    use hypr_onnx::{
        ndarray::{},
        ort::{},
    };
  • Calls to hypr_onnx::ort::inputs! and error conversions via hypr_onnx::{ort, ndarray}

No standalone use ndarray::… or use ort::… remain. No further action required.

crates/audio/src/lib.rs (1)

76-76: Add error handling instead of using unwrap()

The unwrap() call will panic if no input device is found. This violates the coding guideline about error handling.

Apply this diff to propagate the error:

-pub fn from_mic() -> Self {
+pub fn from_mic() -> Result<Self, crate::Error> {
     Self {
         source: AudioSource::RealtimeMic,
-        mic: Some(MicInput::new().unwrap()),
+        mic: Some(MicInput::new()?),
         speaker: None,
         data: None,
-    }
+    }.into()
 }

You'll also need to update the return type to Result<Self, crate::Error>.

Likely an incorrect or invalid review comment.

crates/audio/src/errors.rs (1)

1-8: LGTM!

The error enum is well-structured with appropriate error variants for audio input handling.

Comment on lines +20 to +23
let env_filter = EnvFilter::try_from_default_env().unwrap_or_else(|_| {
EnvFilter::new("info")
.add_directive("ort::logging=error".parse().unwrap())
});
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix formatting issue flagged by pipeline.

The logging configuration logic is correct - it maintains the default "info" level while specifically setting "ort::logging" to "error" level, which coordinates well with the ONNX crate changes. However, the pipeline indicates a formatting issue that needs to be resolved.

Please run the formatter to fix the dprint formatting issue:

dprint fmt
🤖 Prompt for AI Agents
In apps/desktop/src-tauri/src/lib.rs around lines 20 to 23, the code formatting
does not comply with the project's style guidelines, causing a pipeline
formatting error. To fix this, run the dprint formatter tool by executing
"dprint fmt" in the project root directory to automatically correct the
formatting issues in this file.

Comment on lines +159 to +169
let (device, config) = {
let device_guard = self
.current_device
.try_read()
.expect("Failed to read device");
let config_guard = self
.current_config
.try_read()
.expect("Failed to read config");
(device_guard.clone(), config_guard.clone())
};
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Replace expect() with proper error handling

Using try_read().expect() can panic if the lock is held by another thread.

Consider using async read or handling the error:

-// Clone current device and config synchronously using try_read
-let (device, config) = {
-    let device_guard = self
-        .current_device
-        .try_read()
-        .expect("Failed to read device");
-    let config_guard = self
-        .current_config
-        .try_read()
-        .expect("Failed to read config");
-    (device_guard.clone(), config_guard.clone())
-};
+// Clone current device and config
+let (device, config) = {
+    match (self.current_device.try_read(), self.current_config.try_read()) {
+        (Ok(device_guard), Ok(config_guard)) => {
+            (device_guard.clone(), config_guard.clone())
+        }
+        _ => {
+            // Fall back to blocking read if try_read fails
+            let device_guard = self.current_device.blocking_read();
+            let config_guard = self.current_config.blocking_read();
+            (device_guard.clone(), config_guard.clone())
+        }
+    }
+};

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In crates/audio/src/mic.rs around lines 159 to 169, replace the use of
try_read().expect() which can cause a panic if the lock is held by another
thread. Instead, handle the error returned by try_read() gracefully by matching
on the Result and implementing appropriate error handling or fallback logic, or
consider using an async read lock if applicable to avoid blocking and panics.

Comment on lines +276 to +284
Ok(_) => {}
Err(e) => {
if e.is_full() {
tracing::warn!("Audio buffer full, dropping samples");
}
// If disconnected, the stream will be cleaned up
}
}
},
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Consider implementing backpressure for audio buffer

When the buffer is full, samples are being dropped with only a warning log. This could lead to audio discontinuities.

Consider implementing a backpressure mechanism or using a larger buffer:

 match tx.try_send(samples) {
     Ok(_) => {}
     Err(e) => {
         if e.is_full() {
-            tracing::warn!("Audio buffer full, dropping samples");
+            // Apply backpressure by blocking briefly to allow consumer to catch up
+            tracing::warn!("Audio buffer full, applying backpressure");
+            std::thread::sleep(std::time::Duration::from_micros(100));
+            // Try once more after brief pause
+            let _ = tx.try_send(e.into_inner());
         }
         // If disconnected, the stream will be cleaned up
     }
 }

Alternatively, consider increasing the buffer size in line 154 from 64 to a larger value based on expected sample rates.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
match tx.try_send(samples) {
Ok(_) => {}
Err(e) => {
if e.is_full() {
tracing::warn!("Audio buffer full, dropping samples");
}
// If disconnected, the stream will be cleaned up
}
}
match tx.try_send(samples) {
Ok(_) => {}
Err(e) => {
if e.is_full() {
// Apply backpressure by blocking briefly to allow consumer to catch up
tracing::warn!("Audio buffer full, applying backpressure");
std::thread::sleep(std::time::Duration::from_micros(100));
// Try once more after brief pause
let _ = tx.try_send(e.into_inner());
}
// If disconnected, the stream will be cleaned up
}
}
🤖 Prompt for AI Agents
In crates/audio/src/mic.rs around lines 276 to 284, the current code drops audio
samples when the buffer is full, causing potential audio discontinuities. To fix
this, implement a backpressure mechanism by either blocking or retrying the send
operation until there is space in the buffer, or increase the buffer size at
line 154 from 64 to a larger value suitable for the expected sample rates to
reduce the chance of overflow.

@yujonglee
Copy link
Contributor

Will take some UI code and work on separate PR

@yujonglee yujonglee closed this Jul 24, 2025
@yujonglee yujonglee deleted the mic-selector-ui branch July 24, 2025 19:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants