Skip to main content

maolan_engine/
message.rs

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