use crate::WorkerManager;
use lv2_raw::LV2Feature;
use lv2_sys::LV2_BUF_SIZE__boundedBlockLength;
use std::pin::Pin;
use std::sync::atomic::AtomicBool;
use std::sync::Arc;
use std::{collections::HashSet, ffi::CStr};
pub mod options;
pub mod urid_map;
pub mod worker;
#[derive(Clone, Debug)]
pub struct FeaturesBuilder {
pub min_block_length: usize,
pub max_block_length: usize,
}
impl Default for FeaturesBuilder {
fn default() -> FeaturesBuilder {
FeaturesBuilder {
min_block_length: 1,
max_block_length: 4096,
}
}
}
impl FeaturesBuilder {
pub fn build(self, _world: &crate::World) -> Arc<Features> {
let worker_manager = Arc::new(WorkerManager::default());
let keep_worker_thread_alive = Arc::new(AtomicBool::new(true));
let keep_alive = keep_worker_thread_alive.clone();
let workers = worker_manager.clone();
let worker_thread = std::thread::spawn(move || {
while keep_alive.load(std::sync::atomic::Ordering::Relaxed) {
workers.run_workers();
std::thread::sleep(std::time::Duration::from_millis(100));
}
});
let mut features = Features {
urid_map: urid_map::UridMap::new(),
options: options::Options::new(),
min_block_length: self.min_block_length,
max_block_length: self.max_block_length,
bounded_block_length: LV2Feature {
uri: LV2_BUF_SIZE__boundedBlockLength.as_ptr().cast(),
data: std::ptr::null_mut(),
},
worker_manager,
_worker_thread: worker_thread,
keep_worker_thread_alive,
};
features.options.set_int_option(
&features.urid_map,
features.urid_map.map(
CStr::from_bytes_with_nul(b"http://lv2plug.in/ns/ext/buf-size#minBlockLength\0")
.unwrap(),
),
self.min_block_length as i32,
);
features.options.set_int_option(
&features.urid_map,
features.urid_map.map(
CStr::from_bytes_with_nul(b"http://lv2plug.in/ns/ext/buf-size#maxBlockLength\0")
.unwrap(),
),
self.max_block_length as i32,
);
Arc::new(features)
}
}
pub struct Features {
urid_map: Pin<Box<urid_map::UridMap>>,
options: options::Options,
bounded_block_length: LV2Feature,
min_block_length: usize,
max_block_length: usize,
worker_manager: Arc<WorkerManager>,
_worker_thread: std::thread::JoinHandle<()>,
keep_worker_thread_alive: Arc<AtomicBool>,
}
unsafe impl Send for Features {}
unsafe impl Sync for Features {}
impl Features {
pub fn supported_features() -> HashSet<&'static str> {
HashSet::from([
"http://lv2plug.in/ns/ext/urid#map",
"http://lv2plug.in/ns/ext/urid#unmap",
"http://lv2plug.in/ns/ext/options#options",
"http://lv2plug.in/ns/ext/buf-size#boundedBlockLength",
"http://lv2plug.in/ns/ext/worker#schedule",
])
}
pub fn iter_features<'a>(
&'a self,
worker_feature: &'a LV2Feature,
) -> impl Iterator<Item = &'a LV2Feature> {
std::iter::once(self.urid_map.as_urid_map_feature())
.chain(std::iter::once(self.urid_map.as_urid_unmap_feature()))
.chain(std::iter::once(self.options.as_feature()))
.chain(std::iter::once(&self.bounded_block_length))
.chain(std::iter::once(worker_feature))
}
pub fn min_block_length(&self) -> usize {
self.min_block_length
}
pub fn max_block_length(&self) -> usize {
self.max_block_length
}
pub fn urid(&self, uri: &CStr) -> u32 {
self.urid_map.map(uri)
}
pub fn midi_urid(&self) -> lv2_raw::LV2Urid {
self.urid(
std::ffi::CStr::from_bytes_with_nul(b"http://lv2plug.in/ns/ext/midi#MidiEvent\0")
.unwrap(),
)
}
pub fn uri(&self, urid: lv2_raw::LV2Urid) -> Option<&str> {
self.urid_map.unmap(urid)
}
pub fn worker_manager(&self) -> &Arc<WorkerManager> {
&self.worker_manager
}
}
impl std::fmt::Debug for Features {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Features")
.field("urid_map", &self.urid_map)
.field("options", &self.options)
.field("bounded_block_length", &"__uri__")
.field("min_block_length", &self.min_block_length)
.field("max_block_length", &self.max_block_length)
.field("worker_manager", &self.worker_manager)
.field("_worker_thread", &self._worker_thread)
.field("keep_worker_thread_alive", &self.keep_worker_thread_alive)
.finish()
}
}
impl Drop for Features {
fn drop(&mut self) {
self.keep_worker_thread_alive
.store(false, std::sync::atomic::Ordering::Relaxed);
}
}