Permalink
Cannot retrieve contributors at this time
| --- experimental voice allocation module | |
| -- @module lib.voice | |
| -- (voice) Slot class | |
| local Slot = {} | |
| Slot.__index = Slot | |
| function Slot.new(pool, id) | |
| local o = setmetatable({}, Slot) | |
| o.pool = pool | |
| o.id = id | |
| o.active = false | |
| o.on_release = nil | |
| o.on_steal = nil | |
| return o | |
| end | |
| function Slot:release() | |
| self.pool:release(self) | |
| end | |
| -- Rotate allocation class | |
| local Rotate = {} | |
| Rotate.__index = Rotate | |
| function Rotate.new(polyphony, slots) | |
| local o = setmetatable({}, Rotate) | |
| o.polyphony = polyphony | |
| o.slots = slots | |
| o.n = 0 | |
| return o | |
| end | |
| function Rotate:next() | |
| -- voices isn't used | |
| local i = self.n + 1 | |
| if i > self.polyphony then | |
| i = 1 | |
| end | |
| self.n = i | |
| return self.slots[i] | |
| end | |
| -- Random allocation class | |
| local Random = {} | |
| Random.__index = Random | |
| function Random.new(polyphony, slots) | |
| local o = setmetatable({}, Random) | |
| o.polyphony = polyphony | |
| o.slots = slots | |
| return o | |
| end | |
| function Random:next() | |
| -- voices isn't used | |
| return self.slots[math.random(self.polyphony)] | |
| end | |
| -- LRU allocation class | |
| local LRU = {} | |
| LRU.__index = LRU | |
| function LRU.new(polyphony, slots) | |
| local o = setmetatable({}, LRU) | |
| o.slots = slots | |
| o.count = 0 | |
| for _, s in pairs(slots) do | |
| s.n = 0 -- yuck: add field to slot | |
| end | |
| return o | |
| end | |
| function LRU:next() | |
| local count = self.count + 1 | |
| local next = self.slots[1] | |
| local free = nil | |
| self.count = count | |
| for _, slot in pairs(self.slots) do | |
| if not slot.active then | |
| if free == nil or slot.n < free.n then | |
| free = slot | |
| end | |
| elseif slot.n < next.n then | |
| next = slot | |
| end | |
| end | |
| -- choose free voice if possible | |
| if free then next = free end | |
| next.n = count | |
| return next | |
| end | |
| -- Voice class | |
| local Voice = {} | |
| Voice.__index = Voice | |
| -- constants | |
| Voice.MODE_ROTATE = 1 | |
| Voice.MODE_LRU = 2 | |
| Voice.MODE_RANDOM = 3 | |
| --- create a new Voice | |
| -- @tparam number polyphony | |
| -- @tparam number mode | |
| -- @treturn Voice | |
| function Voice.new(polyphony, mode) | |
| local o = setmetatable({}, Voice) | |
| o.polyphony = polyphony | |
| o.mode = mode | |
| o.will_steal = nil -- event callback | |
| o.will_release = nil -- event callback | |
| o.pairings = {} | |
| local slots = {} | |
| for id = 1, polyphony do | |
| slots[id] = Slot.new(o, id) | |
| end | |
| if mode == Voice.MODE_ROTATE then | |
| o.style = Rotate.new(polyphony, slots) | |
| elseif mode == Voice.MODE_RANDOM then | |
| o.style = Random.new(polyphony, slots) | |
| else | |
| o.style = LRU.new(polyphony, slots) | |
| end | |
| return o | |
| end | |
| --- get next available voice Slot from pool, stealing an active slot if needed | |
| function Voice:get() | |
| local slot = self.style:next() | |
| if slot.active then | |
| if self.will_steal then self.will_steal(slot) end | |
| -- ack; nuke any existing pairings | |
| for key, value in pairs(self.pairings) do | |
| if value == slot then | |
| self.pairings[key] = nil | |
| break | |
| end | |
| end | |
| if slot.on_steal then | |
| slot.on_steal(slot) | |
| elseif slot.on_release then | |
| slot.on_release(slot) | |
| end | |
| end | |
| slot.active = true | |
| return slot | |
| end | |
| --- push | |
| function Voice:push(key, slot) | |
| self.pairings[key] = slot | |
| end | |
| --- pop | |
| function Voice:pop(key) | |
| local slot = self.pairings[key] | |
| self.pairings[key] = nil | |
| return slot | |
| end | |
| --- return voice slot to pool | |
| -- @param slot : a Slot obtained from get() | |
| function Voice:release(slot) | |
| if slot.pool == self then | |
| if self.will_release then self.will_release(slot) end | |
| if slot.on_release then slot.on_release(slot) end | |
| slot.active = false | |
| else | |
| print("voice slot: ", slot, "does not belong to pool: ", self) | |
| end | |
| end | |
| return Voice |