expanders.hpp (7153B)
1 #pragma once 2 3 #include <type_traits> 4 5 #include "rack.hpp" 6 #include "module.hpp" 7 8 using namespace rack; 9 10 namespace bogaudio { 11 12 struct ExpanderMessage { 13 int channels = 0; 14 15 virtual ~ExpanderMessage() {} 16 }; 17 18 template<class MSG, class BASE> 19 struct ExpandableModule : BASE { 20 std::function<bool(Model*)> _expanderModel; 21 MSG _messages[2] {}; 22 bool _wasConnected = false; 23 24 ExpandableModule() { 25 static_assert(std::is_base_of<ExpanderMessage, MSG>::value, "type parameter MSG must derive from ExpanderMessage"); 26 static_assert(std::is_base_of<BGModule, BASE>::value, "type parameter BASE must derive from BGModule"); 27 28 BGModule::rightExpander.producerMessage = &_messages[0]; 29 BGModule::rightExpander.consumerMessage = &_messages[1]; 30 } 31 32 void setExpanderModelPredicate(std::function<bool(Model*)> p) { 33 _expanderModel = p; 34 } 35 36 bool expanderConnected() { 37 bool connected = BGModule::rightExpander.module && _expanderModel && _expanderModel(BGModule::rightExpander.module->model); 38 if (!connected && _wasConnected) { 39 _messages[1] = _messages[0] = MSG(); 40 } 41 return _wasConnected = connected; 42 } 43 44 inline MSG* toExpander() { 45 return (MSG*)BGModule::rightExpander.module->leftExpander.producerMessage; 46 } 47 48 inline MSG* fromExpander() { 49 return (MSG*)BGModule::rightExpander.consumerMessage; 50 } 51 52 void process(const BGModule::ProcessArgs& args) override { 53 BASE::process(args); 54 if (BGModule::rightExpander.module) { 55 auto m = toExpander(); 56 if (m) { 57 m->channels = BASE::_channels; 58 } 59 60 BGModule::rightExpander.module->leftExpander.messageFlipRequested = true; 61 } 62 } 63 }; 64 65 // An expander must be to the right of the expanded module to work. 66 template<class MSG, class BASE> 67 struct ExpanderModule : BASE { 68 std::function<bool(Model*)> _baseModel; 69 MSG _messages[2] {}; 70 bool _wasConnected = false; 71 72 ExpanderModule() { 73 static_assert(std::is_base_of<ExpanderMessage, MSG>::value, "type parameter MSG must derive from ExpanderMessage"); 74 static_assert(std::is_base_of<BGModule, BASE>::value, "type parameter BASE must derive from BGModule"); 75 76 BGModule::leftExpander.producerMessage = &_messages[0]; 77 BGModule::leftExpander.consumerMessage = &_messages[1]; 78 } 79 80 void setBaseModelPredicate(std::function<bool(Model*)> p) { 81 _baseModel = p; 82 } 83 84 bool baseConnected() { 85 bool connected = BGModule::leftExpander.module && _baseModel && _baseModel(BGModule::leftExpander.module->model); 86 if (!connected && _wasConnected) { 87 _messages[1] = _messages[0] = MSG(); 88 } 89 return _wasConnected = connected; 90 } 91 92 inline MSG* fromBase() { 93 return (MSG*)BGModule::leftExpander.consumerMessage; 94 } 95 96 inline MSG* toBase() { 97 return (MSG*)BGModule::leftExpander.module->rightExpander.producerMessage; 98 } 99 100 int channels() override final { 101 if (baseConnected()) { 102 return fromBase()->channels; 103 } 104 return 1; 105 } 106 107 void process(const BGModule::ProcessArgs& args) override { 108 BASE::process(args); 109 if (BGModule::leftExpander.module) { 110 BGModule::leftExpander.module->rightExpander.messageFlipRequested = true; 111 } 112 } 113 }; 114 115 template<class E, int N> 116 struct ChainableRegistry { 117 public: 118 struct Chainable { 119 E* _localElements[N] {}; 120 121 virtual ~Chainable() { 122 for (int i = 0; i < N; ++i) { 123 if (_localElements[i]) { 124 delete _localElements[i]; 125 } 126 } 127 } 128 129 void setLocalElements(std::vector<E*> es) { 130 assert(es.size() == N); 131 for (int i = 0; i < N; ++i) { 132 _localElements[i] = es[i]; 133 } 134 } 135 }; 136 137 struct ChainableBase : Chainable { 138 SpinLock _elementsLock; 139 std::vector<E*> _elements; 140 141 void setElements(const std::vector<E*>& elements) { 142 std::lock_guard<SpinLock> lock(_elementsLock); 143 _elements = elements; 144 elementsChanged(); 145 } 146 147 virtual void elementsChanged() {} 148 }; 149 150 typedef Chainable ChainableExpander; 151 152 private: 153 struct Base { 154 ChainableBase& module; 155 std::vector<E*> elements; 156 157 Base(ChainableBase& b) : module(b) { 158 std::copy(b._localElements, b._localElements + N, std::back_inserter(elements)); 159 } 160 }; 161 162 std::mutex _lock; 163 int _nextID = 1; 164 std::unordered_map<int, Base> _bases; 165 166 public: 167 int registerBase(ChainableBase& b) { 168 std::lock_guard<std::mutex> lock(_lock); 169 170 int id = _nextID; 171 ++_nextID; 172 auto p = _bases.emplace(id, Base(b)); 173 b.setElements(p.first->second.elements); 174 return id; 175 } 176 177 void deregisterBase(int id) { 178 std::lock_guard<std::mutex> lock(_lock); 179 _bases.erase(id); 180 } 181 182 bool registerExpander(int baseID, int position, ChainableExpander& x) { 183 std::lock_guard<std::mutex> lock(_lock); 184 185 assert(position > 0); 186 auto base = _bases.find(baseID); 187 if (base != _bases.end()) { 188 int i = N * position; 189 if (i < (int)base->second.elements.size()) { 190 if (base->second.elements[i]) { 191 return false; 192 } 193 } 194 else { 195 base->second.elements.resize(i + N, NULL); 196 } 197 std::copy(x._localElements, x._localElements + N, base->second.elements.begin() + i); 198 199 for (auto i = base->second.elements.begin(), n = base->second.elements.end(); i != n; ++i) { 200 if (!*i) { 201 return true; 202 } 203 } 204 base->second.module.setElements(base->second.elements); 205 206 return true; 207 } 208 209 return false; 210 } 211 212 void deregisterExpander(int baseID, int position) { 213 std::lock_guard<std::mutex> lock(_lock); 214 215 auto base = _bases.find(baseID); 216 if (base != _bases.end()) { 217 int n = N * position; 218 if (n < (int)base->second.elements.size()) { 219 int i = 0; 220 for (; i < n; ++i) { 221 if (!base->second.elements[i]) { 222 break; 223 } 224 } 225 base->second.elements.resize(i); 226 base->second.module.setElements(base->second.elements); 227 } 228 } 229 } 230 231 static ChainableRegistry& registry() { 232 static ChainableRegistry<E, N> instance; 233 return instance; 234 } 235 }; 236 237 struct ChainableExpanderMessage : ExpanderMessage { 238 int baseID = -1; 239 int position = -1; 240 }; 241 242 template<class MESSAGE, class ELEMENT, int N, class BASE> 243 struct ChainableExpandableModule 244 : ExpandableModule<MESSAGE, BASE> 245 , ChainableRegistry<ELEMENT, N>::ChainableBase 246 { 247 ChainableRegistry<ELEMENT, N>& _registry; 248 int _id = -1; 249 250 ChainableExpandableModule() 251 : _registry(ChainableRegistry<ELEMENT, N>::registry()) 252 {} 253 virtual ~ChainableExpandableModule() { 254 _registry.deregisterBase(_id); 255 } 256 257 void registerBase() { 258 _id = _registry.registerBase(*this); 259 } 260 }; 261 262 template<class MESSAGE, class ELEMENT, int N, class BASE> 263 struct ChainableExpanderModule 264 : ExpanderModule<MESSAGE, ExpandableModule<MESSAGE, BASE>> 265 , ChainableRegistry<ELEMENT, N>::ChainableExpander 266 { 267 ChainableRegistry<ELEMENT, N>& _registry; 268 bool _registered = false; 269 int _baseID = -1; 270 int _position = -1; 271 272 ChainableExpanderModule() 273 : _registry(ChainableRegistry<ELEMENT, N>::registry()) 274 {} 275 virtual ~ChainableExpanderModule() { 276 _registry.deregisterExpander(_baseID, _position); 277 } 278 279 void setBaseIDAndPosition(int baseID, int position) { 280 if (_registered && (position <= 0 || position != _position)) { 281 _registry.deregisterExpander(_baseID, _position); 282 _registered = false; 283 _baseID = 0; 284 _position = 0; 285 } 286 else if (!_registered && position > 0 && _registry.registerExpander(baseID, position, *this)) { 287 _registered = true; 288 _baseID = baseID; 289 _position = position; 290 } 291 } 292 }; 293 294 } // namespace bogaudio