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