1use std::ops::Range;
20use std::sync::Arc;
21
22use serde::{Deserialize, Serialize};
23
24use super::info::RecordingInfo;
25use super::source::{check_read, check_read_stored, RecordingSource};
26use crate::error::{DspError, DspResult};
27
28pub const DEFAULT_CHANNEL_BASE: usize = 0;
30
31#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
33#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
34#[serde(deny_unknown_fields)]
35pub struct ChannelPair {
36 pub from: usize,
37 pub to: usize,
38 #[serde(default, skip_serializing_if = "Option::is_none")]
39 pub name: Option<String>,
40}
41
42#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
44#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
45#[serde(deny_unknown_fields)]
46pub struct ChannelMap {
47 #[serde(default = "default_base")]
49 pub base: usize,
50 pub pairs: Vec<ChannelPair>,
51}
52
53fn default_base() -> usize {
54 DEFAULT_CHANNEL_BASE
55}
56
57#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
59#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
60#[serde(deny_unknown_fields)]
61pub struct ChannelMapFile {
62 pub remap: ChannelMap,
63}
64
65impl ChannelMap {
66 pub fn from_order(order: &[usize]) -> Self {
68 let pairs = order.iter().enumerate().map(|(to, &from)| ChannelPair { from, to, name: None }).collect();
69 Self { base: 0, pairs }
70 }
71
72 pub fn order(&self) -> DspResult<Vec<usize>> {
75 let bad = |m: String| Err(DspError::InvalidConfig(format!("channel map: {m}")));
76 if self.pairs.is_empty() {
77 return bad("no pairs".into());
78 }
79 let n = self.pairs.len();
80 let mut order = vec![usize::MAX; n];
81 let mut inputs = std::collections::HashSet::with_capacity(n);
82 for p in &self.pairs {
83 let (Some(from), Some(to)) = (p.from.checked_sub(self.base), p.to.checked_sub(self.base)) else {
84 return bad(format!("pair {} → {} is below base {}", p.from, p.to, self.base));
85 };
86 if to >= n {
87 return bad(format!("output {} is past the last output ({} pairs give outputs {}..{})", p.to, n, self.base, self.base + n - 1));
88 }
89 if order[to] != usize::MAX {
90 return bad(format!("output {} is given twice", p.to));
91 }
92 if !inputs.insert(from) {
93 return bad(format!("input {} is used twice", p.from));
94 }
95 order[to] = from;
96 }
97 Ok(order)
98 }
99
100 pub fn resolve(&self, channels: usize) -> DspResult<Vec<usize>> {
102 let order = self.order()?;
103 if let Some(&c) = order.iter().find(|&&c| c >= channels) {
104 return Err(DspError::InvalidConfig(format!("channel map: input {} does not exist (the recording has {channels} channels)", c + self.base)));
105 }
106 Ok(order)
107 }
108
109 pub fn names(&self) -> Vec<Option<String>> {
111 let mut names = vec![None; self.pairs.len()];
112 for p in &self.pairs {
113 if let Some(slot) = p.to.checked_sub(self.base).and_then(|to| names.get_mut(to)) {
114 slot.clone_from(&p.name);
115 }
116 }
117 names
118 }
119
120 pub fn then(&self, next: &ChannelMap) -> DspResult<ChannelMap> {
123 let (first, second) = (self.order()?, next.order()?);
124 let first_names = self.names();
125 let second_names = next.names();
126 let mut pairs = Vec::with_capacity(second.len());
127 for (to, &mid) in second.iter().enumerate() {
128 let Some(&from) = first.get(mid) else {
129 return Err(DspError::InvalidConfig(format!("channel map: input {} does not exist (the map before gives {} channels)", mid + next.base, first.len())));
130 };
131 pairs.push(ChannelPair { from, to, name: second_names[to].clone().or_else(|| first_names[mid].clone()) });
132 }
133 Ok(ChannelMap { base: 0, pairs })
134 }
135}
136
137pub struct RemappedSource {
140 parent: Arc<dyn RecordingSource>,
141 order: Vec<usize>,
142 info: RecordingInfo,
143}
144
145impl RemappedSource {
146 pub fn new(parent: Arc<dyn RecordingSource>, map: &ChannelMap) -> DspResult<Self> {
147 let p = parent.info();
148 let order = map.resolve(p.channel_count())?;
149 let mut info = p.clone();
150 info.channels = order
151 .iter()
152 .zip(map.names())
153 .map(|(&c, name)| {
154 let mut ch = p.channels[c].clone();
155 if let Some(name) = name {
156 ch.name = name;
157 }
158 ch
159 })
160 .collect();
161 Ok(Self { parent, order, info })
162 }
163
164 pub fn order(&self) -> &[usize] {
166 &self.order
167 }
168
169 fn parent_channels(&self, channels: &[usize]) -> Vec<usize> {
170 channels.iter().map(|&c| self.order[c]).collect()
171 }
172}
173
174impl RecordingSource for RemappedSource {
175 fn info(&self) -> &RecordingInfo {
176 &self.info
177 }
178
179 fn chunk_samples(&self) -> Option<u64> {
180 self.parent.chunk_samples()
181 }
182
183 fn read(&self, channels: &[usize], samples: Range<u64>, out: &mut [f32]) -> DspResult<()> {
184 check_read(&self.info, channels, &samples, out.len())?;
185 self.parent.read(&self.parent_channels(channels), samples, out)
186 }
187
188 fn read_stored(&self, channels: &[usize], samples: Range<u64>, out: &mut [u8]) -> DspResult<()> {
189 check_read_stored(&self.info, channels, &samples, out.len())?;
190 self.parent.read_stored(&self.parent_channels(channels), samples, out)
191 }
192}
193
194#[cfg(test)]
195mod tests {
196 use super::*;
197 use crate::recording::MemoryRecording;
198
199 fn pair(from: usize, to: usize, name: Option<&str>) -> ChannelPair {
200 ChannelPair { from, to, name: name.map(str::to_string) }
201 }
202
203 #[test]
204 fn order_follows_the_pairs_and_checks_the_rules() {
205 let map = ChannelMap { base: 1, pairs: vec![pair(3, 2, None), pair(1, 1, Some("A1"))] };
206 assert_eq!(map.order().unwrap(), vec![0, 2]);
207 assert_eq!(map.names(), vec![Some("A1".to_string()), None]);
208 assert!(map.resolve(2).is_err(), "input 3 of a 2-channel recording");
209 for pairs in [
211 vec![pair(0, 0, None), pair(1, 0, None)],
212 vec![pair(0, 0, None), pair(0, 1, None)],
213 vec![pair(0, 0, None), pair(1, 2, None)],
214 vec![],
215 ] {
216 assert!(ChannelMap { base: 0, pairs }.order().is_err());
217 }
218 assert!(ChannelMap { base: 1, pairs: vec![pair(0, 1, None)] }.order().is_err());
219 }
220
221 #[test]
222 fn maps_compose() {
223 let a = ChannelMap { base: 0, pairs: vec![pair(5, 0, Some("x")), pair(3, 1, Some("y")), pair(1, 2, None)] };
224 let b = ChannelMap { base: 0, pairs: vec![pair(2, 0, Some("z")), pair(0, 1, None)] };
225 let ab = a.then(&b).unwrap();
226 assert_eq!(ab.order().unwrap(), vec![1, 5]);
227 assert_eq!(ab.names(), vec![Some("z".to_string()), Some("x".to_string())]);
228 }
229
230 #[test]
231 fn a_remapped_source_reads_in_the_new_order() {
232 let data: Vec<f32> = (0..3).flat_map(|c| (0..4).map(move |s| (c * 10 + s) as f32)).collect();
234 let parent: Arc<dyn RecordingSource> = Arc::new(MemoryRecording::new("m", data, 3, 1000.0).unwrap());
235 let map = ChannelMap { base: 0, pairs: vec![pair(2, 0, Some("A1")), pair(0, 1, None)] };
236 let r = RemappedSource::new(parent, &map).unwrap();
237 assert_eq!(r.info().channel_count(), 2);
238 assert_eq!(r.info().channels[0].name, "A1");
239 assert_eq!(r.info().channels[1].name, "ch0");
240 let mut out = vec![0.0; 4];
241 r.read(&[1, 0], 1..3, &mut out).unwrap();
242 assert_eq!(out, vec![1.0, 2.0, 21.0, 22.0]);
243 }
244}