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