1use crate::clap::{ClapParameterInfo, ClapPluginInfo};
2pub use crate::connectable::{ConnectableConnection, ConnectableRef};
3#[cfg(unix)]
4use crate::lv2::Lv2PluginInfo;
5use crate::midi::io::MidiEvent;
6use crate::state::TrackHandle;
7use crate::vst3::Vst3PluginInfo;
8use crate::{kind::Kind, modulator::Modulator};
9use std::net::SocketAddr;
10use std::sync::{Arc, atomic::AtomicBool};
11use tokio::sync::mpsc::Sender;
12
13#[derive(Clone, Debug, Copy, PartialEq, serde::Serialize, serde::Deserialize)]
14pub struct TrackColor {
15 pub r: f32,
16 pub g: f32,
17 pub b: f32,
18 pub a: f32,
19}
20
21#[derive(Clone, Debug, Default, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
22pub struct MpeExpressionPoint {
23 pub sample_offset: usize,
24 pub value: u16,
26}
27
28#[derive(Clone, Debug, Default, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
29pub struct MpeExpressionCurve {
30 pub points: Vec<MpeExpressionPoint>,
31}
32
33#[derive(Clone, Debug, Default, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
34pub struct MpeNoteExpression {
35 pub pitch_bend: MpeExpressionCurve,
36 pub pressure: MpeExpressionCurve,
37 pub timbre: MpeExpressionCurve,
38}
39
40#[derive(Clone, Debug)]
41pub struct MidiNoteData {
42 pub start_sample: usize,
43 pub length_samples: usize,
44 pub pitch: u8,
45 pub velocity: u8,
46 pub channel: u8,
47 pub mpe: MpeNoteExpression,
48}
49
50#[derive(Clone, Debug)]
51pub struct MidiControllerData {
52 pub sample: usize,
53 pub controller: u8,
54 pub value: u8,
55 pub channel: u8,
56}
57
58#[derive(Debug, Clone)]
59pub struct MidiRawEventData {
60 pub sample: usize,
61 pub data: Vec<u8>,
62}
63
64#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
65pub struct JackPortInfo {
66 pub name: String,
67 pub kind: Kind,
68 pub is_input: bool,
69 pub is_output: bool,
70 pub is_physical: bool,
71 pub is_maolan: bool,
72}
73
74#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
75pub struct JackConnectionInfo {
76 pub source: String,
77 pub destination: String,
78}
79
80#[derive(Clone, Debug, Default, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
81pub struct JackGraphInfo {
82 pub ports: Vec<JackPortInfo>,
83 pub connections: Vec<JackConnectionInfo>,
84}
85
86#[derive(Clone, Debug)]
87pub struct HwMidiEvent {
88 pub device: String,
89 pub event: MidiEvent,
90}
91
92#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
93pub struct OfflineAutomationPoint {
94 pub sample: usize,
95 pub value: f32,
96}
97
98#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
99pub enum OfflineAutomationTarget {
100 Volume,
101 Balance,
102 MidiCc {
103 channel: u8,
104 cc: u8,
105 },
106 #[cfg(unix)]
107 Lv2Parameter {
108 instance_id: usize,
109 index: u32,
110 min: f32,
111 max: f32,
112 },
113 Vst3Parameter {
114 instance_id: usize,
115 param_id: u32,
116 },
117 ClapParameter {
118 instance_id: usize,
119 param_id: u32,
120 min: f64,
121 max: f64,
122 },
123 MixOsc {
124 addr: String,
125 path: String,
126 },
127}
128
129#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
130pub struct OfflineAutomationLane {
131 pub target: OfflineAutomationTarget,
132 #[serde(default)]
133 pub visible: bool,
134 pub points: Vec<OfflineAutomationPoint>,
135}
136
137impl OfflineAutomationLane {
138 pub fn value_at(&self, sample: usize) -> Option<f32> {
141 let points = &self.points;
142 if points.is_empty() {
143 return None;
144 }
145 if sample <= points[0].sample {
146 return Some(points[0].value.clamp(0.0, 1.0));
147 }
148 let last = points.len().saturating_sub(1);
149 if sample >= points[last].sample {
150 return Some(points[last].value.clamp(0.0, 1.0));
151 }
152 for segment in points.windows(2) {
153 let left = &segment[0];
154 let right = &segment[1];
155 if sample < left.sample || sample > right.sample {
156 continue;
157 }
158 let span = right.sample.saturating_sub(left.sample).max(1) as f32;
159 let t = (sample.saturating_sub(left.sample) as f32 / span).clamp(0.0, 1.0);
160 return Some((left.value + (right.value - left.value) * t).clamp(0.0, 1.0));
161 }
162 None
163 }
164}
165
166#[derive(Clone, Debug)]
167pub struct OfflineBounceWork {
168 pub state: Arc<crate::state::StateSnapshot>,
169 pub track_name: String,
170 pub output_path: String,
171 pub start_sample: usize,
172 pub length_samples: usize,
173 pub tempo_bpm: f64,
174 pub tsig_num: u16,
175 pub tsig_denom: u16,
176 pub automation_lanes: Vec<OfflineAutomationLane>,
177 pub cancel: Arc<AtomicBool>,
178 pub apply_fader: bool,
179}
180
181#[derive(Clone, Debug, Default, serde::Serialize, serde::Deserialize)]
182pub struct PitchCorrectionPointData {
183 pub start_sample: usize,
184 pub length_samples: usize,
185 pub detected_midi_pitch: f32,
186 pub target_midi_pitch: f32,
187 pub clarity: f32,
188}
189
190#[derive(Clone, Debug, PartialEq)]
191pub struct TempoPoint {
192 pub sample: usize,
193 pub bpm: f64,
194}
195
196#[derive(Clone, Debug, PartialEq)]
197pub struct TimeSignaturePoint {
198 pub sample: usize,
199 pub numerator: u16,
200 pub denominator: u16,
201}
202
203#[derive(Clone, Debug, Default, serde::Serialize, serde::Deserialize)]
204pub struct AudioClipData {
205 #[serde(default)]
206 pub id: String,
207 pub name: String,
208 pub start: usize,
209 pub length: usize,
210 pub offset: usize,
211 pub input_channel: usize,
212 pub muted: bool,
213 pub peaks_file: Option<String>,
214 pub fade_enabled: bool,
215 pub fade_in_samples: usize,
216 pub fade_out_samples: usize,
217 pub preview_name: Option<String>,
218 pub source_name: Option<String>,
219 pub source_offset: Option<usize>,
220 pub source_length: Option<usize>,
221 pub pitch_correction_points: Vec<PitchCorrectionPointData>,
222 pub pitch_correction_frame_likeness: Option<f32>,
223 pub pitch_correction_inertia_ms: Option<u16>,
224 pub pitch_correction_formant_compensation: Option<bool>,
225 pub plugin_graph_json: Option<serde_json::Value>,
226 pub grouped_clips: Vec<AudioClipData>,
227}
228
229#[derive(Clone, Debug, Default, serde::Serialize, serde::Deserialize)]
230pub struct MidiClipData {
231 #[serde(default)]
232 pub id: String,
233 pub name: String,
234 pub start: usize,
235 pub length: usize,
236 pub offset: usize,
237 pub input_channel: usize,
238 pub muted: bool,
239 pub grouped_clips: Vec<MidiClipData>,
240}
241
242pub fn generate_clip_id() -> String {
244 uuid::Uuid::new_v4().to_string()
245}
246
247#[derive(Clone, Debug)]
248pub struct ClipMoveFrom {
249 pub track_name: String,
250 pub clip_index: usize,
251}
252
253#[derive(Clone, Debug)]
254pub struct ClipMoveTo {
255 pub track_name: String,
256 pub sample_offset: usize,
257 pub input_channel: usize,
258}
259
260#[derive(Clone, Debug, PartialEq, Eq, Hash)]
261pub enum PluginGraphNode {
262 TrackInput,
263 TrackOutput,
264 ClapPluginInstance(usize),
265 Vst3PluginInstance(usize),
266 #[cfg(unix)]
267 Lv2PluginInstance(usize),
268}
269
270#[derive(Clone, Debug, PartialEq)]
271pub struct PluginGraphPlugin {
272 pub node: PluginGraphNode,
273 pub instance_id: usize,
274 pub format: String,
275 pub uri: String,
276 pub plugin_id: String,
277 pub name: String,
278 pub main_audio_inputs: usize,
279 pub main_audio_outputs: usize,
280 pub audio_inputs: usize,
281 pub audio_outputs: usize,
282 pub midi_inputs: usize,
283 pub midi_outputs: usize,
284 pub state: Option<serde_json::Value>,
285 pub bypassed: bool,
286}
287
288#[cfg(unix)]
289#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
290pub struct Lv2StatePortValue {
291 pub index: u32,
292 pub value: f32,
293}
294
295#[cfg(unix)]
296#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
297pub struct Lv2StateProperty {
298 pub key_uri: String,
299 pub type_uri: String,
300 pub flags: u32,
301 pub value: Vec<u8>,
302}
303
304#[cfg(unix)]
305#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
306pub struct Lv2PluginState {
307 pub port_values: Vec<Lv2StatePortValue>,
308 pub properties: Vec<Lv2StateProperty>,
309}
310
311#[cfg(unix)]
312#[derive(Clone, Debug, PartialEq)]
313pub struct Lv2ControlPortInfo {
314 pub index: u32,
315 pub name: String,
316 pub min: f32,
317 pub max: f32,
318 pub value: f32,
319}
320
321#[derive(Clone, Debug, PartialEq, Eq)]
322pub struct PluginGraphConnection {
323 pub from_node: PluginGraphNode,
324 pub from_port: usize,
325 pub to_node: PluginGraphNode,
326 pub to_port: usize,
327 pub kind: Kind,
328}
329
330pub type PluginGraphSnapshot = (Vec<PluginGraphPlugin>, Vec<PluginGraphConnection>);
331
332#[derive(Clone, Debug, Copy, PartialEq, Eq, Hash)]
333pub enum PluginKind {
334 Clap,
335 Vst3,
336 #[cfg(unix)]
337 Lv2,
338}
339
340#[derive(Clone, Debug)]
341pub enum ProcessTask {
342 Track(TrackHandle),
343 FolderInput(TrackHandle),
344 FolderOutput(TrackHandle),
345 Plugin {
346 track: TrackHandle,
347 kind: PluginKind,
348 index: usize,
349 },
350}
351
352impl PartialEq for ProcessTask {
353 fn eq(&self, other: &Self) -> bool {
354 use ProcessTask::*;
355 match (self, other) {
356 (Track(a), Track(b))
357 | (FolderInput(a), FolderInput(b))
358 | (FolderOutput(a), FolderOutput(b)) => Arc::ptr_eq(a, b),
359 (
360 Plugin {
361 track: a,
362 kind: ka,
363 index: ia,
364 },
365 Plugin {
366 track: b,
367 kind: kb,
368 index: ib,
369 },
370 ) => Arc::ptr_eq(a, b) && ka == kb && ia == ib,
371 _ => false,
372 }
373 }
374}
375
376impl Eq for ProcessTask {}
377
378impl std::hash::Hash for ProcessTask {
379 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
380 use ProcessTask::*;
381 match self {
382 Track(t) | FolderInput(t) | FolderOutput(t) => {
383 Arc::as_ptr(t).hash(state);
384 }
385 Plugin { track, kind, index } => {
386 Arc::as_ptr(track).hash(state);
387 kind.hash(state);
388 index.hash(state);
389 }
390 }
391 }
392}
393
394#[derive(Clone, Debug, PartialEq, Eq, Hash)]
395pub enum Vst3GraphNode {
396 TrackInput,
397 TrackOutput,
398 PluginInstance(usize),
399}
400
401#[derive(Clone, Debug)]
402pub struct Vst3GraphPlugin {
403 pub instance_id: usize,
404 pub name: String,
405 pub path: String,
406 pub audio_inputs: usize,
407 pub audio_outputs: usize,
408 pub parameters: Vec<crate::vst3::port::ParameterInfo>,
409}
410
411#[derive(Clone, Debug, PartialEq, Eq)]
412pub struct Vst3GraphConnection {
413 pub from_node: Vst3GraphNode,
414 pub from_port: usize,
415 pub to_node: Vst3GraphNode,
416 pub to_port: usize,
417 pub kind: Kind,
418}
419
420#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
421pub struct MidiLearnBinding {
422 pub device: Option<String>,
423 pub channel: u8,
424 pub cc: u8,
425}
426
427#[derive(Clone, Copy, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
428pub enum TrackMidiLearnTarget {
429 Volume,
430 Balance,
431 Mute,
432 Solo,
433 Arm,
434 InputMonitor,
435 DiskMonitor,
436}
437
438#[derive(Clone, Copy, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
439pub enum GlobalMidiLearnTarget {
440 PlayPause,
441 Stop,
442 RecordToggle,
443}
444
445#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
446pub enum SessionMidiLearnTarget {
447 Slot {
448 track_name: String,
449 scene_index: usize,
450 },
451 Scene(usize),
452 StopTrack(String),
453 StopAll,
454}
455
456#[derive(Clone, Copy, Debug, PartialEq, Eq)]
457pub enum SessionSlotState {
458 Stopped,
459 Queued,
460 Playing,
461 Stopping,
462}
463
464#[derive(Clone, Copy, Debug, PartialEq, Eq)]
465pub enum LaunchQuantization {
466 None,
467 Beat,
468 Bar,
469 TwoBars,
470 FourBars,
471 EightBars,
472 Eighth,
473 Sixteenth,
474 ThirtySecond,
475 SixtyFourth,
476}
477
478#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
479pub enum TrackAutomationMode {
480 #[default]
481 Read,
482 Touch,
483 Latch,
484 Write,
485}
486
487impl TrackAutomationMode {
488 pub fn as_u8(self) -> u8 {
489 match self {
490 Self::Read => 0,
491 Self::Touch => 1,
492 Self::Latch => 2,
493 Self::Write => 3,
494 }
495 }
496
497 pub fn from_u8(value: u8) -> Self {
498 match value {
499 1 => Self::Touch,
500 2 => Self::Latch,
501 3 => Self::Write,
502 _ => Self::Read,
503 }
504 }
505}
506
507#[derive(Clone, Debug)]
508pub enum SessionAction {
509 LaunchClip {
510 track_name: String,
511 scene_index: usize,
512 clip_id: String,
513 launch_quantization: LaunchQuantization,
514 loop_enabled: bool,
515 loop_start_samples: usize,
516 loop_end_samples: usize,
517 },
518 StopClip {
519 track_name: String,
520 scene_index: usize,
521 launch_quantization: LaunchQuantization,
522 },
523 LaunchScene {
524 scene_index: usize,
525 launch_quantization: LaunchQuantization,
526 },
527 StopScene {
528 scene_index: usize,
529 launch_quantization: LaunchQuantization,
530 },
531 QueueScene {
537 scene_index: usize,
538 launch_quantization: LaunchQuantization,
539 },
540 StopAllClips,
541}
542
543#[derive(Clone, Debug)]
544pub enum Action {
545 Quit,
546 Log {
547 source: String,
548 message: String,
549 },
550 Play,
551 Pause,
552 Stop,
553 SessionPlay,
554 TransportPosition(usize),
555 TransportPositionAt {
556 sample: usize,
557 after_frames: usize,
558 },
559 JumpToEnd,
560 SetLoopEnabled(bool),
561 SetLoopRange(Option<(usize, usize)>),
562 SetPunchEnabled(bool),
563 SetPunchRange(Option<(usize, usize)>),
564 SetMetronomeEnabled(bool),
565 SetTempo(f64),
566 SetTimeSignature {
567 numerator: u16,
568 denominator: u16,
569 },
570 SetTempoMap {
571 tempo_points: Vec<TempoPoint>,
572 time_signature_points: Vec<TimeSignaturePoint>,
573 },
574 SetOscEnabled(bool),
575 SetClipPlaybackEnabled(bool),
576 SetSessionClipPlaybackEnabled(bool),
577 SetRecordEnabled(bool),
578 SetModulators(Vec<Modulator>),
579 SetTrackAutomationLanes {
580 track_name: String,
581 lanes: serde_json::Value,
582 mode: TrackAutomationMode,
583 },
584 TrackAutomationToggleLane {
585 track_name: String,
586 target: OfflineAutomationTarget,
587 },
588 TrackAutomationInsertPoint {
589 track_name: String,
590 target: OfflineAutomationTarget,
591 sample: usize,
592 value: f32,
593 },
594 TrackAutomationDeletePoint {
595 track_name: String,
596 target: OfflineAutomationTarget,
597 sample: usize,
598 },
599 TrackAutomationSetMode {
600 track_name: String,
601 mode: TrackAutomationMode,
602 },
603 SetSessionPath(String),
604 BeginHistoryGroup,
605 EndHistoryGroup,
606 ApplyGroupedActions(Vec<Action>),
607 ClearHistory,
608 BeginSessionRestore,
609 EndSessionRestore,
610 AddTrack {
611 name: String,
612 audio_ins: usize,
613 midi_ins: usize,
614 audio_outs: usize,
615 midi_outs: usize,
616 folder: bool,
617 mixosc_addr: Option<String>,
618 },
619 TrackAddAudioInput(String),
620 TrackAddAudioOutput(String),
621 TrackRemoveAudioInput(String),
622 TrackRemoveAudioOutput(String),
623 AddClip {
624 clip_id: String,
625 name: String,
626 track_name: String,
627 start: usize,
628 length: usize,
629 offset: usize,
630 input_channel: usize,
631 muted: bool,
632 peaks_file: Option<String>,
633 kind: Kind,
634 fade_enabled: bool,
635 fade_in_samples: usize,
636 fade_out_samples: usize,
637 source_name: Option<String>,
638 source_offset: Option<usize>,
639 source_length: Option<usize>,
640 preview_name: Option<String>,
641 pitch_correction_points: Vec<PitchCorrectionPointData>,
642 pitch_correction_frame_likeness: Option<f32>,
643 pitch_correction_inertia_ms: Option<u16>,
644 pitch_correction_formant_compensation: Option<bool>,
645 plugin_graph_json: Option<serde_json::Value>,
646 },
647 AddGroupedClip {
648 track_name: String,
649 kind: Kind,
650 audio_clip: Option<AudioClipData>,
651 midi_clip: Option<MidiClipData>,
652 },
653 RemoveClip {
654 track_name: String,
655 kind: Kind,
656 clip_indices: Vec<usize>,
657 },
658 MoveClipToUnused {
659 track_name: String,
660 kind: Kind,
661 clip_indices: Vec<usize>,
662 },
663 DeleteUnusedClips {
664 clip_ids: Vec<String>,
665 },
666 SetUnusedClips {
667 audio: Vec<AudioClipData>,
668 midi: Vec<MidiClipData>,
669 },
670 SetClipFade {
671 track_name: String,
672 clip_index: usize,
673 kind: Kind,
674 fade_enabled: bool,
675 fade_in_samples: usize,
676 fade_out_samples: usize,
677 },
678 SetClipBounds {
679 track_name: String,
680 clip_index: usize,
681 kind: Kind,
682 start: usize,
683 length: usize,
684 offset: usize,
685 },
686 SyncClipBounds {
687 track_name: String,
688 clip_index: usize,
689 kind: Kind,
690 start: usize,
691 length: usize,
692 offset: usize,
693 },
694 SetClipMuted {
695 track_name: String,
696 clip_index: usize,
697 kind: Kind,
698 muted: bool,
699 },
700 SetClipPluginGraphJson {
701 track_name: String,
702 clip_index: usize,
703 plugin_graph_json: Option<serde_json::Value>,
704 },
705 SetClipPitchCorrection {
706 track_name: String,
707 clip_index: usize,
708 preview_name: Option<String>,
709 source_name: Option<String>,
710 source_offset: Option<usize>,
711 source_length: Option<usize>,
712 pitch_correction_points: Vec<PitchCorrectionPointData>,
713 pitch_correction_frame_likeness: Option<f32>,
714 pitch_correction_inertia_ms: Option<u16>,
715 pitch_correction_formant_compensation: Option<bool>,
716 },
717 RenameClip {
718 track_name: String,
719 kind: Kind,
720 clip_index: usize,
721 new_name: String,
722 },
723 SetClipIdentity {
724 track_name: String,
725 kind: Kind,
726 clip_index: usize,
727 new_id: String,
728 new_name: String,
729 },
730 SetClipSourceName {
731 track_name: String,
732 kind: Kind,
733 clip_index: usize,
734 name: String,
735 },
736 RenameTrack {
737 old_name: String,
738 new_name: String,
739 },
740 RemoveTrack(String),
741 TrackLevel(String, f32),
742 TrackBalance(String, f32),
743 TrackAutomationLevel(String, f32),
744 TrackAutomationBalance(String, f32),
745 TrackMidiCc {
746 track_name: String,
747 channel: u8,
748 cc: u8,
749 value: u8,
750 },
751 TrackMeters {
752 track_name: String,
753 output_db: Vec<f32>,
754 },
755 RequestMeterSnapshot,
756 MeterSnapshot {
757 hw_out_db: Arc<Vec<f32>>,
758 hw_out_lufs: Option<crate::loudness::LoudnessValues>,
759 track_meters: Arc<Vec<(String, Vec<f32>)>>,
760 },
761 RequestTrackList,
762 TrackList(Vec<String>),
763 RequestTransportState,
764 TransportState {
765 sample: usize,
766 tempo_bpm: f64,
767 playing: bool,
768 paused: bool,
769 tsig_num: u16,
770 tsig_denom: u16,
771 },
772 TrackToggleArm(String),
773 TrackToggleMute(String),
774 TrackTogglePhase(String),
775 TrackToggleSolo(String),
776 TrackToggleMaster(String),
777 TrackToggleInputMonitor {
778 track_name: String,
779 lane: usize,
780 },
781 TrackToggleDiskMonitor {
782 track_name: String,
783 lane: usize,
784 },
785 TrackToggleMidiInputMonitor {
786 track_name: String,
787 lane: usize,
788 },
789 TrackToggleMidiDiskMonitor {
790 track_name: String,
791 lane: usize,
792 },
793 TrackSetColor {
794 track_name: String,
795 color: Option<TrackColor>,
796 },
797 TrackArmMidiLearn {
798 track_name: String,
799 target: TrackMidiLearnTarget,
800 },
801 GlobalArmMidiLearn {
802 target: GlobalMidiLearnTarget,
803 },
804 SessionArmMidiLearn {
805 target: SessionMidiLearnTarget,
806 },
807 TrackSetMidiLearnBinding {
808 track_name: String,
809 target: TrackMidiLearnTarget,
810 binding: Option<MidiLearnBinding>,
811 },
812 SetGlobalMidiLearnBinding {
813 target: GlobalMidiLearnTarget,
814 binding: Option<MidiLearnBinding>,
815 },
816 SetSessionMidiLearnBinding {
817 target: SessionMidiLearnTarget,
818 binding: Option<MidiLearnBinding>,
819 },
820 SessionMidiLearnTriggered {
821 target: SessionMidiLearnTarget,
822 },
823 TrackSetFolder {
824 track_name: String,
825 is_folder: bool,
826 },
827 TrackSetParent {
828 track_name: String,
829 parent_name: Option<String>,
830 },
831 TrackToggleFolder {
832 track_name: String,
833 },
834 TrackSetMidiLaneChannel {
835 track_name: String,
836 lane: usize,
837 channel: Option<u8>,
838 },
839 TrackSetMpeZone {
840 track_name: String,
841 manager_channel: u8,
842 member_count: u8,
843 },
844 TrackSetMpePitchBendSensitivity {
845 track_name: String,
846 channel: u8,
847 semitones: u8,
848 },
849 TrackSetFrozen {
850 track_name: String,
851 frozen: bool,
852 },
853 TrackSetSessionSlot {
854 track_name: String,
855 scene_index: usize,
856 clip_id: Option<String>,
857 },
858 TrackSetSessionSlotPlayEnabled {
859 track_name: String,
860 scene_index: usize,
861 enabled: bool,
862 },
863 TrackSetSessionSlotStopEnabled {
864 track_name: String,
865 scene_index: usize,
866 enabled: bool,
867 },
868 TrackOfflineBounce {
869 track_name: String,
870 output_path: String,
871 start_sample: usize,
872 length_samples: usize,
873 automation_lanes: Vec<OfflineAutomationLane>,
874 apply_fader: bool,
875 },
876 TrackOfflineBounceCancel {
877 track_name: String,
878 },
879 TrackOfflineBounceCancelAll,
880 TrackOfflineBounceCanceled {
881 track_name: String,
882 },
883 TrackOfflineBounceProgress {
884 track_name: String,
885 progress: f32,
886 operation: Option<String>,
887 },
888 PianoKey {
889 track_name: String,
890 note: u8,
891 velocity: u8,
892 on: bool,
893 },
894 ModifyMidiNotes {
895 track_name: String,
896 clip_index: usize,
897 note_indices: Vec<usize>,
898 new_notes: Vec<MidiNoteData>,
899 old_notes: Vec<MidiNoteData>,
900 },
901 ModifyMidiControllers {
902 track_name: String,
903 clip_index: usize,
904 controller_indices: Vec<usize>,
905 new_controllers: Vec<MidiControllerData>,
906 old_controllers: Vec<MidiControllerData>,
907 },
908 DeleteMidiControllers {
909 track_name: String,
910 clip_index: usize,
911 controller_indices: Vec<usize>,
912 deleted_controllers: Vec<(usize, MidiControllerData)>,
913 },
914 InsertMidiControllers {
915 track_name: String,
916 clip_index: usize,
917 controllers: Vec<(usize, MidiControllerData)>,
918 },
919 DeleteMidiNotes {
920 track_name: String,
921 clip_index: usize,
922 note_indices: Vec<usize>,
923 deleted_notes: Vec<(usize, MidiNoteData)>,
924 },
925 InsertMidiNotes {
926 track_name: String,
927 clip_index: usize,
928 notes: Vec<(usize, MidiNoteData)>,
929 },
930 SetStepRecording(bool),
931 StepRecordMidiNote {
932 device: String,
933 channel: u8,
934 pitch: u8,
935 velocity: u8,
936 },
937 SetMidiSysExEvents {
938 track_name: String,
939 clip_index: usize,
940 new_sysex_events: Vec<MidiRawEventData>,
941 old_sysex_events: Vec<MidiRawEventData>,
942 },
943 TrackClearDefaultPassthrough {
944 track_name: String,
945 },
946 TrackClearPlugins {
947 track_name: String,
948 },
949 #[cfg(unix)]
950 TrackSetLv2PluginState {
951 track_name: String,
952 instance_id: usize,
953 state: Vec<u8>,
954 },
955 #[cfg(unix)]
956 ClipSetLv2PluginState {
957 track_name: String,
958 clip_idx: usize,
959 instance_id: usize,
960 state: Vec<u8>,
961 },
962 #[cfg(unix)]
963 TrackLv2SnapshotState {
964 track_name: String,
965 instance_id: usize,
966 },
967 #[cfg(unix)]
968 ClipLv2SnapshotState {
969 track_name: String,
970 clip_idx: usize,
971 instance_id: usize,
972 },
973 #[cfg(unix)]
974 TrackLv2StateSnapshot {
975 track_name: String,
976 instance_id: usize,
977 state: Vec<u8>,
978 },
979 #[cfg(unix)]
980 TrackGetLv2PluginControls {
981 track_name: String,
982 instance_id: usize,
983 },
984 #[cfg(unix)]
985 ClipGetLv2PluginControls {
986 track_name: String,
987 clip_idx: usize,
988 instance_id: usize,
989 },
990 #[cfg(unix)]
991 TrackLv2PluginControls {
992 track_name: String,
993 instance_id: usize,
994 controls: Vec<Lv2ControlPortInfo>,
995 instance_access_handle: Option<usize>,
996 },
997 #[cfg(unix)]
998 ClipLv2PluginControls {
999 track_name: String,
1000 clip_idx: usize,
1001 instance_id: usize,
1002 controls: Vec<Lv2ControlPortInfo>,
1003 instance_access_handle: Option<usize>,
1004 },
1005 #[cfg(unix)]
1006 TrackGetLv2Midnam {
1007 track_name: String,
1008 },
1009 #[cfg(unix)]
1010 TrackLv2Midnam {
1011 track_name: String,
1012 note_names: std::collections::HashMap<u8, String>,
1013 },
1014 TrackGetClapNoteNames {
1015 track_name: String,
1016 },
1017 TrackClapNoteNames {
1018 track_name: String,
1019 note_names: std::collections::HashMap<u8, String>,
1020 },
1021 #[cfg(unix)]
1022 TrackSetLv2ControlValue {
1023 track_name: String,
1024 instance_id: usize,
1025 index: u32,
1026 value: f32,
1027 },
1028 #[cfg(unix)]
1029 ClipSetLv2ControlValue {
1030 track_name: String,
1031 clip_idx: usize,
1032 instance_id: usize,
1033 index: u32,
1034 value: f32,
1035 },
1036 #[cfg(unix)]
1037 ClipLv2StateSnapshot {
1038 track_name: String,
1039 clip_idx: usize,
1040 instance_id: usize,
1041 state: Vec<u8>,
1042 },
1043 TrackGetPluginGraph {
1044 track_name: String,
1045 include_state: bool,
1046 },
1047 TrackPluginGraph {
1048 track_name: String,
1049 plugins: Vec<PluginGraphPlugin>,
1050 connections: Vec<PluginGraphConnection>,
1051 connectable_connections: Vec<ConnectableConnection>,
1052 },
1053 TrackConnectPluginAudio {
1054 track_name: String,
1055 from_node: PluginGraphNode,
1056 from_port: usize,
1057 to_node: PluginGraphNode,
1058 to_port: usize,
1059 },
1060 TrackConnectPluginMidi {
1061 track_name: String,
1062 from_node: PluginGraphNode,
1063 from_port: usize,
1064 to_node: PluginGraphNode,
1065 to_port: usize,
1066 },
1067 TrackDisconnectPluginAudio {
1068 track_name: String,
1069 from_node: PluginGraphNode,
1070 from_port: usize,
1071 to_node: PluginGraphNode,
1072 to_port: usize,
1073 },
1074 TrackDisconnectPluginMidi {
1075 track_name: String,
1076 from_node: PluginGraphNode,
1077 from_port: usize,
1078 to_node: PluginGraphNode,
1079 to_port: usize,
1080 },
1081 TrackConnectAudio {
1082 track_name: String,
1083 from: ConnectableRef,
1084 from_port: usize,
1085 to: ConnectableRef,
1086 to_port: usize,
1087 },
1088 TrackDisconnectAudio {
1089 track_name: String,
1090 from: ConnectableRef,
1091 from_port: usize,
1092 to: ConnectableRef,
1093 to_port: usize,
1094 },
1095 TrackConnectMidi {
1096 track_name: String,
1097 from: ConnectableRef,
1098 from_port: usize,
1099 to: ConnectableRef,
1100 to_port: usize,
1101 },
1102 TrackDisconnectMidi {
1103 track_name: String,
1104 from: ConnectableRef,
1105 from_port: usize,
1106 to: ConnectableRef,
1107 to_port: usize,
1108 },
1109 #[cfg(unix)]
1110 ListLv2Plugins,
1111 #[cfg(unix)]
1112 Lv2Plugins(Vec<Lv2PluginInfo>),
1113 #[cfg(unix)]
1114 Lv2PluginsUnavailable {
1115 error: String,
1116 },
1117 ListVst3Plugins,
1118 Vst3Plugins(Vec<Vst3PluginInfo>),
1119 Vst3PluginsUnavailable {
1120 error: String,
1121 },
1122 ListClapPlugins,
1123 ListClapPluginsWithCapabilities,
1124 ClapPlugins(Vec<ClapPluginInfo>),
1125 ClapPluginsUnavailable {
1126 error: String,
1127 },
1128 TrackSetClapParameter {
1129 track_name: String,
1130 instance_id: usize,
1131 param_id: u32,
1132 value: f64,
1133 },
1134 ClipSetClapParameter {
1135 track_name: String,
1136 clip_idx: usize,
1137 instance_id: usize,
1138 param_id: u32,
1139 value: f64,
1140 },
1141 TrackSetClapParameterAt {
1142 track_name: String,
1143 instance_id: usize,
1144 param_id: u32,
1145 value: f64,
1146 frame: u32,
1147 },
1148 TrackBeginClapParameterEdit {
1149 track_name: String,
1150 instance_id: usize,
1151 param_id: u32,
1152 frame: u32,
1153 },
1154 TrackEndClapParameterEdit {
1155 track_name: String,
1156 instance_id: usize,
1157 param_id: u32,
1158 frame: u32,
1159 },
1160 TrackGetClapParameters {
1161 track_name: String,
1162 instance_id: usize,
1163 },
1164 TrackClapParameters {
1165 track_name: String,
1166 instance_id: usize,
1167 parameters: Vec<ClapParameterInfo>,
1168 },
1169 TrackClapSnapshotState {
1170 track_name: String,
1171 instance_id: usize,
1172 },
1173 ClipClapSnapshotState {
1174 track_name: String,
1175 clip_idx: usize,
1176 instance_id: usize,
1177 },
1178 TrackClapStateSnapshot {
1179 track_name: String,
1180 instance_id: usize,
1181 plugin_id: String,
1182 state: crate::clap::ClapPluginState,
1183 },
1184 ClipClapStateSnapshot {
1185 track_name: String,
1186 clip_idx: usize,
1187 instance_id: usize,
1188 plugin_id: String,
1189 state: crate::clap::ClapPluginState,
1190 },
1191 TrackClapStateDirty {
1192 track_name: String,
1193 instance_id: usize,
1194 },
1195 ClipClapStateDirty {
1196 track_name: String,
1197 clip_idx: usize,
1198 instance_id: usize,
1199 },
1200 TrackClapRestoreState {
1201 track_name: String,
1202 instance_id: usize,
1203 state: crate::clap::ClapPluginState,
1204 },
1205 ClipClapRestoreState {
1206 track_name: String,
1207 clip_idx: usize,
1208 instance_id: usize,
1209 state: crate::clap::ClapPluginState,
1210 },
1211 TrackSnapshotAllClapStates {
1212 track_name: String,
1213 },
1214 TrackSnapshotAllClapStatesDone {
1215 track_name: String,
1216 },
1217 TrackLoadClapPlugin {
1218 track_name: String,
1219 plugin_id: String,
1220 instance_id: Option<usize>,
1221 },
1222 TrackUnloadClapPlugin {
1223 track_name: String,
1224 plugin_id: String,
1225 },
1226 TrackUnloadClapPluginInstance {
1227 track_name: String,
1228 instance_id: usize,
1229 },
1230 TrackShowClapGui {
1231 track_name: String,
1232 instance_id: usize,
1233 },
1234 ClipShowClapGui {
1235 track_name: String,
1236 clip_idx: usize,
1237 instance_id: usize,
1238 },
1239 TrackLoadVst3Plugin {
1240 track_name: String,
1241 plugin_id: String,
1242 instance_id: Option<usize>,
1243 },
1244 TrackUnloadVst3Plugin {
1245 track_name: String,
1246 plugin_id: String,
1247 },
1248 TrackUnloadVst3PluginInstance {
1249 track_name: String,
1250 instance_id: usize,
1251 },
1252 TrackShowVst3Gui {
1253 track_name: String,
1254 instance_id: usize,
1255 },
1256 ClipShowVst3Gui {
1257 track_name: String,
1258 clip_idx: usize,
1259 instance_id: usize,
1260 },
1261 #[cfg(unix)]
1262 TrackLoadLv2Plugin {
1263 track_name: String,
1264 plugin_uri: String,
1265 instance_id: Option<usize>,
1266 },
1267 #[cfg(unix)]
1268 TrackUnloadLv2Plugin {
1269 track_name: String,
1270 plugin_uri: String,
1271 },
1272 #[cfg(unix)]
1273 TrackUnloadLv2PluginInstance {
1274 track_name: String,
1275 instance_id: usize,
1276 },
1277 TrackShowLv2Gui {
1278 track_name: String,
1279 instance_id: usize,
1280 },
1281 ClipShowLv2Gui {
1282 track_name: String,
1283 clip_idx: usize,
1284 instance_id: usize,
1285 },
1286 TrackSetPluginResourceDir {
1287 track_name: String,
1288 instance_id: usize,
1289 format: String,
1290 directory: String,
1291 shared: bool,
1292 },
1293 TrackClapCollectResources {
1294 track_name: String,
1295 instance_id: usize,
1296 },
1297 TrackClapResourceFiles {
1298 track_name: String,
1299 instance_id: usize,
1300 files: Vec<(u32, String)>,
1301 },
1302 ClipSetPluginResourceDir {
1303 track_name: String,
1304 clip_idx: usize,
1305 instance_id: usize,
1306 format: String,
1307 directory: String,
1308 shared: bool,
1309 },
1310 ClipClapCollectResources {
1311 track_name: String,
1312 clip_idx: usize,
1313 instance_id: usize,
1314 },
1315 ClipClapResourceFiles {
1316 track_name: String,
1317 clip_idx: usize,
1318 instance_id: usize,
1319 files: Vec<(u32, String)>,
1320 },
1321 TrackGetVst3Graph {
1322 track_name: String,
1323 },
1324 TrackVst3Graph {
1325 track_name: String,
1326 plugins: Vec<Vst3GraphPlugin>,
1327 connections: Vec<Vst3GraphConnection>,
1328 },
1329 TrackSetVst3Parameter {
1330 track_name: String,
1331 instance_id: usize,
1332 param_id: u32,
1333 value: f32,
1334 },
1335 TrackSetPluginBypassed {
1336 track_name: String,
1337 instance_id: usize,
1338 format: String,
1339 bypassed: bool,
1340 },
1341 TrackGetVst3Parameters {
1342 track_name: String,
1343 instance_id: usize,
1344 },
1345 TrackVst3Parameters {
1346 track_name: String,
1347 instance_id: usize,
1348 parameters: Vec<crate::vst3::port::ParameterInfo>,
1349 },
1350 TrackVst3SnapshotState {
1351 track_name: String,
1352 instance_id: usize,
1353 },
1354 ClipVst3SnapshotState {
1355 track_name: String,
1356 clip_idx: usize,
1357 instance_id: usize,
1358 },
1359 TrackVst3StateSnapshot {
1360 track_name: String,
1361 instance_id: usize,
1362 state: crate::vst3::state::Vst3PluginState,
1363 },
1364 ClipVst3StateSnapshot {
1365 track_name: String,
1366 clip_idx: usize,
1367 instance_id: usize,
1368 state: crate::vst3::state::Vst3PluginState,
1369 },
1370 TrackVst3RestoreState {
1371 track_name: String,
1372 instance_id: usize,
1373 state: crate::vst3::state::Vst3PluginState,
1374 },
1375 ClipVst3RestoreState {
1376 track_name: String,
1377 clip_idx: usize,
1378 instance_id: usize,
1379 state: crate::vst3::state::Vst3PluginState,
1380 },
1381 TrackConnectVst3Audio {
1382 track_name: String,
1383 from_node: Vst3GraphNode,
1384 from_port: usize,
1385 to_node: Vst3GraphNode,
1386 to_port: usize,
1387 },
1388 TrackDisconnectVst3Audio {
1389 track_name: String,
1390 from_node: Vst3GraphNode,
1391 from_port: usize,
1392 to_node: Vst3GraphNode,
1393 to_port: usize,
1394 },
1395 ClipMove {
1396 kind: Kind,
1397 from: ClipMoveFrom,
1398 to: ClipMoveTo,
1399 copy: bool,
1400 },
1401 Connect {
1402 from_track: String,
1403 from_port: usize,
1404 to_track: String,
1405 to_port: usize,
1406 kind: Kind,
1407 },
1408 Disconnect {
1409 from_track: String,
1410 from_port: usize,
1411 to_track: String,
1412 to_port: usize,
1413 kind: Kind,
1414 },
1415 OpenAudioDevice {
1416 device: String,
1417 input_device: Option<String>,
1418 sample_rate_hz: i32,
1419 bits: i32,
1420 exclusive: bool,
1421 period_frames: usize,
1422 nperiods: usize,
1423 sync_mode: bool,
1424 actual_period_frames: usize,
1425 input_channels: usize,
1426 output_channels: usize,
1427 bytes_per_frame: usize,
1428 },
1429 JackAddAudioInputPort,
1430 JackRemoveAudioInputPort(usize),
1431 JackAddAudioOutputPort,
1432 JackRemoveAudioOutputPort(usize),
1433 JackGetGraph,
1434 JackGraph(JackGraphInfo),
1435 JackConnect {
1436 source: String,
1437 destination: String,
1438 },
1439 JackDisconnect {
1440 source: String,
1441 destination: String,
1442 },
1443 OpenMidiInputDevice(String),
1444 OpenMidiOutputDevice(String),
1445 RequestSessionDiagnostics,
1446 RequestMidiLearnMappingsReport,
1447 ClearAllMidiLearnBindings,
1448 SessionDiagnosticsReport {
1449 track_count: usize,
1450 frozen_track_count: usize,
1451 audio_clip_count: usize,
1452 midi_clip_count: usize,
1453 #[cfg(unix)]
1454 lv2_instance_count: usize,
1455 vst3_instance_count: usize,
1456 clap_instance_count: usize,
1457 pending_requests: usize,
1458 workers_total: usize,
1459 workers_ready: usize,
1460 pending_hw_midi_events: usize,
1461 playing: bool,
1462 transport_running: bool,
1463 transport_sample: usize,
1464 tempo_bpm: f64,
1465 sample_rate_hz: usize,
1466 cycle_samples: usize,
1467 },
1468 MidiLearnMappingsReport {
1469 lines: Vec<String>,
1470 },
1471 HWInfo {
1472 channels: usize,
1473 rate: usize,
1474 input: bool,
1475 },
1476 MarkHistorySavePoint,
1477 HistoryState {
1478 dirty: bool,
1479 },
1480 Undo,
1481 Redo,
1482 Session(SessionAction),
1483 SessionRuntimeReport {
1484 track_name: String,
1485 scene_index: usize,
1486 state: SessionSlotState,
1487 play_position_samples: usize,
1488 elapsed_samples: usize,
1489 },
1490 Panic,
1491}
1492
1493#[derive(Clone, Debug)]
1494pub enum Message {
1495 Ready(usize),
1496 TracksFinished,
1497
1498 NodeJob(crate::executor::NodeJob),
1500 NodeDone {
1502 worker_id: usize,
1503 epoch: u64,
1504 node: u32,
1505 output_linear: Vec<f32>,
1506 parameter_updates: Vec<Action>,
1507 latency_changed: bool,
1508 },
1509 ProcessOfflineBounce(OfflineBounceWork),
1510 Channel(Sender<Self>),
1511
1512 Request(Action),
1513 OscRequest {
1514 action: Action,
1515 reply_to: SocketAddr,
1516 },
1517 Response(Result<Action, String>),
1518 HWMidiEvents(Vec<HwMidiEvent>),
1519 HWMidiOutEvents(Vec<HwMidiEvent>),
1520 ClearHWMidiOutEvents,
1521 StartAudioPreview {
1522 samples: Arc<Vec<f32>>,
1523 channels: usize,
1524 start_sample: usize,
1525 },
1526 StopAudioPreview,
1527 HWSetPlaying(bool),
1528 HWSetOutputGainBalance {
1529 gain: f32,
1530 balance: f32,
1531 },
1532 HWOpenMidiInputDevice(String),
1533 HWOpenMidiOutputDevice(String),
1534 HWCloseMidiDevices,
1535 HWFinished,
1536 OfflineBounceFinished {
1537 result: Result<Action, String>,
1538 },
1539}
1540
1541#[cfg(test)]
1542mod tests {
1543 use super::{AudioClipData, MidiClipData, PitchCorrectionPointData};
1544 use serde_json::json;
1545
1546 #[test]
1547 fn audio_clip_data_serde_round_trips_nested_groups() {
1548 let clip = AudioClipData {
1549 id: String::new(),
1550 name: "group.wav".to_string(),
1551 start: 12,
1552 length: 96,
1553 offset: 3,
1554 input_channel: 1,
1555 muted: true,
1556 peaks_file: Some("peaks/group.json".to_string()),
1557 fade_enabled: false,
1558 fade_in_samples: 10,
1559 fade_out_samples: 20,
1560 preview_name: Some("preview.wav".to_string()),
1561 source_name: Some("source.wav".to_string()),
1562 source_offset: Some(4),
1563 source_length: Some(88),
1564 pitch_correction_points: vec![PitchCorrectionPointData {
1565 start_sample: 7,
1566 length_samples: 11,
1567 detected_midi_pitch: 60.1,
1568 target_midi_pitch: 61.2,
1569 clarity: 0.8,
1570 }],
1571 pitch_correction_frame_likeness: Some(0.5),
1572 pitch_correction_inertia_ms: Some(123),
1573 pitch_correction_formant_compensation: Some(false),
1574 plugin_graph_json: Some(json!({"plugins":[],"connections":[{"kind":"Audio"}]})),
1575 grouped_clips: vec![AudioClipData {
1576 name: "child.wav".to_string(),
1577 start: 0,
1578 length: 48,
1579 ..AudioClipData::default()
1580 }],
1581 };
1582
1583 let value = serde_json::to_value(&clip).expect("serialize");
1584 let restored: AudioClipData = serde_json::from_value(value).expect("deserialize");
1585
1586 assert_eq!(restored.name, clip.name);
1587 assert_eq!(restored.preview_name, clip.preview_name);
1588 assert_eq!(restored.source_name, clip.source_name);
1589 assert_eq!(restored.plugin_graph_json, clip.plugin_graph_json);
1590 assert_eq!(restored.grouped_clips.len(), 1);
1591 assert_eq!(restored.grouped_clips[0].name, "child.wav");
1592 assert_eq!(restored.pitch_correction_points[0].target_midi_pitch, 61.2);
1593 }
1594
1595 #[test]
1596 fn midi_clip_data_serde_round_trips_nested_groups() {
1597 let clip = MidiClipData {
1598 id: String::new(),
1599 name: "group.mid".to_string(),
1600 start: 5,
1601 length: 64,
1602 offset: 2,
1603 input_channel: 3,
1604 muted: true,
1605 grouped_clips: vec![MidiClipData {
1606 name: "child.mid".to_string(),
1607 start: 0,
1608 length: 32,
1609 ..MidiClipData::default()
1610 }],
1611 };
1612
1613 let value = serde_json::to_value(&clip).expect("serialize");
1614 let restored: MidiClipData = serde_json::from_value(value).expect("deserialize");
1615
1616 assert_eq!(restored.name, clip.name);
1617 assert_eq!(restored.grouped_clips.len(), 1);
1618 assert_eq!(restored.grouped_clips[0].name, "child.mid");
1619 }
1620
1621 #[test]
1622 fn pitch_correction_point_data_serde_round_trips() {
1623 let point = PitchCorrectionPointData {
1624 start_sample: 10,
1625 length_samples: 20,
1626 detected_midi_pitch: 57.5,
1627 target_midi_pitch: 58.0,
1628 clarity: 0.9,
1629 };
1630
1631 let value = serde_json::to_value(&point).expect("serialize");
1632 let restored: PitchCorrectionPointData =
1633 serde_json::from_value(value).expect("deserialize");
1634
1635 assert_eq!(restored.start_sample, 10);
1636 assert_eq!(restored.length_samples, 20);
1637 assert_eq!(restored.detected_midi_pitch, 57.5);
1638 assert_eq!(restored.target_midi_pitch, 58.0);
1639 assert_eq!(restored.clarity, 0.9);
1640 }
1641
1642 #[test]
1643 fn audio_clip_data_deserializes_with_omitted_optional_fields() {
1644 let restored: AudioClipData = serde_json::from_value(json!({
1645 "name": "clip.wav",
1646 "start": 1,
1647 "length": 2,
1648 "offset": 3,
1649 "input_channel": 0,
1650 "muted": false,
1651 "fade_enabled": true,
1652 "fade_in_samples": 240,
1653 "fade_out_samples": 240,
1654 "pitch_correction_points": [],
1655 "grouped_clips": []
1656 }))
1657 .expect("deserialize");
1658
1659 assert_eq!(restored.name, "clip.wav");
1660 assert!(restored.peaks_file.is_none());
1661 assert!(restored.preview_name.is_none());
1662 assert!(restored.source_name.is_none());
1663 assert!(restored.source_offset.is_none());
1664 assert!(restored.source_length.is_none());
1665 assert!(restored.pitch_correction_points.is_empty());
1666 assert!(restored.plugin_graph_json.is_none());
1667 }
1668}