Skip to main content

maolan_engine/
message.rs

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