Skip to main content

vst_audio_protocol/
lib.rs

1#![forbid(unsafe_code)]
2
3//! Control framing and deterministic host supervision from ADR 0021.
4
5use std::collections::VecDeque;
6use std::fmt;
7use std::io::{self, Read, Write};
8use std::time::Duration;
9
10pub mod editor;
11pub mod layout;
12pub mod processing;
13pub use editor::{
14    EditorAction, EditorImage, EditorPlatform, EditorReply, EditorStatus, ParentWindow,
15};
16pub use processing::{BusConfiguration, Capabilities, ProcessingSetup, SampleFormat};
17pub mod state;
18#[derive(Clone, Debug, thiserror::Error)]
19#[error("{0}")]
20pub struct PluginError(pub String);
21
22pub const MAGIC: [u8; 4] = *b"SSRP";
23pub const PROTOCOL_MAJOR: u16 = 4;
24pub const PROTOCOL_MINOR: u16 = 0;
25pub const MAX_MESSAGE_BYTES: usize = 64 * 1024;
26pub const MAX_STATE_BYTES: usize = 64 * 1024 * 1024;
27/// Longest UTF-8 string in a message. Windows long paths are why it is above
28/// MAX_PATH.
29pub const MAX_STRING_BYTES: usize = 4096;
30const FRAME_META_BYTES: usize = 8;
31
32#[derive(Clone, Copy, Debug, Eq, PartialEq)]
33#[repr(u16)]
34pub enum PluginFormat {
35    Vst2 = 2,
36    Vst3 = 3,
37}
38
39#[derive(Clone, Debug, Eq, PartialEq)]
40pub struct Bus {
41    pub channels: u16,
42    pub arrangement: u64,
43}
44
45/// What a declared audio bus is for.
46#[derive(Clone, Copy, Debug, Eq, PartialEq)]
47pub enum BusKind {
48    Main,
49    /// Sidechain inputs and auxiliary outputs.
50    Aux,
51}
52
53/// A bus as the plugin declares it, before any is activated.
54#[derive(Clone, Debug, Eq, PartialEq)]
55pub struct BusInfo {
56    pub layout: Bus,
57    pub kind: BusKind,
58    /// The plugin's own name for the bus; may be empty.
59    pub name: String,
60    /// The plugin asks for this bus to be active unless the host decides otherwise.
61    pub default_active: bool,
62}
63
64impl BusInfo {
65    pub fn configuration(&self, active: bool) -> BusConfiguration {
66        BusConfiguration {
67            layout: self.layout.clone(),
68            active,
69        }
70    }
71}
72
73#[derive(Clone, Debug, Eq, PartialEq)]
74pub struct ClassInfo {
75    pub id: [u8; 16],
76    pub name: String,
77    pub vendor: String,
78    pub category: String,
79}
80
81#[derive(Clone, Debug, PartialEq)]
82pub struct Parameter {
83    pub id: u32,
84    pub name: String,
85    pub unit: String,
86    pub value: f64,
87    pub default: Option<f64>,
88    pub steps: Option<u32>,
89    pub automatable: bool,
90    pub read_only: bool,
91    pub hidden: bool,
92}
93
94#[derive(Clone, Copy, Debug, PartialEq, Eq)]
95pub enum EditPhase {
96    Begin,
97    Perform,
98    End,
99}
100
101#[derive(Clone, Debug, PartialEq)]
102pub enum Message {
103    /// Sent before Hello only when an OS sandbox was requested and installed.
104    Sandboxed {
105        mechanism: String,
106    },
107    Hello {
108        magic: [u8; 4],
109        major: u16,
110        minor: u16,
111    },
112    Load {
113        path: String,
114        format: PluginFormat,
115        class_id: Option<[u8; 16]>,
116    },
117    Configure {
118        rate: u32,
119        max_frames: u32,
120        in_channels: u16,
121        out_channels: u16,
122    },
123    Editor(EditorAction),
124    EditorStatus(EditorStatus),
125    EditorImage(EditorImage),
126    Prepare(ProcessingSetup),
127    Describe,
128    Features(Capabilities),
129    Activate,
130    SetState {
131        blob: Vec<u8>,
132    },
133    GetState,
134    OpenEditor,
135    OpenEditorIn {
136        parent: ParentWindow,
137    },
138    CloseEditor,
139    ListParameters {
140        start: u32,
141        count: u16,
142    },
143    Parameters {
144        total: u32,
145        entries: Vec<Parameter>,
146    },
147    SetParameter {
148        id: u32,
149        value: f64,
150    },
151    RequestError {
152        code: u32,
153        text: String,
154    },
155    Shutdown,
156    Scan {
157        path: String,
158    },
159    Loaded {
160        name: String,
161        vendor: String,
162        latency_frames: u32,
163        inputs: Vec<BusInfo>,
164        outputs: Vec<BusInfo>,
165    },
166    Classes {
167        entries: Vec<ClassInfo>,
168    },
169    LatencyChanged {
170        frames: u32,
171    },
172    State {
173        blob: Vec<u8>,
174    },
175    ParamsChanged,
176    Fault {
177        code: u32,
178        text: String,
179    },
180    /// The reply to a request that returns no data (`Configure`, `Activate`,
181    /// `SetState`, `OpenEditor`). It carries the request's number.
182    Ack,
183    /// The editor window was closed by the user.
184    EditorClosed,
185    EditorResized {
186        width: u32,
187        height: u32,
188    },
189    ParameterEdit {
190        id: u32,
191        phase: EditPhase,
192        value: f64,
193    },
194}
195
196impl Message {
197    pub fn hello() -> Self {
198        Self::Hello {
199            magic: MAGIC,
200            major: PROTOCOL_MAJOR,
201            minor: PROTOCOL_MINOR,
202        }
203    }
204
205    /// The wire kind number, for diagnostics.
206    pub fn kind(&self) -> u16 {
207        match self {
208            Self::Sandboxed { .. } => 29,
209            Self::Hello { .. } => 1,
210            Self::Load { .. } => 2,
211            Self::Configure { .. } => 3,
212            Self::Editor(_) => 26,
213            Self::EditorStatus(_) => 27,
214            Self::EditorImage(_) => 28,
215            Self::Prepare(_) => 23,
216            Self::Describe => 24,
217            Self::Features(_) => 25,
218            Self::Activate => 4,
219            Self::SetState { .. } => 5,
220            Self::GetState => 6,
221            Self::OpenEditor => 7,
222            Self::OpenEditorIn { .. } => 30,
223            Self::CloseEditor => 18,
224            Self::ListParameters { .. } => 19,
225            Self::Parameters { .. } => 20,
226            Self::SetParameter { .. } => 21,
227            Self::RequestError { .. } => 22,
228            Self::Shutdown => 8,
229            Self::Scan { .. } => 9,
230            Self::Loaded { .. } => 10,
231            Self::Classes { .. } => 11,
232            Self::LatencyChanged { .. } => 12,
233            Self::State { .. } => 13,
234            Self::ParamsChanged => 14,
235            Self::Fault { .. } => 15,
236            Self::Ack => 16,
237            Self::EditorClosed => 17,
238            Self::EditorResized { .. } => 31,
239            Self::ParameterEdit { .. } => 32,
240        }
241    }
242}
243
244#[derive(Clone, Debug, PartialEq)]
245pub struct Frame {
246    pub flags: u16,
247    pub request: u32,
248    pub message: Message,
249}
250
251#[derive(Clone, Copy, Debug, Eq, PartialEq)]
252pub struct UnknownFrame {
253    pub kind: u16,
254    pub flags: u16,
255    pub request: u32,
256    pub body_bytes: usize,
257}
258
259#[derive(Clone, Debug, PartialEq)]
260pub enum DecodedFrame {
261    Known(Frame),
262    Unknown(UnknownFrame),
263}
264
265#[derive(Debug)]
266pub enum CodecError {
267    Io(io::Error),
268    Truncated,
269    InvalidLength,
270    TooLarge { length: usize, limit: usize },
271    StringTooLong,
272    InvalidUtf8,
273    InvalidValue,
274    WrongMagic,
275    WrongMajor { got: u16, expected: u16 },
276    TrailingBytes,
277}
278
279impl fmt::Display for CodecError {
280    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
281        write!(formatter, "{self:?}")
282    }
283}
284
285impl std::error::Error for CodecError {}
286
287impl From<io::Error> for CodecError {
288    fn from(error: io::Error) -> Self {
289        Self::Io(error)
290    }
291}
292
293pub fn write_frame(writer: &mut impl Write, frame: &Frame) -> Result<(), CodecError> {
294    let bytes = encode_frame(frame)?;
295    writer.write_all(&bytes)?;
296    Ok(())
297}
298
299pub fn encode_frame(frame: &Frame) -> Result<Vec<u8>, CodecError> {
300    let mut body = Vec::new();
301    encode_message(&frame.message, &mut body)?;
302    let length = FRAME_META_BYTES
303        .checked_add(body.len())
304        .ok_or(CodecError::InvalidLength)?;
305    let limit = length_limit(frame.message.kind());
306    if length > limit {
307        return Err(CodecError::TooLarge { length, limit });
308    }
309    let mut output = Vec::with_capacity(4 + length);
310    output.extend_from_slice(&(length as u32).to_le_bytes());
311    output.extend_from_slice(&frame.message.kind().to_le_bytes());
312    output.extend_from_slice(&frame.flags.to_le_bytes());
313    output.extend_from_slice(&frame.request.to_le_bytes());
314    output.extend_from_slice(&body);
315    Ok(output)
316}
317
318pub fn decode_frame(bytes: &[u8]) -> Result<DecodedFrame, CodecError> {
319    if bytes.len() < 12 {
320        return Err(CodecError::Truncated);
321    }
322    let length = u32::from_le_bytes(bytes[0..4].try_into().expect("four bytes")) as usize;
323    if length < FRAME_META_BYTES {
324        return Err(CodecError::InvalidLength);
325    }
326    if bytes.len() < 4 + length {
327        return Err(CodecError::Truncated);
328    }
329    if bytes.len() != 4 + length {
330        return Err(CodecError::TrailingBytes);
331    }
332    decode_parts(&bytes[4..12], &bytes[12..])
333}
334
335/// Reads one frame. Its 8-byte metadata is read first, and the declared length
336/// is rejected before allocating the body. `Ok(None)` is a clean EOF before a
337/// new frame; a partial frame is an error.
338pub fn read_frame(reader: &mut impl Read) -> Result<Option<DecodedFrame>, CodecError> {
339    let mut length_bytes = [0_u8; 4];
340    let mut first = [0_u8; 1];
341    match reader.read(&mut first) {
342        Ok(0) => return Ok(None),
343        Ok(_) => length_bytes[0] = first[0],
344        Err(error) => return Err(CodecError::Io(error)),
345    }
346    reader.read_exact(&mut length_bytes[1..]).map_err(map_eof)?;
347    let length = u32::from_le_bytes(length_bytes) as usize;
348    if length < FRAME_META_BYTES {
349        return Err(CodecError::InvalidLength);
350    }
351    let mut meta = [0_u8; FRAME_META_BYTES];
352    reader.read_exact(&mut meta).map_err(map_eof)?;
353    let kind = u16::from_le_bytes(meta[0..2].try_into().expect("two bytes"));
354    let limit = length_limit(kind);
355    if length > limit {
356        return Err(CodecError::TooLarge { length, limit });
357    }
358    let mut body = vec![0; length - FRAME_META_BYTES];
359    reader.read_exact(&mut body).map_err(map_eof)?;
360    decode_parts(&meta, &body).map(Some)
361}
362
363fn map_eof(error: io::Error) -> CodecError {
364    if error.kind() == io::ErrorKind::UnexpectedEof {
365        CodecError::Truncated
366    } else {
367        CodecError::Io(error)
368    }
369}
370
371fn length_limit(kind: u16) -> usize {
372    if kind == 28 {
373        return 16 * 1024 * 1024 + 16;
374    }
375    if matches!(kind, 5 | 13) {
376        MAX_STATE_BYTES
377    } else {
378        MAX_MESSAGE_BYTES
379    }
380}
381
382fn decode_parts(meta: &[u8], body: &[u8]) -> Result<DecodedFrame, CodecError> {
383    let kind = u16::from_le_bytes(meta[0..2].try_into().expect("two bytes"));
384    let flags = u16::from_le_bytes(meta[2..4].try_into().expect("two bytes"));
385    let request = u32::from_le_bytes(meta[4..8].try_into().expect("four bytes"));
386    let length = FRAME_META_BYTES + body.len();
387    let limit = length_limit(kind);
388    if length > limit {
389        return Err(CodecError::TooLarge { length, limit });
390    }
391    let Some(message) = decode_message(kind, body)? else {
392        return Ok(DecodedFrame::Unknown(UnknownFrame {
393            kind,
394            flags,
395            request,
396            body_bytes: body.len(),
397        }));
398    };
399    Ok(DecodedFrame::Known(Frame {
400        flags,
401        request,
402        message,
403    }))
404}
405
406fn encode_message(message: &Message, out: &mut Vec<u8>) -> Result<(), CodecError> {
407    match message {
408        Message::Sandboxed { mechanism } => put_string(out, mechanism)?,
409        Message::Hello {
410            magic,
411            major,
412            minor,
413        } => {
414            out.extend_from_slice(magic);
415            put_u16(out, *major);
416            put_u16(out, *minor);
417        }
418        Message::Load {
419            path,
420            format,
421            class_id,
422        } => {
423            put_string(out, path)?;
424            put_u16(out, *format as u16);
425            out.push(u8::from(class_id.is_some()));
426            if let Some(id) = class_id {
427                out.extend_from_slice(id);
428            }
429        }
430        Message::Configure {
431            rate,
432            max_frames,
433            in_channels,
434            out_channels,
435        } => {
436            put_u32(out, *rate);
437            put_u32(out, *max_frames);
438            put_u16(out, *in_channels);
439            put_u16(out, *out_channels);
440        }
441        Message::Activate
442        | Message::Describe
443        | Message::GetState
444        | Message::CloseEditor
445        | Message::OpenEditor
446        | Message::Shutdown
447        | Message::ParamsChanged
448        | Message::Ack
449        | Message::EditorClosed => {}
450        Message::SetState { blob } | Message::State { blob } => out.extend_from_slice(blob),
451        Message::Scan { path } => put_string(out, path)?,
452        Message::Loaded {
453            name,
454            vendor,
455            latency_frames,
456            inputs,
457            outputs,
458        } => {
459            put_string(out, name)?;
460            put_string(out, vendor)?;
461            put_u32(out, *latency_frames);
462            put_bus_infos(out, inputs)?;
463            put_bus_infos(out, outputs)?;
464        }
465        Message::Classes { entries } => {
466            put_u16(
467                out,
468                u16::try_from(entries.len()).map_err(|_| CodecError::InvalidValue)?,
469            );
470            for entry in entries {
471                out.extend_from_slice(&entry.id);
472                put_string(out, &entry.name)?;
473                put_string(out, &entry.vendor)?;
474                put_string(out, &entry.category)?;
475            }
476        }
477        Message::LatencyChanged { frames } => put_u32(out, *frames),
478        Message::EditorResized { width, height } => {
479            if !(1..=8192).contains(width) || !(1..=8192).contains(height) {
480                return Err(CodecError::InvalidValue);
481            }
482            put_u32(out, *width);
483            put_u32(out, *height);
484        }
485        Message::Editor(action) => match action {
486            EditorAction::Show => out.push(0),
487            EditorAction::Hide => out.push(1),
488            EditorAction::Focus => out.push(2),
489            EditorAction::Resize { width, height } => {
490                out.push(3);
491                put_u32(out, *width);
492                put_u32(out, *height);
493            }
494            EditorAction::Scale(scale) => {
495                out.push(4);
496                put_u32(out, scale.to_bits());
497            }
498            EditorAction::Inspect => out.push(5),
499            EditorAction::Capture => out.push(6),
500        },
501        Message::OpenEditorIn { parent } => {
502            let (tag, handle) = match parent {
503                ParentWindow::Win32 { hwnd } => (0, hwnd),
504                ParentWindow::X11 { window } => (1, window),
505            };
506            out.push(tag);
507            put_u64(out, *handle);
508        }
509        Message::EditorStatus(status) => {
510            out.push(status.platform as u8);
511            out.push(u8::from(status.visible));
512            out.push(u8::from(status.embedded));
513            put_u32(out, status.width);
514            put_u32(out, status.height);
515            put_u32(out, status.scale.to_bits());
516            out.push(u8::from(status.resize_supported));
517            out.push(u8::from(status.scale_supported));
518        }
519        Message::EditorImage(image) => {
520            if !image.valid() {
521                return Err(CodecError::InvalidValue);
522            }
523            put_u32(out, image.width);
524            put_u32(out, image.height);
525            out.extend_from_slice(&image.rgba);
526        }
527        Message::Prepare(setup) => {
528            if !setup.valid() {
529                return Err(CodecError::InvalidValue);
530            }
531            put_u32(out, setup.sample_rate);
532            put_u32(out, setup.quantum);
533            out.push(setup.sample_format as u8);
534            out.push(u8::from(setup.offline));
535            for buses in [&setup.inputs, &setup.outputs] {
536                out.push(buses.len() as u8);
537                for bus in buses {
538                    put_u16(out, bus.layout.channels);
539                    put_u64(out, bus.layout.arrangement);
540                    out.push(u8::from(bus.active));
541                }
542            }
543        }
544        Message::Features(caps) => {
545            out.push(u8::from(caps.f64));
546            put_u16(out, caps.input_event_buses);
547            put_u16(out, caps.output_event_buses);
548            out.push(u8::from(caps.editor));
549            out.push(u8::from(caps.sysex));
550            out.push(u8::from(caps.sample_accurate_parameters));
551        }
552        Message::ListParameters { start, count } => {
553            if *count == 0 || *count > 32 {
554                return Err(CodecError::InvalidValue);
555            }
556            put_u32(out, *start);
557            put_u16(out, *count);
558        }
559        Message::SetParameter { id, value } => {
560            if !value.is_finite() || !(0.0..=1.0).contains(value) {
561                return Err(CodecError::InvalidValue);
562            }
563            put_u32(out, *id);
564            put_u64(out, value.to_bits());
565        }
566        Message::ParameterEdit { id, phase, value } => {
567            if !value.is_finite() || !(0.0..=1.0).contains(value) {
568                return Err(CodecError::InvalidValue);
569            }
570            put_u32(out, *id);
571            out.push(match phase {
572                EditPhase::Begin => 0,
573                EditPhase::Perform => 1,
574                EditPhase::End => 2,
575            });
576            put_u64(out, value.to_bits());
577        }
578        Message::Parameters { total, entries } => {
579            if entries.len() > 32 || *total > 16384 {
580                return Err(CodecError::InvalidValue);
581            }
582            put_u32(out, *total);
583            put_u16(out, entries.len() as u16);
584            for entry in entries {
585                put_u32(out, entry.id);
586                out.push(
587                    u8::from(entry.automatable)
588                        | (u8::from(entry.read_only) << 1)
589                        | (u8::from(entry.hidden) << 2),
590                );
591                put_string(out, &entry.name)?;
592                put_string(out, &entry.unit)?;
593                put_u64(out, entry.value.to_bits());
594                out.push(u8::from(entry.default.is_some()));
595                if let Some(value) = entry.default {
596                    put_u64(out, value.to_bits());
597                }
598                out.push(u8::from(entry.steps.is_some()));
599                if let Some(steps) = entry.steps {
600                    put_u32(out, steps);
601                }
602            }
603        }
604        Message::RequestError { code, text } | Message::Fault { code, text } => {
605            put_u32(out, *code);
606            put_string(out, text)?;
607        }
608    }
609    Ok(())
610}
611
612fn decode_message(kind: u16, body: &[u8]) -> Result<Option<Message>, CodecError> {
613    let mut cursor = Cursor::new(body);
614    let message = match kind {
615        1 => {
616            let magic = cursor.array::<4>()?;
617            let major = cursor.u16()?;
618            let minor = cursor.u16()?;
619            if magic != MAGIC {
620                return Err(CodecError::WrongMagic);
621            }
622            if major != PROTOCOL_MAJOR {
623                return Err(CodecError::WrongMajor {
624                    got: major,
625                    expected: PROTOCOL_MAJOR,
626                });
627            }
628            Message::Hello {
629                magic,
630                major,
631                minor,
632            }
633        }
634        29 => Message::Sandboxed {
635            mechanism: cursor.string()?,
636        },
637        2 => {
638            let path = cursor.string()?;
639            let format = match cursor.u16()? {
640                2 => PluginFormat::Vst2,
641                3 => PluginFormat::Vst3,
642                _ => return Err(CodecError::InvalidValue),
643            };
644            let class_id = match cursor.u8()? {
645                0 => None,
646                1 => Some(cursor.array::<16>()?),
647                _ => return Err(CodecError::InvalidValue),
648            };
649            Message::Load {
650                path,
651                format,
652                class_id,
653            }
654        }
655        3 => Message::Configure {
656            rate: cursor.u32()?,
657            max_frames: cursor.u32()?,
658            in_channels: cursor.u16()?,
659            out_channels: cursor.u16()?,
660        },
661        4 => Message::Activate,
662        5 => Message::SetState {
663            blob: body.to_vec(),
664        },
665        6 => Message::GetState,
666        7 => Message::OpenEditor,
667        8 => Message::Shutdown,
668        9 => Message::Scan {
669            path: cursor.string()?,
670        },
671        10 => Message::Loaded {
672            name: cursor.string()?,
673            vendor: cursor.string()?,
674            latency_frames: cursor.u32()?,
675            inputs: cursor.bus_infos()?,
676            outputs: cursor.bus_infos()?,
677        },
678        11 => {
679            let count = cursor.u16()? as usize;
680            let mut entries = Vec::with_capacity(count);
681            for _ in 0..count {
682                entries.push(ClassInfo {
683                    id: cursor.array::<16>()?,
684                    name: cursor.string()?,
685                    vendor: cursor.string()?,
686                    category: cursor.string()?,
687                });
688            }
689            Message::Classes { entries }
690        }
691        12 => Message::LatencyChanged {
692            frames: cursor.u32()?,
693        },
694        13 => Message::State {
695            blob: body.to_vec(),
696        },
697        14 => Message::ParamsChanged,
698        15 => Message::Fault {
699            code: cursor.u32()?,
700            text: cursor.string()?,
701        },
702        16 => Message::Ack,
703        17 => Message::EditorClosed,
704        31 => {
705            let width = cursor.u32()?;
706            let height = cursor.u32()?;
707            if !(1..=8192).contains(&width) || !(1..=8192).contains(&height) {
708                return Err(CodecError::InvalidValue);
709            }
710            Message::EditorResized { width, height }
711        }
712        18 => Message::CloseEditor,
713        26 => Message::Editor(match cursor.u8()? {
714            0 => EditorAction::Show,
715            1 => EditorAction::Hide,
716            2 => EditorAction::Focus,
717            3 => EditorAction::Resize {
718                width: cursor.u32()?,
719                height: cursor.u32()?,
720            },
721            4 => EditorAction::Scale(f32::from_bits(cursor.u32()?)),
722            5 => EditorAction::Inspect,
723            6 => EditorAction::Capture,
724            _ => return Err(CodecError::InvalidValue),
725        }),
726        30 => Message::OpenEditorIn {
727            parent: match cursor.u8()? {
728                0 => ParentWindow::Win32 {
729                    hwnd: cursor.u64()?,
730                },
731                1 => ParentWindow::X11 {
732                    window: cursor.u64()?,
733                },
734                _ => return Err(CodecError::InvalidValue),
735            },
736        },
737        27 => Message::EditorStatus(EditorStatus {
738            platform: match cursor.u8()? {
739                0 => EditorPlatform::Win32,
740                1 => EditorPlatform::Cocoa,
741                2 => EditorPlatform::X11,
742                3 => EditorPlatform::Wayland,
743                _ => return Err(CodecError::InvalidValue),
744            },
745            visible: cursor.boolean()?,
746            embedded: cursor.boolean()?,
747            width: cursor.u32()?,
748            height: cursor.u32()?,
749            scale: f32::from_bits(cursor.u32()?),
750            resize_supported: cursor.boolean()?,
751            scale_supported: cursor.boolean()?,
752        }),
753        28 => {
754            let width = cursor.u32()?;
755            let height = cursor.u32()?;
756            if width == 0
757                || height == 0
758                || width > 2048
759                || height > 2048
760                || cursor.remaining() != width as usize * height as usize * 4
761            {
762                return Err(CodecError::InvalidValue);
763            }
764            let rgba = cursor.take(cursor.remaining())?.to_vec();
765            Message::EditorImage(EditorImage {
766                width,
767                height,
768                rgba,
769            })
770        }
771        23 => {
772            let sample_rate = cursor.u32()?;
773            let quantum = cursor.u32()?;
774            let sample_format = match cursor.u8()? {
775                0 => SampleFormat::F32,
776                1 => SampleFormat::F64,
777                _ => return Err(CodecError::InvalidValue),
778            };
779            let offline = cursor.boolean()?;
780            let mut lists = Vec::new();
781            for _ in 0..2 {
782                let count = cursor.u8()?;
783                if count > 32 {
784                    return Err(CodecError::InvalidValue);
785                }
786                let mut buses = Vec::with_capacity(count as usize);
787                for _ in 0..count {
788                    buses.push(BusConfiguration {
789                        layout: Bus {
790                            channels: cursor.u16()?,
791                            arrangement: cursor.u64()?,
792                        },
793                        active: cursor.boolean()?,
794                    });
795                }
796                lists.push(buses);
797            }
798            let outputs = lists.pop().expect("two lists");
799            let inputs = lists.pop().expect("two lists");
800            let setup = ProcessingSetup {
801                sample_rate,
802                quantum,
803                sample_format,
804                offline,
805                inputs,
806                outputs,
807            };
808            if !setup.valid() {
809                return Err(CodecError::InvalidValue);
810            }
811            Message::Prepare(setup)
812        }
813        24 => Message::Describe,
814        25 => Message::Features(Capabilities {
815            f64: cursor.boolean()?,
816            input_event_buses: cursor.u16()?,
817            output_event_buses: cursor.u16()?,
818            editor: cursor.boolean()?,
819            sysex: cursor.boolean()?,
820            sample_accurate_parameters: cursor.boolean()?,
821        }),
822        19 => {
823            let start = cursor.u32()?;
824            let count = cursor.u16()?;
825            if count == 0 || count > 32 {
826                return Err(CodecError::InvalidValue);
827            }
828            Message::ListParameters { start, count }
829        }
830        20 => {
831            let total = cursor.u32()?;
832            let count = cursor.u16()?;
833            if total > 16384 || count > 32 {
834                return Err(CodecError::InvalidValue);
835            }
836            let mut entries = Vec::with_capacity(count as usize);
837            for _ in 0..count {
838                let id = cursor.u32()?;
839                let flags = cursor.u8()?;
840                if flags & !7 != 0 {
841                    return Err(CodecError::InvalidValue);
842                }
843                let name = cursor.string()?;
844                let unit = cursor.string()?;
845                let value = cursor.normalized()?;
846                let default = match cursor.u8()? {
847                    0 => None,
848                    1 => Some(cursor.normalized()?),
849                    _ => return Err(CodecError::InvalidValue),
850                };
851                let steps = match cursor.u8()? {
852                    0 => None,
853                    1 => Some(cursor.u32()?),
854                    _ => return Err(CodecError::InvalidValue),
855                };
856                entries.push(Parameter {
857                    automatable: flags & 1 != 0,
858                    read_only: flags & 2 != 0,
859                    hidden: flags & 4 != 0,
860                    id,
861                    name,
862                    unit,
863                    value,
864                    default,
865                    steps,
866                });
867            }
868            Message::Parameters { total, entries }
869        }
870        21 => Message::SetParameter {
871            id: cursor.u32()?,
872            value: cursor.normalized()?,
873        },
874        22 => Message::RequestError {
875            code: cursor.u32()?,
876            text: cursor.string()?,
877        },
878        32 => Message::ParameterEdit {
879            id: cursor.u32()?,
880            phase: match cursor.u8()? {
881                0 => EditPhase::Begin,
882                1 => EditPhase::Perform,
883                2 => EditPhase::End,
884                _ => return Err(CodecError::InvalidValue),
885            },
886            value: cursor.normalized()?,
887        },
888        _ => return Ok(None),
889    };
890    if !matches!(kind, 5 | 13) && cursor.remaining() != 0 {
891        return Err(CodecError::TrailingBytes);
892    }
893    Ok(Some(message))
894}
895
896fn put_u16(out: &mut Vec<u8>, value: u16) {
897    out.extend_from_slice(&value.to_le_bytes());
898}
899fn put_u32(out: &mut Vec<u8>, value: u32) {
900    out.extend_from_slice(&value.to_le_bytes());
901}
902fn put_u64(out: &mut Vec<u8>, value: u64) {
903    out.extend_from_slice(&value.to_le_bytes());
904}
905
906fn put_string(out: &mut Vec<u8>, value: &str) -> Result<(), CodecError> {
907    if value.len() > MAX_STRING_BYTES {
908        return Err(CodecError::StringTooLong);
909    }
910    put_u16(out, value.len() as u16);
911    out.extend_from_slice(value.as_bytes());
912    Ok(())
913}
914
915fn put_bus_infos(out: &mut Vec<u8>, buses: &[BusInfo]) -> Result<(), CodecError> {
916    put_u16(
917        out,
918        u16::try_from(buses.len()).map_err(|_| CodecError::InvalidValue)?,
919    );
920    for bus in buses {
921        put_u16(out, bus.layout.channels);
922        put_u64(out, bus.layout.arrangement);
923        out.push(match bus.kind {
924            BusKind::Main => 0,
925            BusKind::Aux => 1,
926        });
927        out.push(u8::from(bus.default_active));
928        put_string(out, &bus.name)?;
929    }
930    Ok(())
931}
932
933struct Cursor<'a> {
934    bytes: &'a [u8],
935    position: usize,
936}
937
938impl<'a> Cursor<'a> {
939    fn new(bytes: &'a [u8]) -> Self {
940        Self { bytes, position: 0 }
941    }
942    fn remaining(&self) -> usize {
943        self.bytes.len() - self.position
944    }
945    fn take(&mut self, count: usize) -> Result<&'a [u8], CodecError> {
946        let end = self
947            .position
948            .checked_add(count)
949            .ok_or(CodecError::Truncated)?;
950        let value = self
951            .bytes
952            .get(self.position..end)
953            .ok_or(CodecError::Truncated)?;
954        self.position = end;
955        Ok(value)
956    }
957    fn array<const N: usize>(&mut self) -> Result<[u8; N], CodecError> {
958        Ok(self.take(N)?.try_into().expect("exact length"))
959    }
960    fn boolean(&mut self) -> Result<bool, CodecError> {
961        match self.u8()? {
962            0 => Ok(false),
963            1 => Ok(true),
964            _ => Err(CodecError::InvalidValue),
965        }
966    }
967    fn u8(&mut self) -> Result<u8, CodecError> {
968        Ok(self.take(1)?[0])
969    }
970    fn u16(&mut self) -> Result<u16, CodecError> {
971        Ok(u16::from_le_bytes(self.array()?))
972    }
973    fn u32(&mut self) -> Result<u32, CodecError> {
974        Ok(u32::from_le_bytes(self.array()?))
975    }
976    fn u64(&mut self) -> Result<u64, CodecError> {
977        Ok(u64::from_le_bytes(self.array()?))
978    }
979    fn normalized(&mut self) -> Result<f64, CodecError> {
980        let value = f64::from_bits(self.u64()?);
981        if value.is_finite() && (0.0..=1.0).contains(&value) {
982            Ok(value)
983        } else {
984            Err(CodecError::InvalidValue)
985        }
986    }
987    fn string(&mut self) -> Result<String, CodecError> {
988        let length = self.u16()? as usize;
989        if length > MAX_STRING_BYTES {
990            return Err(CodecError::StringTooLong);
991        }
992        String::from_utf8(self.take(length)?.to_vec()).map_err(|_| CodecError::InvalidUtf8)
993    }
994    fn bus_infos(&mut self) -> Result<Vec<BusInfo>, CodecError> {
995        let count = self.u16()? as usize;
996        let mut buses = Vec::with_capacity(count);
997        for _ in 0..count {
998            let layout = Bus {
999                channels: self.u16()?,
1000                arrangement: self.u64()?,
1001            };
1002            let kind = match self.u8()? {
1003                0 => BusKind::Main,
1004                1 => BusKind::Aux,
1005                _ => return Err(CodecError::InvalidValue),
1006            };
1007            let flags = self.u8()?;
1008            if flags & !1 != 0 {
1009                return Err(CodecError::InvalidValue);
1010            }
1011            buses.push(BusInfo {
1012                layout,
1013                kind,
1014                name: self.string()?,
1015                default_active: flags & 1 != 0,
1016            });
1017        }
1018        Ok(buses)
1019    }
1020}
1021
1022#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1023pub enum WatchState {
1024    Starting,
1025    Loading,
1026    Configuring,
1027    Running,
1028    Stopping,
1029    /// Stopped on request; it is never restarted.
1030    Stopped,
1031    Backoff,
1032    Disabled,
1033}
1034
1035#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1036pub enum KillReason {
1037    HelloTimeout,
1038    LoadTimeout,
1039    ConfigureTimeout,
1040    HeartbeatTimeout,
1041    RequestTimeout,
1042    StopTimeout,
1043}
1044
1045#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1046pub enum DisableReason {
1047    ThreeFailuresInTenMinutes,
1048}
1049
1050/// What the caller does next. `Kill`, a crash's `EnterFallback` and `Disable`
1051/// all leave the engine on the fallback path until a later `LeaveFallback`.
1052/// `Disable` also ends the process if it is still alive (the third failure can
1053/// be a hang).
1054#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1055pub enum Action {
1056    Kill { reason: KillReason },
1057    Spawn,
1058    Disable { reason: DisableReason },
1059    EnterFallback,
1060    LeaveFallback,
1061    SaveStateNow,
1062    None,
1063}
1064
1065#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1066pub enum Received {
1067    Hello,
1068    Loaded,
1069    Configured,
1070    Response { request: u32 },
1071    ParamsChanged,
1072    EditorClosed,
1073}
1074
1075#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1076pub enum RequestKind {
1077    StateSave,
1078    Editor,
1079}
1080
1081const STARTING_MS: u64 = 5_000;
1082const LOADING_MS: u64 = 60_000;
1083const CONFIGURING_MS: u64 = 10_000;
1084const HEARTBEAT_MS: u64 = 500;
1085/// A longer gap between two heartbeat observations means the host itself (or
1086/// the whole machine) was not running, so it is not counted as silence.
1087const OBSERVER_GAP_MS: u64 = HEARTBEAT_MS / 2;
1088const REQUEST_MS: u64 = 10_000;
1089const STOPPING_MS: u64 = 2_000;
1090const PARAM_QUIET_MS: u64 = 2_000;
1091const FAILURE_WINDOW_MS: u64 = 10 * 60 * 1_000;
1092const BACKOFF_MS: [u64; 3] = [500, 2_000, 8_000];
1093
1094#[derive(Debug)]
1095pub struct HostWatch {
1096    state: WatchState,
1097    entered_at: u64,
1098    heartbeat_value: u64,
1099    heartbeat_at: u64,
1100    observed_at: u64,
1101    pending_requests: Vec<(u32, u64)>,
1102    params_changed_at: Option<u64>,
1103    missed_blocks: u8,
1104    fallback: bool,
1105    failures: VecDeque<u64>,
1106    backoff_attempt: usize,
1107    spawn_at: Option<u64>,
1108}
1109
1110impl HostWatch {
1111    pub fn new(now_ms: u64) -> Self {
1112        Self {
1113            state: WatchState::Starting,
1114            entered_at: now_ms,
1115            heartbeat_value: 0,
1116            heartbeat_at: now_ms,
1117            observed_at: now_ms,
1118            pending_requests: Vec::new(),
1119            params_changed_at: None,
1120            missed_blocks: 0,
1121            fallback: false,
1122            failures: VecDeque::with_capacity(3),
1123            backoff_attempt: 0,
1124            spawn_at: None,
1125        }
1126    }
1127
1128    pub fn state(&self) -> WatchState {
1129        self.state
1130    }
1131
1132    pub fn received(&mut self, now_ms: u64, message: Received) -> Action {
1133        match message {
1134            Received::Hello if self.state == WatchState::Starting => {
1135                self.enter(WatchState::Loading, now_ms)
1136            }
1137            Received::Loaded if self.state == WatchState::Loading => {
1138                self.enter(WatchState::Configuring, now_ms)
1139            }
1140            Received::Configured if self.state == WatchState::Configuring => {
1141                self.enter(WatchState::Running, now_ms);
1142                self.heartbeat_at = now_ms;
1143                self.observed_at = now_ms;
1144                return Action::SaveStateNow;
1145            }
1146            Received::Response { request } => {
1147                self.pending_requests.retain(|(id, _)| *id != request);
1148            }
1149            Received::ParamsChanged => self.params_changed_at = Some(now_ms),
1150            Received::EditorClosed => return Action::SaveStateNow,
1151            _ => {}
1152        }
1153        Action::None
1154    }
1155
1156    /// Silence is counted only across time the host was observing: when the
1157    /// host polls late, the worker may have been frozen together with it
1158    /// (a stalled machine), which is no evidence that the worker stopped.
1159    pub fn heartbeat(&mut self, now_ms: u64, value: u64) -> Action {
1160        let gap = now_ms.saturating_sub(self.observed_at);
1161        if gap > OBSERVER_GAP_MS {
1162            self.heartbeat_at = self.heartbeat_at.saturating_add(gap).min(now_ms);
1163        }
1164        self.observed_at = now_ms;
1165        if value != self.heartbeat_value {
1166            self.heartbeat_value = value;
1167            self.heartbeat_at = now_ms;
1168        }
1169        Action::None
1170    }
1171
1172    pub fn request_sent(&mut self, now_ms: u64, request: u32, _kind: RequestKind) -> Action {
1173        self.pending_requests.retain(|(id, _)| *id != request);
1174        self.pending_requests.push((request, now_ms));
1175        Action::None
1176    }
1177
1178    pub fn block_delivered(&mut self) -> Action {
1179        self.missed_blocks = 0;
1180        if self.fallback {
1181            self.fallback = false;
1182            Action::LeaveFallback
1183        } else {
1184            Action::None
1185        }
1186    }
1187
1188    pub fn block_missed(&mut self) -> Action {
1189        self.missed_blocks = self.missed_blocks.saturating_add(1);
1190        if self.missed_blocks == 3 && !self.fallback {
1191            self.fallback = true;
1192            Action::EnterFallback
1193        } else {
1194            Action::None
1195        }
1196    }
1197
1198    pub fn shutdown(&mut self, now_ms: u64) -> Action {
1199        self.enter(WatchState::Stopping, now_ms);
1200        Action::SaveStateNow
1201    }
1202
1203    pub fn process_exited(&mut self, now_ms: u64, _exit_code: i32) -> Action {
1204        match self.state {
1205            WatchState::Backoff | WatchState::Disabled | WatchState::Stopped => Action::None,
1206            WatchState::Stopping => {
1207                self.enter(WatchState::Stopped, now_ms);
1208                Action::None
1209            }
1210            _ => self.failure(now_ms, None),
1211        }
1212    }
1213
1214    pub fn tick(&mut self, now_ms: u64) -> Action {
1215        if self.state == WatchState::Backoff {
1216            if self.spawn_at.is_some_and(|deadline| now_ms >= deadline) {
1217                self.enter(WatchState::Starting, now_ms);
1218                self.spawn_at = None;
1219                return Action::Spawn;
1220            }
1221            return Action::None;
1222        }
1223        if matches!(self.state, WatchState::Disabled | WatchState::Stopped) {
1224            return Action::None;
1225        }
1226        if self.state == WatchState::Running {
1227            if now_ms.saturating_sub(self.heartbeat_at) >= HEARTBEAT_MS {
1228                return self.failure(now_ms, Some(KillReason::HeartbeatTimeout));
1229            }
1230            if self
1231                .pending_requests
1232                .iter()
1233                .any(|(_, sent)| now_ms.saturating_sub(*sent) >= REQUEST_MS)
1234            {
1235                return self.failure(now_ms, Some(KillReason::RequestTimeout));
1236            }
1237            if self
1238                .params_changed_at
1239                .is_some_and(|changed| now_ms.saturating_sub(changed) >= PARAM_QUIET_MS)
1240            {
1241                self.params_changed_at = None;
1242                return Action::SaveStateNow;
1243            }
1244            return Action::None;
1245        }
1246        let (deadline, reason) = match self.state {
1247            WatchState::Starting => (STARTING_MS, KillReason::HelloTimeout),
1248            WatchState::Loading => (LOADING_MS, KillReason::LoadTimeout),
1249            WatchState::Configuring => (CONFIGURING_MS, KillReason::ConfigureTimeout),
1250            WatchState::Stopping => {
1251                // A host that will not stop is killed; it was leaving anyway,
1252                // so it is not counted as a failure and is not restarted.
1253                if now_ms.saturating_sub(self.entered_at) >= STOPPING_MS {
1254                    self.enter(WatchState::Stopped, now_ms);
1255                    return Action::Kill {
1256                        reason: KillReason::StopTimeout,
1257                    };
1258                }
1259                return Action::None;
1260            }
1261            _ => return Action::None,
1262        };
1263        if now_ms.saturating_sub(self.entered_at) >= deadline {
1264            self.failure(now_ms, Some(reason))
1265        } else {
1266            Action::None
1267        }
1268    }
1269
1270    fn enter(&mut self, state: WatchState, now_ms: u64) {
1271        self.state = state;
1272        self.entered_at = now_ms;
1273    }
1274
1275    fn failure(&mut self, now_ms: u64, kill: Option<KillReason>) -> Action {
1276        while self
1277            .failures
1278            .front()
1279            .is_some_and(|time| now_ms.saturating_sub(*time) > FAILURE_WINDOW_MS)
1280        {
1281            self.failures.pop_front();
1282        }
1283        self.failures.push_back(now_ms);
1284        if self.failures.len() >= 3 {
1285            self.state = WatchState::Disabled;
1286            self.pending_requests.clear();
1287            self.fallback = true;
1288            return Action::Disable {
1289                reason: DisableReason::ThreeFailuresInTenMinutes,
1290            };
1291        }
1292        // The wait grows with each failure and starts again at 0.5 s once no
1293        // other failure is left inside the window.
1294        if self.failures.len() == 1 {
1295            self.backoff_attempt = 0;
1296        }
1297        let delay = BACKOFF_MS[self.backoff_attempt.min(BACKOFF_MS.len() - 1)];
1298        self.backoff_attempt = (self.backoff_attempt + 1).min(BACKOFF_MS.len() - 1);
1299        self.state = WatchState::Backoff;
1300        self.entered_at = now_ms;
1301        self.spawn_at = Some(now_ms.saturating_add(delay));
1302        self.pending_requests.clear();
1303        self.params_changed_at = None;
1304        self.missed_blocks = 0;
1305        self.fallback = true;
1306        if let Some(reason) = kill {
1307            Action::Kill { reason }
1308        } else {
1309            Action::EnterFallback
1310        }
1311    }
1312}
1313
1314/// Two committed state blobs. A failed restore of the newest blob selects the
1315/// previous blob without destroying either copy.
1316#[derive(Clone, Debug, Default, Eq, PartialEq)]
1317pub struct StateHistory {
1318    current: Option<Vec<u8>>,
1319    previous: Option<Vec<u8>>,
1320    restoring_current: bool,
1321}
1322
1323impl StateHistory {
1324    pub fn new() -> Self {
1325        Self::default()
1326    }
1327    pub fn save(&mut self, blob: Vec<u8>) {
1328        if self.current.as_ref() == Some(&blob) {
1329            return;
1330        }
1331        self.previous = self.current.replace(blob);
1332        self.restoring_current = false;
1333    }
1334    pub fn begin_restore(&mut self) -> Option<&[u8]> {
1335        self.restoring_current = self.current.is_some();
1336        self.current.as_deref()
1337    }
1338    pub fn restore_succeeded(&mut self) {
1339        self.restoring_current = false;
1340    }
1341    pub fn crash_after_restore(&mut self) -> Option<&[u8]> {
1342        if self.restoring_current {
1343            self.restoring_current = false;
1344            self.previous.as_deref()
1345        } else {
1346            self.current.as_deref()
1347        }
1348    }
1349    pub fn current(&self) -> Option<&[u8]> {
1350        self.current.as_deref()
1351    }
1352    pub fn previous(&self) -> Option<&[u8]> {
1353        self.previous.as_deref()
1354    }
1355}
1356
1357pub fn duration_ms(duration: Duration) -> u64 {
1358    u64::try_from(duration.as_millis()).unwrap_or(u64::MAX)
1359}