Skip to content

Commit fbb5688

Browse files
committed
wip(voice): dynamic mono/poly voice manager
1 parent 9b5c023 commit fbb5688

File tree

6 files changed

+403
-23
lines changed

6 files changed

+403
-23
lines changed

crates/valib-voice/src/dynamic.rs

Lines changed: 282 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,282 @@
1+
use crate::monophonic::Monophonic;
2+
use crate::polyphonic::Polyphonic;
3+
use crate::{NoteData, Voice, VoiceManager};
4+
use std::fmt;
5+
use std::fmt::Formatter;
6+
use std::ops::Range;
7+
use std::sync::Arc;
8+
use valib_core::dsp::{DSPMeta, DSPProcess};
9+
10+
#[derive(Debug)]
11+
enum Impl<V: Voice> {
12+
Monophonic(Monophonic<V>),
13+
Polyphonic(Polyphonic<V>),
14+
}
15+
16+
impl<V: Voice> DSPMeta for Impl<V> {
17+
type Sample = V::Sample;
18+
19+
fn set_samplerate(&mut self, samplerate: f32) {
20+
match self {
21+
Impl::Monophonic(mono) => mono.set_samplerate(samplerate),
22+
Impl::Polyphonic(poly) => poly.set_samplerate(samplerate),
23+
}
24+
}
25+
26+
fn latency(&self) -> usize {
27+
match self {
28+
Impl::Monophonic(mono) => mono.latency(),
29+
Impl::Polyphonic(poly) => poly.latency(),
30+
}
31+
}
32+
33+
fn reset(&mut self) {
34+
match self {
35+
Impl::Monophonic(mono) => mono.reset(),
36+
Impl::Polyphonic(poly) => poly.reset(),
37+
}
38+
}
39+
}
40+
41+
pub struct DynamicVoice<V: Voice> {
42+
pitch_bend_st: Range<V::Sample>,
43+
poly_voice_capacity: usize,
44+
create_voice: Arc<dyn 'static + Send + Sync + Fn(f32, NoteData<V::Sample>) -> V>,
45+
current_manager: Impl<V>,
46+
legato: bool,
47+
samplerate: f32,
48+
}
49+
50+
impl<V: Voice + fmt::Debug> fmt::Debug for DynamicVoice<V> {
51+
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
52+
f.debug_struct("DynamicVoice")
53+
.field("pitch_bend_st", &self.pitch_bend_st)
54+
.field("poly_voice_capacity", &self.poly_voice_capacity)
55+
.field("create_voice", &"Arc<dyn Fn(f32, NoteData<V::Sample>) -> V")
56+
.field("current_manager", &self.current_manager)
57+
.field("legato", &self.legato)
58+
.field("samplerate", &self.samplerate)
59+
.finish()
60+
}
61+
}
62+
63+
impl<V: 'static + Voice> DynamicVoice<V> {
64+
pub fn new_mono(
65+
samplerate: f32,
66+
poly_voice_capacity: usize,
67+
legato: bool,
68+
create_voice: impl 'static + Send + Sync + Fn(f32, NoteData<V::Sample>) -> V,
69+
) -> Self {
70+
let create_voice = Arc::new(create_voice);
71+
let mono = Monophonic::new(
72+
samplerate,
73+
{
74+
let create_voice = create_voice.clone();
75+
move |sr, nd| create_voice.clone()(sr, nd)
76+
},
77+
legato,
78+
);
79+
let pitch_bend_st = mono.pitch_bend_min_st..mono.pitch_bend_max_st;
80+
Self {
81+
pitch_bend_st,
82+
poly_voice_capacity,
83+
create_voice,
84+
current_manager: Impl::Monophonic(mono),
85+
legato,
86+
samplerate,
87+
}
88+
}
89+
90+
pub fn new_poly(
91+
samplerate: f32,
92+
capacity: usize,
93+
legato: bool,
94+
create_voice: impl 'static + Send + Sync + Fn(f32, NoteData<V::Sample>) -> V,
95+
) -> Self {
96+
let create_voice = Arc::new(create_voice);
97+
let poly = Polyphonic::new(samplerate, capacity, {
98+
let create_voice = create_voice.clone();
99+
move |sr, nd| create_voice.clone()(sr, nd)
100+
});
101+
let pitch_bend_st = poly.pitch_bend_st.clone();
102+
Self {
103+
pitch_bend_st,
104+
poly_voice_capacity: capacity,
105+
create_voice,
106+
current_manager: Impl::Polyphonic(poly),
107+
legato,
108+
samplerate,
109+
}
110+
}
111+
112+
pub fn switch(&mut self, polyphonic: bool) {
113+
let new = match self.current_manager {
114+
Impl::Monophonic(..) if polyphonic => {
115+
let create_voice = self.create_voice.clone();
116+
let mut poly =
117+
Polyphonic::new(self.samplerate, self.poly_voice_capacity, move |sr, nd| {
118+
create_voice.clone()(sr, nd)
119+
});
120+
poly.pitch_bend_st = self.pitch_bend_st.clone();
121+
Impl::Polyphonic(poly)
122+
}
123+
Impl::Polyphonic(..) if !polyphonic => {
124+
let create_voice = self.create_voice.clone();
125+
let mut mono = Monophonic::new(
126+
self.samplerate,
127+
move |sr, nd| create_voice.clone()(sr, nd),
128+
self.legato,
129+
);
130+
mono.pitch_bend_min_st = self.pitch_bend_st.start;
131+
mono.pitch_bend_max_st = self.pitch_bend_st.end;
132+
Impl::Monophonic(mono)
133+
}
134+
_ => {
135+
return;
136+
}
137+
};
138+
self.current_manager = new;
139+
}
140+
141+
pub fn is_monophonic(&self) -> bool {
142+
matches!(self.current_manager, Impl::Monophonic(..))
143+
}
144+
145+
pub fn is_polyphonic(&self) -> bool {
146+
matches!(self.current_manager, Impl::Polyphonic(..))
147+
}
148+
149+
pub fn legato(&self) -> bool {
150+
self.legato
151+
}
152+
153+
pub fn set_legato(&mut self, legato: bool) {
154+
self.legato = legato;
155+
if let Impl::Monophonic(ref mut mono) = self.current_manager {
156+
mono.set_legato(legato);
157+
}
158+
}
159+
160+
pub fn clean_inactive_voices(&mut self) {
161+
match self.current_manager {
162+
Impl::Monophonic(ref mut mono) => mono.clean_voice_if_inactive(),
163+
Impl::Polyphonic(ref mut poly) => poly.clean_inactive_voices(),
164+
}
165+
}
166+
}
167+
168+
impl<V: Voice> DSPMeta for DynamicVoice<V> {
169+
type Sample = V::Sample;
170+
171+
fn set_samplerate(&mut self, samplerate: f32) {
172+
self.samplerate = samplerate;
173+
self.current_manager.set_samplerate(samplerate);
174+
}
175+
176+
fn latency(&self) -> usize {
177+
self.current_manager.latency()
178+
}
179+
180+
fn reset(&mut self) {
181+
self.current_manager.reset();
182+
}
183+
}
184+
185+
impl<V: Voice> VoiceManager for DynamicVoice<V> {
186+
type Voice = V;
187+
type ID = <Polyphonic<V> as VoiceManager>::ID;
188+
189+
fn capacity(&self) -> usize {
190+
self.poly_voice_capacity
191+
}
192+
193+
fn get_voice(&self, id: Self::ID) -> Option<&Self::Voice> {
194+
match self.current_manager {
195+
Impl::Monophonic(ref mono) => mono.get_voice(()),
196+
Impl::Polyphonic(ref poly) => poly.get_voice(id),
197+
}
198+
}
199+
200+
fn get_voice_mut(&mut self, id: Self::ID) -> Option<&mut Self::Voice> {
201+
match self.current_manager {
202+
Impl::Monophonic(ref mut mono) => mono.get_voice_mut(()),
203+
Impl::Polyphonic(ref mut poly) => poly.get_voice_mut(id),
204+
}
205+
}
206+
207+
fn all_voices(&self) -> impl Iterator<Item = Self::ID> {
208+
0..self.poly_voice_capacity
209+
}
210+
211+
fn note_on(&mut self, note_data: NoteData<Self::Sample>) -> Self::ID {
212+
match self.current_manager {
213+
Impl::Monophonic(ref mut mono) => {
214+
mono.note_on(note_data);
215+
0
216+
}
217+
Impl::Polyphonic(ref mut poly) => poly.note_on(note_data),
218+
}
219+
}
220+
221+
fn note_off(&mut self, id: Self::ID, release_velocity: f32) {
222+
match self.current_manager {
223+
Impl::Monophonic(ref mut mono) => {
224+
mono.note_off((), release_velocity);
225+
}
226+
Impl::Polyphonic(ref mut poly) => {
227+
poly.note_off(id, release_velocity);
228+
}
229+
}
230+
}
231+
232+
fn choke(&mut self, id: Self::ID) {
233+
match self.current_manager {
234+
Impl::Monophonic(ref mut mono) => mono.choke(()),
235+
Impl::Polyphonic(ref mut poly) => poly.choke(id),
236+
}
237+
}
238+
239+
fn panic(&mut self) {
240+
match self.current_manager {
241+
Impl::Monophonic(ref mut mono) => mono.panic(),
242+
Impl::Polyphonic(ref mut poly) => poly.panic(),
243+
}
244+
}
245+
246+
fn pitch_bend(&mut self, amount: f64) {
247+
match self.current_manager {
248+
Impl::Monophonic(ref mut mono) => mono.pitch_bend(amount),
249+
Impl::Polyphonic(ref mut poly) => poly.pitch_bend(amount),
250+
}
251+
}
252+
253+
fn aftertouch(&mut self, amount: f64) {
254+
match self.current_manager {
255+
Impl::Monophonic(ref mut mono) => mono.aftertouch(amount),
256+
Impl::Polyphonic(ref mut poly) => poly.aftertouch(amount),
257+
}
258+
}
259+
260+
fn pressure(&mut self, id: Self::ID, pressure: f32) {
261+
match self.current_manager {
262+
Impl::Monophonic(ref mut mono) => mono.glide((), pressure),
263+
Impl::Polyphonic(ref mut poly) => poly.glide(id, pressure),
264+
}
265+
}
266+
267+
fn glide(&mut self, id: Self::ID, semitones: f32) {
268+
match self.current_manager {
269+
Impl::Monophonic(ref mut mono) => mono.glide((), semitones),
270+
Impl::Polyphonic(ref mut poly) => poly.glide(id, semitones),
271+
}
272+
}
273+
}
274+
275+
impl<V: Voice + DSPProcess<0, 1>> DSPProcess<0, 1> for DynamicVoice<V> {
276+
fn process(&mut self, []: [Self::Sample; 0]) -> [Self::Sample; 1] {
277+
match self.current_manager {
278+
Impl::Monophonic(ref mut mono) => mono.process([]),
279+
Impl::Polyphonic(ref mut poly) => poly.process([]),
280+
}
281+
}
282+
}

crates/valib-voice/src/lib.rs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,10 @@
44
//! This crate provides abstractions around voice processing and voice management.
55
use valib_core::dsp::{BlockAdapter, DSPMeta, DSPProcessBlock, SampleAdapter};
66
use valib_core::simd::SimdRealField;
7-
use valib_core::util::midi_to_freq;
7+
use valib_core::util::{midi_to_freq, semitone_to_ratio};
88
use valib_core::Scalar;
99

10+
pub mod dynamic;
1011
pub mod monophonic;
1112
pub mod polyphonic;
1213
#[cfg(feature = "resampled")]
@@ -161,6 +162,8 @@ impl<T: Scalar> Gain<T> {
161162
pub struct NoteData<T> {
162163
/// Note frequency
163164
pub frequency: T,
165+
/// Frequency modulation (pitch bend, glide)
166+
pub modulation_st: T,
164167
/// Note velocity
165168
pub velocity: Velocity<T>,
166169
/// Note gain
@@ -180,12 +183,17 @@ impl<T: Scalar> NoteData<T> {
180183
let pressure = T::zero();
181184
Self {
182185
frequency,
186+
modulation_st: T::zero(),
183187
velocity,
184188
gain,
185189
pan,
186190
pressure,
187191
}
188192
}
193+
194+
pub fn resolve_frequency(&self) -> T {
195+
semitone_to_ratio(self.modulation_st) * self.frequency
196+
}
189197
}
190198

191199
/// Trait for types which manage voices.

0 commit comments

Comments
 (0)