initial code scetch

This commit is contained in:
Bendik Aagaard Lynghaug
2026-03-05 15:41:53 +01:00
commit 8a7acd6d5b
9 changed files with 1554 additions and 0 deletions
+135
View File
@@ -0,0 +1,135 @@
use std::path::PathBuf;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum DeckId {
A = 0,
Z = 1,
S = 2,
X = 3,
}
impl DeckId {
pub fn from_index(i: usize) -> Option<Self> {
match i {
0 => Some(Self::A),
1 => Some(Self::Z),
2 => Some(Self::S),
3 => Some(Self::X),
_ => None,
}
}
pub fn label(self) -> &'static str {
match self {
Self::A => "A",
Self::Z => "Z",
Self::S => "S",
Self::X => "X",
}
}
/// Lowercase node name suffix used in PipeWire node names
pub fn node_suffix(self) -> &'static str {
match self {
Self::A => "a",
Self::Z => "z",
Self::S => "s",
Self::X => "x",
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum PlayState {
Stopped,
Playing,
Paused,
Cued,
}
impl PlayState {
pub fn icon(&self) -> &'static str {
match self {
Self::Playing => "▶",
Self::Paused => "⏸",
Self::Stopped => "⏹",
Self::Cued => "⏮",
}
}
}
#[derive(Debug, Clone)]
pub struct Deck {
pub id: DeckId,
// Track
pub track: Option<PathBuf>,
pub track_name: Option<String>,
// Transport
pub play_state: PlayState,
pub position: u64, // samples
pub duration: u64, // samples
pub sample_rate: u32,
pub cue_point: Option<u64>,
// Per-deck mix controls
/// 0.0 – 1.0, sent to wpctl on the deck's PipeWire stream node
pub fader: f32,
// Effect chain parameters (sent to pw-cli / filter-chain node)
/// Semitones -12.0 .. +12.0
pub pitch: f32,
/// Tempo multiplier 0.5 .. 2.0
pub tempo: f32,
// VU (updated by audio engine)
pub vu: (f32, f32),
// PipeWire object IDs (populated after graph setup)
/// Object ID of the deck's playback stream node
pub stream_node_id: Option<u32>,
/// Object ID of the filter-chain node for this deck
pub filter_node_id: Option<u32>,
}
impl Deck {
pub fn new(id: DeckId) -> Self {
Self {
id,
track: None,
track_name: None,
play_state: PlayState::Stopped,
position: 0,
duration: 0,
sample_rate: 44100,
cue_point: None,
fader: 0.8,
pitch: 0.0,
tempo: 1.0,
vu: (0.0, 0.0),
stream_node_id: None,
filter_node_id: None,
}
}
pub fn is_loaded(&self) -> bool {
self.track.is_some()
}
pub fn progress(&self) -> f32 {
if self.duration == 0 { 0.0 } else {
(self.position as f32 / self.duration as f32).clamp(0.0, 1.0)
}
}
pub fn elapsed(&self) -> (u64, u64) {
let s = self.position / self.sample_rate as u64;
(s / 60, s % 60)
}
pub fn remaining(&self) -> (u64, u64) {
let s = self.duration.saturating_sub(self.position) / self.sample_rate as u64;
(s / 60, s % 60)
}
}