1use sim_kernel::{Error, Result, Symbol};
2use sim_lib_stream_core::{ClockDomain, StreamValue};
3
4use crate::freeze::{context_hash, freeze_meta, stable_hash};
5use crate::{
6 ChainDevice, ChainPlacementPlan, ChainTraceRecord, DirectRecording, FreezeMeta, FrozenPlayable,
7 FrozenPlayerChain, LaneDescriptor, LaneId, LaneKind, Music, PlacedChainDevice, PlayContext,
8 PlayEvent, PlayStream, Playable, PlayableDescriptor, PlayableShape, PlayerDeviceId, PlayerMode,
9 PlayerTargetDescriptor, SourceRecording, TraceAction, render_music_events, stable_event_order,
10};
11
12#[derive(Clone, Debug, PartialEq, Eq)]
14pub struct ChainRender {
15 pub events: Vec<PlayEvent>,
17 pub traces: Vec<ChainTraceRecord>,
19}
20
21#[derive(Clone, Debug)]
23pub struct PlayerChain {
24 pub source_id: Symbol,
26 pub source: Music,
28 pub devices: Vec<ChainDevice>,
30 pub target: PlayerTargetDescriptor,
32}
33
34impl PlayerChain {
35 pub fn new(
37 source_id: Symbol,
38 source: Music,
39 devices: Vec<ChainDevice>,
40 target: PlayerTargetDescriptor,
41 ) -> Self {
42 Self {
43 source_id,
44 source,
45 devices,
46 target,
47 }
48 }
49
50 pub fn stable_devices(&self) -> Vec<&ChainDevice> {
52 let mut devices = self.devices.iter().collect::<Vec<_>>();
53 devices.sort_by(|left, right| {
54 left.order
55 .cmp(&right.order)
56 .then_with(|| left.id.cmp(&right.id))
57 });
58 devices
59 }
60
61 pub fn placement_plan(&self) -> ChainPlacementPlan {
63 ChainPlacementPlan::new(
64 self.stable_devices()
65 .into_iter()
66 .map(|device| PlacedChainDevice {
67 device_id: device.id.clone(),
68 placement: device.placement.clone(),
69 })
70 .collect(),
71 )
72 }
73
74 pub fn render_chain(&self, cx: &PlayContext) -> Result<ChainRender> {
76 let mut events = render_music_events(&self.source, cx)?;
77 let mut traces = Vec::new();
78 let mut sequence = 0;
79 for device in active_devices(self.stable_devices()) {
80 events = process_device(device, events, cx, &mut traces, &mut sequence);
81 stable_event_order(&mut events);
82 }
83 Ok(ChainRender { events, traces })
84 }
85
86 pub fn record_source(&self, cx: &PlayContext) -> SourceRecording {
88 SourceRecording::new(
89 self.source_id.clone(),
90 self.chain_hash(),
91 context_hash(cx),
92 cx.seed,
93 self.placement_plan(),
94 )
95 }
96
97 pub fn record_direct(&self, cx: &PlayContext) -> Result<DirectRecording> {
99 let frozen = self.freeze_chain(cx)?;
100 Ok(DirectRecording {
101 meta: frozen.meta,
102 events: frozen.events,
103 traces: frozen.traces,
104 })
105 }
106
107 pub fn freeze_chain(&self, cx: &PlayContext) -> Result<FrozenPlayerChain> {
109 let render = self.render_chain(cx)?;
110 let meta = self.freeze_meta(cx, &render.events, &render.traces);
111 Ok(FrozenPlayerChain {
112 meta,
113 events: render.events,
114 traces: render.traces,
115 })
116 }
117
118 pub fn freeze_meta(
120 &self,
121 cx: &PlayContext,
122 events: &[PlayEvent],
123 traces: &[ChainTraceRecord],
124 ) -> FreezeMeta {
125 freeze_meta(
126 self.source_id.clone(),
127 self.chain_hash(),
128 cx,
129 self.placement_plan(),
130 events,
131 traces,
132 )
133 }
134
135 pub fn chain_hash(&self) -> String {
137 stable_hash(
138 "player-chain",
139 &(&self.source_id, &self.devices, &self.target),
140 )
141 }
142}
143
144impl Playable for PlayerChain {
145 fn describe(&self) -> Result<PlayableDescriptor> {
146 Ok(PlayableDescriptor {
147 id: Symbol::qualified("music/player-chain", self.source_id.to_string()),
148 lanes: vec![
149 LaneDescriptor::new(
150 LaneId::new("chain-output"),
151 LaneKind::Note,
152 self.target.target.clone(),
153 0,
154 )
155 .map_err(|err| Error::Eval(err.to_string()))?,
156 ],
157 clock_domain: self.target.clock_domain(),
158 latency_class: self.target.latency_class(),
159 shape: PlayableShape::music_object(),
160 })
161 }
162
163 fn render_range(&self, cx: &PlayContext) -> Result<PlayStream> {
164 let render = self.render_chain(cx)?;
165 let items = render
166 .events
167 .iter()
168 .map(|event| event.to_stream_item(ClockDomain::MidiTick.symbol()))
169 .collect::<Result<Vec<_>>>()?;
170 let metadata = cx.stream_metadata(
171 Symbol::qualified("music/play-stream", "player-chain"),
172 items.len(),
173 )?;
174 Ok(StreamValue::pull(metadata, items))
175 }
176
177 fn freeze(&self, cx: &PlayContext) -> Result<FrozenPlayable> {
178 let frozen = self.freeze_chain(cx)?;
179 Ok(FrozenPlayable {
180 descriptor: self.describe()?,
181 events: frozen.events,
182 content_hash: frozen.meta.output_hash,
183 })
184 }
185}
186
187fn active_devices(devices: Vec<&ChainDevice>) -> Vec<&ChainDevice> {
188 let soloed = devices
189 .iter()
190 .any(|device| device.enabled && device.solo && !device.mute);
191 devices
192 .into_iter()
193 .filter(|device| device.enabled && !device.mute)
194 .filter(|device| !soloed || device.solo)
195 .collect()
196}
197
198fn process_device(
199 device: &ChainDevice,
200 input: Vec<PlayEvent>,
201 cx: &PlayContext,
202 traces: &mut Vec<ChainTraceRecord>,
203 sequence: &mut u64,
204) -> Vec<PlayEvent> {
205 if device.bypass {
206 return input;
207 }
208
209 let generated = generated_events(device, cx);
210 let mut output = match device.mode {
211 PlayerMode::Through => {
212 let mut output = routed_events(device, input, traces, sequence);
213 trace_generated(device, &generated, traces, sequence);
214 output.extend(generated);
215 output
216 }
217 PlayerMode::Replace => {
218 trace_dropped(device, input, traces, sequence);
219 trace_generated(device, &generated, traces, sequence);
220 generated
221 }
222 PlayerMode::Filter => filter_events(device, input, traces, sequence),
223 PlayerMode::Sidechain => {
224 trace_generated(device, &generated, traces, sequence);
225 routed_events(device, input, traces, sequence)
226 }
227 PlayerMode::SelfClocked => {
228 trace_dropped(device, input, traces, sequence);
229 trace_generated(device, &generated, traces, sequence);
230 generated
231 }
232 };
233 stable_event_order(&mut output);
234 output
235}
236
237fn generated_events(device: &ChainDevice, cx: &PlayContext) -> Vec<PlayEvent> {
238 device
239 .generated
240 .iter()
241 .filter_map(|event| clip_event(event, cx))
242 .map(|event| route_event(device, event))
243 .collect()
244}
245
246fn filter_events(
247 device: &ChainDevice,
248 input: Vec<PlayEvent>,
249 traces: &mut Vec<ChainTraceRecord>,
250 sequence: &mut u64,
251) -> Vec<PlayEvent> {
252 let mut output = Vec::new();
253 for event in input {
254 if device.filter_lanes.binary_search(event.lane_id()).is_ok() {
255 push_trace(
256 device,
257 TraceAction::Dropped,
258 event,
259 "filtered",
260 traces,
261 sequence,
262 );
263 } else {
264 let routed = route_event(device, event);
265 let action = if device.route_lane.is_some() {
266 TraceAction::Rewritten
267 } else {
268 TraceAction::Routed
269 };
270 push_trace(
271 device,
272 action,
273 routed.clone(),
274 "filtered-through",
275 traces,
276 sequence,
277 );
278 output.push(routed);
279 }
280 }
281 output
282}
283
284fn routed_events(
285 device: &ChainDevice,
286 input: Vec<PlayEvent>,
287 traces: &mut Vec<ChainTraceRecord>,
288 sequence: &mut u64,
289) -> Vec<PlayEvent> {
290 input
291 .into_iter()
292 .map(|event| {
293 let routed = route_event(device, event);
294 let action = if device.route_lane.is_some() {
295 TraceAction::Rewritten
296 } else {
297 TraceAction::Routed
298 };
299 push_trace(device, action, routed.clone(), "routed", traces, sequence);
300 routed
301 })
302 .collect()
303}
304
305fn trace_generated(
306 device: &ChainDevice,
307 generated: &[PlayEvent],
308 traces: &mut Vec<ChainTraceRecord>,
309 sequence: &mut u64,
310) {
311 for event in generated {
312 push_trace(
313 device,
314 TraceAction::Generated,
315 event.clone(),
316 "generated",
317 traces,
318 sequence,
319 );
320 }
321}
322
323fn trace_dropped(
324 device: &ChainDevice,
325 input: Vec<PlayEvent>,
326 traces: &mut Vec<ChainTraceRecord>,
327 sequence: &mut u64,
328) {
329 for event in input {
330 push_trace(
331 device,
332 TraceAction::Dropped,
333 event,
334 "dropped",
335 traces,
336 sequence,
337 );
338 }
339}
340
341fn push_trace(
342 device: &ChainDevice,
343 action: TraceAction,
344 event: PlayEvent,
345 detail: &'static str,
346 traces: &mut Vec<ChainTraceRecord>,
347 sequence: &mut u64,
348) {
349 traces.push(ChainTraceRecord::new(
350 *sequence,
351 PlayerDeviceId::new(device.id.0.clone()),
352 action,
353 event,
354 detail,
355 ));
356 *sequence += 1;
357}
358
359fn clip_event(event: &PlayEvent, cx: &PlayContext) -> Option<PlayEvent> {
360 match event {
361 PlayEvent::Note(note) => {
362 let (time, duration) = cx.range.clip_span(note.time, note.duration)?;
363 let mut note = note.clone();
364 note.time = time;
365 note.duration = duration;
366 Some(PlayEvent::Note(note))
367 }
368 _ if cx.range.contains(event.time()) => Some(event.clone()),
369 _ => None,
370 }
371}
372
373fn route_event(device: &ChainDevice, event: PlayEvent) -> PlayEvent {
374 let Some(lane) = &device.route_lane else {
375 return event;
376 };
377 with_lane(event, lane.clone())
378}
379
380fn with_lane(event: PlayEvent, lane_id: LaneId) -> PlayEvent {
381 match event {
382 PlayEvent::Note(mut event) => {
383 event.lane_id = lane_id;
384 PlayEvent::Note(event)
385 }
386 PlayEvent::Midi(mut event) => {
387 event.lane_id = lane_id;
388 PlayEvent::Midi(event)
389 }
390 PlayEvent::Pitch(mut event) => {
391 event.lane_id = lane_id;
392 PlayEvent::Pitch(event)
393 }
394 PlayEvent::Control(mut event) => {
395 event.lane_id = lane_id;
396 PlayEvent::Control(event)
397 }
398 PlayEvent::Audio(mut event) => {
399 event.lane_id = lane_id;
400 PlayEvent::Audio(event)
401 }
402 PlayEvent::Playable(mut event) => {
403 event.lane_id = lane_id;
404 PlayEvent::Playable(event)
405 }
406 PlayEvent::Performance(mut event) => {
407 event.lane_id = lane_id;
408 PlayEvent::Performance(event)
409 }
410 PlayEvent::Diagnostic(mut event) => {
411 event.lane_id = lane_id;
412 PlayEvent::Diagnostic(event)
413 }
414 PlayEvent::Trace(mut event) => {
415 event.lane_id = lane_id;
416 PlayEvent::Trace(event)
417 }
418 }
419}