Skip to main content

rill_router/mixer/
channel.rs

1//! Mixer channel implementation
2
3/// Channel mode (mono or stereo)
4#[derive(Debug, Clone, Copy, PartialEq)]
5pub enum ChannelMode {
6    /// Single-channel input (summed to stereo with pan).
7    Mono,
8    /// Two-channel input (left/right).
9    Stereo,
10}
11
12/// Configuration for a mixer channel.
13///
14/// Controls the channel name, mono/stereo mode, volume (0.0–1.0),
15/// pan (-1.0 left, 0.0 center, 1.0 right), mute, and solo state.
16#[derive(Debug, Clone)]
17pub struct ChannelConfig {
18    /// Display name for the channel.
19    pub name: String,
20    /// Mono or stereo channel mode.
21    pub mode: ChannelMode,
22    /// Volume (0.0 = silence, 1.0 = unity).
23    pub volume: f32,
24    /// Pan (-1.0 = full left, 0.0 = center, 1.0 = full right).
25    pub pan: f32,
26    /// Whether the channel is muted.
27    pub muted: bool,
28    /// Whether the channel is soloed (overrides mute).
29    pub soloed: bool,
30}
31
32impl Default for ChannelConfig {
33    fn default() -> Self {
34        Self {
35            name: "Channel".to_string(),
36            mode: ChannelMode::Mono,
37            volume: 1.0,
38            pan: 0.0,
39            muted: false,
40            soloed: false,
41        }
42    }
43}
44
45/// Runtime state of a mixer channel
46#[derive(Debug, Clone)]
47pub struct ChannelState {
48    config: ChannelConfig,
49    /// Current volume with smoothing
50    current_volume: f32,
51    /// Current pan
52    current_pan: f32,
53    /// Smoothing factor (0.0 - 1.0)
54    smoothing: f32,
55}
56
57impl ChannelState {
58    /// Create a new channel state
59    pub fn new(config: ChannelConfig) -> Self {
60        let current_volume = config.volume;
61        let current_pan = config.pan;
62        Self {
63            config,
64            current_volume,
65            current_pan,
66            smoothing: 0.1, // default smoothing
67        }
68    }
69
70    /// Process a mono sample through the channel with pan
71    pub fn process_mono(&mut self, input: f32) -> (f32, f32) {
72        if self.config.muted {
73            return (0.0, 0.0);
74        }
75
76        // Smooth volume and pan
77        self.current_volume += (self.config.volume - self.current_volume) * self.smoothing;
78        self.current_pan += (self.config.pan - self.current_pan) * self.smoothing;
79
80        // Apply pan (simple constant power panning)
81        let (left_gain, right_gain) = if self.current_pan <= 0.0 {
82            (1.0, 1.0 + self.current_pan)
83        } else {
84            (1.0 - self.current_pan, 1.0)
85        };
86
87        let left_out = input * self.current_volume * left_gain;
88        let right_out = input * self.current_volume * right_gain;
89
90        (left_out, right_out)
91    }
92
93    /// Process a stereo sample through the channel
94    pub fn process_stereo(&mut self, left: f32, right: f32) -> (f32, f32) {
95        if self.config.muted {
96            return (0.0, 0.0);
97        }
98
99        // Smooth volume and pan
100        self.current_volume += (self.config.volume - self.current_volume) * self.smoothing;
101        self.current_pan += (self.config.pan - self.current_pan) * self.smoothing;
102
103        // Apply pan (simple constant power panning)
104        let (left_gain, right_gain) = if self.current_pan <= 0.0 {
105            (1.0, 1.0 + self.current_pan)
106        } else {
107            (1.0 - self.current_pan, 1.0)
108        };
109
110        let left_out = left * self.current_volume * left_gain;
111        let right_out = right * self.current_volume * right_gain;
112
113        (left_out, right_out)
114    }
115
116    /// Update configuration
117    pub fn set_config(&mut self, config: ChannelConfig) {
118        self.config = config;
119    }
120
121    /// Get current configuration
122    pub fn config(&self) -> &ChannelConfig {
123        &self.config
124    }
125
126    /// Set smoothing factor (0.0 = instant, 1.0 = very slow)
127    pub fn set_smoothing(&mut self, factor: f32) {
128        self.smoothing = factor.clamp(0.0, 1.0);
129    }
130
131    /// Get current volume (after smoothing)
132    pub fn current_volume(&self) -> f32 {
133        self.current_volume
134    }
135
136    /// Get current pan (after smoothing)
137    pub fn current_pan(&self) -> f32 {
138        self.current_pan
139    }
140}