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, 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 Play,
282 Pause,
283 Stop,
284 TransportPosition(usize),
285 TransportPositionAt {
286 sample: usize,
287 after_frames: usize,
288 },
289 JumpToEnd,
290 SetLoopEnabled(bool),
291 SetLoopRange(Option<(usize, usize)>),
292 SetPunchEnabled(bool),
293 SetPunchRange(Option<(usize, usize)>),
294 SetMetronomeEnabled(bool),
295 SetTempo(f64),
296 SetTimeSignature {
297 numerator: u16,
298 denominator: u16,
299 },
300 SetOscEnabled(bool),
301 SetClipPlaybackEnabled(bool),
302 SetRecordEnabled(bool),
303 SetSessionPath(String),
304 BeginHistoryGroup,
305 EndHistoryGroup,
306 ApplyGroupedActions(Vec<Action>),
307 ClearHistory,
308 BeginSessionRestore,
309 EndSessionRestore,
310 AddTrack {
311 name: String,
312 audio_ins: usize,
313 midi_ins: usize,
314 audio_outs: usize,
315 midi_outs: usize,
316 },
317 TrackAddAudioInput(String),
318 TrackAddAudioOutput(String),
319 TrackRemoveAudioInput(String),
320 TrackRemoveAudioOutput(String),
321 AddClip {
322 name: String,
323 track_name: String,
324 start: usize,
325 length: usize,
326 offset: usize,
327 input_channel: usize,
328 muted: bool,
329 peaks_file: Option<String>,
330 kind: Kind,
331 fade_enabled: bool,
332 fade_in_samples: usize,
333 fade_out_samples: usize,
334 source_name: Option<String>,
335 source_offset: Option<usize>,
336 source_length: Option<usize>,
337 preview_name: Option<String>,
338 pitch_correction_points: Vec<PitchCorrectionPointData>,
339 pitch_correction_frame_likeness: Option<f32>,
340 pitch_correction_inertia_ms: Option<u16>,
341 pitch_correction_formant_compensation: Option<bool>,
342 plugin_graph_json: Option<serde_json::Value>,
343 },
344 AddGroupedClip {
345 track_name: String,
346 kind: Kind,
347 audio_clip: Option<AudioClipData>,
348 midi_clip: Option<MidiClipData>,
349 },
350 RemoveClip {
351 track_name: String,
352 kind: Kind,
353 clip_indices: Vec<usize>,
354 },
355 SetClipFade {
356 track_name: String,
357 clip_index: usize,
358 kind: Kind,
359 fade_enabled: bool,
360 fade_in_samples: usize,
361 fade_out_samples: usize,
362 },
363 SetClipBounds {
364 track_name: String,
365 clip_index: usize,
366 kind: Kind,
367 start: usize,
368 length: usize,
369 offset: usize,
370 },
371 SyncClipBounds {
372 track_name: String,
373 clip_index: usize,
374 kind: Kind,
375 start: usize,
376 length: usize,
377 offset: usize,
378 },
379 SetClipMuted {
380 track_name: String,
381 clip_index: usize,
382 kind: Kind,
383 muted: bool,
384 },
385 SetClipPluginGraphJson {
386 track_name: String,
387 clip_index: usize,
388 plugin_graph_json: Option<serde_json::Value>,
389 },
390 SetClipPitchCorrection {
391 track_name: String,
392 clip_index: usize,
393 preview_name: Option<String>,
394 source_name: Option<String>,
395 source_offset: Option<usize>,
396 source_length: Option<usize>,
397 pitch_correction_points: Vec<PitchCorrectionPointData>,
398 pitch_correction_frame_likeness: Option<f32>,
399 pitch_correction_inertia_ms: Option<u16>,
400 pitch_correction_formant_compensation: Option<bool>,
401 },
402 RenameClip {
403 track_name: String,
404 kind: Kind,
405 clip_index: usize,
406 new_name: String,
407 },
408 SetClipSourceName {
409 track_name: String,
410 kind: Kind,
411 clip_index: usize,
412 name: String,
413 },
414 RenameTrack {
415 old_name: String,
416 new_name: String,
417 },
418 RemoveTrack(String),
419 TrackLevel(String, f32),
420 TrackBalance(String, f32),
421 TrackAutomationLevel(String, f32),
422 TrackAutomationBalance(String, f32),
423 TrackAutomationMute(String, bool),
424 TrackMeters {
425 track_name: String,
426 output_db: Vec<f32>,
427 },
428 RequestMeterSnapshot,
429 MeterSnapshot {
430 hw_out_db: Arc<Vec<f32>>,
431 track_meters: Arc<Vec<(String, Vec<f32>)>>,
432 },
433 TrackToggleArm(String),
434 TrackToggleMute(String),
435 TrackTogglePhase(String),
436 TrackToggleSolo(String),
437 TrackToggleMaster(String),
438 TrackToggleInputMonitor(String),
439 TrackToggleDiskMonitor(String),
440 TrackSetColor {
441 track_name: String,
442 color: Option<TrackColor>,
443 },
444 TrackArmMidiLearn {
445 track_name: String,
446 target: TrackMidiLearnTarget,
447 },
448 GlobalArmMidiLearn {
449 target: GlobalMidiLearnTarget,
450 },
451 TrackSetMidiLearnBinding {
452 track_name: String,
453 target: TrackMidiLearnTarget,
454 binding: Option<MidiLearnBinding>,
455 },
456 SetGlobalMidiLearnBinding {
457 target: GlobalMidiLearnTarget,
458 binding: Option<MidiLearnBinding>,
459 },
460 TrackSetVcaMaster {
461 track_name: String,
462 master_track: Option<String>,
463 },
464 TrackSetFolder {
465 track_name: String,
466 is_folder: bool,
467 },
468 TrackSetParent {
469 track_name: String,
470 parent_name: Option<String>,
471 },
472 TrackToggleFolder {
473 track_name: String,
474 },
475 TrackSetMidiLaneChannel {
476 track_name: String,
477 lane: usize,
478 channel: Option<u8>,
479 },
480 TrackSetFrozen {
481 track_name: String,
482 frozen: bool,
483 },
484 TrackOfflineBounce {
485 track_name: String,
486 output_path: String,
487 start_sample: usize,
488 length_samples: usize,
489 automation_lanes: Vec<OfflineAutomationLane>,
490 apply_fader: bool,
491 },
492 TrackOfflineBounceCancel {
493 track_name: String,
494 },
495 TrackOfflineBounceCancelAll,
496 TrackOfflineBounceCanceled {
497 track_name: String,
498 },
499 TrackOfflineBounceProgress {
500 track_name: String,
501 progress: f32,
502 operation: Option<String>,
503 },
504 PianoKey {
505 track_name: String,
506 note: u8,
507 velocity: u8,
508 on: bool,
509 },
510 ModifyMidiNotes {
511 track_name: String,
512 clip_index: usize,
513 note_indices: Vec<usize>,
514 new_notes: Vec<MidiNoteData>,
515 old_notes: Vec<MidiNoteData>,
516 },
517 ModifyMidiControllers {
518 track_name: String,
519 clip_index: usize,
520 controller_indices: Vec<usize>,
521 new_controllers: Vec<MidiControllerData>,
522 old_controllers: Vec<MidiControllerData>,
523 },
524 DeleteMidiControllers {
525 track_name: String,
526 clip_index: usize,
527 controller_indices: Vec<usize>,
528 deleted_controllers: Vec<(usize, MidiControllerData)>,
529 },
530 InsertMidiControllers {
531 track_name: String,
532 clip_index: usize,
533 controllers: Vec<(usize, MidiControllerData)>,
534 },
535 DeleteMidiNotes {
536 track_name: String,
537 clip_index: usize,
538 note_indices: Vec<usize>,
539 deleted_notes: Vec<(usize, MidiNoteData)>,
540 },
541 InsertMidiNotes {
542 track_name: String,
543 clip_index: usize,
544 notes: Vec<(usize, MidiNoteData)>,
545 },
546 SetMidiSysExEvents {
547 track_name: String,
548 clip_index: usize,
549 new_sysex_events: Vec<MidiRawEventData>,
550 old_sysex_events: Vec<MidiRawEventData>,
551 },
552 TrackClearDefaultPassthrough {
553 track_name: String,
554 },
555 #[cfg(all(unix, not(target_os = "macos")))]
556 TrackSetLv2PluginState {
557 track_name: String,
558 instance_id: usize,
559 state: Vec<u8>,
560 },
561 #[cfg(all(unix, not(target_os = "macos")))]
562 ClipSetLv2PluginState {
563 track_name: String,
564 clip_idx: usize,
565 instance_id: usize,
566 state: Vec<u8>,
567 },
568 #[cfg(all(unix, not(target_os = "macos")))]
569 TrackGetLv2PluginControls {
570 track_name: String,
571 instance_id: usize,
572 },
573 #[cfg(all(unix, not(target_os = "macos")))]
574 ClipGetLv2PluginControls {
575 track_name: String,
576 clip_idx: usize,
577 instance_id: usize,
578 },
579 #[cfg(all(unix, not(target_os = "macos")))]
580 TrackLv2PluginControls {
581 track_name: String,
582 instance_id: usize,
583 controls: Vec<Lv2ControlPortInfo>,
584 instance_access_handle: Option<usize>,
585 },
586 #[cfg(all(unix, not(target_os = "macos")))]
587 ClipLv2PluginControls {
588 track_name: String,
589 clip_idx: usize,
590 instance_id: usize,
591 controls: Vec<Lv2ControlPortInfo>,
592 instance_access_handle: Option<usize>,
593 },
594 #[cfg(all(unix, not(target_os = "macos")))]
595 TrackGetLv2Midnam {
596 track_name: String,
597 },
598 #[cfg(all(unix, not(target_os = "macos")))]
599 TrackLv2Midnam {
600 track_name: String,
601 note_names: std::collections::HashMap<u8, String>,
602 },
603 TrackGetClapNoteNames {
604 track_name: String,
605 },
606 TrackClapNoteNames {
607 track_name: String,
608 note_names: std::collections::HashMap<u8, String>,
609 },
610 #[cfg(all(unix, not(target_os = "macos")))]
611 TrackSetLv2ControlValue {
612 track_name: String,
613 instance_id: usize,
614 index: u32,
615 value: f32,
616 },
617 #[cfg(all(unix, not(target_os = "macos")))]
618 ClipSetLv2ControlValue {
619 track_name: String,
620 clip_idx: usize,
621 instance_id: usize,
622 index: u32,
623 value: f32,
624 },
625 #[cfg(all(unix, not(target_os = "macos")))]
626 ClipLv2StateSnapshot {
627 track_name: String,
628 clip_idx: usize,
629 instance_id: usize,
630 state: Vec<u8>,
631 },
632 TrackGetPluginGraph {
633 track_name: String,
634 },
635 TrackPluginGraph {
636 track_name: String,
637 plugins: Vec<PluginGraphPlugin>,
638 connections: Vec<PluginGraphConnection>,
639 },
640 TrackConnectPluginAudio {
641 track_name: String,
642 from_node: PluginGraphNode,
643 from_port: usize,
644 to_node: PluginGraphNode,
645 to_port: usize,
646 },
647 TrackConnectPluginMidi {
648 track_name: String,
649 from_node: PluginGraphNode,
650 from_port: usize,
651 to_node: PluginGraphNode,
652 to_port: usize,
653 },
654 TrackDisconnectPluginAudio {
655 track_name: String,
656 from_node: PluginGraphNode,
657 from_port: usize,
658 to_node: PluginGraphNode,
659 to_port: usize,
660 },
661 TrackDisconnectPluginMidi {
662 track_name: String,
663 from_node: PluginGraphNode,
664 from_port: usize,
665 to_node: PluginGraphNode,
666 to_port: usize,
667 },
668 #[cfg(all(unix, not(target_os = "macos")))]
669 ListLv2Plugins,
670 #[cfg(all(unix, not(target_os = "macos")))]
671 Lv2Plugins(Vec<Lv2PluginInfo>),
672 ListVst3Plugins,
673 Vst3Plugins(Vec<Vst3PluginInfo>),
674 ListClapPlugins,
675 ListClapPluginsWithCapabilities,
676 ClapPlugins(Vec<ClapPluginInfo>),
677 TrackSetClapParameter {
678 track_name: String,
679 instance_id: usize,
680 param_id: u32,
681 value: f64,
682 },
683 ClipSetClapParameter {
684 track_name: String,
685 clip_idx: usize,
686 instance_id: usize,
687 param_id: u32,
688 value: f64,
689 },
690 TrackSetClapParameterAt {
691 track_name: String,
692 instance_id: usize,
693 param_id: u32,
694 value: f64,
695 frame: u32,
696 },
697 TrackBeginClapParameterEdit {
698 track_name: String,
699 instance_id: usize,
700 param_id: u32,
701 frame: u32,
702 },
703 TrackEndClapParameterEdit {
704 track_name: String,
705 instance_id: usize,
706 param_id: u32,
707 frame: u32,
708 },
709 TrackGetClapParameters {
710 track_name: String,
711 instance_id: usize,
712 },
713 TrackClapParameters {
714 track_name: String,
715 instance_id: usize,
716 parameters: Vec<ClapParameterInfo>,
717 },
718 TrackClapSnapshotState {
719 track_name: String,
720 instance_id: usize,
721 },
722 ClipClapSnapshotState {
723 track_name: String,
724 clip_idx: usize,
725 instance_id: usize,
726 },
727 TrackClapStateSnapshot {
728 track_name: String,
729 instance_id: usize,
730 plugin_path: String,
731 state: crate::clap::ClapPluginState,
732 },
733 ClipClapStateSnapshot {
734 track_name: String,
735 clip_idx: usize,
736 instance_id: usize,
737 plugin_path: String,
738 state: crate::clap::ClapPluginState,
739 },
740 TrackClapRestoreState {
741 track_name: String,
742 instance_id: usize,
743 state: crate::clap::ClapPluginState,
744 },
745 ClipClapRestoreState {
746 track_name: String,
747 clip_idx: usize,
748 instance_id: usize,
749 state: crate::clap::ClapPluginState,
750 },
751 TrackSnapshotAllClapStates {
752 track_name: String,
753 },
754 TrackSnapshotAllClapStatesDone {
755 track_name: String,
756 },
757 TrackLoadClapPlugin {
758 track_name: String,
759 plugin_path: String,
760 instance_id: Option<usize>,
761 },
762 TrackUnloadClapPlugin {
763 track_name: String,
764 plugin_path: String,
765 },
766 TrackUnloadClapPluginInstance {
767 track_name: String,
768 instance_id: usize,
769 },
770 TrackShowClapGui {
771 track_name: String,
772 instance_id: usize,
773 },
774 TrackLoadVst3Plugin {
775 track_name: String,
776 plugin_path: String,
777 instance_id: Option<usize>,
778 },
779 TrackUnloadVst3Plugin {
780 track_name: String,
781 plugin_path: String,
782 },
783 TrackUnloadVst3PluginInstance {
784 track_name: String,
785 instance_id: usize,
786 },
787 TrackShowVst3Gui {
788 track_name: String,
789 instance_id: usize,
790 },
791 #[cfg(all(unix, not(target_os = "macos")))]
792 TrackLoadLv2Plugin {
793 track_name: String,
794 plugin_uri: String,
795 instance_id: Option<usize>,
796 },
797 #[cfg(all(unix, not(target_os = "macos")))]
798 TrackUnloadLv2Plugin {
799 track_name: String,
800 plugin_uri: String,
801 },
802 #[cfg(all(unix, not(target_os = "macos")))]
803 TrackUnloadLv2PluginInstance {
804 track_name: String,
805 instance_id: usize,
806 },
807 TrackShowLv2Gui {
808 track_name: String,
809 instance_id: usize,
810 },
811 TrackGetVst3Graph {
812 track_name: String,
813 },
814 TrackVst3Graph {
815 track_name: String,
816 plugins: Vec<Vst3GraphPlugin>,
817 connections: Vec<Vst3GraphConnection>,
818 },
819 TrackSetVst3Parameter {
820 track_name: String,
821 instance_id: usize,
822 param_id: u32,
823 value: f32,
824 },
825 TrackSetPluginBypassed {
826 track_name: String,
827 instance_id: usize,
828 format: String,
829 bypassed: bool,
830 },
831 TrackGetVst3Parameters {
832 track_name: String,
833 instance_id: usize,
834 },
835 TrackVst3Parameters {
836 track_name: String,
837 instance_id: usize,
838 parameters: Vec<crate::vst3::port::ParameterInfo>,
839 },
840 TrackVst3SnapshotState {
841 track_name: String,
842 instance_id: usize,
843 },
844 ClipVst3SnapshotState {
845 track_name: String,
846 clip_idx: usize,
847 instance_id: usize,
848 },
849 TrackVst3StateSnapshot {
850 track_name: String,
851 instance_id: usize,
852 state: crate::vst3::state::Vst3PluginState,
853 },
854 ClipVst3StateSnapshot {
855 track_name: String,
856 clip_idx: usize,
857 instance_id: usize,
858 state: crate::vst3::state::Vst3PluginState,
859 },
860 TrackVst3RestoreState {
861 track_name: String,
862 instance_id: usize,
863 state: crate::vst3::state::Vst3PluginState,
864 },
865 TrackConnectVst3Audio {
866 track_name: String,
867 from_node: Vst3GraphNode,
868 from_port: usize,
869 to_node: Vst3GraphNode,
870 to_port: usize,
871 },
872 TrackDisconnectVst3Audio {
873 track_name: String,
874 from_node: Vst3GraphNode,
875 from_port: usize,
876 to_node: Vst3GraphNode,
877 to_port: usize,
878 },
879 ClipMove {
880 kind: Kind,
881 from: ClipMoveFrom,
882 to: ClipMoveTo,
883 copy: bool,
884 },
885 Connect {
886 from_track: String,
887 from_port: usize,
888 to_track: String,
889 to_port: usize,
890 kind: Kind,
891 },
892 Disconnect {
893 from_track: String,
894 from_port: usize,
895 to_track: String,
896 to_port: usize,
897 kind: Kind,
898 },
899 OpenAudioDevice {
900 device: String,
901 input_device: Option<String>,
902 sample_rate_hz: i32,
903 bits: i32,
904 exclusive: bool,
905 period_frames: usize,
906 realtime_frames: usize,
907 low_watermark_frames: usize,
908 nperiods: usize,
909 sync_mode: bool,
910 hybrid_enabled: bool,
911 actual_period_frames: usize,
912 input_channels: usize,
913 output_channels: usize,
914 bytes_per_frame: usize,
915 },
916 JackAddAudioInputPort,
917 JackRemoveAudioInputPort(usize),
918 JackAddAudioOutputPort,
919 JackRemoveAudioOutputPort(usize),
920 OpenMidiInputDevice(String),
921 OpenMidiOutputDevice(String),
922 RequestSessionDiagnostics,
923 RequestMidiLearnMappingsReport,
924 ClearAllMidiLearnBindings,
925 SessionDiagnosticsReport {
926 track_count: usize,
927 frozen_track_count: usize,
928 audio_clip_count: usize,
929 midi_clip_count: usize,
930 #[cfg(all(unix, not(target_os = "macos")))]
931 lv2_instance_count: usize,
932 vst3_instance_count: usize,
933 clap_instance_count: usize,
934 pending_requests: usize,
935 workers_total: usize,
936 workers_ready: usize,
937 pending_hw_midi_events: usize,
938 playing: bool,
939 transport_sample: usize,
940 tempo_bpm: f64,
941 sample_rate_hz: usize,
942 cycle_samples: usize,
943 },
944 MidiLearnMappingsReport {
945 lines: Vec<String>,
946 },
947 HWInfo {
948 channels: usize,
949 rate: usize,
950 input: bool,
951 },
952 MarkHistorySavePoint,
953 HistoryState {
954 dirty: bool,
955 },
956 Undo,
957 Redo,
958 Panic,
959}
960
961#[derive(Clone, Debug)]
962pub enum Message {
963 Ready(usize),
964 Finished {
965 worker_id: usize,
966 track_name: String,
967 output_linear: Vec<f32>,
968 process_epoch: usize,
969 parameter_updates: Vec<Action>,
970 },
971 TracksFinished,
972
973 ProcessTrack(Arc<UnsafeMutex<Box<Track>>>),
974 ProcessOfflineBounce(OfflineBounceWork),
975 Channel(Sender<Self>),
976
977 Request(Action),
978 Response(Result<Action, String>),
979 HWMidiEvents(Vec<HwMidiEvent>),
980 HWMidiOutEvents(Vec<HwMidiEvent>),
981 ClearHWMidiOutEvents,
982 HWFinished,
983 OfflineBounceFinished {
984 result: Result<Action, String>,
985 },
986}
987
988#[cfg(test)]
989mod tests {
990 use super::{AudioClipData, MidiClipData, PitchCorrectionPointData};
991 use serde_json::json;
992
993 #[test]
994 fn audio_clip_data_serde_round_trips_nested_groups() {
995 let clip = AudioClipData {
996 name: "group.wav".to_string(),
997 start: 12,
998 length: 96,
999 offset: 3,
1000 input_channel: 1,
1001 muted: true,
1002 peaks_file: Some("peaks/group.json".to_string()),
1003 fade_enabled: false,
1004 fade_in_samples: 10,
1005 fade_out_samples: 20,
1006 preview_name: Some("preview.wav".to_string()),
1007 source_name: Some("source.wav".to_string()),
1008 source_offset: Some(4),
1009 source_length: Some(88),
1010 pitch_correction_points: vec![PitchCorrectionPointData {
1011 start_sample: 7,
1012 length_samples: 11,
1013 detected_midi_pitch: 60.1,
1014 target_midi_pitch: 61.2,
1015 clarity: 0.8,
1016 }],
1017 pitch_correction_frame_likeness: Some(0.5),
1018 pitch_correction_inertia_ms: Some(123),
1019 pitch_correction_formant_compensation: Some(false),
1020 plugin_graph_json: Some(json!({"plugins":[],"connections":[{"kind":"Audio"}]})),
1021 grouped_clips: vec![AudioClipData {
1022 name: "child.wav".to_string(),
1023 start: 0,
1024 length: 48,
1025 ..AudioClipData::default()
1026 }],
1027 };
1028
1029 let value = serde_json::to_value(&clip).expect("serialize");
1030 let restored: AudioClipData = serde_json::from_value(value).expect("deserialize");
1031
1032 assert_eq!(restored.name, clip.name);
1033 assert_eq!(restored.preview_name, clip.preview_name);
1034 assert_eq!(restored.source_name, clip.source_name);
1035 assert_eq!(restored.plugin_graph_json, clip.plugin_graph_json);
1036 assert_eq!(restored.grouped_clips.len(), 1);
1037 assert_eq!(restored.grouped_clips[0].name, "child.wav");
1038 assert_eq!(restored.pitch_correction_points[0].target_midi_pitch, 61.2);
1039 }
1040
1041 #[test]
1042 fn midi_clip_data_serde_round_trips_nested_groups() {
1043 let clip = MidiClipData {
1044 name: "group.mid".to_string(),
1045 start: 5,
1046 length: 64,
1047 offset: 2,
1048 input_channel: 3,
1049 muted: true,
1050 grouped_clips: vec![MidiClipData {
1051 name: "child.mid".to_string(),
1052 start: 0,
1053 length: 32,
1054 ..MidiClipData::default()
1055 }],
1056 };
1057
1058 let value = serde_json::to_value(&clip).expect("serialize");
1059 let restored: MidiClipData = serde_json::from_value(value).expect("deserialize");
1060
1061 assert_eq!(restored.name, clip.name);
1062 assert_eq!(restored.grouped_clips.len(), 1);
1063 assert_eq!(restored.grouped_clips[0].name, "child.mid");
1064 }
1065
1066 #[test]
1067 fn pitch_correction_point_data_serde_round_trips() {
1068 let point = PitchCorrectionPointData {
1069 start_sample: 10,
1070 length_samples: 20,
1071 detected_midi_pitch: 57.5,
1072 target_midi_pitch: 58.0,
1073 clarity: 0.9,
1074 };
1075
1076 let value = serde_json::to_value(&point).expect("serialize");
1077 let restored: PitchCorrectionPointData =
1078 serde_json::from_value(value).expect("deserialize");
1079
1080 assert_eq!(restored.start_sample, 10);
1081 assert_eq!(restored.length_samples, 20);
1082 assert_eq!(restored.detected_midi_pitch, 57.5);
1083 assert_eq!(restored.target_midi_pitch, 58.0);
1084 assert_eq!(restored.clarity, 0.9);
1085 }
1086
1087 #[test]
1088 fn audio_clip_data_deserializes_with_omitted_optional_fields() {
1089 let restored: AudioClipData = serde_json::from_value(json!({
1090 "name": "clip.wav",
1091 "start": 1,
1092 "length": 2,
1093 "offset": 3,
1094 "input_channel": 0,
1095 "muted": false,
1096 "fade_enabled": true,
1097 "fade_in_samples": 240,
1098 "fade_out_samples": 240,
1099 "pitch_correction_points": [],
1100 "grouped_clips": []
1101 }))
1102 .expect("deserialize");
1103
1104 assert_eq!(restored.name, "clip.wav");
1105 assert!(restored.peaks_file.is_none());
1106 assert!(restored.preview_name.is_none());
1107 assert!(restored.source_name.is_none());
1108 assert!(restored.source_offset.is_none());
1109 assert!(restored.source_length.is_none());
1110 assert!(restored.pitch_correction_points.is_empty());
1111 assert!(restored.plugin_graph_json.is_none());
1112 }
1113}