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