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