1use crate::protocol::*;
5use base64::Engine;
6use std::fs::File;
7use std::io::{Read, Write};
8use std::path::Path;
9
10#[derive(Debug, Clone)]
12pub struct HostError {
13 pub code: String,
16 pub message: String,
17}
18
19impl HostError {
20 pub fn new(code: &str, message: impl Into<String>) -> Self {
21 HostError { code: code.into(), message: message.into() }
22 }
23}
24
25pub type HostResult<T> = Result<T, HostError>;
26
27#[derive(Debug, Clone, Copy)]
29pub struct Activation {
30 pub latency: u32,
31 pub tail: Option<u32>,
32}
33
34pub trait Backend {
36 fn host_name(&self) -> &'static str;
38 fn formats(&self) -> Vec<String>;
39 fn probe(&mut self, path: &Path) -> HostResult<Vec<Descriptor>>;
40 fn load(&mut self, path: &Path, plugin_id: Option<&str>) -> HostResult<PluginInfo>;
41 fn get_params(&mut self) -> HostResult<Vec<ParamValue>>;
42 fn set_params(&mut self, params: &[ParamAssign]) -> HostResult<Vec<ParamValue>>;
43 fn list_presets(&mut self) -> HostResult<Vec<PresetInfo>>;
44 fn load_preset(&mut self, preset: &str) -> HostResult<Vec<ParamValue>>;
45 fn get_state(&mut self) -> HostResult<Vec<u8>>;
46 fn set_state(&mut self, state: &[u8]) -> HostResult<()>;
47 fn activate(&mut self, sample_rate: f64, block_size: u32) -> HostResult<Activation>;
50 fn process(
52 &mut self,
53 inputs: &[&[f32]],
54 outputs: &mut [&mut [f32]],
55 frames: u32,
56 events: &[Event],
57 ) -> HostResult<()>;
58 fn deactivate(&mut self);
59 fn main_channels(&self) -> (u32, u32);
61}
62
63fn take_protocol_stdout() -> File {
66 #[cfg(unix)]
67 unsafe {
68 use std::os::fd::FromRawFd;
69 let fd = libc::dup(1);
70 if fd >= 0 {
71 libc::dup2(2, 1);
72 return File::from_raw_fd(fd);
73 }
74 File::from_raw_fd(1)
75 }
76 #[cfg(windows)]
77 unsafe {
78 use std::os::windows::io::FromRawHandle;
79 let fd = libc::dup(1);
80 if fd >= 0 {
81 libc::dup2(2, 1);
82 let h = libc::get_osfhandle(fd);
83 return File::from_raw_handle(h as _);
84 }
85 let h = libc::get_osfhandle(1);
86 File::from_raw_handle(h as _)
87 }
88}
89
90struct Shm {
91 map: memmap2::MmapMut,
92 layout: ShmLayout,
93}
94
95pub fn serve(backend: &mut dyn Backend) -> i32 {
97 let mut out = take_protocol_stdout();
98 let stdin = std::io::stdin();
99 let mut input = stdin.lock();
100 serve_on(backend, &mut input, &mut out)
101}
102
103pub fn serve_on(backend: &mut dyn Backend, input: &mut dyn Read, out: &mut dyn Write) -> i32 {
105 let mut shm: Option<Shm> = None;
106 let mut block_size = 0u32;
107 loop {
108 let frame = match read_frame(input) {
109 Ok(Some(f)) => f,
110 Ok(None) => return 0,
111 Err(e) => {
112 eprintln!("ferraus-plughost: broken control pipe: {e}");
113 return 3;
114 }
115 };
116 let req: Request = match serde_json::from_slice(&frame) {
117 Ok(r) => r,
118 Err(e) => {
119 let reply = Reply::Error { code: "bad_request".into(), message: format!("cannot parse request: {e}") };
120 if write_frame(out, &reply).is_err() {
121 return 3;
122 }
123 continue;
124 }
125 };
126 let quit = matches!(req, Request::Quit);
127 let reply = handle(backend, req, &mut shm, &mut block_size);
128 if write_frame(out, &reply).is_err() {
129 return 3;
130 }
131 if quit {
132 return 0;
133 }
134 }
135}
136
137fn err(e: HostError) -> Reply {
138 Reply::Error { code: e.code, message: e.message }
139}
140
141fn handle(backend: &mut dyn Backend, req: Request, shm: &mut Option<Shm>, block_size: &mut u32) -> Reply {
142 let b64 = base64::engine::general_purpose::STANDARD;
143 match req {
144 Request::Hello => Reply::Hello {
145 host: backend.host_name().into(),
146 version: env!("CARGO_PKG_VERSION").into(),
147 protocol: PROTOCOL_VERSION,
148 formats: backend.formats(),
149 },
150 Request::Probe { path } => match backend.probe(Path::new(&path)) {
151 Ok(plugins) => Reply::Probe { plugins },
152 Err(e) => err(e),
153 },
154 Request::Load { path, plugin_id } => match backend.load(Path::new(&path), plugin_id.as_deref()) {
155 Ok(info) => Reply::Loaded { info },
156 Err(e) => err(e),
157 },
158 Request::SetParams { params } => match backend.set_params(¶ms) {
159 Ok(params) => Reply::Params { params },
160 Err(e) => err(e),
161 },
162 Request::GetParams => match backend.get_params() {
163 Ok(params) => Reply::Params { params },
164 Err(e) => err(e),
165 },
166 Request::ListPresets => match backend.list_presets() {
167 Ok(presets) => Reply::Presets { presets },
168 Err(e) => err(e),
169 },
170 Request::LoadPreset { preset } => match backend.load_preset(&preset) {
171 Ok(params) => Reply::Params { params },
172 Err(e) => err(e),
173 },
174 Request::GetState => match backend.get_state() {
175 Ok(s) => Reply::State { state_b64: b64.encode(s) },
176 Err(e) => err(e),
177 },
178 Request::SetState { state_b64 } => match b64.decode(state_b64.trim()) {
179 Ok(bytes) => match backend.set_state(&bytes) {
180 Ok(()) => Reply::Ok,
181 Err(e) => err(e),
182 },
183 Err(e) => err(HostError::new("bad_request", format!("state is not base64: {e}"))),
184 },
185 Request::Activate { sample_rate, block_size: bs, shm_path } => {
186 let map = match File::options()
187 .read(true)
188 .write(true)
189 .open(&shm_path)
190 .and_then(|f| unsafe { memmap2::MmapMut::map_mut(&f) })
191 {
192 Ok(m) => m,
193 Err(e) => {
194 return err(HostError::new("bad_request", format!("cannot map shared memory {shm_path}: {e}")))
195 }
196 };
197 let layout = match ShmLayout::read_header(&map) {
198 Ok(l) => l,
199 Err(e) => return err(HostError::new("bad_request", e)),
200 };
201 if layout.block_size != bs {
202 return err(HostError::new("bad_request", "block size differs from the shared memory header"));
203 }
204 match backend.activate(sample_rate, bs) {
205 Ok(a) => {
206 let (ci, co) = backend.main_channels();
207 *shm = Some(Shm { map, layout });
208 *block_size = bs;
209 Reply::Activated { latency: a.latency, tail: a.tail, channels_in: ci, channels_out: co }
210 }
211 Err(e) => err(e),
212 }
213 }
214 Request::Process { slot, frames, events } => {
215 let Some(s) = shm.as_mut() else {
216 return err(HostError::new("bad_request", "process before activate"));
217 };
218 let l = s.layout;
219 if slot >= l.slots || frames > *block_size || frames > l.block_size {
220 return err(HostError::new("bad_request", "slot or frame count out of range"));
221 }
222 std::sync::atomic::fence(std::sync::atomic::Ordering::SeqCst);
223 let base = s.map.as_mut_ptr();
224 let n = frames as usize;
225 let ins: Vec<&[f32]> =
228 (0..l.channels_in).map(|c| unsafe { &*f32_slice_mut(base, l.input_offset(slot, c), n) }).collect();
229 let mut outs: Vec<&mut [f32]> =
230 (0..l.channels_out).map(|c| unsafe { f32_slice_mut(base, l.output_offset(slot, c), n) }).collect();
231 for o in outs.iter_mut() {
232 o.fill(0.0);
233 }
234 let r = backend.process(&ins, &mut outs, frames, &events);
235 std::sync::atomic::fence(std::sync::atomic::Ordering::SeqCst);
236 match r {
237 Ok(()) => Reply::Processed { slot },
238 Err(e) => err(e),
239 }
240 }
241 Request::Deactivate => {
242 backend.deactivate();
243 *shm = None;
244 Reply::Ok
245 }
246 Request::Quit => {
247 backend.deactivate();
248 *shm = None;
249 Reply::Ok
250 }
251 }
252}