sim_lib_stream_jack/
backend.rs1use sim_kernel::{Error, Result, Symbol};
2use sim_lib_stream_core::{BufferPolicy, StreamMedia};
3use sim_lib_stream_host::{
4 HostBackend, HostBackendCapability, HostBackendInfo, HostClockInfo, HostDeviceInventory,
5 HostDeviceSpec, HostDirection, HostLatencyInfo, HostOpenStream, HostPortSpec, HostStreamConfig,
6 HostStreamConfigRequest,
7};
8
9use crate::{JackClient, JackTiming, jack_clock_symbol};
10
11pub fn jack_backend_symbol() -> Symbol {
13 Symbol::qualified("stream/host", "jack")
14}
15
16pub fn jack_transport_symbol() -> Symbol {
18 Symbol::qualified("stream/transport", "jack")
19}
20
21#[derive(Clone, Debug)]
23pub struct JackBackend {
24 info: HostBackendInfo,
25 clients: Vec<JackClient>,
26}
27
28impl Default for JackBackend {
29 fn default() -> Self {
30 Self::new(Vec::new())
31 }
32}
33
34impl JackBackend {
35 pub fn new(clients: Vec<JackClient>) -> Self {
40 Self {
41 info: HostBackendInfo::new(
42 jack_backend_symbol(),
43 jack_transport_symbol(),
44 StreamMedia::Pcm,
45 true,
46 )
47 .with_capabilities(capabilities_for(&clients, true)),
48 clients,
49 }
50 }
51
52 pub fn fake() -> Self {
59 let clients = vec![JackClient::sim_default().expect("valid SIM JACK client")];
60 Self {
61 info: HostBackendInfo::new(
62 jack_backend_symbol(),
63 jack_transport_symbol(),
64 StreamMedia::Pcm,
65 false,
66 )
67 .with_capabilities(capabilities_for(&clients, false)),
68 clients,
69 }
70 }
71
72 pub fn list_clients(&self) -> &[JackClient] {
74 &self.clients
75 }
76
77 pub fn sim_client(&self) -> Option<&JackClient> {
79 self.clients.iter().find(|client| client.name() == "SIM")
80 }
81
82 pub fn open_sim_client(&self, capacity: usize) -> Result<HostOpenStream> {
90 let client = self
91 .sim_client()
92 .ok_or_else(|| Error::Eval("JACK SIM client was not found".to_owned()))?;
93 self.open(request(client, HostDirection::Duplex, capacity)?)
94 }
95
96 fn require_client(&self, client_id: &Symbol, direction: HostDirection) -> Result<&JackClient> {
97 let Some(client) = self
98 .clients
99 .iter()
100 .find(|candidate| candidate.id() == client_id)
101 else {
102 return Err(Error::Eval(format!(
103 "JACK client {client_id} was not found"
104 )));
105 };
106 if !client.is_compatible_with(direction) {
107 return Err(Error::TypeMismatch {
108 expected: "JACK client with requested audio direction",
109 found: "JACK client with another audio direction",
110 });
111 }
112 Ok(client)
113 }
114}
115
116impl HostBackend for JackBackend {
117 fn info(&self) -> &HostBackendInfo {
118 &self.info
119 }
120
121 fn enumerate(&self) -> Result<HostDeviceInventory> {
122 let devices = self
123 .clients
124 .iter()
125 .map(|client| {
126 Ok(HostDeviceSpec::new(
127 client.id().clone(),
128 jack_backend_symbol(),
129 StreamMedia::Pcm,
130 client.direction(),
131 jack_clock_symbol(),
132 BufferPolicy::bounded(client.timing().block_frames())?,
133 ))
134 })
135 .collect::<Result<Vec<_>>>()?;
136 let ports = self
137 .clients
138 .iter()
139 .flat_map(JackClient::ports)
140 .map(|port| {
141 HostPortSpec::new(
142 port.id().clone(),
143 port.client().clone(),
144 jack_backend_symbol(),
145 port.media(),
146 port.direction(),
147 )
148 })
149 .collect();
150 Ok(HostDeviceInventory::new(jack_backend_symbol())
151 .with_devices(devices)
152 .with_ports(ports))
153 }
154
155 fn open(&self, request: HostStreamConfigRequest) -> Result<HostOpenStream> {
156 if request.backend() != self.info.id() {
157 return Err(Error::Eval(format!(
158 "JACK backend cannot open {} requests",
159 request.backend()
160 )));
161 }
162 if request.media() != StreamMedia::Pcm {
163 return Err(Error::TypeMismatch {
164 expected: "PCM stream request",
165 found: "non-PCM stream request",
166 });
167 }
168 let direction = request.direction();
169 let client = self.require_client(request.device(), direction)?;
170 let timing = client.timing();
171 let config = HostStreamConfig::from_request(
172 request,
173 latency_for(direction, timing),
174 HostClockInfo::new(jack_clock_symbol(), Some(timing.sample_rate_hz()), true),
175 );
176 Ok(HostOpenStream::new(config))
177 }
178}
179
180fn request(
181 client: &JackClient,
182 direction: HostDirection,
183 capacity: usize,
184) -> Result<HostStreamConfigRequest> {
185 Ok(HostStreamConfigRequest::new(
186 jack_backend_symbol(),
187 client.id().clone(),
188 StreamMedia::Pcm,
189 direction,
190 BufferPolicy::bounded(capacity)?,
191 ))
192}
193
194fn capabilities_for(clients: &[JackClient], fake: bool) -> Vec<HostBackendCapability> {
195 let mut capabilities = Vec::new();
196 if clients.iter().any(|client| client.audio_inputs() > 0) {
197 capabilities.push(HostBackendCapability::AudioInput);
198 }
199 if clients.iter().any(|client| client.audio_outputs() > 0) {
200 capabilities.push(HostBackendCapability::AudioOutput);
201 }
202 if clients.iter().any(|client| client.midi_inputs() > 0) {
203 capabilities.push(HostBackendCapability::MidiInput);
204 }
205 if clients.iter().any(|client| client.midi_outputs() > 0) {
206 capabilities.push(HostBackendCapability::MidiOutput);
207 }
208 if clients
209 .iter()
210 .any(|client| client.direction() == HostDirection::Duplex)
211 {
212 capabilities.push(HostBackendCapability::Duplex);
213 }
214 capabilities.push(HostBackendCapability::Hotplug);
215 capabilities.push(HostBackendCapability::Reconnect);
216 if fake {
217 capabilities.push(HostBackendCapability::Offline);
218 capabilities.push(HostBackendCapability::Fake);
219 }
220 capabilities
221}
222
223fn latency_for(direction: HostDirection, timing: JackTiming) -> HostLatencyInfo {
224 match direction {
225 HostDirection::Input => HostLatencyInfo::new(timing.input_latency_frames(), 0),
226 HostDirection::Output => HostLatencyInfo::new(0, timing.output_latency_frames()),
227 HostDirection::Duplex => HostLatencyInfo::new(
228 timing.input_latency_frames(),
229 timing.output_latency_frames(),
230 ),
231 }
232}