Skip to main content

violet_spatial_decoder/
lib.rs

1/*!
2Adds a spatial decoder input to xpans Violet
3
4## Supported Formats
5The only supported format is xpans Spatial Record (XSR), as it is the only
6format in the xpans Ecosystem at the time of writing. In the future, more
7formats and spatial codecs will be supported.
8*/
9use std::{marker::PhantomData, thread::JoinHandle};
10
11use wreath::{Reader, RingReader, RingWriter, Writer, ring_buf};
12use xpans_xsr::SpatialSampleMap;
13
14use violet_core::{Connector, Source, spatial_input::SpatialInput};
15
16/**
17Stores metadata about the spatial stream.
18Splits into a `SpatialDecoder` and `SpatialDecoderTask`.
19*/
20pub struct SpatialDecoderInfo<Scene, T>
21where
22    Scene: AsRef<SpatialSampleMap<usize, u16, T>>,
23{
24    scene: Scene,
25    source_count: usize,
26    duration: usize,
27    phantom_data: PhantomData<T>,
28}
29
30impl<Scene, T> SpatialDecoderInfo<Scene, T>
31where
32    T: Copy + Default,
33    Scene: AsRef<SpatialSampleMap<usize, u16, T>>,
34{
35    /**
36    Creates a `SpatialDecoderInfo` using a xpans Spatial Record (XSR) map.
37
38    The source count and duration (in samples) of the scene must also be
39    given.
40    */
41    pub fn new(scene: Scene, source_count: usize, duration: usize) -> Self {
42        Self {
43            scene,
44            source_count,
45            duration,
46            phantom_data: PhantomData,
47        }
48    }
49    /**
50    Splits this struct into a `SpatialDecoder` and `SpatialDecoderTask`.
51
52    The resulting `SpatialDecoder` is the input you will give the renderer,
53    and the `SpatialDecoderTask` allows you to spawn the decoder task that
54    can decode the stream on a seperate thread.
55    */
56    pub fn into_pair(
57        self,
58        write_capacity: usize,
59    ) -> (SpatialDecoder<T>, SpatialDecoderTask<Scene, T>) {
60        let (reader, writer) = ring_buf(1, write_capacity);
61        let decoder = SpatialDecoder {
62            reader,
63            source_count: self.source_count,
64        };
65        let task = SpatialDecoderTask { writer, info: self };
66        (decoder, task)
67    }
68}
69
70/// Contains data necessary to start the spatial decoder process.
71pub struct SpatialDecoderTask<Scene, T>
72where
73    Scene: AsRef<SpatialSampleMap<usize, u16, T>>,
74{
75    writer: RingWriter<Source<T>>,
76    info: SpatialDecoderInfo<Scene, T>,
77}
78
79impl<Scene, T> SpatialDecoderTask<Scene, T>
80where
81    T: Default + Copy,
82    Scene: AsRef<SpatialSampleMap<usize, u16, T>>,
83{
84    /// Returns the inner `SpatialDecoderInfo`.
85    pub fn info(&self) -> &SpatialDecoderInfo<Scene, T> {
86        &self.info
87    }
88    /**
89    Run the spatial decoder process on the current thread.
90
91    Running the spatial decoder process on the same thread as the renderer
92    will likely not give desired results.
93    */
94    pub fn run(self, cancelled: impl Fn() -> bool, paused: impl Fn() -> bool) {
95        spatial_decoder_process(
96            self.info.scene,
97            self.info.source_count,
98            self.info.duration,
99            self.writer,
100            cancelled,
101            paused,
102        );
103    }
104}
105
106impl<Scene, T> SpatialDecoderTask<Scene, T>
107where
108    T: Default + Copy + Send + 'static,
109    Scene: AsRef<SpatialSampleMap<usize, u16, T>> + Send + 'static,
110{
111    /// Spawns a standard library thread that runs the spatial decoder process.
112    pub fn spawn_and_run(
113        self,
114        cancelled: impl Fn() -> bool + Send + 'static,
115        paused: impl Fn() -> bool + Send + 'static,
116    ) -> JoinHandle<()> {
117        std::thread::spawn(move || self.run(cancelled, paused))
118    }
119}
120
121/// The actual spatial input that the renderer gets spatial data from.
122pub struct SpatialDecoder<T> {
123    reader: RingReader<Source<T>>,
124    source_count: usize,
125}
126
127impl<T: Default + Copy> Connector for SpatialDecoder<T> {
128    fn advance(&mut self, frames: usize) {
129        self.reader
130            .advance_read_position_by(frames * self.source_count);
131    }
132
133    fn frames_available(&self) -> Option<usize> {
134        let reads_available = self.reader.real_reads_available() / self.source_count;
135        if (reads_available == 0) && self.reader.is_closed() {
136            return None;
137        }
138        Some(reads_available)
139    }
140}
141impl<T: Default + Copy> SpatialInput for SpatialDecoder<T> {
142    type Scalar = T;
143
144    fn source(&self, source: usize, frame: usize) -> Source<Self::Scalar> {
145        let frame = frame * self.source_count;
146        let index = frame + source;
147        self.reader.read_forward(index)
148    }
149
150    fn source_count(&self) -> usize {
151        self.source_count
152    }
153}
154
155// fn interleaved_index(channel_count: usize, frame: usize, channel: usize) -> usize {
156//     let frame_start = channel_count * frame;
157//     channel + frame_start
158// }
159
160fn spatial_decoder_process<Scene, T>(
161    spatial_scene: Scene,
162    source_count: usize,
163    duration: usize,
164    writer: RingWriter<Source<T>>,
165    cancelled: impl Fn() -> bool,
166    paused: impl Fn() -> bool,
167) where
168    T: Default + Copy,
169    Scene: AsRef<SpatialSampleMap<usize, u16, T>>,
170{
171    let mut frame = 0usize;
172    let mut sources = vec![Source::default(); source_count];
173    while frame < duration {
174        if cancelled() {
175            break;
176        }
177        if !writer.writes_are_available(source_count) || paused() {
178            continue;
179        }
180        if let Some(events) = spatial_scene.as_ref().get(&frame) {
181            for event in events {
182                apply_changes(&mut sources[event.id as usize], &event.changes);
183            }
184        }
185        for (i, source) in sources.iter().enumerate() {
186            writer.write_forward(i, *source);
187        }
188        writer.advance_write_position_by(source_count);
189        frame += 1;
190    }
191}
192
193fn apply_changes<T: Copy>(source: &mut Source<T>, changes: &xpans_xsr::Changes<T>) {
194    changes.pos_x.map(|v| source.pos_x = v);
195    changes.pos_y.map(|v| source.pos_y = v);
196    changes.pos_z.map(|v| source.pos_z = v);
197    changes.ext_x.map(|v| source.ext_x = v);
198    changes.ext_y.map(|v| source.ext_y = v);
199    changes.ext_z.map(|v| source.ext_z = v);
200}