Skip to content

Commit ef3c22c

Browse files
committed
Fine-grained midi resource allocation
1 parent 1efe846 commit ef3c22c

File tree

13 files changed

+351
-140
lines changed

13 files changed

+351
-140
lines changed

caw/examples/live_example.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,8 +50,8 @@ fn main() {
5050
]
5151
.into_iter()
5252
.sum::<Sig<_>>()
53-
* 0.5) //knob("drum vol").build())
54-
.shared();
53+
* knob("drum vol").build())
54+
.shared();
5555
out.set_channel(|channel| {
5656
let voice = key_events.clone().mono_voice();
5757
let (note, gate) = key_looper(voice.triggered_note(), clock.clone())

midi-udp-widgets-app-lib/src/button.rs

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,18 @@ lazy_static! {
1515
}
1616

1717
fn new_button(title: String) -> Sig<SigShared<Button>> {
18+
let (channel, key) = midi::alloc_key();
19+
let note = Note::from_midi_index(key);
1820
BY_TITLE.get_or_insert(title.as_str(), || {
19-
let widget =
20-
Widget::new(title.clone(), midi::alloc_channel(), "button", vec![])
21-
.unwrap();
21+
let widget = Widget::new(
22+
title.clone(),
23+
channel,
24+
"button",
25+
vec![format!("--note={}", note)],
26+
)
27+
.unwrap();
2228
// The choice of note here is arbitrary.
23-
sig_shared(widget.channel().key_press(Note::default()))
29+
sig_shared(widget.channel().key_press(note))
2430
})
2531
}
2632

midi-udp-widgets-app-lib/src/knob.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,8 @@ fn new_knob_with_space(
2626
sensitivity: f32,
2727
) -> KnobWithSpace {
2828
BY_TITLE.get_or_insert(title.as_str(), || {
29-
let channel = midi::alloc_channel();
30-
let controller = midi::alloc_controller(channel);
31-
let space_controller = midi::alloc_controller(channel);
29+
let (channel, controller) = midi::alloc_controller();
30+
let (space_channel, space_controller) = midi::alloc_controller();
3231
let widget = Widget::new(
3332
title.clone(),
3433
channel,
@@ -37,6 +36,7 @@ fn new_knob_with_space(
3736
format!("--controller={}", controller),
3837
format!("--initial-value={}", initial_value_01),
3938
format!("--sensitivity={}", sensitivity),
39+
format!("--space-channel={}", space_channel),
4040
format!("--space-controller={}", space_controller),
4141
],
4242
)
Lines changed: 251 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,67 +1,276 @@
11
use lazy_static::lazy_static;
22
use midly::num::{u4, u7};
3-
use std::{collections::HashMap, sync::Mutex};
3+
use std::sync::Mutex;
44

5-
struct ChannelAllocator {
6-
next_channel: Option<u4>,
7-
next_controller_index_by_channel: HashMap<u4, Option<u7>>,
5+
pub type MidiChannel = u4;
6+
type MidiResource = u7;
7+
pub type MidiController = MidiResource;
8+
pub type MidiKey = MidiResource;
9+
10+
#[derive(Debug)]
11+
struct AllMidiChannelsAreAlreadyAllocated;
12+
13+
#[derive(Debug)]
14+
struct AllMidiResourcesAreAlreadyAllocated;
15+
16+
struct GenericAllocator {
17+
next: Option<u8>,
18+
max: u8,
819
}
920

10-
impl ChannelAllocator {
21+
impl GenericAllocator {
22+
fn new(max: u8) -> Self {
23+
Self { next: Some(0), max }
24+
}
25+
26+
fn alloc(&mut self) -> Option<u8> {
27+
self.next.map(|next| {
28+
self.next = if next == self.max {
29+
None
30+
} else {
31+
Some(next + 1)
32+
};
33+
next
34+
})
35+
}
36+
}
37+
38+
struct MidiChannelAllocator(GenericAllocator);
39+
40+
impl MidiChannelAllocator {
1141
fn new() -> Self {
42+
Self(GenericAllocator::new(u4::max_value().into()))
43+
}
44+
45+
fn alloc(
46+
&mut self,
47+
) -> Result<MidiChannel, AllMidiChannelsAreAlreadyAllocated> {
48+
match self.0.alloc() {
49+
Some(value) => Ok(value.into()),
50+
None => Err(AllMidiChannelsAreAlreadyAllocated),
51+
}
52+
}
53+
}
54+
55+
/// Allocator for 7-bit midi resources such as controllers and keys
56+
struct MidiResourceAllocator(GenericAllocator);
57+
impl MidiResourceAllocator {
58+
fn new() -> Self {
59+
Self(GenericAllocator::new(u7::max_value().into()))
60+
}
61+
62+
fn alloc(
63+
&mut self,
64+
) -> Result<MidiResource, AllMidiResourcesAreAlreadyAllocated> {
65+
match self.0.alloc() {
66+
Some(value) => Ok(value.into()),
67+
None => Err(AllMidiResourcesAreAlreadyAllocated),
68+
}
69+
}
70+
}
71+
72+
impl Default for MidiResourceAllocator {
73+
fn default() -> Self {
74+
Self::new()
75+
}
76+
}
77+
78+
#[derive(Clone, Copy, Debug)]
79+
enum MidiResourceType {
80+
Controller,
81+
Key,
82+
}
83+
84+
#[derive(Default)]
85+
struct MidiResourceTable<T> {
86+
controller: T,
87+
key: T,
88+
}
89+
90+
impl<T> MidiResourceTable<T> {
91+
fn get_mut(&mut self, type_: MidiResourceType) -> &mut T {
92+
match type_ {
93+
MidiResourceType::Controller => &mut self.controller,
94+
MidiResourceType::Key => &mut self.key,
95+
}
96+
}
97+
98+
fn get(&self, type_: MidiResourceType) -> &T {
99+
match type_ {
100+
MidiResourceType::Controller => &self.controller,
101+
MidiResourceType::Key => &self.key,
102+
}
103+
}
104+
}
105+
106+
/// Allocates resources associated with a particular channel
107+
struct MidiResourceAllocators {
108+
resource_table: MidiResourceTable<MidiResourceAllocator>,
109+
channel: MidiChannel,
110+
}
111+
112+
impl MidiResourceAllocators {
113+
fn new(channel: MidiChannel) -> Self {
12114
Self {
13-
next_channel: Some(0.into()),
14-
next_controller_index_by_channel: HashMap::new(),
115+
resource_table: Default::default(),
116+
channel,
15117
}
16118
}
119+
}
17120

18-
fn alloc_channel(&mut self) -> u4 {
19-
let channel = self.next_channel.expect("Can't allocate MIDI channel because all MIDI channels are in use already.");
20-
self.next_channel = if channel == u4::max_value() {
21-
None
22-
} else {
23-
Some((u8::from(channel) + 1).into())
121+
/// Allocates resources (controllers, keys) or entire channels. Resources are allocated from a pool
122+
/// of channels managed internally by this type.
123+
struct MidiAllocator {
124+
channel_allocator: MidiChannelAllocator,
125+
resource_allocators_pool: Vec<MidiResourceAllocators>,
126+
resource_pool_indices: MidiResourceTable<usize>,
127+
}
128+
129+
impl MidiAllocator {
130+
fn new() -> Self {
131+
let mut channel_allocator = MidiChannelAllocator::new();
132+
let resource_allocator = {
133+
let channel = channel_allocator
134+
.alloc()
135+
.expect("First allocation should always succeed.");
136+
MidiResourceAllocators::new(channel)
24137
};
25-
channel
138+
Self {
139+
channel_allocator,
140+
resource_allocators_pool: vec![resource_allocator],
141+
resource_pool_indices: MidiResourceTable {
142+
controller: 0,
143+
key: 0,
144+
},
145+
}
146+
}
147+
148+
fn alloc_channel(
149+
&mut self,
150+
) -> Result<MidiChannel, AllMidiChannelsAreAlreadyAllocated> {
151+
self.channel_allocator.alloc()
26152
}
27153

28-
fn alloc_controller(&mut self, channel: u4) -> u7 {
29-
if let Some(next_channel) = self.next_channel {
30-
assert!(
31-
channel < next_channel,
32-
"Attempted to allocate controller on unallocated channel."
33-
);
154+
fn alloc_resource(
155+
&mut self,
156+
type_: MidiResourceType,
157+
) -> Result<(MidiChannel, MidiResource), AllMidiChannelsAreAlreadyAllocated>
158+
{
159+
if let Some(resource_allocators) = self
160+
.resource_allocators_pool
161+
.get_mut(*self.resource_pool_indices.get(type_))
162+
{
163+
match resource_allocators.resource_table.get_mut(type_).alloc() {
164+
Ok(resource_index) => {
165+
Ok((resource_allocators.channel, resource_index))
166+
}
167+
Err(AllMidiResourcesAreAlreadyAllocated) => {
168+
let resource_pool_index =
169+
self.resource_pool_indices.get_mut(type_);
170+
*resource_pool_index += 1;
171+
let resource_allocators = if let Some(resource_allocators) =
172+
self.resource_allocators_pool
173+
.get_mut(*resource_pool_index)
174+
{
175+
resource_allocators
176+
} else {
177+
let channel = self.channel_allocator.alloc()?;
178+
self.resource_allocators_pool
179+
.push(MidiResourceAllocators::new(channel));
180+
assert_eq!(
181+
*resource_pool_index,
182+
self.resource_allocators_pool.len()
183+
);
184+
self.resource_allocators_pool.last_mut().unwrap()
185+
};
186+
let resource_index = resource_allocators
187+
.resource_table
188+
.get_mut(type_)
189+
.alloc()
190+
.expect("First allocation should always succeed.");
191+
Ok((resource_allocators.channel, resource_index))
192+
}
193+
}
194+
} else {
195+
panic!("Resource pool index should always be a valid index.");
34196
}
35-
let next_controller = self
36-
.next_controller_index_by_channel
37-
.entry(channel)
38-
.or_insert_with(|| Some(0.into()));
39-
if let Some(controller) = *next_controller {
40-
*next_controller = if controller == u7::max_value() {
41-
None
42-
} else {
43-
Some((u8::from(controller) + 1).into())
44-
};
45-
controller
197+
}
198+
199+
/// Allocate a pair of resources from the same channel. If the first resource would be the
200+
/// final resource from its channel, it is leaked and a pair of resources are allocated from
201+
/// the subsequent channel instead.
202+
fn alloc_resource_2(
203+
&mut self,
204+
type_: MidiResourceType,
205+
) -> Result<
206+
(MidiChannel, MidiResource, MidiResource),
207+
AllMidiChannelsAreAlreadyAllocated,
208+
> {
209+
let r0 = self.alloc_resource(type_)?;
210+
let r1 = self.alloc_resource(type_)?;
211+
if r0.0 == r1.0 {
212+
Ok((r0.0, r0.1, r1.1))
46213
} else {
47-
panic!(
48-
"Can't allocate MIDI controller because all MIDI controllers on channel {} are in use already.",
49-
channel
50-
);
214+
let r2 = self.alloc_resource(type_)?;
215+
assert_eq!(r1.0, r2.0);
216+
Ok((r1.0, r1.1, r2.1))
51217
}
52218
}
219+
220+
fn alloc_controller(
221+
&mut self,
222+
) -> Result<(MidiChannel, MidiController), AllMidiChannelsAreAlreadyAllocated>
223+
{
224+
self.alloc_resource(MidiResourceType::Controller)
225+
}
226+
227+
fn alloc_controller_2(
228+
&mut self,
229+
) -> Result<
230+
(MidiChannel, MidiController, MidiController),
231+
AllMidiChannelsAreAlreadyAllocated,
232+
> {
233+
self.alloc_resource_2(MidiResourceType::Controller)
234+
}
235+
236+
fn alloc_key(
237+
&mut self,
238+
) -> Result<(MidiChannel, MidiKey), AllMidiChannelsAreAlreadyAllocated>
239+
{
240+
self.alloc_resource(MidiResourceType::Key)
241+
}
53242
}
54243

55244
lazy_static! {
56-
static ref CHANNEL_ALLOCATOR: Mutex<ChannelAllocator> =
57-
Mutex::new(ChannelAllocator::new());
245+
static ref ALLOCATOR: Mutex<MidiAllocator> =
246+
Mutex::new(MidiAllocator::new());
247+
}
248+
249+
pub fn alloc_channel() -> MidiChannel {
250+
ALLOCATOR
251+
.lock()
252+
.unwrap()
253+
.alloc_channel()
254+
.expect("All MIDI channels are already allocated.")
255+
}
256+
257+
pub fn alloc_controller() -> (MidiChannel, MidiController) {
258+
ALLOCATOR.lock().unwrap().alloc_controller().expect(
259+
"Allocating a new controller would require allocating a new channel, \
260+
but all MIDI channels are already allocated.",
261+
)
58262
}
59263

60-
pub fn alloc_channel() -> u4 {
61-
CHANNEL_ALLOCATOR.lock().unwrap().alloc_channel()
264+
pub fn alloc_controller_2() -> (MidiChannel, MidiController, MidiController) {
265+
ALLOCATOR.lock().unwrap().alloc_controller_2().expect(
266+
"Allocating a new controller would require allocating a new channel, \
267+
but all MIDI channels are already allocated.",
268+
)
62269
}
63270

64-
pub fn alloc_controller(channel: u4) -> u7 {
65-
let mut allocator = CHANNEL_ALLOCATOR.lock().unwrap();
66-
allocator.alloc_controller(channel)
271+
pub fn alloc_key() -> (MidiChannel, MidiKey) {
272+
ALLOCATOR.lock().unwrap().alloc_key().expect(
273+
"Allocating a new controller would require allocating a new key, \
274+
but all MIDI channels are already allocated.",
275+
)
67276
}

midi-udp-widgets-app-lib/src/num_keys_bit_7.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,15 +22,15 @@ lazy_static! {
2222

2323
fn new_num_keys_bits_7_with_space(title: String) -> NumKeysBits7WithSpace {
2424
BY_TITLE.get_or_insert(title.as_str(), || {
25-
let channel = midi::alloc_channel();
26-
let controller = midi::alloc_controller(channel);
27-
let space_controller = midi::alloc_controller(channel);
25+
let (channel, controller) = midi::alloc_controller();
26+
let (space_channel, space_controller) = midi::alloc_controller();
2827
let widget = Widget::new(
2928
title.clone(),
3029
channel,
3130
"num-keys-bits7",
3231
vec![
3332
format!("--controller={}", controller),
33+
format!("--space-channel={}", space_channel),
3434
format!("--space-controller={}", space_controller),
3535
],
3636
)

0 commit comments

Comments
 (0)