BogaudioModules

BogaudioModules for VCV Rack
Log | Files | Refs | README | LICENSE

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