|
1 | 1 | use lazy_static::lazy_static; |
2 | 2 | use midly::num::{u4, u7}; |
3 | | -use std::{collections::HashMap, sync::Mutex}; |
| 3 | +use std::sync::Mutex; |
4 | 4 |
|
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, |
8 | 19 | } |
9 | 20 |
|
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 { |
11 | 41 | 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 { |
12 | 114 | Self { |
13 | | - next_channel: Some(0.into()), |
14 | | - next_controller_index_by_channel: HashMap::new(), |
| 115 | + resource_table: Default::default(), |
| 116 | + channel, |
15 | 117 | } |
16 | 118 | } |
| 119 | +} |
17 | 120 |
|
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) |
24 | 137 | }; |
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() |
26 | 152 | } |
27 | 153 |
|
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."); |
34 | 196 | } |
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)) |
46 | 213 | } 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)) |
51 | 217 | } |
52 | 218 | } |
| 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 | + } |
53 | 242 | } |
54 | 243 |
|
55 | 244 | 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 | + ) |
58 | 262 | } |
59 | 263 |
|
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 | + ) |
62 | 269 | } |
63 | 270 |
|
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 | + ) |
67 | 276 | } |
0 commit comments