diff --git a/README.md b/README.md index 9e5cd81..142a9a2 100644 --- a/README.md +++ b/README.md @@ -9,13 +9,13 @@ LICENCE : CC BY A software server with gui for up to 4 lasers live actions. Think creative like Laser "battles", planetarium,... -No .ild file here, you run your client that generate/send point lists to LJ. Any redis capable programming langage is fine. +This software is in python 2.7 but you run and write your clients separatly in any redis capable programming langage (50+ : https://redis.io/clients). Needs at least : an etherdream DAC connected to an ILDA laser, RJ 45 IP network (gigabits only !! no wifi, 100 mpbs doesn't work well with several lasers) Nozosc : Semi modular synthetizers from Nozoids can send a lot of their inner sound curves and be displayed in many ways, i.e VCO 1 on X axis and LFO 2 on Y axis. -The server approach is based on redis. One process per etherdream is spawn to : retrieve the given point list from redis, warp, resample and manage the given etherdream DAC dialog. +The server approach is based on redis. One process per etherdream is spawn : to retrieve the given point list from redis, warp, resample and manage the given etherdream dialog. LJ supports Linux and OS X. Windows is unkown but welcome, if someone want to jump in and care about it. @@ -48,10 +48,12 @@ LJ supports Linux and OS X. Windows is unkown but welcome, if someone want to ju LJ is meant for Live, so a lot of parameters can be changed via OSC/midi, webUI,... -This is *critical and flickering reason #1* if not managed properly : use static network configuration, especially if you move your gear for different venues. +This is *critical and flickering reason #1* if not managed properly, especially you have several lasers. Our "always working solution" : +We use static network configuration, as we regularly move our gear for different venues. + Our Etherdreams controllers have static IPs defined in their SDcard from 192.168.1.1 to 192.168.1.9. Because wifi will always finally sucks for many reasons, our computers are *gigabits wired connected* with 192.168.1.10 and after. Don't trust end user gear marketing on wifi. We have a big *laser dedicated gigabit switch*. We provide Internet through wifi on a different network address like 192.168.2.x diff --git a/clients/nodeclient.js b/clients/nodeclient.js index cc40f91..7f101b4 100644 --- a/clients/nodeclient.js +++ b/clients/nodeclient.js @@ -2,7 +2,7 @@ // In compatible LJay string format (pythonic lists) var redis = require("redis"), -client = redis.createClient(6379,'192.168.1.13'); +client = redis.createClient(6379,'127.0.0.1'); diff --git a/gstt.py b/gstt.py index aa1f735..a48e1be 100644 --- a/gstt.py +++ b/gstt.py @@ -34,11 +34,6 @@ simuPL = 1 lasersIPS = ['192.168.1.5','192.168.1.6','192.168.1.3','192.168.1.4'] -# gstt.laserPLS : What point list is sent to what laser. -# ** Will be overridden by the ConfigName (see below) file values ** -lasersPLS = [0,1,2,0] - - # gstt.kpps stores kpps for each laser. # ** Will be overridden by the ConfigName (see below) file values ** kpps = [25000,25000,25000,25000] @@ -46,10 +41,6 @@ kpps = [25000,25000,25000,25000] # gstt.GridDisplay : if = 1 Curve points actually sent to PL are replaced by a grid GridDisplay = [0,0,0,0] -# with 4 laser available, 4 PL only are necessary -PL = [[],[],[],[]] - - # Transformation Matrix for each laser EDH = [[], [], [], []] @@ -72,8 +63,8 @@ maxCurvesByLaser = 4 # For glitch art : change position and decrease number of points added by newdac.py # shortline for lines shorter than 4000 (in etherdream coordinates) # i.e (0.25,3) means add 3 points at 25% on the line. -stepshortline = [ (1.0, 8)] -stepslongline = [ (0.25, 3), (0.75, 3), (1.0, 10)] +stepshortline = [(1.0, 8)] +stepslongline = [(0.25, 3), (0.75, 3), (1.0, 10)] #curveColor = [255,0,0] * maxCurvesByLaser @@ -91,8 +82,6 @@ YTimeAxe=30000 #curveX = [255,255,255] * maxCurvesByLaser #curveY = [255,255,255] * maxCurvesByLaser -Mode = 5 - point = [0,0,0] # gstt.colormode select what to display. Can be changed with /noteon 57-64 @@ -125,22 +114,8 @@ cc[22]= 60 fov = 4 * cc[22] - -''' -Also vailable with args : -v Value - -if debug = 1 you get : - - -if debug = 2 you get : -- dac errors - -''' - - JumpFlag =0 - # nice X (cc 5) Y (cc 6) curve at first cc[5] = cc[6] = 60 diff --git a/homographyp.py b/homographyp.py index 2270432..eff19b3 100755 --- a/homographyp.py +++ b/homographyp.py @@ -221,16 +221,21 @@ def newEDH(mylaser): # EDH matrix is H x Hwarp #gstt.EDH[mylaser] = np.dot(H,Hwarp) - print "Laser",mylaser,"NEW EDH computed, sending to redis..." - r.set('/EDH/'+str(mylaser), np.array2string(gstt.EDH[mylaser], separator=',')) - + print "Laser",mylaser,"New EDH computed, sending to redis..." + if r.set('/EDH/'+str(mylaser), np.array2string(gstt.EDH[mylaser], separator=',')) == True: + r.set('/order/'+str(mylaser), 1) + print "New EDH sent." + else: + print "New EDH not sent." + ''' # Laser bit 0 = 0 and bit 1 = 1 : New EDH order = r.get('/order') print order neworder = order & ~(1<< mylaser*2) neworder = neworder | (1<< 1+mylaser*2) r.set('/order', str(neworder)) - + ''' + if gstt.debug >1: print "" print "laser ", mylaser diff --git a/las.py b/las.py index c66b6c0..87742d3 100644 --- a/las.py +++ b/las.py @@ -39,9 +39,11 @@ def UserOn(laser): def NewEDH(laser): + print "New EDH requested for laser ", laser settings.Write() - print "New EDH for laser ", laser - r.set('/order/'+str(laser), 1) + homographyp.newEDH(laser) + + #r.set('/order/'+str(laser), 1) # Laser bit 0 = 0 and bit 1 = 1 : New EDH #order = r.get('/order') #neworder = order & ~(1<< laser*2) @@ -69,22 +71,21 @@ def GridOn(laser): #r.set('/order', str(neworder)) -def Resampler(laser): +def Resampler(laser,lsteps): + # lsteps is a string like : "[ (1.0, 8),(0.25, 3), (0.75, 3), (1.0, 10)]" print "Resampler change for laser ", laser + r.set('/resampler/' + str(laser), lsteps) r.set('/order/'+str(laser), 4) - - def handler(oscpath, args): pathlength = len(oscpath) - if pathlength == 2: + if pathlength == 3: laser = int(oscpath[2]) else: - laser = int(oscpath[2]) - + laser = int(oscpath[3]) # /grid/lasernumber value (0 or 1) if oscpath[1] == "grid": @@ -113,31 +114,35 @@ def handler(oscpath, args): if oscpath[1] == "ip": print "New IP for laser ", laser gstt.lasersIPS[laser]= args[0] - NewEDH(laser) + settings.Write() + # /kpps/lasernumber value # Live change of kpps is not implemented in newdac.py. Change will effect next startup. if oscpath[1] == "kpps": print "New kpps for laser ", laser, " next startup", args[0] gstt.kpps[laser]= int(args[0]) - NewEDH(laser) + settings.Write() # /angle/lasernumber value if oscpath[1] == "angle": print "New Angle modification for laser ", oscpath[2], ":", args[0] gstt.finANGLE[laser] += int(args[0]) - homographyp.newEDH(laser) NewEDH(laser) - - # /intens/lasernumber value if oscpath[1] == "intens": - print "New intensity requested for laser ", oscpath[2], ":", args[0] + print "New intensity requested for laser ", laser, ":", args[0] print "Change not implemented yet" + # /resampler/lasernumber lsteps + # lsteps is a string like "[ (1.0, 8),(0.25, 3), (0.75, 3), (1.0, 10)]" + if oscpath[1] == "resampler": + Resampler(laser,args[0]) + + # /mouse/lasernumber value (0 or 1) if oscpath[1] == "mouse": @@ -154,13 +159,11 @@ def handler(oscpath, args): if args[0] == "0": print "swap X : -1 for laser ", laser gstt.swapX[laser]= -1 - homographyp.newEDH(laser) NewEDH(laser) else: print "swap X : 1 for laser ", laser gstt.swapX[laser]= 1 - homographyp.newEDH(laser) NewEDH(laser) # /swap/Y/lasernumber value (0 or 1) @@ -168,41 +171,35 @@ def handler(oscpath, args): if args[0] == "0": print "swap Y : -1 for laser ", laser gstt.swapY[laser]= -1 - homographyp.newEDH(laser) NewEDH(laser) else: print "swap Y : 1 for laser ", laser gstt.swapY[laser]= 1 - homographyp.newEDH(laser) NewEDH(laser) # /loffset/X/lasernumber value if oscpath[1] == "loffset" and oscpath[2] == "X": - print "offset/X laser ", laser, "modified : ", args[0] + print "offset/X laser", laser, "modified to", args[0] gstt.centerX[laser] -= int(args[0]) - homographyp.newEDH(laser) NewEDH(laser) # /loffset/Y/lasernumber value if oscpath[1] == "loffset" and oscpath[2] == "Y": - print "offset/Y laser ", laser, "modified : ", args[0] + print "offset/Y laser", laser, "modified to", args[0] gstt.centerY[laser] -= int(args[0]) - homographyp.newEDH(laser) NewEDH(laser) # /scale/X/lasernumber value if oscpath[1] == "scale" and oscpath[2] == "X": - print "scale/X laser ", laser , "modified : ", args[0] + print "scale/X laser", laser , "modified to", args[0] gstt.zoomX[laser] += int(args[0]) - homographyp.newEDH(laser) NewEDH(laser) # /scale/Y/lasernumber value if oscpath[1] == "scale" and oscpath[2] == "Y": - print "scale/Y laser ", laser, "modified : ", args[0] + print "scale/Y laser", laser, "modified to", args[0] gstt.zoomY[laser] += int(args[0]) - homographyp.newEDH(laser) NewEDH(laser) ''' diff --git a/mainy.conf b/mainy.conf index db89a4c..27e1890 100644 --- a/mainy.conf +++ b/mainy.conf @@ -2,32 +2,31 @@ set = 5 curve = 0 lasernumber = 1 -debug = 0 +debug = 1 ljayserverip = 127.0.0.1 nozoscip = 127.0.0.1 bhoroscip = 127.0.0.1 [laser0] -pl = 0 color = -1 ip = 192.168.1.4 kpps = 25000 centerx = 0 -centery = 0 +centery = -650 zoomx = 49.2 -zoomy = 49.0 +zoomy = 65.0 sizex = 31450 sizey = 32000 finangle = 0.0 swapx = 1 -swapy = -1 +swapy = 1 +lsteps = [ (1.0, 8),(0.25, 3), (0.75, 3), (1.0, 10)] warpdest = [[-1500., 1500.], [ 1500., 1500.], [ 1500.,-1500.], [-1500.,-1500.]] [laser1] -pl = 1 color = -1 ip = 192.168.1.5 kpps = 25000 @@ -40,13 +39,13 @@ sizey = 32000 finangle = 0.0 swapx = 1 swapy = 1 +lsteps = [ (1.0, 8),(0.25, 3), (0.75, 3), (1.0, 10)] warpdest = [[-1500., 1500.], [ 1500., 1500.], [ 1500.,-1500.], [-1500.,-1500.]] [laser2] -pl = 2 color = -1 ip = 192.168.1.6 kpps = 25000 @@ -59,13 +58,13 @@ sizey = 32000 finangle = -0.008 swapx = 1 swapy = 1 +lsteps = [(1.0, 8),(0.25, 3), (0.75, 3), (1.0, 10)] warpdest = [[-1500., 1500.], [ 1500., 1500.], [ 1500.,-1500.], [-1500.,-1500.]] [laser3] -pl = 3 color = -1 ip = 192.168.1.3 kpps = 25000 @@ -78,6 +77,7 @@ sizey = 32000 finangle = 0.0 swapx = -1 swapy = -1 +lsteps = [(1.0, 8),(0.25, 3), (0.75, 3), (1.0, 10)] warpdest = [[-1500., 1500.], [ 1500., 1500.], [ 1500.,-1500.], diff --git a/mainyservers.py b/mainyservers.py index d525069..eacf491 100644 --- a/mainyservers.py +++ b/mainyservers.py @@ -8,10 +8,6 @@ Laser server + webUI servers (ws + OSC) todo : -r.set('/resampler/0', '[ (1.0, 8),(0.25, 3), (0.75, 3), (1.0, 10)]') -r.set('/resampler/1', '[ (1.0, 8),(0.25, 3), (0.75, 3), (1.0, 10)]') -r.set('/resampler/2', '[ (1.0, 8),(0.25, 3), (0.75, 3), (1.0, 10)]') -r.set('/resampler/3', '[ (1.0, 8),(0.25, 3), (0.75, 3), (1.0, 10)]') ''' from __future__ import absolute_import @@ -45,6 +41,7 @@ from websocket_server import WebsocketServer import types, thread, time r = redis.StrictRedis(host=gstt.LjayServerIP , port=6379, db=0) +args =[0,0] def dac_process(number, pl): while True: @@ -93,7 +90,7 @@ wsPORT = 9001 # With Bhorosc # OSC Server : relay OSC message from Bhorosc outport 8002 to UI -#oscIPin = "192.168.1.10" +#oscIPin = "192.168.1.10"s bhoroscIPin = serverIP bhoroscPORTin = 8002 @@ -107,6 +104,8 @@ bhoroscPORTout = 8001 NozoscIPout = nozoscIP NozoscPORTout = 8003 + +print bhoroscIPin oscserver = OSCServer( (bhoroscIPin, bhoroscPORTin) ) oscserver.timeout = 0 OSCRunning = True @@ -162,7 +161,7 @@ def sendnozosc(oscaddress,oscargs=''): pass #time.sleep(0.001) -# NOT USED see las.py + # OSC default path handler : send OSC message from Bhorosc 8002 to UI via websocket 9001 def handler(path, tags, args, source): @@ -173,17 +172,7 @@ def handler(path, tags, args, source): print "default handler" print "Bhorosc said : ", path, oscpath, args sendWSall(path + " " + str(args[0])) - las.handler(oscpath,args) - - ''' - # /lstt/number value - if oscpath[1] == "lstt": - sendWSall(path + " " + str(args[0])) - # /status string - if oscpath[1] == "status": - sendWSall(path + " " + str(args[0])) - ''' # RAW OSC Frame available ? @@ -213,9 +202,7 @@ def osc_thread(): time.sleep(1) osc_frame() - - - for laserid in range(0,lasernumber): # Laser not used -> led is not lit + for laserid in range(0,gstt.LaserNumber): # Laser not used -> led is not lit lstt = r.get('/lstt/'+ str(laserid)) #print "laserid", laserid,"lstt",lstt @@ -226,9 +213,9 @@ def osc_thread(): if lstt == "2": # Dac PLAYING (2) -> led is green (1) sendWSall("/lstt/" + str(laserid) + " 1") - # This is used not working : lack never change. Todo : retest. + lack= r.get('/lack/'+str(laserid)) - #print "laserid", laserid,"lack",lack + print "laserid", laserid,"lack",lack if lack == 'a': # Dac sent ACK ("a") -> led is green (1) sendWSall("/lack/" + str(laserid) +" 1") if lack == 'F': # Dac sent FULL ("F") -> led is orange (5) @@ -247,7 +234,7 @@ def osc_thread(): # last number of points sent to etherdream buffer sendWSall("/points/" + str(laserid) + " " + str(r.get('/cap/'+str(laserid)))) - #sendWSall("/plframe/" + str(laserid) ) # + " " + str(r.get('/pl/'+str(laserid)))) + sendWSall("/plframe/" + str(laserid) + " " + str(r.get('/pl/'+str(laserid)))) # WIP Too much packets -> flood webUI : Draw all PL point lists in JS canvas in WebUI @@ -276,6 +263,7 @@ def osc_thread(): # Called for every WS client connecting (after handshake) def new_client(client, server): + print("New WS client connected and was given id %d" % client['id']) sendWSall("/status Hello %d" % client['id']) @@ -284,14 +272,14 @@ def new_client(client, server): sendWSall("/kpps/" + str(laserid)+ " " + str(gstt.kpps[laserid])) if gstt.swapX[laserid] == 1: - sendWSall("swap/X/" + str(laserid)+ " 1") + sendWSall("/swap/X/" + str(laserid)+ " 1") else: - sendWSall("swap/X/" + str(laserid)+ " 0") + sendWSall("/swap/X/" + str(laserid)+ " 0") if gstt.swapY[laserid] == 1: - sendWSall("swap/Y/" + str(laserid)+ " 1") + sendWSall("/swap/Y/" + str(laserid)+ " 1") else: - sendWSall("swap/Y/" + str(laserid)+ " 0") + sendWSall("/swap/Y/" + str(laserid)+ " 0") # Called for every WS client disconnecting def client_left(client, server): @@ -302,19 +290,24 @@ def client_left(client, server): def message_received(client, server, message): if len(message) > 200: message = message[:200]+'..' + if gstt.debug >0: + print ("") print("WS Client(%d) said: %s" % (client['id'], message)) + oscpath = message.split(" ") + args[0] = float(oscpath[1]) + #print oscpath[0].split("/"),oscpath[1] + las.handler(oscpath[0].split("/"),args) # current UI has no dedicated off button so /on 0 trigs /off to bhorosc if oscpath[0] == "/on": if oscpath[1] == "1": - - sendbhorosc("/on") else: sendbhorosc("/off") else: + print "sending to bhorosc",oscpath[0],oscpath[1] sendbhorosc(oscpath[0],oscpath[1]) @@ -354,7 +347,7 @@ if r.set('/pl/3', str(random_points)) == True: print "/pl/3 ", ast.literal_eval(r.get('/pl/3')) # Order all lasers to show these random shapes at startup -> tell all 4 laser process to USER PLs -for laserid in range(0,lasernumber+1): +for laserid in range(0,gstt.LaserNumber): r.set('/order/'+str(laserid), 0) @@ -429,7 +422,7 @@ finally: for laserid in range(0,lasernumber+1): - print "Redis Etherdream",laserid,"feedback reset." + print "Laser",laserid,"feedbacks reset." r.set('/lack/'+str(laserid),64) r.set('/lstt/'+str(laserid),64) r.set('/cap/'+str(laserid),0) diff --git a/newdacp.py b/newdacp.py index 1e4620a..68571fd 100644 --- a/newdacp.py +++ b/newdacp.py @@ -3,10 +3,10 @@ # -*- mode: Python -*- ''' -LJay v0.8.0 +LJay/LJ v0.8.0 newdacp.py -Unhanced version (redis and process style) of the etherdream python library from j4cDAC. +Enhanced version (redis and process style) of the etherdream python library from j4cDAC. LICENCE : CC Sam Neurohack, pclf @@ -38,7 +38,7 @@ Geometric corrections : import socket import time import struct -from gstt import debug, PL +#from gstt import debug import gstt import math from itertools import cycle @@ -157,13 +157,13 @@ class DAC(object): if math.hypot(delta_x, delta_y) < 4000: # For glitch art : decrease lsteps - l_steps = [ (1.0, 8)] - #l_steps = gstt.stepshortline + #l_steps = [ (1.0, 8)] + l_steps = gstt.stepshortline else: # For glitch art : decrease lsteps - l_steps = [ (0.25, 3), (0.75, 3), (1.0, 10)] - #_steps = gstt.stepslongline + #l_steps = [ (0.25, 3), (0.75, 3), (1.0, 10)] + l_steps = gstt.stepslongline for e in l_steps: step = e[0] @@ -255,11 +255,11 @@ class DAC(object): #print "pl :", self.pl #print "EDH/"+str(self.mylaser),r.get('/EDH/'+str(self.mylaser)) if r.get('/EDH/'+str(self.mylaser)) == None: - #print "Laser",self.mylaser,"NO EDH !! Computing one..." + print "Laser",self.mylaser,"NO EDH !! Computing one..." homographyp.newEDH(self.mylaser) else: gstt.EDH[self.mylaser] = np.array(ast.literal_eval(r.get('/EDH/'+str(self.mylaser)))) - #print "Laser",self.mylaser,"found its EDH in redis" + print "Laser",self.mylaser,"found its EDH in redis" #print gstt.EDH[self.mylaser] ''' @@ -358,7 +358,7 @@ class DAC(object): self.pl = ast.literal_eval(r.get('/pl/'+str(self.mylaser))) else: - # recompute EDH + # Get the new EDH if order == 1: print "Laser",self.mylaser,"new EDH ORDER in redis" gstt.EDH[self.mylaser]= np.array(ast.literal_eval(r.get('/EDH/'+str(self.mylaser)))) @@ -379,7 +379,7 @@ class DAC(object): # Resampler Modification if order == 4: self.resampler = ast.literal_eval(r.get('/resampler/'+str(self.mylaser))) - print "resampler for", self.mylaser, ":",self.resampler + print "newdacp resetting lsteps for", self.mylaser, ":",self.resampler gstt.stepshortline = self.resampler[0] gstt.stepslongline[0] = self.resampler[1] gstt.stepslongline[1] = self.resampler[2] @@ -445,6 +445,7 @@ class DAC(object): # print "Took %f" % (t1 - t0, ) if not started: + print "starting laser", self.mylaser, "with", gstt.kpps[self.mylaser],"kpps" self.begin(0, gstt.kpps[self.mylaser]) started = 1 diff --git a/settings.py b/settings.py index 6c7ad04..479e976 100644 --- a/settings.py +++ b/settings.py @@ -58,7 +58,6 @@ def Read(): for i in range(4): laser = 'laser' + str(i) gstt.lasersIPS[i]= config.get(laser, 'ip') - gstt.lasersPLS[i] = config.getint(laser, 'PL') gstt.kpps[i] = config.getint(laser, 'kpps') #gstt.lasersPLcolor[i] = config.getint(laser, 'color') gstt.centerX[i]= config.getint(laser, 'centerx') diff --git a/webui/index.html b/webui/index.html index f2ca6be..2e4514f 100644 --- a/webui/index.html +++ b/webui/index.html @@ -42,6 +42,7 @@ }, onMessage: function (e) { var res = e.data.split(" "); + var pl = "" //console.log(e.data) //console.log(res[0].substring(0,6)) switch (res[0].substring(0,6)) { @@ -49,12 +50,13 @@ _WS.showstatus(e.data.slice(8)); break; case "/plfra": - console.log(e.data.slice(11)); + pl = e.data.slice(11) + console.log(pl); break; - case "/plpoi": + case "/plpoi": //console.log("plpoint"); break; - default: + default: console.log(res[0] + " " + res[1]) //console.log(res[1]) document.getElementById(res[0].slice(1)).value = res[1];