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