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