Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 12 additions & 4 deletions native-bridge/src/audio/engine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -987,18 +987,26 @@ impl AudioEngine {
false // Default to not muted - let audio through
};

// DRY monitoring: hardware bypass for zero-latency monitoring
// WET monitoring: apply effects chain for local monitoring
let should_monitor = monitoring_enabled && !is_muted;

if should_monitor {
if let Ok(state) = processing_state.try_read() {
if let Ok(mut state) = processing_state.try_write() {
let gain = state.track_state.input_gain_linear();
let volume = state.track_state.volume;
let (pan_left, pan_right) = state.track_state.pan_gains();

// Apply gain, volume, and pan to stereo pairs
// 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 = gain * volume * mon_vol;
let base_gain = volume * mon_vol;
chunk[0] *= base_gain * pan_left;
chunk[1] *= base_gain * pan_right;
}
Expand Down
17 changes: 16 additions & 1 deletion native-bridge/src/effects/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,36 @@ use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct EffectsSettings {
// Base effects (15)
// Base effects (15) - all have defaults so partial updates work
#[serde(default)]
pub wah: WahSettings,
#[serde(default)]
pub overdrive: OverdriveSettings,
#[serde(default)]
pub distortion: DistortionSettings,
#[serde(default)]
pub amp: AmpSettings,
#[serde(default)]
pub cabinet: CabinetSettings,
#[serde(default)]
pub noise_gate: NoiseGateSettings,
#[serde(default)]
pub eq: EqSettings,
#[serde(default)]
pub compressor: CompressorSettings,
#[serde(default)]
pub chorus: ChorusSettings,
#[serde(default)]
pub flanger: FlangerSettings,
#[serde(default)]
pub phaser: PhaserSettings,
#[serde(default)]
pub delay: DelaySettings,
#[serde(default)]
pub tremolo: TremoloSettings,
#[serde(default)]
pub reverb: ReverbSettings,
#[serde(default)]
pub limiter: LimiterSettings,
// Extended effects (20)
#[serde(default)]
Expand Down
20 changes: 6 additions & 14 deletions src/components/daw/native-bridge-gate.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -72,16 +72,8 @@ export function NativeBridgeGate({
}
};

// If already connected and running, auto-proceed
useEffect(() => {
if (isConnected && isRunning) {
// Small delay to show the success state
const timer = setTimeout(() => {
onJoinAsPerformer();
}, 500);
return () => clearTimeout(timer);
}
}, [isConnected, isRunning, onJoinAsPerformer]);
// Note: We don't auto-proceed anymore - user should explicitly click "Join as Performer"
// Audio starts AFTER joining, so isRunning will be false on this screen

return (
<div className="min-h-screen bg-gradient-to-br from-slate-900 via-indigo-950 to-slate-900 flex items-center justify-center p-4">
Expand Down Expand Up @@ -229,21 +221,21 @@ export function NativeBridgeGate({

{/* Join Options */}
<div className="space-y-3 pt-2">
{/* Performer option */}
{/* Performer option - only requires connection, audio starts after joining */}
<Button
size="lg"
disabled={!isConnected || !isRunning}
disabled={!isConnected}
onClick={onJoinAsPerformer}
className={cn(
"w-full h-14 text-base",
isConnected && isRunning
isConnected
? "bg-gradient-to-r from-indigo-600 to-purple-600 hover:from-indigo-500 hover:to-purple-500"
: "bg-slate-700 text-slate-400"
)}
>
<Mic className="w-5 h-5 mr-2" />
Join as Performer
{isConnected && isRunning && (
{isConnected && latency.total > 0 && (
<span className="ml-2 text-xs bg-white/20 px-2 py-0.5 rounded-full">
{latency.total.toFixed(0)}ms
</span>
Expand Down
9 changes: 8 additions & 1 deletion src/hooks/useNativeBridge.ts
Original file line number Diff line number Diff line change
Expand Up @@ -406,17 +406,24 @@ export function useNativeBridge() {
nativeBridge.setSampleRate(state.sampleRate);
nativeBridge.setChannelConfig(channelConfigToUse);

// Send track state to native bridge for direct monitoring
// Send track state AND effects to native bridge for direct monitoring
if (currentUserId && primaryTrack) {
nativeBridge.updateTrackState(primaryTrack.id, {
isArmed: primaryTrack.isArmed,
isMuted: primaryTrack.isMuted,
isSolo: primaryTrack.isSolo,
volume: primaryTrack.volume,
pan: primaryTrack.pan ?? 0,
inputGainDb: primaryTrack.audioSettings.inputGain || 0,
monitoringEnabled: primaryTrack.audioSettings.directMonitoring ?? true,
monitoringVolume: primaryTrack.audioSettings.monitoringVolume ?? 1,
});

// Send effects to native bridge on startup
if (primaryTrack.audioSettings.effects) {
console.log('[useNativeBridge] Sending initial effects to native bridge');
nativeBridge.updateEffects(primaryTrack.id, primaryTrack.audioSettings.effects);
}
}

// Start audio - processors are already ready to receive it
Expand Down
11 changes: 8 additions & 3 deletions src/lib/audio/native-bridge.ts
Original file line number Diff line number Diff line change
Expand Up @@ -340,9 +340,9 @@ export class NativeBridge {
break;

case 'streamHealth':
// Only log if there's an issue (overflow or unhealthy)
if (msg.overflowCount > 0 || !msg.isHealthy) {
console.warn('[NativeBridge] Stream health issue:', msg);
// Only log overflow issues - empty buffer on reconnect is normal
if (msg.overflowCount > 0) {
console.warn('[NativeBridge] Buffer overflow:', msg.overflowCount, 'batches,', msg.overflowSamples, 'samples dropped');
}
this.emit('streamHealth', {
bufferOccupancy: msg.bufferOccupancy,
Expand Down Expand Up @@ -593,6 +593,11 @@ export class NativeBridge {
* Update effects
*/
updateEffects(trackId: string, effects: Partial<UnifiedEffectsChain>): void {
// Log which effects are enabled for debugging
const enabledEffects = Object.entries(effects)
.filter(([, v]) => v && typeof v === 'object' && 'enabled' in v && v.enabled)
.map(([k]) => k);
console.log('[NativeBridge] updateEffects:', { trackId, enabledEffects });
this.send({ type: 'updateEffects', trackId: trackId, effects });
}

Expand Down
Loading