use gled_midi::{HostEvent, native::NativeHost};
use kanal::Receiver;
use thiserror::Error;
use tracing::info;
use crate::{
action::UiAction,
audio::AudioPool,
engine::{self, EngineConfig, EngineConnector},
host_temperature,
input::{Input, artnet},
network_stats, output_state,
pipeline::{
extract_output::{ExtractOutput, PreviewOutput},
output_sender,
},
storage,
};
#[derive(Debug, Error)]
pub enum StartError {
#[error("the action queue was already set up")]
ActionQueue,
#[error("could not start the output: {0:#}")]
Output(anyhow::Error),
#[error("no GPU works")]
NoAdapter,
#[error("could not start the engine: {0:#}")]
Engine(anyhow::Error),
}
#[derive(Debug)]
pub struct EngineStart {
actions: Receiver<UiAction>,
extract_output: ExtractOutput,
artnet_control: Receiver<Vec<u8>>,
preview_output: Receiver<PreviewOutput>,
network_stats: Receiver<(f64, f64)>,
temperature: Receiver<Option<f32>>,
midi: NativeHost,
midi_events: Receiver<HostEvent>,
}
pub fn services() -> Result<EngineStart, StartError> {
let actions = UiAction::init_queue().ok_or(StartError::ActionQueue)?;
storage::start_thread();
let (midi_events, midi_event_receiver) = kanal::unbounded();
let midi = NativeHost::start(midi_events);
let network_stats = network_stats::start_thread();
let temperature = host_temperature::start_thread();
let (extract_output, output_receiver, preview_output) = ExtractOutput::new();
let artnet_inputs = output_sender::start(output_receiver).map_err(StartError::Output)?;
let (artnet_bridge, artnet_control) = artnet::start_thread(artnet_inputs);
Input::init(artnet_bridge);
output_state::init();
Ok(EngineStart {
actions,
extract_output,
artnet_control,
preview_output,
network_stats,
temperature,
midi,
midi_events: midi_event_receiver,
})
}
impl EngineStart {
pub fn spawn(self, adapter: wgpu::Adapter) -> Result<EngineConnector, StartError> {
engine::spawn(
EngineConfig {
adapter,
extract_output: self.extract_output,
artnet_control_receiver: self.artnet_control,
preview_output: self.preview_output,
network_stats: self.network_stats,
temperature: self.temperature,
midi: self.midi,
midi_events: self.midi_events,
audio_pool: AudioPool::default(),
},
self.actions,
)
.map_err(StartError::Engine)
}
}
pub async fn headless_adapter() -> Result<wgpu::Adapter, StartError> {
let instance =
wgpu::Instance::new(wgpu::InstanceDescriptor::new_without_display_handle_from_env());
let adapters = instance.enumerate_adapters(wgpu::Backends::all()).await;
let adapter = crate::gpu_choice::choose(&adapters, |_| true).ok_or(StartError::NoAdapter)?;
let adapter_info = adapter.get_info();
info!(
name = %adapter_info.name,
backend = ?adapter_info.backend,
"rendering on GPU adapter"
);
Ok(adapter)
}