--[[ Copyright (c) 2011, AudioScience, Inc. Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php The below assumes 2 ASI2416s as follows: ASI2416-8170, static IP of localhost (see below) ASI2416-8170, static IP of remotehost (see below) ======================== opto to relay(s) ========================== simple relay(s) following an opto input /---------\ /---------\ | opto 1 |-------------------------------------->| relay 1 | \---------/ | \---------/ | /---------\ \----------->| relay 2 | \---------/ pulse extending an opto trigger /---------\ /-----------------\ /---------\ | opto 2 |-------------->| pulse extend 1s |---->| relay 3 | \---------/ | \-----------------/ \---------/ | /-----------------\ /---------\ \----------| pulse extend 2s |---->| relay 4 | \-----------------/ \---------/ relay output reflects logical combination of opto inputs /---------\ +-----+ /---------\ | opto 3 |------------------>| AND |------------>| relay 5 | \---------/ +-----+ \---------/ /---------\ | | opto 4 |---------------------/ \---------/ send opto trigger to a remote device /---------\ /----------------\ | opto 6 |-------------------------------------->| remote relay 15 | \---------/ \----------------/ /---------\ /----------------\ | opto 7 |-------------------------------------->| remote relay 16 | \---------/ \----------------/ ======================== silence detection ========================= /------------\ +------------------+ /---------\ | line out 1 |-------->| silence detector |------>| relay 6 | \------------/ +------------------+ \---------/ /------------\ +------------------+ /---------\ | line out 2 |-------->| silence detector |------>| relay 7 | \------------/ +------------------+ \---------/ ======================== push to talk ============================== /---------\ /----------------\ | opto 8 |------------------------------->| line in 3 mute | \---------/ \----------------/ /---------\ /----------------\ | opto 9 |------------------------------->| line in 4 mute | \---------/ \----------------/ =================== VOX with assigned priority ===================== /-----------\ +-------+ +----------+ /----------------\ | line in 5 |-->| meter |-->| Input |-->| line in 5 mute | \-----------/ +-------+ | RMS | \----------------/ /-----------\ +-------+ | | /----------------\ | line in 6 |-->| meter |-->| Apply |-->| line in 6 mute | \-----------/ +-------+ | priority | \----------------/ /-----------\ +-------+ | | /----------------\ | line in 7 |-->| meter |-->| Unmute a |-->| line in 7 mute | \-----------/ +-------+ | single | \----------------/ /-----------\ +-------+ | line | /----------------\ | line in 8 |-->| meter |-->| |-->| line in 8 mute | \-----------/ +-------+ +----------+ \----------------/ --]] --[[ -------------------------------------------------------------------------- See http://lua-users.org/wiki/ObjectOrientationClassClosureExample for a description of the simple class type objects that are used below. --]] --[[ -------------------------------------------------------------------------- TriggerExtender class sets up a relay to follow an opto input trigger and extends the trigger duration by a defined amount. Methods: TriggerExtender.new(opto_input_obj, relay_output_obj, extend_time_in_milliseconds) - creates a trigger extender object - returns the created object TriggerExtender.process(current_time_in_milliseconds) - runs the process by reading opto input and updating relay output - returns opto state and changed flag TriggerExtender.process_opto(current_time_in_milliseconds, opto_state, opto_changed_flag) - takes opto state and changed flags as inputs. - this should be used it a single opto input is controlling multiple relays. - returns opto state and changed flag Useage: setup pulse[1] = TriggerExtender.new(opto[2], relay[3],1000) run pulse[1].process(current_time) --]] TriggerExtender = { new = function(opto, relay, extend_time) local self = {} -- member vars self.start_time = 0 self.opto = opto self.relay = relay self.extend_time = extend_time self.state = false self.active = false -- methods self.process_opto = function(current_time, state, changed) if changed then if state then self.start_time = current_time self.active = true self.relay:set(true) end else if self.active then if asi.difftime( current_time, self.start_time ) > self.extend_time then self.active = false self.relay:set(false) end end end return state, changed end self.process = function(current_time) local state, changed = self.opto:get() return self.process_opto(current_time, state, changed) end return self end } --[[ -------------------------------------------------------------------------- SilenceToRelay class sets up a relay to follow a Silence Detector. Methods: SilenceToRelay.new(silence_detector_obj, relay_output_obj, duration) - creates a silence to relay object where duration specifies the silence duration required before the relay is triggered - returns the created object SilenceToRelay.process() - runs the process by reading the silence detector and updating relay output Useage: setup silence[1] = SilenceToRelay.new(silence, relay[4]) run silence[1].process() --]] SilenceToRelay = { new = function(silence, relay, duration) local self = {} -- member vars self.silence = silence self.relay = relay self.state = false self.active = false self.triggered = false self.duration = duration self.time_ref = 0 -- methods self.process = function(current_time) local silent, changed = self.silence:get() if changed then if silent then self.state = true self.time_ref = current_time else self.state = false self.relay:set(false) self.triggered= false end else if self.state and not self.triggered then if asi.difftime( current_time, self.time_ref) > self.duration then self.state = false self.triggered= true self.relay:set(true) end end end end return self end } --[[ -------------------------------------------------------------------------- VoxWithPriority class sets up a method for applying Vox to multiple inputs with programmable priority Methods: VoxWithPriority.new(meters, mutes, priorities, threshold) - uses lists of meters and mutes to create Vox processing oobj that has assignable priorities. - returns the created object VoxWithPriority.process() - runs the process by reading the silence detector and updating relay output Useage: setup vox = VoxWithPriority.new(meters, mutes, priorities, threshold) run vox.process() --]] VoxWithPriority = { new = function(meters, mutes, priorities, threshold) local self = {} -- member vars self.meters = meters self.mutes = mutes self.priorities = priorities self.threshold = threshold self.mute_states = {} for i,v in ipairs(self.priorities) do self.mute_states[v] = {} self.mute_states[v]["current"] = true; self.mute_states[v]["last"] = true; self.mutes[v]:set(true) end -- methods self.process = function() -- clear all mutes for i,v in ipairs(self.priorities) do self.mute_states[v]["current"] = true end -- find the highest priority to un-mute for i,v in ipairs(self.priorities) do local level = self.meters[v]:get() if level > threshold then self.mute_states[v]["current"] = false break end end -- apply mute settings for i,v in ipairs(priorities) do if self.mute_states[v]["current"] ~= self.mute_states[v]["last"] then self.mutes[v]:set(self.mute_states[v]["current"]) self.mute_states[v]["last"] = self.mute_states[v]["current"] end end end return self end } --[[ -------------------------------------------------------------------------- Initialize and create the objects that will be used in this script. --]] localhost = "192.168.1.70" remotehost = "192.168.1.80" current_time = asi.time() -- create all optos and relays opto = asi.new("hpi://"..localhost.."/adapter/gpio/inputs") relay = asi.new("hpi://"..localhost.."/adapter/gpio/outputs") -- create interface to remote host relays remote_relay = asi.new("hpi://"..remotehost.."/adapter/gpio/outputs") -- trigger extend vars triggers = {} triggers[1] = TriggerExtender.new(opto[2], relay[3], 1000) triggers[2] = TriggerExtender.new(opto[2], relay[4], 2000) -- silence detection silence = {} for x=1,2 do detector = asi.new("hpi://"..localhost.."/analog_out/"..x.."/silence_detector/state") silence[x] = SilenceToRelay.new(detector, relay[x+5], 1000) end -- push to talk push_to_talk = {} -- map push-to-talk indexes push_to_talk[8] = asi.new("hpi://"..localhost.."/analog_in/3/volume/mute") push_to_talk[9] = asi.new("hpi://"..localhost.."/analog_in/4/volume/mute") -- VOX vox_meters = {} vox_mutes = {} for x=5,8 do vox_meters[x] = asi.new("hpi://"..localhost.."/analog_in/"..x.."/meter/rms") vox_mutes[x] = asi.new("hpi://"..localhost.."/analog_in/"..x.."/volume/mute") end vox_priorities = {8,6,5,7} vox = VoxWithPriority.new(vox_meters, vox_mutes, vox_priorities, -30) while ( asi.events() ) do current_time = asi.time() -- opto 1 to relays 1,2 state, changed = opto[1]:get() if changed then relay[1]:set(state) relay[2]:set(state) end -- opto 2 pulse extend to relays 3, 4 state, changed = triggers[1].process(current_time) triggers[2].process_opto(current_time, state, changed) -- relay 5 = opto 3 and opto 4 s3, c3 = opto[3]:get() s4, c4 = opto[4]:get() if c3 or c4 then relay[5]:set(s3 and s4) end -- opto 6 and 7 sent to remote relays 15 and 16 for x=6,7 do state, changed = opto[x]:get() if changed then remote_relay[x+9]:set(state) end end -- silence detection for x=1,2 do silence[x].process(current_time) end -- push to talk for x=8,9 do state, changed = opto[x]:get() if changed then push_to_talk[x]:set(not state) end end -- VOX vox.process() end