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 QueueScene {
484 scene_index: usize,
485 launch_quantization: LaunchQuantization,
486 },
487 StopAllClips,
488}
489
490#[derive(Clone, Debug)]
491pub enum Action {
492 Quit,
493 Log {
494 source: String,
495 message: String,
496 },
497 Play,
498 Pause,
499 Stop,
500 SessionPlay,
501 TransportPosition(usize),
502 TransportPositionAt {
503 sample: usize,
504 after_frames: usize,
505 },
506 JumpToEnd,
507 SetLoopEnabled(bool),
508 SetLoopRange(Option<(usize, usize)>),
509 SetPunchEnabled(bool),
510 SetPunchRange(Option<(usize, usize)>),
511 SetMetronomeEnabled(bool),
512 SetTempo(f64),
513 SetTimeSignature {
514 numerator: u16,
515 denominator: u16,
516 },
517 SetTempoMap {
518 tempo_points: Vec<TempoPoint>,
519 time_signature_points: Vec<TimeSignaturePoint>,
520 },
521 SetOscEnabled(bool),
522 SetClipPlaybackEnabled(bool),
523 SetSessionClipPlaybackEnabled(bool),
524 SetRecordEnabled(bool),
525 SetModulators(Vec<Modulator>),
526 SetTrackAutomationLanes {
527 track_name: String,
528 lanes: serde_json::Value,
529 mode: TrackAutomationMode,
530 },
531 TrackAutomationToggleLane {
532 track_name: String,
533 target: OfflineAutomationTarget,
534 },
535 TrackAutomationInsertPoint {
536 track_name: String,
537 target: OfflineAutomationTarget,
538 sample: usize,
539 value: f32,
540 },
541 TrackAutomationDeletePoint {
542 track_name: String,
543 target: OfflineAutomationTarget,
544 sample: usize,
545 },
546 TrackAutomationSetMode {
547 track_name: String,
548 mode: TrackAutomationMode,
549 },
550 SetSessionPath(String),
551 BeginHistoryGroup,
552 EndHistoryGroup,
553 ApplyGroupedActions(Vec<Action>),
554 ClearHistory,
555 BeginSessionRestore,
556 EndSessionRestore,
557 AddTrack {
558 name: String,
559 audio_ins: usize,
560 midi_ins: usize,
561 audio_outs: usize,
562 midi_outs: usize,
563 folder: bool,
564 },
565 TrackAddAudioInput(String),
566 TrackAddAudioOutput(String),
567 TrackRemoveAudioInput(String),
568 TrackRemoveAudioOutput(String),
569 AddClip {
570 clip_id: String,
571 name: String,
572 track_name: String,
573 start: usize,
574 length: usize,
575 offset: usize,
576 input_channel: usize,
577 muted: bool,
578 peaks_file: Option<String>,
579 kind: Kind,
580 fade_enabled: bool,
581 fade_in_samples: usize,
582 fade_out_samples: usize,
583 source_name: Option<String>,
584 source_offset: Option<usize>,
585 source_length: Option<usize>,
586 preview_name: Option<String>,
587 pitch_correction_points: Vec<PitchCorrectionPointData>,
588 pitch_correction_frame_likeness: Option<f32>,
589 pitch_correction_inertia_ms: Option<u16>,
590 pitch_correction_formant_compensation: Option<bool>,
591 plugin_graph_json: Option<serde_json::Value>,
592 },
593 AddGroupedClip {
594 track_name: String,
595 kind: Kind,
596 audio_clip: Option<AudioClipData>,
597 midi_clip: Option<MidiClipData>,
598 },
599 RemoveClip {
600 track_name: String,
601 kind: Kind,
602 clip_indices: Vec<usize>,
603 },
604 MoveClipToUnused {
605 track_name: String,
606 kind: Kind,
607 clip_indices: Vec<usize>,
608 },
609 DeleteUnusedClips {
610 clip_ids: Vec<String>,
611 },
612 SetUnusedClips {
613 audio: Vec<AudioClipData>,
614 midi: Vec<MidiClipData>,
615 },
616 SetClipFade {
617 track_name: String,
618 clip_index: usize,
619 kind: Kind,
620 fade_enabled: bool,
621 fade_in_samples: usize,
622 fade_out_samples: usize,
623 },
624 SetClipBounds {
625 track_name: String,
626 clip_index: usize,
627 kind: Kind,
628 start: usize,
629 length: usize,
630 offset: usize,
631 },
632 SyncClipBounds {
633 track_name: String,
634 clip_index: usize,
635 kind: Kind,
636 start: usize,
637 length: usize,
638 offset: usize,
639 },
640 SetClipMuted {
641 track_name: String,
642 clip_index: usize,
643 kind: Kind,
644 muted: bool,
645 },
646 SetClipPluginGraphJson {
647 track_name: String,
648 clip_index: usize,
649 plugin_graph_json: Option<serde_json::Value>,
650 },
651 SetClipPitchCorrection {
652 track_name: String,
653 clip_index: usize,
654 preview_name: Option<String>,
655 source_name: Option<String>,
656 source_offset: Option<usize>,
657 source_length: Option<usize>,
658 pitch_correction_points: Vec<PitchCorrectionPointData>,
659 pitch_correction_frame_likeness: Option<f32>,
660 pitch_correction_inertia_ms: Option<u16>,
661 pitch_correction_formant_compensation: Option<bool>,
662 },
663 RenameClip {
664 track_name: String,
665 kind: Kind,
666 clip_index: usize,
667 new_name: String,
668 },
669 SetClipIdentity {
670 track_name: String,
671 kind: Kind,
672 clip_index: usize,
673 new_id: String,
674 new_name: String,
675 },
676 SetClipSourceName {
677 track_name: String,
678 kind: Kind,
679 clip_index: usize,
680 name: String,
681 },
682 RenameTrack {
683 old_name: String,
684 new_name: String,
685 },
686 RemoveTrack(String),
687 TrackLevel(String, f32),
688 TrackBalance(String, f32),
689 TrackAutomationLevel(String, f32),
690 TrackAutomationBalance(String, f32),
691 TrackMidiCc {
692 track_name: String,
693 channel: u8,
694 cc: u8,
695 value: u8,
696 },
697 TrackMeters {
698 track_name: String,
699 output_db: Vec<f32>,
700 },
701 RequestMeterSnapshot,
702 MeterSnapshot {
703 hw_out_db: Arc<Vec<f32>>,
704 track_meters: Arc<Vec<(String, Vec<f32>)>>,
705 },
706 RequestTrackList,
707 TrackList(Vec<String>),
708 RequestTransportState,
709 TransportState {
710 sample: usize,
711 tempo_bpm: f64,
712 playing: bool,
713 paused: bool,
714 tsig_num: u16,
715 tsig_denom: u16,
716 },
717 TrackToggleArm(String),
718 TrackToggleMute(String),
719 TrackTogglePhase(String),
720 TrackToggleSolo(String),
721 TrackToggleMaster(String),
722 TrackToggleInputMonitor {
723 track_name: String,
724 lane: usize,
725 },
726 TrackToggleDiskMonitor {
727 track_name: String,
728 lane: usize,
729 },
730 TrackToggleMidiInputMonitor {
731 track_name: String,
732 lane: usize,
733 },
734 TrackToggleMidiDiskMonitor {
735 track_name: String,
736 lane: usize,
737 },
738 TrackSetColor {
739 track_name: String,
740 color: Option<TrackColor>,
741 },
742 TrackArmMidiLearn {
743 track_name: String,
744 target: TrackMidiLearnTarget,
745 },
746 GlobalArmMidiLearn {
747 target: GlobalMidiLearnTarget,
748 },
749 SessionArmMidiLearn {
750 target: SessionMidiLearnTarget,
751 },
752 TrackSetMidiLearnBinding {
753 track_name: String,
754 target: TrackMidiLearnTarget,
755 binding: Option<MidiLearnBinding>,
756 },
757 SetGlobalMidiLearnBinding {
758 target: GlobalMidiLearnTarget,
759 binding: Option<MidiLearnBinding>,
760 },
761 SetSessionMidiLearnBinding {
762 target: SessionMidiLearnTarget,
763 binding: Option<MidiLearnBinding>,
764 },
765 SessionMidiLearnTriggered {
766 target: SessionMidiLearnTarget,
767 },
768 TrackSetFolder {
769 track_name: String,
770 is_folder: bool,
771 },
772 TrackSetParent {
773 track_name: String,
774 parent_name: Option<String>,
775 },
776 TrackToggleFolder {
777 track_name: String,
778 },
779 TrackSetMidiLaneChannel {
780 track_name: String,
781 lane: usize,
782 channel: Option<u8>,
783 },
784 TrackSetFrozen {
785 track_name: String,
786 frozen: bool,
787 },
788 TrackSetSessionSlot {
789 track_name: String,
790 scene_index: usize,
791 clip_id: Option<String>,
792 },
793 TrackSetSessionSlotPlayEnabled {
794 track_name: String,
795 scene_index: usize,
796 enabled: bool,
797 },
798 TrackSetSessionSlotStopEnabled {
799 track_name: String,
800 scene_index: usize,
801 enabled: bool,
802 },
803 TrackOfflineBounce {
804 track_name: String,
805 output_path: String,
806 start_sample: usize,
807 length_samples: usize,
808 automation_lanes: Vec<OfflineAutomationLane>,
809 apply_fader: bool,
810 },
811 TrackOfflineBounceCancel {
812 track_name: String,
813 },
814 TrackOfflineBounceCancelAll,
815 TrackOfflineBounceCanceled {
816 track_name: String,
817 },
818 TrackOfflineBounceProgress {
819 track_name: String,
820 progress: f32,
821 operation: Option<String>,
822 },
823 PianoKey {
824 track_name: String,
825 note: u8,
826 velocity: u8,
827 on: bool,
828 },
829 ModifyMidiNotes {
830 track_name: String,
831 clip_index: usize,
832 note_indices: Vec<usize>,
833 new_notes: Vec<MidiNoteData>,
834 old_notes: Vec<MidiNoteData>,
835 },
836 ModifyMidiControllers {
837 track_name: String,
838 clip_index: usize,
839 controller_indices: Vec<usize>,
840 new_controllers: Vec<MidiControllerData>,
841 old_controllers: Vec<MidiControllerData>,
842 },
843 DeleteMidiControllers {
844 track_name: String,
845 clip_index: usize,
846 controller_indices: Vec<usize>,
847 deleted_controllers: Vec<(usize, MidiControllerData)>,
848 },
849 InsertMidiControllers {
850 track_name: String,
851 clip_index: usize,
852 controllers: Vec<(usize, MidiControllerData)>,
853 },
854 DeleteMidiNotes {
855 track_name: String,
856 clip_index: usize,
857 note_indices: Vec<usize>,
858 deleted_notes: Vec<(usize, MidiNoteData)>,
859 },
860 InsertMidiNotes {
861 track_name: String,
862 clip_index: usize,
863 notes: Vec<(usize, MidiNoteData)>,
864 },
865 SetStepRecording(bool),
866 StepRecordMidiNote {
867 device: String,
868 channel: u8,
869 pitch: u8,
870 velocity: u8,
871 },
872 SetMidiSysExEvents {
873 track_name: String,
874 clip_index: usize,
875 new_sysex_events: Vec<MidiRawEventData>,
876 old_sysex_events: Vec<MidiRawEventData>,
877 },
878 TrackClearDefaultPassthrough {
879 track_name: String,
880 },
881 TrackClearPlugins {
882 track_name: String,
883 },
884 #[cfg(all(unix, not(target_os = "macos")))]
885 TrackSetLv2PluginState {
886 track_name: String,
887 instance_id: usize,
888 state: Vec<u8>,
889 },
890 #[cfg(all(unix, not(target_os = "macos")))]
891 ClipSetLv2PluginState {
892 track_name: String,
893 clip_idx: usize,
894 instance_id: usize,
895 state: Vec<u8>,
896 },
897 #[cfg(all(unix, not(target_os = "macos")))]
898 TrackLv2SnapshotState {
899 track_name: String,
900 instance_id: usize,
901 },
902 #[cfg(all(unix, not(target_os = "macos")))]
903 ClipLv2SnapshotState {
904 track_name: String,
905 clip_idx: usize,
906 instance_id: usize,
907 },
908 #[cfg(all(unix, not(target_os = "macos")))]
909 TrackLv2StateSnapshot {
910 track_name: String,
911 instance_id: usize,
912 state: Vec<u8>,
913 },
914 #[cfg(all(unix, not(target_os = "macos")))]
915 TrackGetLv2PluginControls {
916 track_name: String,
917 instance_id: usize,
918 },
919 #[cfg(all(unix, not(target_os = "macos")))]
920 ClipGetLv2PluginControls {
921 track_name: String,
922 clip_idx: usize,
923 instance_id: usize,
924 },
925 #[cfg(all(unix, not(target_os = "macos")))]
926 TrackLv2PluginControls {
927 track_name: String,
928 instance_id: usize,
929 controls: Vec<Lv2ControlPortInfo>,
930 instance_access_handle: Option<usize>,
931 },
932 #[cfg(all(unix, not(target_os = "macos")))]
933 ClipLv2PluginControls {
934 track_name: String,
935 clip_idx: usize,
936 instance_id: usize,
937 controls: Vec<Lv2ControlPortInfo>,
938 instance_access_handle: Option<usize>,
939 },
940 #[cfg(all(unix, not(target_os = "macos")))]
941 TrackGetLv2Midnam {
942 track_name: String,
943 },
944 #[cfg(all(unix, not(target_os = "macos")))]
945 TrackLv2Midnam {
946 track_name: String,
947 note_names: std::collections::HashMap<u8, String>,
948 },
949 TrackGetClapNoteNames {
950 track_name: String,
951 },
952 TrackClapNoteNames {
953 track_name: String,
954 note_names: std::collections::HashMap<u8, String>,
955 },
956 #[cfg(all(unix, not(target_os = "macos")))]
957 TrackSetLv2ControlValue {
958 track_name: String,
959 instance_id: usize,
960 index: u32,
961 value: f32,
962 },
963 #[cfg(all(unix, not(target_os = "macos")))]
964 ClipSetLv2ControlValue {
965 track_name: String,
966 clip_idx: usize,
967 instance_id: usize,
968 index: u32,
969 value: f32,
970 },
971 #[cfg(all(unix, not(target_os = "macos")))]
972 ClipLv2StateSnapshot {
973 track_name: String,
974 clip_idx: usize,
975 instance_id: usize,
976 state: Vec<u8>,
977 },
978 TrackGetPluginGraph {
979 track_name: String,
980 include_state: bool,
981 },
982 TrackPluginGraph {
983 track_name: String,
984 plugins: Vec<PluginGraphPlugin>,
985 connections: Vec<PluginGraphConnection>,
986 connectable_connections: Vec<ConnectableConnection>,
987 },
988 TrackConnectPluginAudio {
989 track_name: String,
990 from_node: PluginGraphNode,
991 from_port: usize,
992 to_node: PluginGraphNode,
993 to_port: usize,
994 },
995 TrackConnectPluginMidi {
996 track_name: String,
997 from_node: PluginGraphNode,
998 from_port: usize,
999 to_node: PluginGraphNode,
1000 to_port: usize,
1001 },
1002 TrackDisconnectPluginAudio {
1003 track_name: String,
1004 from_node: PluginGraphNode,
1005 from_port: usize,
1006 to_node: PluginGraphNode,
1007 to_port: usize,
1008 },
1009 TrackDisconnectPluginMidi {
1010 track_name: String,
1011 from_node: PluginGraphNode,
1012 from_port: usize,
1013 to_node: PluginGraphNode,
1014 to_port: usize,
1015 },
1016 TrackConnectAudio {
1017 track_name: String,
1018 from: ConnectableRef,
1019 from_port: usize,
1020 to: ConnectableRef,
1021 to_port: usize,
1022 },
1023 TrackDisconnectAudio {
1024 track_name: String,
1025 from: ConnectableRef,
1026 from_port: usize,
1027 to: ConnectableRef,
1028 to_port: usize,
1029 },
1030 TrackConnectMidi {
1031 track_name: String,
1032 from: ConnectableRef,
1033 from_port: usize,
1034 to: ConnectableRef,
1035 to_port: usize,
1036 },
1037 TrackDisconnectMidi {
1038 track_name: String,
1039 from: ConnectableRef,
1040 from_port: usize,
1041 to: ConnectableRef,
1042 to_port: usize,
1043 },
1044 #[cfg(all(unix, not(target_os = "macos")))]
1045 ListLv2Plugins,
1046 #[cfg(all(unix, not(target_os = "macos")))]
1047 Lv2Plugins(Vec<Lv2PluginInfo>),
1048 #[cfg(all(unix, not(target_os = "macos")))]
1049 Lv2PluginsUnavailable {
1050 error: String,
1051 },
1052 ListVst3Plugins,
1053 Vst3Plugins(Vec<Vst3PluginInfo>),
1054 Vst3PluginsUnavailable {
1055 error: String,
1056 },
1057 ListClapPlugins,
1058 ListClapPluginsWithCapabilities,
1059 ClapPlugins(Vec<ClapPluginInfo>),
1060 ClapPluginsUnavailable {
1061 error: String,
1062 },
1063 TrackSetClapParameter {
1064 track_name: String,
1065 instance_id: usize,
1066 param_id: u32,
1067 value: f64,
1068 },
1069 ClipSetClapParameter {
1070 track_name: String,
1071 clip_idx: usize,
1072 instance_id: usize,
1073 param_id: u32,
1074 value: f64,
1075 },
1076 TrackSetClapParameterAt {
1077 track_name: String,
1078 instance_id: usize,
1079 param_id: u32,
1080 value: f64,
1081 frame: u32,
1082 },
1083 TrackBeginClapParameterEdit {
1084 track_name: String,
1085 instance_id: usize,
1086 param_id: u32,
1087 frame: u32,
1088 },
1089 TrackEndClapParameterEdit {
1090 track_name: String,
1091 instance_id: usize,
1092 param_id: u32,
1093 frame: u32,
1094 },
1095 TrackGetClapParameters {
1096 track_name: String,
1097 instance_id: usize,
1098 },
1099 TrackClapParameters {
1100 track_name: String,
1101 instance_id: usize,
1102 parameters: Vec<ClapParameterInfo>,
1103 },
1104 TrackClapSnapshotState {
1105 track_name: String,
1106 instance_id: usize,
1107 },
1108 ClipClapSnapshotState {
1109 track_name: String,
1110 clip_idx: usize,
1111 instance_id: usize,
1112 },
1113 TrackClapStateSnapshot {
1114 track_name: String,
1115 instance_id: usize,
1116 plugin_id: String,
1117 state: crate::clap::ClapPluginState,
1118 },
1119 ClipClapStateSnapshot {
1120 track_name: String,
1121 clip_idx: usize,
1122 instance_id: usize,
1123 plugin_id: String,
1124 state: crate::clap::ClapPluginState,
1125 },
1126 TrackClapStateDirty {
1127 track_name: String,
1128 instance_id: usize,
1129 },
1130 ClipClapStateDirty {
1131 track_name: String,
1132 clip_idx: usize,
1133 instance_id: usize,
1134 },
1135 TrackClapRestoreState {
1136 track_name: String,
1137 instance_id: usize,
1138 state: crate::clap::ClapPluginState,
1139 },
1140 ClipClapRestoreState {
1141 track_name: String,
1142 clip_idx: usize,
1143 instance_id: usize,
1144 state: crate::clap::ClapPluginState,
1145 },
1146 TrackSnapshotAllClapStates {
1147 track_name: String,
1148 },
1149 TrackSnapshotAllClapStatesDone {
1150 track_name: String,
1151 },
1152 TrackLoadClapPlugin {
1153 track_name: String,
1154 plugin_id: String,
1155 instance_id: Option<usize>,
1156 },
1157 TrackUnloadClapPlugin {
1158 track_name: String,
1159 plugin_id: String,
1160 },
1161 TrackUnloadClapPluginInstance {
1162 track_name: String,
1163 instance_id: usize,
1164 },
1165 TrackShowClapGui {
1166 track_name: String,
1167 instance_id: usize,
1168 },
1169 TrackLoadVst3Plugin {
1170 track_name: String,
1171 plugin_id: String,
1172 instance_id: Option<usize>,
1173 },
1174 TrackUnloadVst3Plugin {
1175 track_name: String,
1176 plugin_id: String,
1177 },
1178 TrackUnloadVst3PluginInstance {
1179 track_name: String,
1180 instance_id: usize,
1181 },
1182 TrackShowVst3Gui {
1183 track_name: String,
1184 instance_id: usize,
1185 },
1186 #[cfg(all(unix, not(target_os = "macos")))]
1187 TrackLoadLv2Plugin {
1188 track_name: String,
1189 plugin_uri: String,
1190 instance_id: Option<usize>,
1191 },
1192 #[cfg(all(unix, not(target_os = "macos")))]
1193 TrackUnloadLv2Plugin {
1194 track_name: String,
1195 plugin_uri: String,
1196 },
1197 #[cfg(all(unix, not(target_os = "macos")))]
1198 TrackUnloadLv2PluginInstance {
1199 track_name: String,
1200 instance_id: usize,
1201 },
1202 TrackShowLv2Gui {
1203 track_name: String,
1204 instance_id: usize,
1205 },
1206 TrackSetPluginResourceDir {
1207 track_name: String,
1208 instance_id: usize,
1209 format: String,
1210 directory: String,
1211 },
1212 TrackClapFileReferences {
1213 track_name: String,
1214 instance_id: usize,
1215 refs: Vec<(u32, String)>,
1216 },
1217 TrackUpdateClapFileReference {
1218 track_name: String,
1219 instance_id: usize,
1220 index: u32,
1221 path: String,
1222 },
1223 ClipSetPluginResourceDir {
1224 track_name: String,
1225 clip_idx: usize,
1226 instance_id: usize,
1227 format: String,
1228 directory: String,
1229 },
1230 ClipClapFileReferences {
1231 track_name: String,
1232 clip_idx: usize,
1233 instance_id: usize,
1234 refs: Vec<(u32, String)>,
1235 },
1236 ClipUpdateClapFileReference {
1237 track_name: String,
1238 clip_idx: usize,
1239 instance_id: usize,
1240 index: u32,
1241 path: String,
1242 },
1243 TrackGetVst3Graph {
1244 track_name: String,
1245 },
1246 TrackVst3Graph {
1247 track_name: String,
1248 plugins: Vec<Vst3GraphPlugin>,
1249 connections: Vec<Vst3GraphConnection>,
1250 },
1251 TrackSetVst3Parameter {
1252 track_name: String,
1253 instance_id: usize,
1254 param_id: u32,
1255 value: f32,
1256 },
1257 TrackSetPluginBypassed {
1258 track_name: String,
1259 instance_id: usize,
1260 format: String,
1261 bypassed: bool,
1262 },
1263 TrackGetVst3Parameters {
1264 track_name: String,
1265 instance_id: usize,
1266 },
1267 TrackVst3Parameters {
1268 track_name: String,
1269 instance_id: usize,
1270 parameters: Vec<crate::vst3::port::ParameterInfo>,
1271 },
1272 TrackVst3SnapshotState {
1273 track_name: String,
1274 instance_id: usize,
1275 },
1276 ClipVst3SnapshotState {
1277 track_name: String,
1278 clip_idx: usize,
1279 instance_id: usize,
1280 },
1281 TrackVst3StateSnapshot {
1282 track_name: String,
1283 instance_id: usize,
1284 state: crate::vst3::state::Vst3PluginState,
1285 },
1286 ClipVst3StateSnapshot {
1287 track_name: String,
1288 clip_idx: usize,
1289 instance_id: usize,
1290 state: crate::vst3::state::Vst3PluginState,
1291 },
1292 TrackVst3RestoreState {
1293 track_name: String,
1294 instance_id: usize,
1295 state: crate::vst3::state::Vst3PluginState,
1296 },
1297 ClipVst3RestoreState {
1298 track_name: String,
1299 clip_idx: usize,
1300 instance_id: usize,
1301 state: crate::vst3::state::Vst3PluginState,
1302 },
1303 TrackConnectVst3Audio {
1304 track_name: String,
1305 from_node: Vst3GraphNode,
1306 from_port: usize,
1307 to_node: Vst3GraphNode,
1308 to_port: usize,
1309 },
1310 TrackDisconnectVst3Audio {
1311 track_name: String,
1312 from_node: Vst3GraphNode,
1313 from_port: usize,
1314 to_node: Vst3GraphNode,
1315 to_port: usize,
1316 },
1317 ClipMove {
1318 kind: Kind,
1319 from: ClipMoveFrom,
1320 to: ClipMoveTo,
1321 copy: bool,
1322 },
1323 Connect {
1324 from_track: String,
1325 from_port: usize,
1326 to_track: String,
1327 to_port: usize,
1328 kind: Kind,
1329 },
1330 Disconnect {
1331 from_track: String,
1332 from_port: usize,
1333 to_track: String,
1334 to_port: usize,
1335 kind: Kind,
1336 },
1337 OpenAudioDevice {
1338 device: String,
1339 input_device: Option<String>,
1340 sample_rate_hz: i32,
1341 bits: i32,
1342 exclusive: bool,
1343 period_frames: usize,
1344 nperiods: usize,
1345 sync_mode: bool,
1346 actual_period_frames: usize,
1347 input_channels: usize,
1348 output_channels: usize,
1349 bytes_per_frame: usize,
1350 },
1351 JackAddAudioInputPort,
1352 JackRemoveAudioInputPort(usize),
1353 JackAddAudioOutputPort,
1354 JackRemoveAudioOutputPort(usize),
1355 JackGetGraph,
1356 JackGraph(JackGraphInfo),
1357 JackConnect {
1358 source: String,
1359 destination: String,
1360 },
1361 JackDisconnect {
1362 source: String,
1363 destination: String,
1364 },
1365 OpenMidiInputDevice(String),
1366 OpenMidiOutputDevice(String),
1367 RequestSessionDiagnostics,
1368 RequestMidiLearnMappingsReport,
1369 ClearAllMidiLearnBindings,
1370 SessionDiagnosticsReport {
1371 track_count: usize,
1372 frozen_track_count: usize,
1373 audio_clip_count: usize,
1374 midi_clip_count: usize,
1375 #[cfg(all(unix, not(target_os = "macos")))]
1376 lv2_instance_count: usize,
1377 vst3_instance_count: usize,
1378 clap_instance_count: usize,
1379 pending_requests: usize,
1380 workers_total: usize,
1381 workers_ready: usize,
1382 pending_hw_midi_events: usize,
1383 playing: bool,
1384 transport_running: bool,
1385 transport_sample: usize,
1386 tempo_bpm: f64,
1387 sample_rate_hz: usize,
1388 cycle_samples: usize,
1389 },
1390 MidiLearnMappingsReport {
1391 lines: Vec<String>,
1392 },
1393 HWInfo {
1394 channels: usize,
1395 rate: usize,
1396 input: bool,
1397 },
1398 MarkHistorySavePoint,
1399 HistoryState {
1400 dirty: bool,
1401 },
1402 Undo,
1403 Redo,
1404 Session(SessionAction),
1405 SessionRuntimeReport {
1406 track_name: String,
1407 scene_index: usize,
1408 state: SessionSlotState,
1409 play_position_samples: usize,
1410 elapsed_samples: usize,
1411 },
1412 Panic,
1413}
1414
1415#[derive(Clone, Debug)]
1416pub enum Message {
1417 Ready(usize),
1418 TracksFinished,
1419
1420 NodeJob(crate::executor::NodeJob),
1422 NodeDone {
1424 worker_id: usize,
1425 epoch: u64,
1426 node: u32,
1427 output_linear: Vec<f32>,
1428 parameter_updates: Vec<Action>,
1429 },
1430 ProcessOfflineBounce(OfflineBounceWork),
1431 Channel(Sender<Self>),
1432
1433 Request(Action),
1434 OscRequest {
1435 action: Action,
1436 reply_to: SocketAddr,
1437 },
1438 Response(Result<Action, String>),
1439 HWMidiEvents(Vec<HwMidiEvent>),
1440 HWMidiOutEvents(Vec<HwMidiEvent>),
1441 ClearHWMidiOutEvents,
1442 HWSetPlaying(bool),
1443 HWSetOutputGainBalance {
1444 gain: f32,
1445 balance: f32,
1446 },
1447 HWOpenMidiInputDevice(String),
1448 HWOpenMidiOutputDevice(String),
1449 HWCloseMidiDevices,
1450 HWFinished,
1451 OfflineBounceFinished {
1452 result: Result<Action, String>,
1453 },
1454}
1455
1456#[cfg(test)]
1457mod tests {
1458 use super::{AudioClipData, MidiClipData, PitchCorrectionPointData};
1459 use serde_json::json;
1460
1461 #[test]
1462 fn audio_clip_data_serde_round_trips_nested_groups() {
1463 let clip = AudioClipData {
1464 id: String::new(),
1465 name: "group.wav".to_string(),
1466 start: 12,
1467 length: 96,
1468 offset: 3,
1469 input_channel: 1,
1470 muted: true,
1471 peaks_file: Some("peaks/group.json".to_string()),
1472 fade_enabled: false,
1473 fade_in_samples: 10,
1474 fade_out_samples: 20,
1475 preview_name: Some("preview.wav".to_string()),
1476 source_name: Some("source.wav".to_string()),
1477 source_offset: Some(4),
1478 source_length: Some(88),
1479 pitch_correction_points: vec![PitchCorrectionPointData {
1480 start_sample: 7,
1481 length_samples: 11,
1482 detected_midi_pitch: 60.1,
1483 target_midi_pitch: 61.2,
1484 clarity: 0.8,
1485 }],
1486 pitch_correction_frame_likeness: Some(0.5),
1487 pitch_correction_inertia_ms: Some(123),
1488 pitch_correction_formant_compensation: Some(false),
1489 plugin_graph_json: Some(json!({"plugins":[],"connections":[{"kind":"Audio"}]})),
1490 grouped_clips: vec![AudioClipData {
1491 name: "child.wav".to_string(),
1492 start: 0,
1493 length: 48,
1494 ..AudioClipData::default()
1495 }],
1496 };
1497
1498 let value = serde_json::to_value(&clip).expect("serialize");
1499 let restored: AudioClipData = serde_json::from_value(value).expect("deserialize");
1500
1501 assert_eq!(restored.name, clip.name);
1502 assert_eq!(restored.preview_name, clip.preview_name);
1503 assert_eq!(restored.source_name, clip.source_name);
1504 assert_eq!(restored.plugin_graph_json, clip.plugin_graph_json);
1505 assert_eq!(restored.grouped_clips.len(), 1);
1506 assert_eq!(restored.grouped_clips[0].name, "child.wav");
1507 assert_eq!(restored.pitch_correction_points[0].target_midi_pitch, 61.2);
1508 }
1509
1510 #[test]
1511 fn midi_clip_data_serde_round_trips_nested_groups() {
1512 let clip = MidiClipData {
1513 id: String::new(),
1514 name: "group.mid".to_string(),
1515 start: 5,
1516 length: 64,
1517 offset: 2,
1518 input_channel: 3,
1519 muted: true,
1520 grouped_clips: vec![MidiClipData {
1521 name: "child.mid".to_string(),
1522 start: 0,
1523 length: 32,
1524 ..MidiClipData::default()
1525 }],
1526 };
1527
1528 let value = serde_json::to_value(&clip).expect("serialize");
1529 let restored: MidiClipData = serde_json::from_value(value).expect("deserialize");
1530
1531 assert_eq!(restored.name, clip.name);
1532 assert_eq!(restored.grouped_clips.len(), 1);
1533 assert_eq!(restored.grouped_clips[0].name, "child.mid");
1534 }
1535
1536 #[test]
1537 fn pitch_correction_point_data_serde_round_trips() {
1538 let point = PitchCorrectionPointData {
1539 start_sample: 10,
1540 length_samples: 20,
1541 detected_midi_pitch: 57.5,
1542 target_midi_pitch: 58.0,
1543 clarity: 0.9,
1544 };
1545
1546 let value = serde_json::to_value(&point).expect("serialize");
1547 let restored: PitchCorrectionPointData =
1548 serde_json::from_value(value).expect("deserialize");
1549
1550 assert_eq!(restored.start_sample, 10);
1551 assert_eq!(restored.length_samples, 20);
1552 assert_eq!(restored.detected_midi_pitch, 57.5);
1553 assert_eq!(restored.target_midi_pitch, 58.0);
1554 assert_eq!(restored.clarity, 0.9);
1555 }
1556
1557 #[test]
1558 fn audio_clip_data_deserializes_with_omitted_optional_fields() {
1559 let restored: AudioClipData = serde_json::from_value(json!({
1560 "name": "clip.wav",
1561 "start": 1,
1562 "length": 2,
1563 "offset": 3,
1564 "input_channel": 0,
1565 "muted": false,
1566 "fade_enabled": true,
1567 "fade_in_samples": 240,
1568 "fade_out_samples": 240,
1569 "pitch_correction_points": [],
1570 "grouped_clips": []
1571 }))
1572 .expect("deserialize");
1573
1574 assert_eq!(restored.name, "clip.wav");
1575 assert!(restored.peaks_file.is_none());
1576 assert!(restored.preview_name.is_none());
1577 assert!(restored.source_name.is_none());
1578 assert!(restored.source_offset.is_none());
1579 assert!(restored.source_length.is_none());
1580 assert!(restored.pitch_correction_points.is_empty());
1581 assert!(restored.plugin_graph_json.is_none());
1582 }
1583}