1use crate::instrument::{ArticulationType, SampleZone};
4
5#[derive(Debug, Clone, PartialEq)]
7pub enum VoicePhase {
8 Attack,
10 Sustain,
12 Release,
14 Done,
16}
17
18#[derive(Debug, Clone)]
20pub struct EnvelopeState {
21 pub level: f32,
22 pub phase: EnvPhase,
23}
24
25#[derive(Debug, Clone, PartialEq)]
26pub enum EnvPhase { Attack, Decay, Sustain, Release, Done }
27
28impl EnvelopeState {
29 pub fn new() -> Self {
30 Self { level: 0.0, phase: EnvPhase::Attack }
31 }
32
33 pub fn tick(&mut self, attack_rate: f32, decay_rate: f32, sustain: f32, release_rate: f32) {
34 match self.phase {
35 EnvPhase::Attack => {
36 self.level += attack_rate;
37 if self.level >= 1.0 { self.level = 1.0; self.phase = EnvPhase::Decay; }
38 }
39 EnvPhase::Decay => {
40 self.level -= decay_rate;
41 if self.level <= sustain { self.level = sustain; self.phase = EnvPhase::Sustain; }
42 }
43 EnvPhase::Sustain => {}
44 EnvPhase::Release => {
45 self.level -= release_rate;
46 if self.level <= 0.0 { self.level = 0.0; self.phase = EnvPhase::Done; }
47 }
48 EnvPhase::Done => {}
49 }
50 }
51
52 pub fn release(&mut self) {
53 if self.phase != EnvPhase::Done {
54 self.phase = EnvPhase::Release;
55 }
56 }
57
58 pub fn is_done(&self) -> bool { self.phase == EnvPhase::Done }
59}
60
61pub struct SamplerVoice {
63 pub note: u8,
65 pub channel: u8,
67 pub velocity: f32,
69 pub position: f64,
71 pub pitch_ratio: f64,
73 pub volume: f32,
75 pub phase: VoicePhase,
77 pub envelope: EnvelopeState,
79 pub zone_id: String,
81 pub articulation: ArticulationType,
83 pub loop_start: usize,
85 pub loop_end: usize,
87 pub key_held: bool,
89}
90
91impl SamplerVoice {
92 pub fn new(
93 note: u8,
94 channel: u8,
95 velocity: f32,
96 pitch_ratio: f64,
97 volume: f32,
98 zone: &SampleZone,
99 ) -> Self {
100 let (loop_start, loop_end) = match &zone.articulation {
101 ArticulationType::SustainLoop { loop_start, loop_end } => (*loop_start, *loop_end),
102 _ => (0, 0),
103 };
104 Self {
105 note,
106 channel,
107 velocity,
108 position: 0.0,
109 pitch_ratio,
110 volume,
111 phase: VoicePhase::Attack,
112 envelope: EnvelopeState::new(),
113 zone_id: zone.id.clone(),
114 articulation: zone.articulation.clone(),
115 loop_start,
116 loop_end,
117 key_held: true,
118 }
119 }
120
121 pub fn release(&mut self) {
123 self.key_held = false;
124 self.envelope.release();
125 if self.phase == VoicePhase::Sustain || self.phase == VoicePhase::Attack {
126 self.phase = VoicePhase::Release;
127 }
128 }
129
130 pub fn is_done(&self) -> bool {
132 self.phase == VoicePhase::Done || self.envelope.is_done()
133 }
134
135 pub fn advance(&mut self, buffer_frames: usize) -> f64 {
138 let pos = self.position;
139 self.position += self.pitch_ratio;
140
141 match &self.articulation {
142 ArticulationType::SustainLoop { loop_start, loop_end } => {
143 if self.key_held && self.position >= *loop_end as f64 {
144 self.position = *loop_start as f64 + (self.position - *loop_end as f64);
145 self.phase = VoicePhase::Sustain;
146 } else if !self.key_held && self.position >= buffer_frames as f64 {
147 self.phase = VoicePhase::Done;
148 }
149 }
150 ArticulationType::OneShot => {
151 if self.position >= buffer_frames as f64 {
152 self.phase = VoicePhase::Done;
153 }
154 }
155 ArticulationType::SustainRelease => {
156 if self.position >= buffer_frames as f64 {
157 self.phase = VoicePhase::Done;
158 }
159 }
160 }
161
162 pos
163 }
164}