Skip to content

Conversation

@rishi-jat
Copy link

@rishi-jat rishi-jat commented Nov 28, 2025

/claim #3244

Description

Fixes #3244 - Prevents duplicate recordings when both a Bluetooth device and macOS system audio are recording simultaneously.

Problem

When a Bluetooth device is connected and macOS system audio recording is started (or vice versa), both sources send audio streams to the backend, creating duplicate conversations.

Solution

Implemented priority-based recording coordination where system audio takes precedence on desktop platforms:

  1. Guard in streamDeviceRecording(): Prevents device auto-start when system audio is already active
  2. Stop logic in streamSystemAudioRecording(): Stops active device recording before starting system audio

Both guards are scoped to desktop platforms only (PlatformService.isDesktop), ensuring zero impact on mobile.

Changes

@rishi-jat
Copy link
Author

@aaravgarg please have a look when you get a chance. Thanks

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request aims to prevent duplicate recordings when both a Bluetooth device and macOS system audio are active. The changes introduce guards to coordinate recording, giving system audio precedence on desktop platforms. My review identifies a critical race condition and a high-severity code duplication issue. I've provided suggestions to resolve these problems, ensuring the state management is robust and the code is more maintainable.

Comment on lines 762 to 767
if (recordingState == RecordingState.deviceRecord && _recordingDevice != null) {
debugPrint("Stopping device recording to start system audio");
await stopStreamDeviceRecording(cleanDevice: false);
}

updateRecordingState(RecordingState.initialising);
Copy link
Contributor

Choose a reason for hiding this comment

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

critical

This change introduces a critical race condition. During await stopStreamDeviceRecording(cleanDevice: false);, the recordingState remains RecordingState.deviceRecord. If another operation (e.g., streamDeviceRecording) is triggered during this await, it will see the old state and might start conflicting operations, leading to unpredictable behavior.

To fix this, the state should be updated to RecordingState.initialising before any await calls. This acts as a lock to prevent other operations from starting. I've suggested inlining the relevant parts of stopStreamDeviceRecording to correctly manage the state transitions without causing UI flickers or incorrect states.

Suggested change
if (recordingState == RecordingState.deviceRecord && _recordingDevice != null) {
debugPrint("Stopping device recording to start system audio");
await stopStreamDeviceRecording(cleanDevice: false);
}
updateRecordingState(RecordingState.initialising);
if (recordingState == RecordingState.deviceRecord && _recordingDevice != null) {
debugPrint("Stopping device recording to start system audio");
updateRecordingState(RecordingState.initialising);
// Inlining `stopStreamDeviceRecording` to manage state correctly and avoid race conditions.
await _cleanupCurrentState();
await _socket?.stop(reason: 'stop stream device recording');
} else {
updateRecordingState(RecordingState.initialising);
}

Comment on lines +727 to 734
if (PlatformService.isDesktop &&
recordingState == RecordingState.systemAudioRecord) {
debugPrint("Skipping device recording - system audio is active");
if (device != null) _updateRecordingDevice(device); // Still track device
return;
}

if (device != null) _updateRecordingDevice(device);
Copy link
Contributor

Choose a reason for hiding this comment

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

high

There's code duplication here. The call if (device != null) _updateRecordingDevice(device); appears both inside the new if block (line 730) and after it (line 734). This can lead to maintenance issues if one instance is updated and the other is forgotten.

To improve maintainability and remove redundancy, you can move this call to before the if block and remove the other two instances. This ensures the device is updated regardless of the recording state, which matches the current logic but in a cleaner way.

Suggested change
if (PlatformService.isDesktop &&
recordingState == RecordingState.systemAudioRecord) {
debugPrint("Skipping device recording - system audio is active");
if (device != null) _updateRecordingDevice(device); // Still track device
return;
}
if (device != null) _updateRecordingDevice(device);
if (device != null) _updateRecordingDevice(device);
// Prevent device recording if system audio is active (desktop only)
if (PlatformService.isDesktop &&
recordingState == RecordingState.systemAudioRecord) {
debugPrint("Skipping device recording - system audio is active");
return;
}

@rishi-jat
Copy link
Author

Just a note regarding the Gemini bot review: its comments are not applicable to this provider. stopStreamDeviceRecording() is already the central, correct stop routine, and inlining cleanup as suggested would duplicate logic and break consistency with the rest of the codebase. The duplication note is valid, but the suggested patch is incorrect. The current fix keeps changes minimal and aligned with existing patterns.

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.

fix double recording when both device and macOS app is on ($100)

1 participant