Feat: WIP terse code documentation
This commit is contained in:
@@ -6,6 +6,7 @@ use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||
|
||||
use crate::{DEFAULT_LENGTH, MAX_BANKS, MAX_PATTERNS, MAX_STEPS};
|
||||
|
||||
/// Speed multiplier for a pattern, expressed as a rational fraction.
|
||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||
pub struct PatternSpeed {
|
||||
pub num: u8,
|
||||
@@ -37,10 +38,12 @@ impl PatternSpeed {
|
||||
Self::OCTO,
|
||||
];
|
||||
|
||||
/// Return the speed as a floating-point multiplier.
|
||||
pub fn multiplier(&self) -> f64 {
|
||||
self.num as f64 / self.denom as f64
|
||||
}
|
||||
|
||||
/// Format as a human-readable label (e.g. "2x", "1/4x").
|
||||
pub fn label(&self) -> String {
|
||||
if self.denom == 1 {
|
||||
format!("{}x", self.num)
|
||||
@@ -49,6 +52,7 @@ impl PatternSpeed {
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the next faster preset, or self if already at maximum.
|
||||
pub fn next(&self) -> Self {
|
||||
let current = self.multiplier();
|
||||
Self::PRESETS
|
||||
@@ -58,6 +62,7 @@ impl PatternSpeed {
|
||||
.unwrap_or(*self)
|
||||
}
|
||||
|
||||
/// Return the next slower preset, or self if already at minimum.
|
||||
pub fn prev(&self) -> Self {
|
||||
let current = self.multiplier();
|
||||
Self::PRESETS
|
||||
@@ -68,6 +73,7 @@ impl PatternSpeed {
|
||||
.unwrap_or(*self)
|
||||
}
|
||||
|
||||
/// Parse a speed label like "2x" or "1/4x" into a `PatternSpeed`.
|
||||
pub fn from_label(s: &str) -> Option<Self> {
|
||||
let s = s.trim().trim_end_matches('x');
|
||||
if let Some((num, denom)) = s.split_once('/') {
|
||||
@@ -139,6 +145,7 @@ impl<'de> Deserialize<'de> for PatternSpeed {
|
||||
}
|
||||
}
|
||||
|
||||
/// Quantization grid for launching patterns.
|
||||
#[derive(Clone, Copy, Serialize, Deserialize, Default, PartialEq, Eq)]
|
||||
pub enum LaunchQuantization {
|
||||
Immediate,
|
||||
@@ -151,6 +158,7 @@ pub enum LaunchQuantization {
|
||||
}
|
||||
|
||||
impl LaunchQuantization {
|
||||
/// Human-readable label for display.
|
||||
pub fn label(&self) -> &'static str {
|
||||
match self {
|
||||
Self::Immediate => "Immediate",
|
||||
@@ -162,6 +170,7 @@ impl LaunchQuantization {
|
||||
}
|
||||
}
|
||||
|
||||
/// Cycle to the next longer quantization, clamped at `Bars8`.
|
||||
pub fn next(&self) -> Self {
|
||||
match self {
|
||||
Self::Immediate => Self::Beat,
|
||||
@@ -173,6 +182,7 @@ impl LaunchQuantization {
|
||||
}
|
||||
}
|
||||
|
||||
/// Cycle to the next shorter quantization, clamped at `Immediate`.
|
||||
pub fn prev(&self) -> Self {
|
||||
match self {
|
||||
Self::Immediate => Self::Immediate,
|
||||
@@ -185,6 +195,7 @@ impl LaunchQuantization {
|
||||
}
|
||||
}
|
||||
|
||||
/// How a pattern synchronizes when launched: restart or phase-lock.
|
||||
#[derive(Clone, Copy, Serialize, Deserialize, Default, PartialEq, Eq)]
|
||||
pub enum SyncMode {
|
||||
#[default]
|
||||
@@ -193,6 +204,7 @@ pub enum SyncMode {
|
||||
}
|
||||
|
||||
impl SyncMode {
|
||||
/// Human-readable label for display.
|
||||
pub fn label(&self) -> &'static str {
|
||||
match self {
|
||||
Self::Reset => "Reset",
|
||||
@@ -200,6 +212,7 @@ impl SyncMode {
|
||||
}
|
||||
}
|
||||
|
||||
/// Toggle between Reset and PhaseLock.
|
||||
pub fn toggle(&self) -> Self {
|
||||
match self {
|
||||
Self::Reset => Self::PhaseLock,
|
||||
@@ -208,6 +221,7 @@ impl SyncMode {
|
||||
}
|
||||
}
|
||||
|
||||
/// What happens when a pattern finishes: loop, stop, or chain to another.
|
||||
#[derive(Clone, Copy, Serialize, Deserialize, Default, PartialEq, Eq)]
|
||||
pub enum FollowUp {
|
||||
#[default]
|
||||
@@ -217,6 +231,7 @@ pub enum FollowUp {
|
||||
}
|
||||
|
||||
impl FollowUp {
|
||||
/// Human-readable label for display.
|
||||
pub fn label(&self) -> &'static str {
|
||||
match self {
|
||||
Self::Loop => "Loop",
|
||||
@@ -225,6 +240,7 @@ impl FollowUp {
|
||||
}
|
||||
}
|
||||
|
||||
/// Cycle forward through follow-up modes.
|
||||
pub fn next_mode(&self) -> Self {
|
||||
match self {
|
||||
Self::Loop => Self::Stop,
|
||||
@@ -233,6 +249,7 @@ impl FollowUp {
|
||||
}
|
||||
}
|
||||
|
||||
/// Cycle backward through follow-up modes.
|
||||
pub fn prev_mode(&self) -> Self {
|
||||
match self {
|
||||
Self::Loop => Self::Chain { bank: 0, pattern: 0 },
|
||||
@@ -246,6 +263,7 @@ fn is_default_follow_up(f: &FollowUp) -> bool {
|
||||
*f == FollowUp::default()
|
||||
}
|
||||
|
||||
/// Single step in a pattern, holding a Forth script and optional metadata.
|
||||
#[derive(Clone, Serialize, Deserialize)]
|
||||
pub struct Step {
|
||||
pub active: bool,
|
||||
@@ -257,10 +275,12 @@ pub struct Step {
|
||||
}
|
||||
|
||||
impl Step {
|
||||
/// True if all fields are at their default values.
|
||||
pub fn is_default(&self) -> bool {
|
||||
self.active && self.script.is_empty() && self.source.is_none() && self.name.is_none()
|
||||
}
|
||||
|
||||
/// True if the script is non-empty.
|
||||
pub fn has_content(&self) -> bool {
|
||||
!self.script.is_empty()
|
||||
}
|
||||
@@ -277,6 +297,7 @@ impl Default for Step {
|
||||
}
|
||||
}
|
||||
|
||||
/// Sequence of steps with playback settings (speed, quantization, sync, follow-up).
|
||||
#[derive(Clone)]
|
||||
pub struct Pattern {
|
||||
pub steps: Vec<Step>,
|
||||
@@ -447,14 +468,17 @@ impl Default for Pattern {
|
||||
}
|
||||
|
||||
impl Pattern {
|
||||
/// Borrow a step by index.
|
||||
pub fn step(&self, index: usize) -> Option<&Step> {
|
||||
self.steps.get(index)
|
||||
}
|
||||
|
||||
/// Mutably borrow a step by index.
|
||||
pub fn step_mut(&mut self, index: usize) -> Option<&mut Step> {
|
||||
self.steps.get_mut(index)
|
||||
}
|
||||
|
||||
/// Set the active length, clamped to `[1, MAX_STEPS]`.
|
||||
pub fn set_length(&mut self, length: usize) {
|
||||
let length = length.clamp(1, MAX_STEPS);
|
||||
while self.steps.len() < length {
|
||||
@@ -463,6 +487,7 @@ impl Pattern {
|
||||
self.length = length;
|
||||
}
|
||||
|
||||
/// Follow the source chain from `index` to find the originating step.
|
||||
pub fn resolve_source(&self, index: usize) -> usize {
|
||||
let mut current = index;
|
||||
for _ in 0..self.steps.len() {
|
||||
@@ -479,20 +504,22 @@ impl Pattern {
|
||||
index
|
||||
}
|
||||
|
||||
/// Return the script at the resolved source of `index`.
|
||||
pub fn resolve_script(&self, index: usize) -> Option<&str> {
|
||||
let source_idx = self.resolve_source(index);
|
||||
self.steps.get(source_idx).map(|s| s.script.as_str())
|
||||
}
|
||||
|
||||
/// Count active-length steps that have a script or a source reference.
|
||||
pub fn content_step_count(&self) -> usize {
|
||||
self.steps[..self.length]
|
||||
.iter()
|
||||
.filter(|s| s.has_content() || s.source.is_some())
|
||||
.count()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// Collection of patterns forming a bank.
|
||||
#[derive(Clone, Serialize, Deserialize)]
|
||||
pub struct Bank {
|
||||
pub patterns: Vec<Pattern>,
|
||||
@@ -501,6 +528,7 @@ pub struct Bank {
|
||||
}
|
||||
|
||||
impl Bank {
|
||||
/// Count patterns that contain at least one non-empty step.
|
||||
pub fn content_pattern_count(&self) -> usize {
|
||||
self.patterns
|
||||
.iter()
|
||||
@@ -518,6 +546,7 @@ impl Default for Bank {
|
||||
}
|
||||
}
|
||||
|
||||
/// Top-level project: banks, tempo, sample paths, and prelude script.
|
||||
#[derive(Clone, Serialize, Deserialize)]
|
||||
pub struct Project {
|
||||
pub banks: Vec<Bank>,
|
||||
@@ -548,14 +577,17 @@ impl Default for Project {
|
||||
}
|
||||
|
||||
impl Project {
|
||||
/// Borrow a pattern by bank and pattern index.
|
||||
pub fn pattern_at(&self, bank: usize, pattern: usize) -> &Pattern {
|
||||
&self.banks[bank].patterns[pattern]
|
||||
}
|
||||
|
||||
/// Mutably borrow a pattern by bank and pattern index.
|
||||
pub fn pattern_at_mut(&mut self, bank: usize, pattern: usize) -> &mut Pattern {
|
||||
&mut self.banks[bank].patterns[pattern]
|
||||
}
|
||||
|
||||
/// Pad banks, patterns, and steps to their maximum sizes after deserialization.
|
||||
pub fn normalize(&mut self) {
|
||||
self.banks.resize_with(MAX_BANKS, Bank::default);
|
||||
for bank in &mut self.banks {
|
||||
|
||||
Reference in New Issue
Block a user