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