1#![forbid(unsafe_code)]
4use std::{env, fs, path::Path};
5use vst_audio_protocol::{
6 Capabilities, DecodedFrame, EditPhase, EditorAction, EditorImage, EditorPlatform,
7 EditorScaling, EditorStatus, Frame, Message, ParentWindow, PluginFormat, ProcessingSetup,
8 SampleFormat, decode_frame, encode_frame,
9};
10
11fn messages() -> Vec<(&'static str, Message)> {
12 vec![
13 ("hello", Message::hello()),
14 (
15 "load",
16 Message::Load {
17 path: "plugin.vst3".into(),
18 format: PluginFormat::Vst3,
19 class_id: None,
20 },
21 ),
22 (
23 "configure",
24 Message::Configure {
25 rate: 48_000,
26 max_frames: 512,
27 in_channels: 2,
28 out_channels: 2,
29 },
30 ),
31 ("activate", Message::Activate),
32 (
33 "set-state",
34 Message::SetState {
35 blob: b"state".to_vec(),
36 },
37 ),
38 ("get-state", Message::GetState),
39 (
40 "open-editor",
41 Message::OpenEditor {
42 scaling: EditorScaling::Auto,
43 },
44 ),
45 ("shutdown", Message::Shutdown),
46 (
47 "scan",
48 Message::Scan {
49 path: "plugin.vst3".into(),
50 },
51 ),
52 (
53 "loaded",
54 Message::Loaded {
55 name: "plugin".into(),
56 vendor: "vendor".into(),
57 latency_frames: 0,
58 inputs: vec![],
59 outputs: vec![],
60 },
61 ),
62 ("classes", Message::Classes { entries: vec![] }),
63 ("latency-changed", Message::LatencyChanged { frames: 64 }),
64 (
65 "state",
66 Message::State {
67 blob: b"state".to_vec(),
68 },
69 ),
70 ("params-changed", Message::ParamsChanged),
71 (
72 "fault",
73 Message::Fault {
74 code: 1,
75 text: "fault".into(),
76 },
77 ),
78 ("ack", Message::Ack),
79 ("editor-closed", Message::EditorClosed),
80 ("close-editor", Message::CloseEditor),
81 (
82 "list-parameters",
83 Message::ListParameters { start: 0, count: 1 },
84 ),
85 (
86 "parameters",
87 Message::Parameters {
88 total: 0,
89 entries: vec![],
90 },
91 ),
92 ("set-parameter", Message::SetParameter { id: 1, value: 0.5 }),
93 (
94 "request-error",
95 Message::RequestError {
96 code: 1,
97 text: "error".into(),
98 },
99 ),
100 (
101 "prepare",
102 Message::Prepare(ProcessingSetup {
103 sample_rate: 48_000,
104 quantum: 512,
105 sample_format: SampleFormat::F32,
106 offline: false,
107 inputs: vec![],
108 outputs: vec![],
109 }),
110 ),
111 ("describe", Message::Describe),
112 ("features", Message::Features(Capabilities::default())),
113 ("editor", Message::Editor(EditorAction::Show)),
114 (
115 "editor-status",
116 Message::EditorStatus(EditorStatus {
117 platform: EditorPlatform::X11,
118 scaling: EditorScaling::Auto,
119 visible: true,
120 embedded: false,
121 width: 640,
122 height: 480,
123 scale: 1.0,
124 resize_supported: true,
125 scale_supported: true,
126 }),
127 ),
128 (
129 "editor-image",
130 Message::EditorImage(EditorImage {
131 width: 1,
132 height: 1,
133 rgba: vec![32, 64, 128, 255],
134 }),
135 ),
136 (
137 "sandboxed",
138 Message::Sandboxed {
139 mechanism: "test".into(),
140 },
141 ),
142 (
143 "open-editor-in",
144 Message::OpenEditorIn {
145 parent: ParentWindow::X11 { window: 1 },
146 scaling: EditorScaling::Auto,
147 },
148 ),
149 (
150 "editor-resized",
151 Message::EditorResized {
152 width: 640,
153 height: 480,
154 },
155 ),
156 (
157 "parameter-edit",
158 Message::ParameterEdit {
159 id: 1,
160 phase: EditPhase::Perform,
161 value: 0.5,
162 },
163 ),
164 ]
165}
166
167fn verify(directory: &Path) {
168 let mut kinds = Vec::new();
169 for entry in fs::read_dir(directory)
170 .expect("read corpus")
171 .map(Result::unwrap)
172 {
173 let path = entry.path();
174 if path.extension().and_then(|value| value.to_str()) != Some("bin") {
175 continue;
176 }
177 let bytes = fs::read(&path).expect("read seed");
178 let DecodedFrame::Known(frame) = decode_frame(&bytes).expect("seed must decode") else {
179 panic!("{} decoded as Unknown", path.display());
180 };
181 assert_eq!(
182 encode_frame(&frame).expect("re-encode seed"),
183 bytes,
184 "{}",
185 path.display()
186 );
187 kinds.push(frame.message.kind());
188 }
189 kinds.sort_unstable();
190 assert_eq!(kinds, (1_u16..=32).collect::<Vec<_>>());
191 println!(
192 "verified {} Known control seeds (kinds 1..=32)",
193 kinds.len()
194 );
195}
196
197fn main() {
198 let mut args = env::args_os().skip(1);
199 let mode = args.next().expect("mode");
200 let directory = args.next().expect("directory");
201 assert!(args.next().is_none(), "unexpected argument");
202 let directory = Path::new(&directory);
203 if mode == "generate" {
204 fs::create_dir_all(directory).expect("create output");
205 let messages = messages();
206 assert_eq!(messages.len(), 32);
207 for (expected_kind, (name, message)) in (1_u16..=32).zip(messages) {
208 assert_eq!(message.kind(), expected_kind, "{name}");
209 let bytes = encode_frame(&Frame {
210 flags: 0,
211 request: u32::from(expected_kind),
212 message,
213 })
214 .expect("encode seed");
215 assert!(
216 matches!(decode_frame(&bytes), Ok(DecodedFrame::Known(_))),
217 "{name}"
218 );
219 fs::write(
220 directory.join(format!("known-{expected_kind:02}-{name}.bin")),
221 bytes,
222 )
223 .expect("write seed");
224 }
225 } else if mode != "verify" {
226 panic!("mode must be generate or verify");
227 }
228 verify(directory);
229}