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 #[cfg(all(unix, not(target_os = "macos")))]
799 Lv2PluginsUnavailable {
800 error: String,
801 },
802 ListVst3Plugins,
803 Vst3Plugins(Vec<Vst3PluginInfo>),
804 Vst3PluginsUnavailable {
805 error: String,
806 },
807 ListClapPlugins,
808 ListClapPluginsWithCapabilities,
809 ClapPlugins(Vec<ClapPluginInfo>),
810 ClapPluginsUnavailable {
811 error: String,
812 },
813 TrackSetClapParameter {
814 track_name: String,
815 instance_id: usize,
816 param_id: u32,
817 value: f64,
818 },
819 ClipSetClapParameter {
820 track_name: String,
821 clip_idx: usize,
822 instance_id: usize,
823 param_id: u32,
824 value: f64,
825 },
826 TrackSetClapParameterAt {
827 track_name: String,
828 instance_id: usize,
829 param_id: u32,
830 value: f64,
831 frame: u32,
832 },
833 TrackBeginClapParameterEdit {
834 track_name: String,
835 instance_id: usize,
836 param_id: u32,
837 frame: u32,
838 },
839 TrackEndClapParameterEdit {
840 track_name: String,
841 instance_id: usize,
842 param_id: u32,
843 frame: u32,
844 },
845 TrackGetClapParameters {
846 track_name: String,
847 instance_id: usize,
848 },
849 TrackClapParameters {
850 track_name: String,
851 instance_id: usize,
852 parameters: Vec<ClapParameterInfo>,
853 },
854 TrackClapSnapshotState {
855 track_name: String,
856 instance_id: usize,
857 },
858 ClipClapSnapshotState {
859 track_name: String,
860 clip_idx: usize,
861 instance_id: usize,
862 },
863 TrackClapStateSnapshot {
864 track_name: String,
865 instance_id: usize,
866 plugin_path: String,
867 state: crate::clap::ClapPluginState,
868 },
869 ClipClapStateSnapshot {
870 track_name: String,
871 clip_idx: usize,
872 instance_id: usize,
873 plugin_path: String,
874 state: crate::clap::ClapPluginState,
875 },
876 TrackClapStateDirty {
877 track_name: String,
878 instance_id: usize,
879 },
880 ClipClapStateDirty {
881 track_name: String,
882 clip_idx: usize,
883 instance_id: usize,
884 },
885 TrackClapRestoreState {
886 track_name: String,
887 instance_id: usize,
888 state: crate::clap::ClapPluginState,
889 },
890 ClipClapRestoreState {
891 track_name: String,
892 clip_idx: usize,
893 instance_id: usize,
894 state: crate::clap::ClapPluginState,
895 },
896 TrackSnapshotAllClapStates {
897 track_name: String,
898 },
899 TrackSnapshotAllClapStatesDone {
900 track_name: String,
901 },
902 TrackLoadClapPlugin {
903 track_name: String,
904 plugin_path: String,
905 instance_id: Option<usize>,
906 },
907 TrackUnloadClapPlugin {
908 track_name: String,
909 plugin_path: String,
910 },
911 TrackUnloadClapPluginInstance {
912 track_name: String,
913 instance_id: usize,
914 },
915 TrackShowClapGui {
916 track_name: String,
917 instance_id: usize,
918 },
919 TrackLoadVst3Plugin {
920 track_name: String,
921 plugin_path: String,
922 instance_id: Option<usize>,
923 },
924 TrackUnloadVst3Plugin {
925 track_name: String,
926 plugin_path: String,
927 },
928 TrackUnloadVst3PluginInstance {
929 track_name: String,
930 instance_id: usize,
931 },
932 TrackShowVst3Gui {
933 track_name: String,
934 instance_id: usize,
935 },
936 #[cfg(all(unix, not(target_os = "macos")))]
937 TrackLoadLv2Plugin {
938 track_name: String,
939 plugin_uri: String,
940 instance_id: Option<usize>,
941 },
942 #[cfg(all(unix, not(target_os = "macos")))]
943 TrackUnloadLv2Plugin {
944 track_name: String,
945 plugin_uri: String,
946 },
947 #[cfg(all(unix, not(target_os = "macos")))]
948 TrackUnloadLv2PluginInstance {
949 track_name: String,
950 instance_id: usize,
951 },
952 TrackShowLv2Gui {
953 track_name: String,
954 instance_id: usize,
955 },
956 TrackSetPluginResourceDir {
957 track_name: String,
958 instance_id: usize,
959 format: String,
960 directory: String,
961 },
962 TrackClapFileReferences {
963 track_name: String,
964 instance_id: usize,
965 refs: Vec<(u32, String)>,
966 },
967 TrackUpdateClapFileReference {
968 track_name: String,
969 instance_id: usize,
970 index: u32,
971 path: String,
972 },
973 ClipSetPluginResourceDir {
974 track_name: String,
975 clip_idx: usize,
976 instance_id: usize,
977 format: String,
978 directory: String,
979 },
980 ClipClapFileReferences {
981 track_name: String,
982 clip_idx: usize,
983 instance_id: usize,
984 refs: Vec<(u32, String)>,
985 },
986 ClipUpdateClapFileReference {
987 track_name: String,
988 clip_idx: usize,
989 instance_id: usize,
990 index: u32,
991 path: String,
992 },
993 TrackGetVst3Graph {
994 track_name: String,
995 },
996 TrackVst3Graph {
997 track_name: String,
998 plugins: Vec<Vst3GraphPlugin>,
999 connections: Vec<Vst3GraphConnection>,
1000 },
1001 TrackSetVst3Parameter {
1002 track_name: String,
1003 instance_id: usize,
1004 param_id: u32,
1005 value: f32,
1006 },
1007 TrackSetPluginBypassed {
1008 track_name: String,
1009 instance_id: usize,
1010 format: String,
1011 bypassed: bool,
1012 },
1013 TrackGetVst3Parameters {
1014 track_name: String,
1015 instance_id: usize,
1016 },
1017 TrackVst3Parameters {
1018 track_name: String,
1019 instance_id: usize,
1020 parameters: Vec<crate::vst3::port::ParameterInfo>,
1021 },
1022 TrackVst3SnapshotState {
1023 track_name: String,
1024 instance_id: usize,
1025 },
1026 ClipVst3SnapshotState {
1027 track_name: String,
1028 clip_idx: usize,
1029 instance_id: usize,
1030 },
1031 TrackVst3StateSnapshot {
1032 track_name: String,
1033 instance_id: usize,
1034 state: crate::vst3::state::Vst3PluginState,
1035 },
1036 ClipVst3StateSnapshot {
1037 track_name: String,
1038 clip_idx: usize,
1039 instance_id: usize,
1040 state: crate::vst3::state::Vst3PluginState,
1041 },
1042 TrackVst3RestoreState {
1043 track_name: String,
1044 instance_id: usize,
1045 state: crate::vst3::state::Vst3PluginState,
1046 },
1047 TrackConnectVst3Audio {
1048 track_name: String,
1049 from_node: Vst3GraphNode,
1050 from_port: usize,
1051 to_node: Vst3GraphNode,
1052 to_port: usize,
1053 },
1054 TrackDisconnectVst3Audio {
1055 track_name: String,
1056 from_node: Vst3GraphNode,
1057 from_port: usize,
1058 to_node: Vst3GraphNode,
1059 to_port: usize,
1060 },
1061 ClipMove {
1062 kind: Kind,
1063 from: ClipMoveFrom,
1064 to: ClipMoveTo,
1065 copy: bool,
1066 },
1067 Connect {
1068 from_track: String,
1069 from_port: usize,
1070 to_track: String,
1071 to_port: usize,
1072 kind: Kind,
1073 },
1074 Disconnect {
1075 from_track: String,
1076 from_port: usize,
1077 to_track: String,
1078 to_port: usize,
1079 kind: Kind,
1080 },
1081 OpenAudioDevice {
1082 device: String,
1083 input_device: Option<String>,
1084 sample_rate_hz: i32,
1085 bits: i32,
1086 exclusive: bool,
1087 period_frames: usize,
1088 nperiods: usize,
1089 sync_mode: bool,
1090 actual_period_frames: usize,
1091 input_channels: usize,
1092 output_channels: usize,
1093 bytes_per_frame: usize,
1094 },
1095 JackAddAudioInputPort,
1096 JackRemoveAudioInputPort(usize),
1097 JackAddAudioOutputPort,
1098 JackRemoveAudioOutputPort(usize),
1099 OpenMidiInputDevice(String),
1100 OpenMidiOutputDevice(String),
1101 RequestSessionDiagnostics,
1102 RequestMidiLearnMappingsReport,
1103 ClearAllMidiLearnBindings,
1104 SessionDiagnosticsReport {
1105 track_count: usize,
1106 frozen_track_count: usize,
1107 audio_clip_count: usize,
1108 midi_clip_count: usize,
1109 #[cfg(all(unix, not(target_os = "macos")))]
1110 lv2_instance_count: usize,
1111 vst3_instance_count: usize,
1112 clap_instance_count: usize,
1113 pending_requests: usize,
1114 workers_total: usize,
1115 workers_ready: usize,
1116 pending_hw_midi_events: usize,
1117 playing: bool,
1118 transport_sample: usize,
1119 tempo_bpm: f64,
1120 sample_rate_hz: usize,
1121 cycle_samples: usize,
1122 },
1123 MidiLearnMappingsReport {
1124 lines: Vec<String>,
1125 },
1126 HWInfo {
1127 channels: usize,
1128 rate: usize,
1129 input: bool,
1130 },
1131 MarkHistorySavePoint,
1132 HistoryState {
1133 dirty: bool,
1134 },
1135 Undo,
1136 Redo,
1137 Panic,
1138}
1139
1140#[derive(Clone, Debug)]
1141pub enum Message {
1142 Ready(usize),
1143 Finished {
1144 worker_id: usize,
1145 task: ProcessTask,
1146 output_linear: Vec<f32>,
1147 process_epoch: usize,
1148 parameter_updates: Vec<Action>,
1149 },
1150 TracksFinished,
1151
1152 ProcessTask(ProcessTask),
1153 ProcessOfflineBounce(OfflineBounceWork),
1154 Channel(Sender<Self>),
1155
1156 Request(Action),
1157 Response(Result<Action, String>),
1158 HWMidiEvents(Vec<HwMidiEvent>),
1159 HWMidiOutEvents(Vec<HwMidiEvent>),
1160 ClearHWMidiOutEvents,
1161 HWFinished,
1162 OfflineBounceFinished {
1163 result: Result<Action, String>,
1164 },
1165}
1166
1167#[cfg(test)]
1168mod tests {
1169 use super::{AudioClipData, MidiClipData, PitchCorrectionPointData};
1170 use serde_json::json;
1171
1172 #[test]
1173 fn audio_clip_data_serde_round_trips_nested_groups() {
1174 let clip = AudioClipData {
1175 name: "group.wav".to_string(),
1176 start: 12,
1177 length: 96,
1178 offset: 3,
1179 input_channel: 1,
1180 muted: true,
1181 peaks_file: Some("peaks/group.json".to_string()),
1182 fade_enabled: false,
1183 fade_in_samples: 10,
1184 fade_out_samples: 20,
1185 preview_name: Some("preview.wav".to_string()),
1186 source_name: Some("source.wav".to_string()),
1187 source_offset: Some(4),
1188 source_length: Some(88),
1189 pitch_correction_points: vec![PitchCorrectionPointData {
1190 start_sample: 7,
1191 length_samples: 11,
1192 detected_midi_pitch: 60.1,
1193 target_midi_pitch: 61.2,
1194 clarity: 0.8,
1195 }],
1196 pitch_correction_frame_likeness: Some(0.5),
1197 pitch_correction_inertia_ms: Some(123),
1198 pitch_correction_formant_compensation: Some(false),
1199 plugin_graph_json: Some(json!({"plugins":[],"connections":[{"kind":"Audio"}]})),
1200 grouped_clips: vec![AudioClipData {
1201 name: "child.wav".to_string(),
1202 start: 0,
1203 length: 48,
1204 ..AudioClipData::default()
1205 }],
1206 };
1207
1208 let value = serde_json::to_value(&clip).expect("serialize");
1209 let restored: AudioClipData = serde_json::from_value(value).expect("deserialize");
1210
1211 assert_eq!(restored.name, clip.name);
1212 assert_eq!(restored.preview_name, clip.preview_name);
1213 assert_eq!(restored.source_name, clip.source_name);
1214 assert_eq!(restored.plugin_graph_json, clip.plugin_graph_json);
1215 assert_eq!(restored.grouped_clips.len(), 1);
1216 assert_eq!(restored.grouped_clips[0].name, "child.wav");
1217 assert_eq!(restored.pitch_correction_points[0].target_midi_pitch, 61.2);
1218 }
1219
1220 #[test]
1221 fn midi_clip_data_serde_round_trips_nested_groups() {
1222 let clip = MidiClipData {
1223 name: "group.mid".to_string(),
1224 start: 5,
1225 length: 64,
1226 offset: 2,
1227 input_channel: 3,
1228 muted: true,
1229 grouped_clips: vec![MidiClipData {
1230 name: "child.mid".to_string(),
1231 start: 0,
1232 length: 32,
1233 ..MidiClipData::default()
1234 }],
1235 };
1236
1237 let value = serde_json::to_value(&clip).expect("serialize");
1238 let restored: MidiClipData = serde_json::from_value(value).expect("deserialize");
1239
1240 assert_eq!(restored.name, clip.name);
1241 assert_eq!(restored.grouped_clips.len(), 1);
1242 assert_eq!(restored.grouped_clips[0].name, "child.mid");
1243 }
1244
1245 #[test]
1246 fn pitch_correction_point_data_serde_round_trips() {
1247 let point = PitchCorrectionPointData {
1248 start_sample: 10,
1249 length_samples: 20,
1250 detected_midi_pitch: 57.5,
1251 target_midi_pitch: 58.0,
1252 clarity: 0.9,
1253 };
1254
1255 let value = serde_json::to_value(&point).expect("serialize");
1256 let restored: PitchCorrectionPointData =
1257 serde_json::from_value(value).expect("deserialize");
1258
1259 assert_eq!(restored.start_sample, 10);
1260 assert_eq!(restored.length_samples, 20);
1261 assert_eq!(restored.detected_midi_pitch, 57.5);
1262 assert_eq!(restored.target_midi_pitch, 58.0);
1263 assert_eq!(restored.clarity, 0.9);
1264 }
1265
1266 #[test]
1267 fn audio_clip_data_deserializes_with_omitted_optional_fields() {
1268 let restored: AudioClipData = serde_json::from_value(json!({
1269 "name": "clip.wav",
1270 "start": 1,
1271 "length": 2,
1272 "offset": 3,
1273 "input_channel": 0,
1274 "muted": false,
1275 "fade_enabled": true,
1276 "fade_in_samples": 240,
1277 "fade_out_samples": 240,
1278 "pitch_correction_points": [],
1279 "grouped_clips": []
1280 }))
1281 .expect("deserialize");
1282
1283 assert_eq!(restored.name, "clip.wav");
1284 assert!(restored.peaks_file.is_none());
1285 assert!(restored.preview_name.is_none());
1286 assert!(restored.source_name.is_none());
1287 assert!(restored.source_offset.is_none());
1288 assert!(restored.source_length.is_none());
1289 assert!(restored.pitch_correction_points.is_empty());
1290 assert!(restored.plugin_graph_json.is_none());
1291 }
1292}