use std::collections::BTreeSet;
use sim_kernel::{Expr, Symbol};
use sim_lib_stream_core::RateContract;
use crate::{LaneDescriptor, LaneKind};
const DESCRIPTOR_NS: &str = "music/component-descriptor";
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum MusicCapability {
Playable,
Player,
Modulator,
Oscillator,
PerformanceSource,
Renderable,
}
impl MusicCapability {
pub fn wire_label(self) -> &'static str {
match self {
Self::Playable => "playable",
Self::Player => "player",
Self::Modulator => "modulator",
Self::Oscillator => "oscillator",
Self::PerformanceSource => "performance-source",
Self::Renderable => "renderable",
}
}
pub fn symbol(self) -> Symbol {
Symbol::qualified("music/capability", self.wire_label())
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum MusicComponentCategory {
PlayerFamily,
Instrument,
Control,
}
impl MusicComponentCategory {
pub fn wire_label(self) -> &'static str {
match self {
Self::PlayerFamily => "player-family",
Self::Instrument => "instrument",
Self::Control => "control",
}
}
pub fn symbol(self) -> Symbol {
Symbol::qualified("music/component-category", self.wire_label())
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum MusicPortDirection {
Input,
Output,
Sidechain,
}
impl MusicPortDirection {
pub fn wire_label(self) -> &'static str {
match self {
Self::Input => "input",
Self::Output => "output",
Self::Sidechain => "sidechain",
}
}
pub fn symbol(self) -> Symbol {
Symbol::qualified("music/port-direction", self.wire_label())
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum MusicUnit {
None,
Beats,
Ticks,
Percent,
Semitone,
Hertz,
}
impl MusicUnit {
pub fn wire_label(self) -> &'static str {
match self {
Self::None => "none",
Self::Beats => "beats",
Self::Ticks => "ticks",
Self::Percent => "percent",
Self::Semitone => "semitone",
Self::Hertz => "hertz",
}
}
pub fn symbol(self) -> Symbol {
Symbol::qualified("music/unit", self.wire_label())
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum DeterminismPolicy {
Deterministic,
Seeded,
LiveInput,
}
impl DeterminismPolicy {
pub fn wire_label(self) -> &'static str {
match self {
Self::Deterministic => "deterministic",
Self::Seeded => "seeded",
Self::LiveInput => "live-input",
}
}
pub fn symbol(self) -> Symbol {
Symbol::qualified("music/determinism", self.wire_label())
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct MusicPortDescriptor {
pub id: Symbol,
pub label: String,
pub direction: MusicPortDirection,
pub rate: RateContract,
pub accepted_event_families: Vec<LaneKind>,
pub output_families: Vec<LaneKind>,
}
impl MusicPortDescriptor {
pub fn new(
id: Symbol,
label: impl Into<String>,
direction: MusicPortDirection,
rate: RateContract,
) -> Self {
Self {
id,
label: label.into(),
direction,
rate,
accepted_event_families: Vec::new(),
output_families: Vec::new(),
}
}
pub fn with_events(mut self, accepted: Vec<LaneKind>, output: Vec<LaneKind>) -> Self {
self.accepted_event_families = stable_lane_kinds(accepted);
self.output_families = stable_lane_kinds(output);
self
}
pub fn to_expr(&self) -> Expr {
Expr::Map(vec![
field("id", Expr::Symbol(self.id.clone())),
field("label", Expr::String(self.label.clone())),
field("direction", Expr::Symbol(self.direction.symbol())),
field("rate", rate_expr(self.rate)),
field("accepted", lane_kind_list(&self.accepted_event_families)),
field("output", lane_kind_list(&self.output_families)),
])
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct MusicParamDescriptor {
pub id: Symbol,
pub label: String,
pub unit: MusicUnit,
pub rate: RateContract,
pub default: Expr,
}
impl MusicParamDescriptor {
pub fn new(
id: Symbol,
label: impl Into<String>,
unit: MusicUnit,
rate: RateContract,
default: Expr,
) -> Self {
Self {
id,
label: label.into(),
unit,
rate,
default,
}
}
pub fn to_expr(&self) -> Expr {
Expr::Map(vec![
field("id", Expr::Symbol(self.id.clone())),
field("label", Expr::String(self.label.clone())),
field("unit", Expr::Symbol(self.unit.symbol())),
field("rate", rate_expr(self.rate)),
field("default", self.default.clone()),
])
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct MusicComponentDescriptor {
pub id: Symbol,
pub label: String,
pub category: MusicComponentCategory,
pub capabilities: BTreeSet<MusicCapability>,
pub ports: Vec<MusicPortDescriptor>,
pub lanes: Vec<LaneDescriptor>,
pub params: Vec<MusicParamDescriptor>,
pub rate: RateContract,
pub determinism: DeterminismPolicy,
pub accepted_event_families: Vec<LaneKind>,
pub output_families: Vec<LaneKind>,
pub latency: Symbol,
pub implemented: bool,
}
impl MusicComponentDescriptor {
pub fn new(
id: Symbol,
label: impl Into<String>,
category: MusicComponentCategory,
rate: RateContract,
) -> Self {
let latency = rate.latency_class().symbol();
Self {
id,
label: label.into(),
category,
capabilities: BTreeSet::new(),
ports: Vec::new(),
lanes: Vec::new(),
params: Vec::new(),
rate,
determinism: DeterminismPolicy::Deterministic,
accepted_event_families: Vec::new(),
output_families: Vec::new(),
latency,
implemented: true,
}
}
pub fn with_capability(mut self, capability: MusicCapability) -> Self {
self.capabilities.insert(capability);
self
}
pub fn with_port(mut self, port: MusicPortDescriptor) -> Self {
self.ports.push(port);
self.ports.sort_by(|left, right| left.id.cmp(&right.id));
self
}
pub fn with_lane(mut self, lane: LaneDescriptor) -> Self {
self.lanes.push(lane);
self.lanes = crate::stable_lane_order(self.lanes);
self
}
pub fn with_param(mut self, param: MusicParamDescriptor) -> Self {
self.params.push(param);
self.params.sort_by(|left, right| left.id.cmp(&right.id));
self
}
pub fn with_events(mut self, accepted: Vec<LaneKind>, output: Vec<LaneKind>) -> Self {
self.accepted_event_families = stable_lane_kinds(accepted);
self.output_families = stable_lane_kinds(output);
self
}
pub fn with_determinism(mut self, determinism: DeterminismPolicy) -> Self {
self.determinism = determinism;
self
}
pub fn with_implemented(mut self, implemented: bool) -> Self {
self.implemented = implemented;
self
}
pub fn has_capability(&self, capability: MusicCapability) -> bool {
self.capabilities.contains(&capability)
}
pub fn to_expr(&self) -> Expr {
Expr::Map(vec![
field("id", Expr::Symbol(self.id.clone())),
field("label", Expr::String(self.label.clone())),
field("category", Expr::Symbol(self.category.symbol())),
field(
"capabilities",
Expr::Vector(
self.capabilities
.iter()
.map(|capability| Expr::Symbol(capability.symbol()))
.collect(),
),
),
field(
"ports",
Expr::Vector(
self.ports
.iter()
.map(MusicPortDescriptor::to_expr)
.collect(),
),
),
field(
"lanes",
Expr::Vector(self.lanes.iter().map(lane_expr).collect()),
),
field(
"params",
Expr::Vector(
self.params
.iter()
.map(MusicParamDescriptor::to_expr)
.collect(),
),
),
field("rate", rate_expr(self.rate)),
field("determinism", Expr::Symbol(self.determinism.symbol())),
field("accepted", lane_kind_list(&self.accepted_event_families)),
field("output", lane_kind_list(&self.output_families)),
field("latency", Expr::Symbol(self.latency.clone())),
field("implemented", Expr::Bool(self.implemented)),
])
}
}
fn stable_lane_kinds(mut kinds: Vec<LaneKind>) -> Vec<LaneKind> {
kinds.sort();
kinds.dedup();
kinds
}
fn lane_kind_list(kinds: &[LaneKind]) -> Expr {
Expr::Vector(
kinds
.iter()
.map(|kind| Expr::Symbol(kind.symbol()))
.collect(),
)
}
fn lane_expr(lane: &LaneDescriptor) -> Expr {
Expr::Map(vec![
field("id", Expr::String(lane.id.0.clone())),
field("kind", Expr::Symbol(lane.kind.symbol())),
field("target", Expr::Symbol(lane.target.symbol())),
field("order", Expr::String(lane.order.to_string())),
])
}
pub(crate) fn rate_expr(rate: RateContract) -> Expr {
Expr::Map(vec![
field("clock-domain", Expr::Symbol(rate.clock_domain().symbol())),
field("latency-class", Expr::Symbol(rate.latency_class().symbol())),
field(
"nominal-rate-hz",
Expr::String(
rate.nominal_rate_hz()
.map(|rate| rate.to_string())
.unwrap_or_else(|| "none".to_owned()),
),
),
])
}
fn field(name: &'static str, value: Expr) -> (Expr, Expr) {
(Expr::Symbol(Symbol::qualified(DESCRIPTOR_NS, name)), value)
}