diff --git a/native-bridge/src/audio/engine.rs b/native-bridge/src/audio/engine.rs index 08b67a2c..8ac6b222 100644 --- a/native-bridge/src/audio/engine.rs +++ b/native-bridge/src/audio/engine.rs @@ -208,6 +208,10 @@ pub struct AudioEngine { // Connection health tracking last_browser_read_time: Arc, + + // Diagnostic counters for effects processing + effects_applied_count: Arc, + effects_skipped_count: Arc, } impl AudioEngine { @@ -248,6 +252,9 @@ impl AudioEngine { browser_stream_overflow_count: Arc::new(AtomicU64::new(0)), browser_stream_overflow_samples: Arc::new(AtomicU64::new(0)), last_browser_read_time: Arc::new(AtomicU64::new(0)), + // Diagnostic counters for effects processing + effects_applied_count: Arc::new(AtomicU64::new(0)), + effects_skipped_count: Arc::new(AtomicU64::new(0)), }) } @@ -382,8 +389,33 @@ impl AudioEngine { } pub fn update_effects(&self, effects: crate::effects::EffectsSettings) { - if let Ok(mut state) = self.processing_state.write() { - state.effects_chain.update_settings(effects); + // Log which effects are enabled for debugging + let enabled_effects: Vec<&str> = [ + ("wah", effects.wah.enabled), + ("overdrive", effects.overdrive.enabled), + ("distortion", effects.distortion.enabled), + ("amp", effects.amp.enabled), + ("reverb", effects.reverb.enabled), + ("delay", effects.delay.enabled), + ("chorus", effects.chorus.enabled), + ("compressor", effects.compressor.enabled), + ("eq", effects.eq.enabled), + ] + .iter() + .filter(|(_, enabled)| *enabled) + .map(|(name, _)| *name) + .collect(); + + info!("Updating effects chain. Enabled: {:?}", enabled_effects); + + match self.processing_state.write() { + Ok(mut state) => { + state.effects_chain.update_settings(effects); + info!("Effects chain updated successfully"); + } + Err(e) => { + tracing::error!("Failed to acquire write lock for effects update: {:?}", e); + } } } @@ -980,7 +1012,22 @@ impl AudioEngine { stereo_buffer.push(right_sample); } + // Apply effects FIRST so they're included in both browser stream and local monitoring + // This ensures other users in the session hear the effects too + if let Ok(mut state) = processing_state.try_write() { + let gain = state.track_state.input_gain_linear(); + + // Apply input gain + for sample in stereo_buffer.iter_mut() { + *sample *= gain; + } + + // Process through effects chain + state.effects_chain.process(stereo_buffer); + } + // Calculate input levels (stereo) - interleaved L/R samples + // Note: levels are post-effects now let (level_l, level_r) = stereo_buffer .chunks_exact(2) .fold((0.0_f32, 0.0_f32), |(max_l, max_r), chunk| { @@ -1015,23 +1062,17 @@ impl AudioEngine { false // Default to not muted - let audio through }; - // WET monitoring: apply effects chain for local monitoring + // Local monitoring: apply volume, pan, and push to output + // Note: Effects were already applied above (before browser stream) let should_monitor = monitoring_enabled && !is_muted; if should_monitor { - if let Ok(mut state) = processing_state.try_write() { - let gain = state.track_state.input_gain_linear(); + // Apply volume and pan for local monitoring + // Effects are already applied, just need volume/pan + if let Ok(state) = processing_state.try_read() { let volume = state.track_state.volume; let (pan_left, pan_right) = state.track_state.pan_gains(); - // Apply input gain first - for sample in stereo_buffer.iter_mut() { - *sample *= gain; - } - - // Process through effects chain (if any effects are enabled) - state.effects_chain.process(stereo_buffer); - // Apply volume, pan, and monitoring volume to stereo pairs for chunk in stereo_buffer.chunks_exact_mut(2) { let base_gain = volume * mon_vol; diff --git a/native-bridge/src/effects/types.rs b/native-bridge/src/effects/types.rs index 2b7089c1..5e48c2e4 100644 --- a/native-bridge/src/effects/types.rs +++ b/native-bridge/src/effects/types.rs @@ -13,7 +13,7 @@ pub struct EffectsSettings { pub overdrive: OverdriveSettings, #[serde(default)] pub distortion: DistortionSettings, - #[serde(default)] + #[serde(default, alias = "ampSimulator")] pub amp: AmpSettings, #[serde(default)] pub cabinet: CabinetSettings, diff --git a/src/hooks/useNativeBridge.ts b/src/hooks/useNativeBridge.ts index c31c441e..24c26ecd 100644 --- a/src/hooks/useNativeBridge.ts +++ b/src/hooks/useNativeBridge.ts @@ -252,17 +252,21 @@ export function useNativeBridge() { // Auto-start native bridge when room is connected and bridge is preferred // This ensures users don't have to manually start audio after joining a room + // DISABLED: This was causing issues by trying to start without an ASIO device selected + // Users must manually start audio from settings after selecting their device + /* useEffect(() => { // Subscribe to room store for connection changes const unsubscribeRoom = useRoomStore.subscribe((roomState) => { const bridgeState = useBridgeAudioStore.getState(); - // Check if we should auto-start: room connected + bridge connected + bridge preferred + not already running + // Check if we should auto-start: room connected + bridge connected + bridge preferred + not already running + device selected if ( roomState.isConnected && bridgeState.isConnected && bridgeState.preferNativeBridge && - !bridgeState.isRunning + !bridgeState.isRunning && + bridgeState.inputDevice // Must have a device selected ) { console.log('[useNativeBridge] Room connected with bridge preferred, auto-starting audio...'); // Use a small delay to ensure all other initialization has completed @@ -275,6 +279,7 @@ export function useNativeBridge() { latestBridge.isConnected && latestBridge.preferNativeBridge && !latestBridge.isRunning && + latestBridge.inputDevice && startAudioRef.current ) { console.log('[useNativeBridge] Executing auto-start via ref...'); @@ -292,6 +297,7 @@ export function useNativeBridge() { unsubscribeRoom(); }; }, []); + */ // Connect to bridge const connect = useCallback(async () => { diff --git a/src/stores/bridge-audio-store.ts b/src/stores/bridge-audio-store.ts index 237b275c..0610507c 100644 --- a/src/stores/bridge-audio-store.ts +++ b/src/stores/bridge-audio-store.ts @@ -98,11 +98,14 @@ export const useBridgeAudioStore = create()( // Persisted settings selectedInputDeviceId: null, selectedOutputDeviceId: null, + // Default to MONO for most common use case (single microphone/instrument) + // Stereo users will configure this in settings inputChannelConfig: { - channelCount: 2, + channelCount: 1, leftChannel: 0, - rightChannel: 1, + rightChannel: undefined, }, + // Output stays stereo for monitoring outputChannelConfig: { channelCount: 2, leftChannel: 0, diff --git a/src/stores/user-tracks-store.ts b/src/stores/user-tracks-store.ts index 80a2b743..38d8e414 100644 --- a/src/stores/user-tracks-store.ts +++ b/src/stores/user-tracks-store.ts @@ -20,11 +20,12 @@ export const TRACK_COLORS = [ '#c084fc', // Purple ]; -// Default channel configuration (stereo) +// Default channel configuration (MONO - most common for single input) +// Users with stereo inputs can change this in settings const DEFAULT_CHANNEL_CONFIG: InputChannelConfig = { - channelCount: 2, + channelCount: 1, leftChannel: 0, - rightChannel: 1, + rightChannel: undefined, }; // Default audio settings with extended effects chain