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