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