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