1use sim_kernel::{Error, Result, Symbol};
4use sim_lib_stream_core::{BufferPolicy, ClockDomain, StreamMedia};
5
6use crate::{
7 HostBackend, HostBackendCapability, HostBackendInfo, HostClockInfo, HostDeviceInventory,
8 HostDeviceSpec, HostDirection, HostLatencyInfo, HostOpenStream, HostPortSpec, HostStreamConfig,
9 HostStreamConfigRequest,
10};
11
12pub fn fake_backend_symbol() -> Symbol {
14 Symbol::qualified("stream/host", "fake")
15}
16
17#[derive(Clone, Debug)]
19pub struct FakeBackend {
20 info: HostBackendInfo,
21 inventory: HostDeviceInventory,
22}
23
24impl Default for FakeBackend {
25 fn default() -> Self {
26 Self::new()
27 }
28}
29
30impl FakeBackend {
31 pub fn new() -> Self {
33 let backend = fake_backend_symbol();
34 let data = fake_device("fake/data", StreamMedia::Data, HostDirection::Input, 8);
35 let midi = fake_device("fake/midi", StreamMedia::Midi, HostDirection::Input, 8);
36 let pcm = fake_device("fake/pcm", StreamMedia::Pcm, HostDirection::Output, 8);
37 let ports = vec![
38 fake_port(
39 "fake/data/in",
40 data.id(),
41 StreamMedia::Data,
42 HostDirection::Input,
43 ),
44 fake_port(
45 "fake/midi/in",
46 midi.id(),
47 StreamMedia::Midi,
48 HostDirection::Input,
49 ),
50 fake_port(
51 "fake/pcm/out",
52 pcm.id(),
53 StreamMedia::Pcm,
54 HostDirection::Output,
55 ),
56 ];
57 Self {
58 info: HostBackendInfo::new(
59 backend.clone(),
60 Symbol::qualified("stream/transport", "fake"),
61 StreamMedia::Data,
62 false,
63 )
64 .with_capabilities(vec![
65 HostBackendCapability::Fake,
66 HostBackendCapability::Offline,
67 HostBackendCapability::MidiInput,
68 HostBackendCapability::AudioOutput,
69 ]),
70 inventory: HostDeviceInventory::new(backend)
71 .with_devices(vec![data, midi, pcm])
72 .with_ports(ports),
73 }
74 }
75
76 pub fn data_request(capacity: usize) -> Result<HostStreamConfigRequest> {
78 Ok(HostStreamConfigRequest::new(
79 fake_backend_symbol(),
80 Symbol::new("fake/data"),
81 StreamMedia::Data,
82 HostDirection::Input,
83 BufferPolicy::bounded(capacity)?,
84 ))
85 }
86}
87
88impl HostBackend for FakeBackend {
89 fn info(&self) -> &HostBackendInfo {
90 &self.info
91 }
92
93 fn enumerate(&self) -> Result<HostDeviceInventory> {
94 Ok(self.inventory.clone())
95 }
96
97 fn open(&self, request: HostStreamConfigRequest) -> Result<HostOpenStream> {
98 if request.backend() != self.info.id() {
99 return Err(Error::Eval(format!(
100 "fake backend cannot open {} requests",
101 request.backend()
102 )));
103 }
104 let Some(device) = self
105 .inventory
106 .devices()
107 .iter()
108 .find(|device| device.id() == request.device())
109 else {
110 return Err(Error::Eval(format!(
111 "fake backend has no device {}",
112 request.device()
113 )));
114 };
115 if device.media() != request.media() {
116 return Err(Error::TypeMismatch {
117 expected: "request media matching fake device",
118 found: "request media for another fake device",
119 });
120 }
121 if device.direction() != request.direction() {
122 return Err(Error::TypeMismatch {
123 expected: "request direction matching fake device",
124 found: "request direction for another fake device",
125 });
126 }
127 let config = HostStreamConfig::from_request(
128 request,
129 HostLatencyInfo::default(),
130 HostClockInfo::new(fake_clock(device.media()), None, true),
131 );
132 Ok(HostOpenStream::new(config))
133 }
134}
135
136fn fake_device(
137 id: &str,
138 media: StreamMedia,
139 direction: HostDirection,
140 capacity: usize,
141) -> HostDeviceSpec {
142 HostDeviceSpec::new(
143 Symbol::new(id),
144 fake_backend_symbol(),
145 media,
146 direction,
147 fake_clock(media),
148 BufferPolicy::bounded(capacity).expect("valid fake buffer"),
149 )
150}
151
152fn fake_clock(media: StreamMedia) -> Symbol {
153 match media {
154 StreamMedia::Pcm => ClockDomain::Sample.symbol(),
155 StreamMedia::Midi => ClockDomain::MidiTick.symbol(),
156 StreamMedia::Diagnostic | StreamMedia::Data => ClockDomain::ServerFrame.symbol(),
157 }
158}
159
160fn fake_port(
161 id: &str,
162 device: &Symbol,
163 media: StreamMedia,
164 direction: HostDirection,
165) -> HostPortSpec {
166 HostPortSpec::new(
167 Symbol::new(id),
168 device.clone(),
169 fake_backend_symbol(),
170 media,
171 direction,
172 )
173}