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