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    pub apply_fader: bool,
88}
89
90#[derive(Clone, Debug, Default, serde::Serialize, serde::Deserialize)]
91pub struct PitchCorrectionPointData {
92    pub start_sample: usize,
93    pub length_samples: usize,
94    pub detected_midi_pitch: f32,
95    pub target_midi_pitch: f32,
96    pub clarity: f32,
97}
98
99#[derive(Clone, Debug, Default, serde::Serialize, serde::Deserialize)]
100pub struct AudioClipData {
101    pub name: String,
102    pub start: usize,
103    pub length: usize,
104    pub offset: usize,
105    pub input_channel: usize,
106    pub muted: bool,
107    pub peaks_file: Option<String>,
108    pub fade_enabled: bool,
109    pub fade_in_samples: usize,
110    pub fade_out_samples: usize,
111    pub preview_name: Option<String>,
112    pub source_name: Option<String>,
113    pub source_offset: Option<usize>,
114    pub source_length: Option<usize>,
115    pub pitch_correction_points: Vec<PitchCorrectionPointData>,
116    pub pitch_correction_frame_likeness: Option<f32>,
117    pub pitch_correction_inertia_ms: Option<u16>,
118    pub pitch_correction_formant_compensation: Option<bool>,
119    pub plugin_graph_json: Option<serde_json::Value>,
120    pub grouped_clips: Vec<AudioClipData>,
121}
122
123#[derive(Clone, Debug, Default, serde::Serialize, serde::Deserialize)]
124pub struct MidiClipData {
125    pub name: String,
126    pub start: usize,
127    pub length: usize,
128    pub offset: usize,
129    pub input_channel: usize,
130    pub muted: bool,
131    pub grouped_clips: Vec<MidiClipData>,
132}
133
134#[derive(Clone, Debug)]
135pub struct ClipMoveFrom {
136    pub track_name: String,
137    pub clip_index: usize,
138}
139
140#[derive(Clone, Debug)]
141pub struct ClipMoveTo {
142    pub track_name: String,
143    pub sample_offset: usize,
144    pub input_channel: usize,
145}
146
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#[derive(Clone, Debug, PartialEq)]
157pub struct PluginGraphPlugin {
158    pub node: PluginGraphNode,
159    pub instance_id: usize,
160    pub format: String,
161    pub uri: String,
162    pub plugin_id: String,
163    pub name: String,
164    pub main_audio_inputs: usize,
165    pub main_audio_outputs: usize,
166    pub audio_inputs: usize,
167    pub audio_outputs: usize,
168    pub midi_inputs: usize,
169    pub midi_outputs: usize,
170    pub state: Option<serde_json::Value>,
171    pub bypassed: bool,
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#[derive(Clone, Debug, PartialEq, Eq)]
208pub struct PluginGraphConnection {
209    pub from_node: PluginGraphNode,
210    pub from_port: usize,
211    pub to_node: PluginGraphNode,
212    pub to_port: usize,
213    pub kind: Kind,
214}
215
216pub type PluginGraphSnapshot = (Vec<PluginGraphPlugin>, Vec<PluginGraphConnection>);
217
218// VST3 graph types
219#[derive(Clone, Debug, PartialEq, Eq, Hash)]
220pub enum Vst3GraphNode {
221    TrackInput,
222    TrackOutput,
223    PluginInstance(usize),
224}
225
226#[derive(Clone, Debug)]
227pub struct Vst3GraphPlugin {
228    pub instance_id: usize,
229    pub name: String,
230    pub path: String,
231    pub audio_inputs: usize,
232    pub audio_outputs: usize,
233    pub parameters: Vec<crate::vst3::port::ParameterInfo>,
234}
235
236#[derive(Clone, Debug, PartialEq, Eq)]
237pub struct Vst3GraphConnection {
238    pub from_node: Vst3GraphNode,
239    pub from_port: usize,
240    pub to_node: Vst3GraphNode,
241    pub to_port: usize,
242    pub kind: Kind,
243}
244
245#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
246pub struct MidiLearnBinding {
247    pub device: Option<String>,
248    pub channel: u8,
249    pub cc: u8,
250}
251
252#[derive(Clone, Copy, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
253pub enum TrackMidiLearnTarget {
254    Volume,
255    Balance,
256    Mute,
257    Solo,
258    Arm,
259    InputMonitor,
260    DiskMonitor,
261}
262
263#[derive(Clone, Copy, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
264pub enum GlobalMidiLearnTarget {
265    PlayPause,
266    Stop,
267    RecordToggle,
268}
269
270#[derive(Clone, Debug)]
271pub enum Action {
272    Quit,
273    Play,
274    Pause,
275    Stop,
276    TransportPosition(usize),
277    JumpToEnd,
278    SetLoopEnabled(bool),
279    SetLoopRange(Option<(usize, usize)>),
280    SetPunchEnabled(bool),
281    SetPunchRange(Option<(usize, usize)>),
282    SetMetronomeEnabled(bool),
283    SetTempo(f64),
284    SetTimeSignature {
285        numerator: u16,
286        denominator: u16,
287    },
288    SetOscEnabled(bool),
289    SetClipPlaybackEnabled(bool),
290    SetRecordEnabled(bool),
291    SetSessionPath(String),
292    BeginHistoryGroup,
293    EndHistoryGroup,
294    ClearHistory,
295    BeginSessionRestore,
296    EndSessionRestore,
297    AddTrack {
298        name: String,
299        audio_ins: usize,
300        midi_ins: usize,
301        audio_outs: usize,
302        midi_outs: usize,
303    },
304    TrackAddAudioInput(String),
305    TrackAddAudioOutput(String),
306    TrackRemoveAudioInput(String),
307    TrackRemoveAudioOutput(String),
308    AddClip {
309        name: String,
310        track_name: String,
311        start: usize,
312        length: usize,
313        offset: usize,
314        input_channel: usize,
315        muted: bool,
316        peaks_file: Option<String>,
317        kind: Kind,
318        fade_enabled: bool,
319        fade_in_samples: usize,
320        fade_out_samples: usize,
321        source_name: Option<String>,
322        source_offset: Option<usize>,
323        source_length: Option<usize>,
324        preview_name: Option<String>,
325        pitch_correction_points: Vec<PitchCorrectionPointData>,
326        pitch_correction_frame_likeness: Option<f32>,
327        pitch_correction_inertia_ms: Option<u16>,
328        pitch_correction_formant_compensation: Option<bool>,
329        plugin_graph_json: Option<serde_json::Value>,
330    },
331    AddGroupedClip {
332        track_name: String,
333        kind: Kind,
334        audio_clip: Option<AudioClipData>,
335        midi_clip: Option<MidiClipData>,
336    },
337    RemoveClip {
338        track_name: String,
339        kind: Kind,
340        clip_indices: Vec<usize>,
341    },
342    SetClipFade {
343        track_name: String,
344        clip_index: usize,
345        kind: Kind,
346        fade_enabled: bool,
347        fade_in_samples: usize,
348        fade_out_samples: usize,
349    },
350    SetClipBounds {
351        track_name: String,
352        clip_index: usize,
353        kind: Kind,
354        start: usize,
355        length: usize,
356        offset: usize,
357    },
358    SetClipMuted {
359        track_name: String,
360        clip_index: usize,
361        kind: Kind,
362        muted: bool,
363    },
364    SetClipPluginGraphJson {
365        track_name: String,
366        clip_index: usize,
367        plugin_graph_json: Option<serde_json::Value>,
368    },
369    SetClipPitchCorrection {
370        track_name: String,
371        clip_index: usize,
372        preview_name: Option<String>,
373        source_name: Option<String>,
374        source_offset: Option<usize>,
375        source_length: Option<usize>,
376        pitch_correction_points: Vec<PitchCorrectionPointData>,
377        pitch_correction_frame_likeness: Option<f32>,
378        pitch_correction_inertia_ms: Option<u16>,
379        pitch_correction_formant_compensation: Option<bool>,
380    },
381    RenameClip {
382        track_name: String,
383        kind: Kind,
384        clip_index: usize,
385        new_name: String,
386    },
387    RenameTrack {
388        old_name: String,
389        new_name: String,
390    },
391    RemoveTrack(String),
392    TrackLevel(String, f32),
393    TrackBalance(String, f32),
394    TrackAutomationLevel(String, f32),
395    TrackAutomationBalance(String, f32),
396    TrackAutomationMute(String, bool),
397    TrackMeters {
398        track_name: String,
399        output_db: Vec<f32>,
400    },
401    RequestMeterSnapshot,
402    MeterSnapshot {
403        hw_out_db: Arc<Vec<f32>>,
404        track_meters: Arc<Vec<(String, Vec<f32>)>>,
405    },
406    TrackToggleArm(String),
407    TrackToggleMute(String),
408    TrackTogglePhase(String),
409    TrackToggleSolo(String),
410    TrackToggleInputMonitor(String),
411    TrackToggleDiskMonitor(String),
412    TrackArmMidiLearn {
413        track_name: String,
414        target: TrackMidiLearnTarget,
415    },
416    GlobalArmMidiLearn {
417        target: GlobalMidiLearnTarget,
418    },
419    TrackSetMidiLearnBinding {
420        track_name: String,
421        target: TrackMidiLearnTarget,
422        binding: Option<MidiLearnBinding>,
423    },
424    SetGlobalMidiLearnBinding {
425        target: GlobalMidiLearnTarget,
426        binding: Option<MidiLearnBinding>,
427    },
428    TrackSetVcaMaster {
429        track_name: String,
430        master_track: Option<String>,
431    },
432    TrackSetMidiLaneChannel {
433        track_name: String,
434        lane: usize,
435        channel: Option<u8>,
436    },
437    TrackSetFrozen {
438        track_name: String,
439        frozen: bool,
440    },
441    TrackOfflineBounce {
442        track_name: String,
443        output_path: String,
444        start_sample: usize,
445        length_samples: usize,
446        automation_lanes: Vec<OfflineAutomationLane>,
447        apply_fader: bool,
448    },
449    TrackOfflineBounceCancel {
450        track_name: String,
451    },
452    TrackOfflineBounceCancelAll,
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    TrackSetPluginBypassed {
781        track_name: String,
782        instance_id: usize,
783        format: String,
784        bypassed: bool,
785    },
786    TrackGetVst3Parameters {
787        track_name: String,
788        instance_id: usize,
789    },
790    TrackVst3Parameters {
791        track_name: String,
792        instance_id: usize,
793        parameters: Vec<crate::vst3::port::ParameterInfo>,
794    },
795    TrackGetVst3Processor {
796        track_name: String,
797        instance_id: usize,
798    },
799    ClipGetVst3Processor {
800        track_name: String,
801        clip_idx: usize,
802        instance_id: usize,
803    },
804    TrackVst3Processor {
805        track_name: String,
806        instance_id: usize,
807        processor: Arc<crate::vst3::Vst3Processor>,
808    },
809    ClipVst3Processor {
810        track_name: String,
811        clip_idx: usize,
812        instance_id: usize,
813        processor: Arc<crate::vst3::Vst3Processor>,
814    },
815    TrackVst3SnapshotState {
816        track_name: String,
817        instance_id: usize,
818    },
819    ClipVst3SnapshotState {
820        track_name: String,
821        clip_idx: usize,
822        instance_id: usize,
823    },
824    TrackVst3StateSnapshot {
825        track_name: String,
826        instance_id: usize,
827        state: crate::vst3::state::Vst3PluginState,
828    },
829    ClipVst3StateSnapshot {
830        track_name: String,
831        clip_idx: usize,
832        instance_id: usize,
833        state: crate::vst3::state::Vst3PluginState,
834    },
835    TrackVst3RestoreState {
836        track_name: String,
837        instance_id: usize,
838        state: crate::vst3::state::Vst3PluginState,
839    },
840    TrackConnectVst3Audio {
841        track_name: String,
842        from_node: Vst3GraphNode,
843        from_port: usize,
844        to_node: Vst3GraphNode,
845        to_port: usize,
846    },
847    TrackDisconnectVst3Audio {
848        track_name: String,
849        from_node: Vst3GraphNode,
850        from_port: usize,
851        to_node: Vst3GraphNode,
852        to_port: usize,
853    },
854    ClipMove {
855        kind: Kind,
856        from: ClipMoveFrom,
857        to: ClipMoveTo,
858        copy: bool,
859    },
860    Connect {
861        from_track: String,
862        from_port: usize,
863        to_track: String,
864        to_port: usize,
865        kind: Kind,
866    },
867    Disconnect {
868        from_track: String,
869        from_port: usize,
870        to_track: String,
871        to_port: usize,
872        kind: Kind,
873    },
874    OpenAudioDevice {
875        device: String,
876        input_device: Option<String>,
877        sample_rate_hz: i32,
878        bits: i32,
879        exclusive: bool,
880        period_frames: usize,
881        nperiods: usize,
882        sync_mode: bool,
883    },
884    JackAddAudioInputPort,
885    JackRemoveAudioInputPort(usize),
886    JackAddAudioOutputPort,
887    JackRemoveAudioOutputPort(usize),
888    OpenMidiInputDevice(String),
889    OpenMidiOutputDevice(String),
890    RequestSessionDiagnostics,
891    RequestMidiLearnMappingsReport,
892    ClearAllMidiLearnBindings,
893    SessionDiagnosticsReport {
894        track_count: usize,
895        frozen_track_count: usize,
896        audio_clip_count: usize,
897        midi_clip_count: usize,
898        #[cfg(all(unix, not(target_os = "macos")))]
899        lv2_instance_count: usize,
900        vst3_instance_count: usize,
901        clap_instance_count: usize,
902        pending_requests: usize,
903        workers_total: usize,
904        workers_ready: usize,
905        pending_hw_midi_events: usize,
906        playing: bool,
907        transport_sample: usize,
908        tempo_bpm: f64,
909        sample_rate_hz: usize,
910        cycle_samples: usize,
911    },
912    MidiLearnMappingsReport {
913        lines: Vec<String>,
914    },
915    HWInfo {
916        channels: usize,
917        rate: usize,
918        input: bool,
919    },
920    Undo,
921    Redo,
922    Panic,
923}
924
925#[derive(Clone, Debug)]
926pub enum Message {
927    Ready(usize),
928    Finished {
929        worker_id: usize,
930        track_name: String,
931        output_linear: Vec<f32>,
932        process_epoch: usize,
933    },
934    TracksFinished,
935
936    ProcessTrack(Arc<UnsafeMutex<Box<Track>>>),
937    ProcessOfflineBounce(OfflineBounceWork),
938    Channel(Sender<Self>),
939
940    Request(Action),
941    Response(Result<Action, String>),
942    HWMidiEvents(Vec<HwMidiEvent>),
943    HWMidiOutEvents(Vec<HwMidiEvent>),
944    ClearHWMidiOutEvents,
945    HWFinished,
946    OfflineBounceFinished {
947        result: Result<Action, String>,
948    },
949}
950
951#[cfg(test)]
952mod tests {
953    use super::{AudioClipData, MidiClipData, PitchCorrectionPointData};
954    use serde_json::json;
955
956    #[test]
957    fn audio_clip_data_serde_round_trips_nested_groups() {
958        let clip = AudioClipData {
959            name: "group.wav".to_string(),
960            start: 12,
961            length: 96,
962            offset: 3,
963            input_channel: 1,
964            muted: true,
965            peaks_file: Some("peaks/group.json".to_string()),
966            fade_enabled: false,
967            fade_in_samples: 10,
968            fade_out_samples: 20,
969            preview_name: Some("preview.wav".to_string()),
970            source_name: Some("source.wav".to_string()),
971            source_offset: Some(4),
972            source_length: Some(88),
973            pitch_correction_points: vec![PitchCorrectionPointData {
974                start_sample: 7,
975                length_samples: 11,
976                detected_midi_pitch: 60.1,
977                target_midi_pitch: 61.2,
978                clarity: 0.8,
979            }],
980            pitch_correction_frame_likeness: Some(0.5),
981            pitch_correction_inertia_ms: Some(123),
982            pitch_correction_formant_compensation: Some(false),
983            plugin_graph_json: Some(json!({"plugins":[],"connections":[{"kind":"Audio"}]})),
984            grouped_clips: vec![AudioClipData {
985                name: "child.wav".to_string(),
986                start: 0,
987                length: 48,
988                ..AudioClipData::default()
989            }],
990        };
991
992        let value = serde_json::to_value(&clip).expect("serialize");
993        let restored: AudioClipData = serde_json::from_value(value).expect("deserialize");
994
995        assert_eq!(restored.name, clip.name);
996        assert_eq!(restored.preview_name, clip.preview_name);
997        assert_eq!(restored.source_name, clip.source_name);
998        assert_eq!(restored.plugin_graph_json, clip.plugin_graph_json);
999        assert_eq!(restored.grouped_clips.len(), 1);
1000        assert_eq!(restored.grouped_clips[0].name, "child.wav");
1001        assert_eq!(restored.pitch_correction_points[0].target_midi_pitch, 61.2);
1002    }
1003
1004    #[test]
1005    fn midi_clip_data_serde_round_trips_nested_groups() {
1006        let clip = MidiClipData {
1007            name: "group.mid".to_string(),
1008            start: 5,
1009            length: 64,
1010            offset: 2,
1011            input_channel: 3,
1012            muted: true,
1013            grouped_clips: vec![MidiClipData {
1014                name: "child.mid".to_string(),
1015                start: 0,
1016                length: 32,
1017                ..MidiClipData::default()
1018            }],
1019        };
1020
1021        let value = serde_json::to_value(&clip).expect("serialize");
1022        let restored: MidiClipData = serde_json::from_value(value).expect("deserialize");
1023
1024        assert_eq!(restored.name, clip.name);
1025        assert_eq!(restored.grouped_clips.len(), 1);
1026        assert_eq!(restored.grouped_clips[0].name, "child.mid");
1027    }
1028
1029    #[test]
1030    fn pitch_correction_point_data_serde_round_trips() {
1031        let point = PitchCorrectionPointData {
1032            start_sample: 10,
1033            length_samples: 20,
1034            detected_midi_pitch: 57.5,
1035            target_midi_pitch: 58.0,
1036            clarity: 0.9,
1037        };
1038
1039        let value = serde_json::to_value(&point).expect("serialize");
1040        let restored: PitchCorrectionPointData =
1041            serde_json::from_value(value).expect("deserialize");
1042
1043        assert_eq!(restored.start_sample, 10);
1044        assert_eq!(restored.length_samples, 20);
1045        assert_eq!(restored.detected_midi_pitch, 57.5);
1046        assert_eq!(restored.target_midi_pitch, 58.0);
1047        assert_eq!(restored.clarity, 0.9);
1048    }
1049
1050    #[test]
1051    fn audio_clip_data_deserializes_with_omitted_optional_fields() {
1052        let restored: AudioClipData = serde_json::from_value(json!({
1053            "name": "clip.wav",
1054            "start": 1,
1055            "length": 2,
1056            "offset": 3,
1057            "input_channel": 0,
1058            "muted": false,
1059            "fade_enabled": true,
1060            "fade_in_samples": 240,
1061            "fade_out_samples": 240,
1062            "pitch_correction_points": [],
1063            "grouped_clips": []
1064        }))
1065        .expect("deserialize");
1066
1067        assert_eq!(restored.name, "clip.wav");
1068        assert!(restored.peaks_file.is_none());
1069        assert!(restored.preview_name.is_none());
1070        assert!(restored.source_name.is_none());
1071        assert!(restored.source_offset.is_none());
1072        assert!(restored.source_length.is_none());
1073        assert!(restored.pitch_correction_points.is_empty());
1074        assert!(restored.plugin_graph_json.is_none());
1075    }
1076}