use atomic_float::AtomicF32;
use std::{
collections::HashMap,
sync::{Arc, atomic::Ordering},
};
#[derive(Clone, Debug, PartialEq)]
pub enum ParamError {
ParamNotFound,
ParamMetaNotFound,
}
#[derive(Clone, Debug, PartialEq, Hash)]
pub struct ParamKey(usize);
#[derive(Debug, Default, Clone, PartialEq)]
pub struct ParamStore {
data: Arc<[AtomicF32]>,
}
impl ParamStore {
pub fn new(data: Arc<[AtomicF32]>) -> Self {
Self { data }
}
#[inline(always)]
pub fn get(&self, key: &ParamKey) -> Result<f32, ParamError> {
self.data
.get(key.0)
.map(|v| v.load(Ordering::Relaxed))
.ok_or(ParamError::ParamNotFound)
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct ParamMeta {
pub name: String,
pub min: f32,
pub max: f32,
pub default: f32,
}
impl Default for ParamMeta {
fn default() -> Self {
Self {
name: "Uninitialized".into(),
min: 0.0,
max: 1.0,
default: 0.0,
}
}
}
#[derive(Debug, Clone)]
pub struct ParamStoreFrontend {
store: Arc<[AtomicF32]>,
meta: Box<[ParamMeta]>,
param_lookup: HashMap<String, ParamKey>,
}
impl ParamStoreFrontend {
pub fn new(
store: Arc<[AtomicF32]>,
meta: Box<[ParamMeta]>,
param_lookup: HashMap<String, ParamKey>,
) -> Self {
Self {
store,
meta,
param_lookup,
}
}
pub fn set_param(&self, key: ParamKey, val: f32) -> Result<(), ParamError> {
let meta = self.meta.get(key.0).ok_or(ParamError::ParamMetaNotFound)?;
let clamped = val.clamp(meta.min, meta.max);
if let Some(item) = self.store.get(key.0) {
item.store(clamped, Ordering::Relaxed);
return Ok(());
}
Err(ParamError::ParamNotFound)
}
pub fn get_param(&self, key: ParamKey) -> Result<f32, ParamError> {
match self.store.get(key.0) {
Some(inner) => Ok(inner.load(Ordering::Relaxed)),
None => Err(ParamError::ParamNotFound),
}
}
pub fn get_key(&self, name: &'static str) -> Result<ParamKey, ParamError> {
match self.param_lookup.get(name) {
Some(inner) => Ok(inner.clone()),
None => Err(ParamError::ParamNotFound),
}
}
pub fn get_all(&self) -> Vec<f32> {
self.store
.iter()
.map(|x| x.load(Ordering::Relaxed))
.collect::<Vec<f32>>()
}
}
#[derive(Default, Debug, Clone)]
pub struct ParamStoreBuilder {
meta: Vec<ParamMeta>,
param_lookup: HashMap<String, ParamKey>,
}
impl ParamStoreBuilder {
pub fn add_param(&mut self, unique_name: String, meta: ParamMeta) -> ParamKey {
let key = ParamKey(self.meta.len());
self.meta.push(meta);
self.param_lookup.insert(unique_name, key.clone());
key
}
pub fn build(self) -> (ParamStoreFrontend, ParamStore) {
let data_vec = self
.meta
.iter()
.map(|x| {
assert!(x.default <= x.max);
assert!(x.default >= x.min);
AtomicF32::new(x.default)
})
.collect::<Vec<AtomicF32>>();
let data: Arc<[AtomicF32]> = Arc::from(data_vec);
let store = ParamStore::new(data.clone());
let frontend = ParamStoreFrontend::new(data, self.meta.into(), self.param_lookup);
(frontend, store)
}
}