-
Notifications
You must be signed in to change notification settings - Fork 301
Expand file tree
/
Copy pathchannel_router.rs
More file actions
306 lines (263 loc) · 9.12 KB
/
channel_router.rs
File metadata and controls
306 lines (263 loc) · 9.12 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
// Channel router types and implementation.
use crate::{Sample, Source};
use std::{
cmp::min,
error::Error,
fmt,
sync::mpsc::{channel, Receiver, Sender},
};
/// A matrix to map inputs to outputs according to a gain
///
/// A two-dimensional matrix of `f32`s:
/// - The first dimension is respective to the input channels
/// - The second is respective to the output channels
///
/// Thus, if a value at `map[1,1]` is 0.2, this signifies that the signal on
/// channel 1 should be mixed into channel 1 with a coefficient of 0.2.
pub type ChannelMap = Vec<Vec<f32>>;
// doing this as Vec<Vec<atomic_float::AtomicF32>> would require feature=experimental, so I decided
// to just use a channel to do updates.
//
// Doing it as a HashMap<(u16,u16), f32> is an option too but there's a penalty hashing these
// values, there's ways to speed that up though. It'd be great if the object upgraded its
// implementation if it got sufficiently big.
// pub fn empty_channel_map(inputs: u16, outputs: u16) -> ChannelMap {
// vec![vec![0.0f32; outputs.into()]; inputs.into()]
// }
/// Internal function that builds a [`ChannelRouter<I>`] object.
pub fn channel_router<I>(
input: I,
channel_count: u16,
channel_map: ChannelMap,
) -> (ChannelRouterController, ChannelRouterSource<I>)
where
I: Source,
I::Item: Sample,
{
ChannelRouterSource::new(input, channel_count, channel_map)
}
struct ChannelRouterMessage(usize, usize, f32);
/// `ChannelRouterController::map()` returns this error if the router source has been dropped.
#[derive(Debug, Eq, PartialEq)]
pub struct ChannelRouterControllerError {}
impl fmt::Display for ChannelRouterControllerError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "<ChannelRouterControllerError>")
}
}
impl Error for ChannelRouterControllerError {}
/// A controller type that sends gain updates to a corresponding [`ChannelRouterSource`].
#[derive(Debug, Clone)]
pub struct ChannelRouterController {
sender: Sender<ChannelRouterMessage>,
}
impl ChannelRouterController {
/// Set or update the gain setting for a channel mapping.
///
/// A channel from the input may be routed to any number of channels in the output, and a
/// channel in the output may be a mix of any number of channels in the input.
///
/// Successive calls to `map` with the same `from` and `to` arguments will replace the
/// previous gain value with the new one.
pub fn map(
&mut self,
from: u16,
to: u16,
gain: f32,
) -> Result<(), ChannelRouterControllerError> {
if self
.sender
.send(ChannelRouterMessage(from as usize, to as usize, gain))
.is_err()
{
Err(ChannelRouterControllerError {})
} else {
Ok(())
}
}
}
/// A source for extracting, reordering, mixing and duplicating audio between
/// channels.
#[derive(Debug)]
pub struct ChannelRouterSource<I>
where
I: Source,
I::Item: Sample,
{
/// Input [`Source`]
input: I,
/// Mapping of input to output channels
channel_map: ChannelMap,
/// The output channel that [`next()`] will return next.
current_channel: u16,
/// The number of output channels
channel_count: u16,
/// The current input audio frame
input_buffer: Vec<I::Item>,
/// Communication channel with the controller
receiver: Receiver<ChannelRouterMessage>,
}
impl<I> ChannelRouterSource<I>
where
I: Source,
I::Item: Sample,
{
/// Creates a new [`ChannelRouter<I>`].
///
/// The new `ChannelRouter` will read samples from `input` and will mix and map them according
/// to `channel_mappings` into its output samples.
///
/// # Panics
///
/// - if `channel_count` is not equal to `channel_map`'s second dimension
/// - if `input.channels()` is not equal to `channel_map`'s first dimension
pub fn new(
input: I,
channel_count: u16,
channel_map: ChannelMap,
) -> (ChannelRouterController, Self) {
assert!(channel_count as usize == channel_map[0].len());
assert!(input.channels() as usize == channel_map.len());
let (tx, rx) = channel();
let controller = ChannelRouterController { sender: tx };
let source = Self {
input,
channel_map,
current_channel: channel_count,
// this will cause the input buffer to fill on first call to next()
channel_count,
// channel_count is redundant, it's implicit in the channel_map dimensions
// but maybe it's saving us some time, we do check this value a lot.
input_buffer: vec![],
receiver: rx,
};
(controller, source)
}
/// Destroys this router and returns the underlying source.
#[inline]
pub fn into_inner(self) -> I {
self.input
}
/// Get mutable access to the inner source.
#[inline]
pub fn inner_mut(&mut self) -> &mut I {
&mut self.input
}
}
impl<I> Source for ChannelRouterSource<I>
where
I: Source,
I::Item: Sample,
{
#[inline]
fn current_span_len(&self) -> Option<usize> {
self.input.current_span_len()
}
#[inline]
fn channels(&self) -> u16 {
self.channel_count
}
#[inline]
fn sample_rate(&self) -> u32 {
self.input.sample_rate()
}
#[inline]
fn total_duration(&self) -> Option<std::time::Duration> {
self.input.total_duration()
}
}
impl<I> Iterator for ChannelRouterSource<I>
where
I: Source,
I::Item: Sample,
{
type Item = I::Item;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
if self.current_channel >= self.channel_count {
// We've reached the end of the frame, time to grab another one from the input
let input_channels = self.input.channels() as usize;
// This might be too fussy, a source should never break a frame in the middle of an
// audio frame.
let samples_to_take = min(
input_channels,
self.input.current_span_len().unwrap_or(usize::MAX),
);
// fill the input buffer. If the input is exhausted and returning None this will make
// the input buffer zero length
self.input_buffer = self.inner_mut().take(samples_to_take).collect();
self.current_channel = 0;
for change in self.receiver.try_iter() {
self.channel_map[change.0][change.1] = change.2;
}
}
// Find the output sample for current_channel
let retval = self
.input_buffer
.iter()
.zip(&self.channel_map)
.map(|(in_sample, input_gains)| {
// the way this works, the input_buffer need not be totally full, the router will
// work with whatever samples are available and the missing samples will be assumed
// to be equilibrium.
let gain = input_gains[self.current_channel as usize];
in_sample.amplify(gain)
})
.reduce(|a, b| a.saturating_add(b));
self.current_channel += 1;
retval
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
self.input.size_hint()
}
}
#[cfg(test)]
mod tests {
use crate::buffer::SamplesBuffer;
use crate::source::channel_router::*;
#[test]
fn test_stereo_to_mono() {
let input = SamplesBuffer::new(2, 1, [0u16, 2u16, 4u16, 6u16]);
let map = vec![vec![0.5f32], vec![0.5f32]];
let (_, test_source) = ChannelRouterSource::new(input, 1, map);
let v1: Vec<u16> = test_source.take(4).collect();
assert_eq!(v1.len(), 2);
assert_eq!(v1[0], 1u16);
assert_eq!(v1[1], 5u16);
}
#[test]
fn test_upmix() {
let input = SamplesBuffer::new(1, 1, [0i16, -10, 10, 20, -20, -50, -30, 40]);
let map = vec![vec![1.0f32, 0.5f32, 2.0f32]];
let (_, test_source) = ChannelRouterSource::new(input, 3, map);
assert_eq!(test_source.channels(), 3);
let v1: Vec<i16> = test_source.take(1000).collect();
assert_eq!(v1.len(), 24);
assert_eq!(
v1,
[
0i16, 0, 0, -10, -5, -20, 10, 5, 20, 20, 10, 40, -20, -10, -40, -50, -25, -100,
-30, -15, -60, 40, 20, 80
]
);
}
#[test]
fn test_updates() {
let input = SamplesBuffer::new(2, 1, [0i16, 0i16, -1i16, -1i16, 1i16, 2i16, -4i16, -3i16]);
let initial_map = vec![vec![1.0f32], vec![1.0f32]];
let (mut controller, mut source) = ChannelRouterSource::new(input, 1, initial_map);
let v1: Vec<i16> = source.by_ref().take(2).collect();
assert_eq!(v1.len(), 2);
assert_eq!(v1[0], 0i16);
assert_eq!(v1[1], -2i16);
let r1 = controller.map(0, 0, 0.0f32);
let r2 = controller.map(1, 0, 2.0f32);
assert_eq!(r1, Ok(()));
assert_eq!(r2, Ok(()));
let v2: Vec<i16> = source.take(3).collect();
assert_eq!(v2.len(), 2);
assert_eq!(v2[0], 4i16);
assert_eq!(v2[1], -6i16);
}
}