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