newUI, python3,...

This commit is contained in:
sam 2020-09-19 14:28:56 +02:00
parent 0bb0049f02
commit e9d3009ffb
551 changed files with 22992 additions and 787437 deletions

302
libs3/LPD8.py Normal file
View file

@ -0,0 +1,302 @@
#!/usr/bin/python3
# -*- coding: utf-8 -*-
"""
LPD8
v0.7.0
LPD8 Handler.
Start a dedicated thread to handle incoming events from LPD8 midi controller.
Depending on selected destination computer (Prog Chg + Pad number) actions will be done
locally or forwarded via OSC to given computer. Remote computer must run bhorpad or
maxwellator.
# Note
# Program Change button selected : change destination computer
# CC rotary -> midi CC.
by Sam Neurohack
from /team/laser
"""
import time
import rtmidi
from rtmidi.midiutil import open_midiinput
from threading import Thread
from rtmidi.midiconstants import (CHANNEL_PRESSURE, CONTROLLER_CHANGE, NOTE_ON, NOTE_OFF,
PITCH_BEND, POLY_PRESSURE, PROGRAM_CHANGE)
from mido import MidiFile
import mido
import sys
import os
ljpath = r'%s' % os.getcwd().replace('\\','/')
from . import midi3, launchpad
#import midimacros, maxwellmacros
import traceback
from queue import Queue
#from libs import macros
import json, subprocess
from .OSC3 import OSCServer, OSCClient, OSCMessage
import socket
myIP = "127.0.0.1"
print('LPD8 startup...')
myHostName = socket.gethostname()
print(("Name of the localhost is {}".format(myHostName)))
#myIP = socket.gethostbyname(myHostName)
print(("IP address of the localhost is {}".format(myIP)))
print(('Used IP', myIP))
OSCinPort = 8080
maxwellatorPort = 8090
LPD8queue = Queue()
mode = "maxwell"
mididest = 'Session 1'
midichannel = 1
CChannel = 0
CCvalue = 0
Here = -1
ModeCallback = ''
computerIP = ['127.0.0.1','192.168.2.95','192.168.2.52','127.0.0.1',
'127.0.0.1','127.0.0.1','127.0.0.1','127.0.0.1']
computer = 0
# /cc cc number value
def cc(ccnumber, value, dest=mididest):
midi3.MidiMsg([CONTROLLER_CHANGE+midichannel-1,ccnumber,value], dest)
def NoteOn(note,velocity, dest=mididest):
midi3.NoteOn(note,velocity, mididest)
def NoteOff(note, dest=mididest):
midi3.NoteOn(note, mididest)
def ComputerUpdate(comput):
global computer
computer = comput
# Client to export buttons actions from LPD8 or bhoreal
def SendOSC(ip,port,oscaddress,oscargs=''):
oscmsg = OSCMessage()
oscmsg.setAddress(oscaddress)
oscmsg.append(oscargs)
osclient = OSCClient()
osclient.connect((ip, port))
print(("sending OSC message : ", oscmsg, "to", ip, ":", port))
try:
osclient.sendto(oscmsg, (ip, port))
oscmsg.clearData()
return True
except:
print(('Connection to', ip, 'refused : died ?'))
return False
#
# Events from LPD8 buttons
#
# Process events coming from LPD8 in a separate thread.
def MidinProcess(LPD8queue):
global computer
while True:
LPD8queue_get = LPD8queue.get
msg = LPD8queue_get()
#print (msg)
# Note
if msg[0]==NOTE_ON:
# note mode
ModeNote(msg[1], msg[2], mididest)
'''
# ModeOS
if msg[2] > 0:
ModeOS(msg[0])
'''
# Program Change button selected : change destination computer
if msg[0]==PROGRAM_CHANGE:
print(("Program change : ", str(msg[1])))
# Change destination computer mode
print(("Destination computer",int(msg[1])))
computer = int(msg[1])
# CC rotary -> midi CC.
if msg[0] == CONTROLLER_CHANGE:
print(("CC :", msg[1], msg[2]))
if computer == 0 or computer == 1:
cc(int(msg[1]), int(msg[2]))
else:
SendOSC(computerIP[computer-1], maxwellatorPort, '/cc', [int(msg[1]), int(msg[2])])
#
# Notes = midi notes
#
def ModeNote(note, velocity, mididest):
print(('computer',computer))
# todo : decide whether its 0 or 1 !!!
if computer == 0 or computer == 1:
midi3.NoteOn(arg, velocity, mididest)
if velocity == 127:
pass
#print ('NoteON', BhorNoteXY(x,y),notename , "velocity", velocity )
#Disp(notename)
else:
midi3.NoteOff(arg)
#print ('NoteOFF', BhorNoteXY(x,y),notename , "velocity", velocity )
#
# Notes = OS Macros
#
def ModeOS(arg):
macroname = 'n'+arg
macronumber = findMacros(macroname,'OS')
if macronumber != -1:
eval(macros['OS'][macronumber]["code"])
else:
print("no Code yet")
LPD8queue = Queue()
# LPD8 Mini call back : new msg forwarded to LPD8 queue
class LPD8AddQueue(object):
def __init__(self, port):
self.port = port
#print("LPD8AddQueue", self.port)
self._wallclock = time.time()
def __call__(self, event, data=None):
message, deltatime = event
self._wallclock += deltatime
print()
print(("[%s] @%0.6f %r" % (self.port, self._wallclock, message)))
LPD8queue.put(message)
#
# Modes :
#
# Load Matrix only macros (for the moment) in macros.json
def LoadMacros():
global macros
print()
print("Loading LPD8 Macros...")
if os.path.exists('libs/matrix.json'):
#print('File libs/matrix.json exits')
f=open("libs/matrix.json","r")
elif os.path.exists(ljpath+'/../../libs/matrix.json'):
#print('File '+ljpath+'/../../libs/matrix.json exits')
f=open(ljpath+"/../../libs/matrix.json","r")
s = f.read()
macros = json.loads(s)
print((len(macros['OS']),"Macros"))
print("Loaded.")
# return macroname number for given type 'OS', 'Maxwell'
def findMacros(macroname,macrotype):
#print("searching", macroname,'...')
position = -1
for counter in range(len(macros[macrotype])):
#print (counter,macros[macrotype][counter]['name'],macros[macrotype][counter]['code'])
if macroname == macros[macrotype][counter]['name']:
#print(macroname, "is ", counter)
position = counter
return position
# Not assigned buttons
def DefaultMacro(arg):
print(("DefaultMacro", arg))
#
# Default macros
#
LPD8macros = {
"n1": {"command": DefaultMacro, "default": 1},
"n2": {"command": DefaultMacro, "default": 2},
"n3": {"command": DefaultMacro, "default": 3},
"n4": {"command": DefaultMacro, "default": 4},
"n5": {"command": DefaultMacro, "default": 5},
"n6": {"command": DefaultMacro, "default": 6},
"n7": {"command": DefaultMacro, "default": 7},
"n8": {"command": DefaultMacro, "default": 8}
}
def Run(macroname, macroargs=''):
doit = LPD8macros[macroname]["command"]
if macroargs=='':
macroargs = LPD8macros[macroname]["default"]
#print("Running", doit, "with args", macroargs )
doit(macroargs)
LoadMacros()

2873
libs3/OSC3.py Normal file

File diff suppressed because it is too large Load diff

212
libs3/OSCom.py Normal file
View file

@ -0,0 +1,212 @@
#!/usr/bin/python3
# -*- coding: utf-8 -*-
# -*- mode: Python -*-
'''
OSCcom for jamidi v0.1b
OSCom.Start(serverIP, OSCPORT)
default handler : handler(path, tags, args, source)
register particular OSC command in Start(): i.e oscserver.addMsgHandler( "/n", Note)
Launch
print("Launching OSC Server", serverIP,':', OSCPORT)
OSCom.Start(serverIP, OSCPORT)
'''
from . import midi3
#import socket
import types, json
from .OSC3 import OSCServer, OSCClient, OSCMessage
import _thread, time
from . import gstt
import WScom, UDPcom
from . import midi3
#base36 = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"]
def GetTime():
return time.strftime("%a, %d %b %Y %H:%M:%S", time.localtime())
# this method of reporting timeouts only works by convention
# that before calling handle_request() field .timed_out is
# set to False
def handle_timeout(self):
self.timed_out = True
def Start(serverIP, OSCPORT):
global oscserver
#print(GetTime(),gstt.oscname, gstt.Confs[gstt.oscname][0]["midichan"])
#print(gstt.Confs)
#print(gstt.Confs[gstt.oscname])
for i in range(len(gstt.Confs[gstt.oscname])):
print((GetTime(),gstt.oscname, gstt.Confs[gstt.oscname][i]["midichan"]))
oscserver = OSCServer( (serverIP, OSCPORT) )
oscserver.timeout = 0
# funny python's way to add a method to an instance of a class
import types
oscserver.handle_timeout = types.MethodType(handle_timeout, oscserver)
oscserver.addMsgHandler( "default", handler )
oscserver.addMsgHandler( "/n", Note)
oscserver.addMsgHandler( "/c", CC)
oscserver.addMsgHandler( "/p", PB)
_thread.start_new_thread(osc_thread, ())
# RAW OSC Frame available ?
def OSCframe():
# clear timed_out flag
oscserver.timed_out = False
# handle all pending requests then return
while not oscserver.timed_out:
oscserver.handle_request()
# OSC server Thread : handler, dacs reports and simulator points sender to UI.
def osc_thread():
#print("osc Thread launched")
try:
while True:
time.sleep(0.005)
OSCframe()
except Exception as e:
import sys, traceback
print('\n---------------------')
print(('Exception: %s' % e))
print('- - - - - - - - - - -')
traceback.print_tb(sys.exc_info()[2])
print("\n")
# Properly close the system. Todo
def Stop():
oscserver.close()
# default handler
def handler(path, tags, args, source):
oscaddress = ''.join(path.split("/"))
print()
print(("Jamidi Default OSC Handler got from " + str(source[0]),"OSC msg", path, "args", args))
#print("OSC address", path)
#print("find.. /bhoreal ?", path.find('/bhoreal'))
if len(args) > 0:
#print("with args", args)
pass
'''
# for example
if path == '/truc':
arg1 = args[0]
arg2 = args[1])
'''
'''
MIDI NOTES
=n in ORCA
/n in OSC
ORCA OSC
=nmonv /n m o n v
m : midi channel (0-15 / ORCA 0-F)
o : octave (0-8 / ORCA 0-7)
n : Note A to G
v : velocity 0-Z will output (v/36)*127
'''
def Note(path, tags, args, source):
#print('Note from ORCA received',args)
midichannel = int(args[0],36)
octave = int(args[1],36)
note = args[2]
velocity = int((int(args[3],36)/36)*127)
if note.istitle() == True:
notename = str(note)+ str(octave)
else:
notename = str(note)+ "#"+ str(octave)
if gstt.debug > 0:
print(("incoming note", note, octave, notename, midi3.note2midi(notename) ))
for mididevice in midi3.findJamDevices(gstt.oscname):
midi3.NoteOn(midi3.note2midi(notename), velocity, mididevice)
#midi3.NoteOn(int(wspath[1]), int(wspath[2]), gstt.Confs[wscommand[1]][0]["mididevice"])
'''
CC
=c in ORCA
/c in OSC
ORCA OSC
=cmcd /c m n d
m : midi channel
n : number (0-35 / ORCA 0-Z)
d : data 0-Z will output (d/36)*127
'''
def CC(path, tags, args, source):
midichannel = int(args[0],36)
ccvr = int(args[1],36)
ccvl = int((int(args[2],36)/36)*127)
if gstt.debug > 0:
print(("ccvr=%d/ccvl=%d"%(ccvr,ccvl)))
if gstt.oscname == "ocs2":
gstt.crtvalueOCS2[ccvr]=ccvl
else:
gstt.crtvalueMMO3[ccvr]=ccvl
for mididevice in midi3.findJamDevices(gstt.oscname):
midi3.cc(gstt.Confs[gstt.oscname][0]["midichan"], ccvr, ccvl, mididevice)
def PB(path, tags, args, source):
#print("Pitch number",ccnumber, value)
midichannel = int(args[0])
ccnumber = int(args[1])
ccdata = int(args[3])
'''
# If needed to send some OSC
def SendOSC(ip,port,oscaddress,oscargs=''):
oscmsg = OSCMessage()
oscmsg.setAddress(oscaddress)
oscmsg.append(oscargs)
osclient = OSCClient()
osclient.connect((ip, port))
if gstt.debug == True :
print("sending OSC message : ", oscmsg, "to", ip, ":", port)
try:
osclient.sendto(oscmsg, (ip, port))
oscmsg.clearData()
return True
except:
print ('Connection to', ip, 'refused : died ?')
return False
'''

0
libs3/__init__.py Normal file
View file

107
libs3/alink.py Normal file
View file

@ -0,0 +1,107 @@
#!/usr/bin/python3
# -*- coding: utf-8 -*-
# -*- mode: Python -*-
'''
Ableton Link
LICENCE : CC
Sam Neurohack
Get:
git clone --recursive https://github.com/gonzaloflirt/link-python.git
Build:
Make sure python 3 is installed on your system.
mkdir build
cd build
cmake ..
cmake --build .
'''
import midix
import sys
prevphase = 0
bpm = 120
def Start():
global lnk
import link
print("Link ENABLED")
lnk = link.Link(120)
lnk.enabled = True
lnk.startStopSyncEnabled = True
linked = True
def BeatEvent():
global lnk, prevphase
lnkstr = lnk.captureSessionState()
link_time = lnk.clock().micros();
tempo_str = '{0:.2f}'.format(lnkstr.tempo())
bpm = float(tempo_str)
#beatstep.SendOSCUI('/bpm', [bpm])
beats_str = '{0:.2f}'.format(lnkstr.beatAtTime(link_time, 0))
playing_str = str(lnkstr.isPlaying()) # always False ???
phase = lnkstr.phaseAtTime(link_time, 4)
# new beat ?
if int(phase) != prevphase:
prevphase = int(phase)
#print("LINK BPM:",bpm)
sys.stdout.write("Beat "+str(beats_str) + ' \r')
sys.stdout.flush()
midix.SendUI('/beats', [beats_str])
#alink.SendOSCUI('/states/cc/'+str(ccnumber), [value])
currentbeat = float(beats_str)
#midix.SendAU('/aurora/beats', beats_str)
#AllStatus("Beat "+str(beats_str))
# Change current Link Tempo.
def newtempo(tempo):
global lnk
#print("val2", val2, "tempo", tempo)
if linked == True:
lnk.enabled = False
lnk.startStopSyncEnabled = False
lnk = link.Link(tempo)
lnk.enabled = True
lnk.startStopSyncEnabled = True
bpm = tempo
print(("New BPM", bpm))
midix.SendUI('/bpm', [bpm])
else:
print("Link is disabled")
#
def BPMAdj(val1, keyname):
print((gstt.currentbpm))
# + 1
if val1 == 1:
newtempo(gstt.currentbpm+1)
# -1
if val1 == 127 and gstt.currentbpm > 0:
newtempo(gstt.currentbpm-1)

370
libs3/artnet.py Normal file
View file

@ -0,0 +1,370 @@
#!/usr/bin/python3
# -*- coding: utf-8 -*-
"""
ArtNet/DMX Handler :
v0.7.0
by Sam Neurohack
from /team/laser
Artnet receving code from https://github.com/kongr45gpen/simply-artnet
Laser selection
one universe / laser
Plugin selection
banck change/scene/
"""
import random
import pysimpledmx
from serial.tools import list_ports
import serial,time
from threading import Thread
import socket
import struct
import types
from sys import platform, version
import sys
import argparse, traceback
import os
import log
is_py2 = version[0] == '2'
if is_py2:
from OSC import OSCServer, OSCClient, OSCMessage
else:
from OSC3 import OSCServer, OSCClient, OSCMessage
ljpath = r'%s' % os.getcwd().replace('\\','/')
# import from shell
#sys.path.append('../../libs')
#import from LJ
sys.path.append(ljpath +'/libs/')
import lj23layers as lj
#
# Init
#
OSCinPort = 8032
sock=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
sock.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
sock.bind(('',6454))
dmxeq = {}
dmxstates = []
dmxinit = False
universe = []
for i in range(1,514):
dmxstates.append(-1)
print ("")
log.infog("Artnet v0.1")
print ("Arguments parsing if needed...")
argsparser = argparse.ArgumentParser(description="Artnet & DMX for LJ")
argsparser.add_argument("-u","--universe",help="Universe, not implemented (0 by default)",type=int)
argsparser.add_argument("-s","--subuniverse",help="Subniverse, not implemented (0 by default)",type=int)
argsparser.add_argument("-r","--redisIP",help="IP of the Redis server used by LJ (127.0.0.1 by default) ",type=str)
argsparser.add_argument("-m","--myIP",help="Local IP (127.0.0.1 by default) ",type=str)
argsparser.add_argument("-v","--verbose",help="Verbosity level (0 by default)",type=int)
args = argsparser.parse_args()
# Universe
if args.universe:
universenb = args.universe
else:
universenb = 0
# Universe
if args.subuniverse:
subuniversenb = args.subuniverse
else:
subuniversenb = 0
# Debug level
if args.verbose:
debug = args.verbose
else:
debug = 0
# Redis Computer IP
if args.redisIP != None:
redisIP = args.redisIP
else:
redisIP = '127.0.0.1'
# myIP
if args.myIP != None:
myIP = args.myIP
else:
myIP = '127.0.0.1'
r = lj.Config(redisIP, 255, "artnet")
lj.WebStatus("Artnet init...")
def lhex(h):
return ':'.join(x.encode('hex') for x in h)
def senddmx0():
for channel in range (1,512):
senddmx(channel,0)
def senddmx(channel, value):
print("Setting channel %d to %d" % (i,value))
#mydmx.setChannel((channel + 1 ), value, autorender=True)
# calling render() is better more reliable to actually sending data
# Some strange bug. Need to add one to required dmx channel is done automatically
mydmx.setChannel((channel ), value)
mydmx.render()
print("Sending DMX Channel : ", str(channel), " value : ", str(value))
def updateDmxValue(channel, val):
#
if dmxstates[channel] == -1:
dmxstates[channel] = val
# DMX UPDATE!!! WOW!!!
if dmxstates[channel] != val:
dmxstates[channel] = val
print("updating channel", channel, "with ", val)
if mydmx != False:
senddmx(channel, ord(val))
# Search for DMX devices
#ljnozoids.WebStatus("Available serial devices")
print("Available serial devices...")
ports = list(list_ports.comports())
portnumber = 0
# Get all serial ports names
for i, p in enumerate(ports):
print(i, ":", p)
if p[0]== "/dev/ttyUSB0":
portname[portnumber] = p[0]
portnumber += 1
if platform == 'darwin' and p[1].find("DMX USB PRO") != -1:
portname[portnumber] = p[0]
portnumber += 1
# ljnozoids.WebStatus("Found " + str(portnumber) +" Nozoids.")
print("Found", portnumber, "DMX devices")
if portnumber > 0:
print("with serial names", portname)
mydmx = pysimpledmx.DMXConnection(gstt.serdmx[0])
senddmx0()
time.sleep(1)
# Send a random value to channel 1
vrand=random.randint(0,255)
senddmx(1,vrand)
else:
mydmx = False
print("No DMX found, Art-Net receiver only.")
#
# OSC
#
oscserver = OSCServer( (myIP, OSCinPort) )
oscserver.timeout = 0
#oscrun = True
# this method of reporting timeouts only works by convention
# that before calling handle_request() field .timed_out is
# set to False
def handle_timeout(self):
self.timed_out = True
# funny python's way to add a method to an instance of a class
oscserver.handle_timeout = types.MethodType(handle_timeout, oscserver)
# default handler
def OSChandler(path, tags, args, source):
oscaddress = ''.join(path.split("/"))
print(("Default OSC Handler : msg from Client : " + str(source[0]),))
print(("OSC address", path, "with",))
if len(args) > 0:
print(("args", args))
else:
print("noargs")
#oscIPout = str(source[0])
#osclient.connect((oscIPout, oscPORTout))
# RAW OSC Frame available ?
def OSCframe():
# clear timed_out flag
print ("oscframe")
oscserver.timed_out = False
# handle all pending requests then return
while not oscserver.timed_out:
oscserver.handle_request()
# Stop osc server
def OSCstop():
oscrun = False
oscserver.close()
# /sendmx channel value
def OSCsendmx(path, tags, args, source):
channel = args[0]
val = args[1]
updateDmxValue(channel, val)
lj.addOSCdefaults(oscserver)
lj.SendLJ("/pong", "artnet")
lj.WebStatus("Artnet Running...")
log.infog("Artnet running...")
print()
oscserver.addMsgHandler( "/sendmx", OSCsendmx )
#
# Running...
#
'''
print ("Starting, use Ctrl+C to stop")
print (lj.oscrun)
'''
try:
while lj.oscrun:
data = sock.recv(10240)
if len(data) < 20:
continue
if data[0:7] != "Art-Net" or data[7] != "\0":
print("artnet package")
#lj.WebStatus("Artnet package")
continue
OSCframe()
if ord(data[8]) != 0x00 or ord(data[9]) != 0x50:
print("OpDmx")
continue
print(("oscrun", lj.oscrun))
protverhi = ord(data[10])
protverlo = ord(data[11])
sequence = ord(data[12])
physical = ord(data[13])
subuni = ord(data[14])
net = ord(data[15])
lengthhi = ord(data[16])
length = ord(data[17])
dmx = data[18:]
print((data[0:7], "version :",lhex(data[10])+lhex(data[11]), "sequence :", sequence, "physical", physical, "subuni",subuni,"net", net))
for i in range(0,510):
updateDmxValue(i+1,dmx[i])
except Exception:
traceback.print_exc()
finally:
lj.ClosePlugin()
sock.close()
OSCstop()
'''
import sys, socket, math, time
from ctypes import *
class Artnet:
class ArtNetDMXOut(LittleEndianStructure):
PORT = 0x1936
_fields_ = [("id", c_char * 8),
("opcode", c_ushort),
("protverh", c_ubyte),
("protver", c_ubyte),
("sequence", c_ubyte),
("physical", c_ubyte),
("universe", c_ushort),
("lengthhi", c_ubyte),
("length", c_ubyte),
("payload", c_ubyte * 512)]
def __init__(self):
self.id = b"Art-Net"
self.opcode = 0x5000
self.protver = 14
self.universe = 0
self.lengthhi = 2
def __init__(self):
self.artnet = Artnet.ArtNetDMXOut()
self.S = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
for i in range(512):
self.artnet.payload[i] = 0
def send(self,data,IP,port):
# send(送るデータ,IPアドレス,ポート番号)
self.artnet.universe = port
for i in range(512):
if(i < len(data)):
self.artnet.payload[i] = data[i]
else:
break
self.S.sendto(self.artnet,(IP,Artnet.ArtNetDMXOut.PORT))
if __name__ == '__main__':
artnet = Artnet()
data = [0] * 512
for i in range(150):
data[i*3+0] = 0
data[i*3+1] = 0
data[i*3+2] = 0
artnet.send(data,"133.15.42.111",5)
'''

292
libs3/audio.py Normal file
View file

@ -0,0 +1,292 @@
#!/usr/bin/python3
# -*- coding: utf-8 -*-
"""
Audio Spectrum analyser
v0.7.0
- summed given fft in n bands, but re normalized between 0 - 70?
- Peaks L and R
- amplitude for given target frequency and PEAK frequency
- "music note" to given frequency
- Real FFT, Imaginary FFT, Real + imaginary FFT
- threshold detection
todo :
by Sam Neurohack
from /team/laser
for python 2 & 3
Stereo : CHANNELS = 2
mono : CHANNELS = 1
"""
import numpy as np
import pyaudio
from math import log, pow
#import matplotlib.pyplot as plt
#from scipy.interpolate import Akima1DInterpolator
#import matplotlib.pyplot as plt
DEVICE = 3
CHANNELS = 2
START = 0
RATE = 44100 # time resolution of the recording device (Hz)
CHUNK = 4096 # number of data points to read at a time. Almost 10 update/second
TARGET = 2100 # show only this one frequency
A4 = 440
C0 = A4*pow(2, -4.75)
name = ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"]
data = []
p = pyaudio.PyAudio() # start the PyAudio class
stream = p.open(format = pyaudio.paInt16, channels = CHANNELS, input_device_index = DEVICE, rate=RATE, input=True,
frames_per_buffer=CHUNK) #uses default input device
#
# Audio devices & audiogen functions
#
def list_devices():
# List all audio input devices
p = pyaudio.PyAudio()
i = 0
n = p.get_device_count()
print((n,"devices found"))
while i < n:
dev = p.get_device_info_by_index(i)
if dev['maxInputChannels'] > 0:
print((str(i)+'. '+dev['name']))
i += 1
def valid_input_devices(self):
"""
See which devices can be opened for microphone input.
call this when no PyAudio object is loaded.
"""
mics=[]
for device in range(self.p.get_device_count()):
if self.valid_test(device):
mics.append(device)
if len(mics)==0:
print("no microphone devices found!")
else:
print(("found %d microphone devices: %s"%(len(mics),mics)))
return mics
def loop():
try:
#plt.ion()
#plt.axis([x[0], x[-1], -0.1, max_f])
fftbands = [0,1,2,3,4,5,6,7,8,9]
plt.xlabel('frequencies')
plt.ylabel('amplitude')
data = audioinput()
drawfreq, fft = allfft(data)
#lines = plt.plot(drawfreq, fft)
#plt.axis([drawfreq[0], drawfreq[-1], 0, np.max(fft)])
#plt.plot(drawfreq, fft)
#plt.show()
#line, = plt.plot(fftbands, levels(fft,10))
line, = plt.plot(drawfreq, fft)
#while True :
for i in range(50):
data = audioinput()
# smooth the FFT by windowing data
#data = data * np.hanning(len(data))
# conversion to -1 to +1
# normed_samples = (data / float(np.iinfo(np.int16).max))
# Left is channel 0
dataL = data[0::2]
# Right is channel 1
dataR = data[1::2]
# Peaks L and R
peakL = np.abs(np.max(dataL)-np.min(dataL))/CHUNK
peakR = np.abs(np.max(dataR)-np.min(dataR))/CHUNK
# print(peakL, peakR)
drawfreq, fft = allfft(data)
#fft, fftr, ffti, fftb, drawfreq = allfft(data)
#line.set_ydata(levels(fft,10))
line.set_ydata(fft)
plt.pause(0.01)
#print(drawfreq)
#print(fft)
#print (levels(fft,10))
#line.set_ydata(fft)
#plt.pause(0.01) # pause avec duree en secondes
# lines = plt.plot(x, y)
#lines[0].set_ydata(fft)
#plt.legend(['s=%4.2f' % s])
#plt.draw()
#plt.show()
'''
targetpower,freqPeak = basicfft(audioinput(stream))
print("amplitude", targetpower, "@", TARGET, "Hz")
if freqPeak > 0.0:
print("peak frequency: %d Hz"%freqPeak, pitch(freqPeak))
'''
plt.show()
except KeyboardInterrupt:
stream.stop_stream()
stream.close()
p.terminate()
print("End...")
# Close properly
def close():
stream.stop_stream()
stream.close()
p.terminate()
# Return "music note" to given frequency
def pitch(freq):
h = round(12*(log(freq/C0)/log(2)))
octave = h // 12
n = h % 12
return name[n] + str(octave)
# Return summed given fft in n bands, but re normalized 0 - 70
def levels(fourier, bands):
size = int(len(fourier))
levels = [0.0] * bands
# Add up for n bands
# remove normalizer if you want raw added data in all bands
normalizer = size/bands
#print (size,bands,size/bands)
levels = [sum(fourier[I:int(I+size/bands)])/normalizer for I in range(0, size, int(size/bands))][:bands]
for band in range(bands):
if levels[band] == np.NINF:
levels[band] =0
return levels
# read CHUNK size in audio buffer
def audioinput():
# When reading from our 16-bit stereo stream, we receive 4 characters (0-255) per
# sample. To get them in a more convenient form, numpy provides
# fromstring() which will for each 16 bits convert it into a nicer form and
# turn the string into an array.
return np.fromstring(stream.read(CHUNK),dtype=np.int16)
# power for given TARGET frequency and PEAK frequency
# do fft first. No conversion in 'powers'
def basicfft(data):
#data = data * np.hanning(len(data)) # smooth the FFT by windowing data
fft = abs(np.fft.fft(data).real)
#fft = 10*np.log10(fft)
fft = fft[:int(len(fft)/2)] # first half of fft
freq = np.fft.fftfreq(CHUNK,1.0/RATE)
freq = freq[:int(len(freq)/2)] # first half of FFTfreq
assert freq[-1]>TARGET, "ERROR: increase chunk size"
# return power for given TARGET frequency and peak frequency
return fft[np.where(freq > TARGET)[0][0]], freq[np.where(fft == np.max(fft))[0][0]]+1
# todo : Try if data = 1024 ?
# in "power' (0-70?) get Real FFT, Imaginary FFT, Real + imaginary FFT
def allfft(data):
#print ("allfft", len(data))
fft = np.fft.fft(data)
#print("fft",len(fft))
fftr = 10*np.log10(abs(fft.real))[:int(len(data)/2)]
ffti = 10*np.log10(abs(fft.imag))[:int(len(data)/2)]
fftb = 10*np.log10(np.sqrt(fft.imag**2+fft.real**2))[:int(len(data)/2)]
#print("fftb",len(fftb))
drawfreq = np.fft.fftfreq(np.arange(len(data)).shape[-1])[:int(len(data)/2)]
drawfreq = drawfreq*RATE/1000 #make the frequency scale
#return fft, fftr, ffti, fftb, drawfreq
return drawfreq, fftb
# Draw Original datas
# X : np.arange(len(data))/float(rate)*1000
# Y : data
# Draw real FFT
# X : drawfreq
# Y : fftr
# Draw imaginary
# X : drawfreq
# Y : ffti
# Draw Real + imaginary
# X : drawfreq
# Y : fftb
# True if any value in the data is greater than threshold and after a certain delay
def ding(right,threshold):
if max(right) > threshold and time.time() - last_run > min_delay:
return True
else:
return False
last_run = time.time()
if __name__ == "__main__":
loop()
'''
x = np.linspace(0, 3, 100)
k = 2*np.pi
w = 2*np.pi
dt = 0.01
t = 0
for i in range(50):
y = np.cos(k*x - w*t)
if i == 0:
line, = plt.plot(x, y)
else:
line.set_ydata(y)
plt.pause(0.01) # pause avec duree en secondes
t = t + dt
plt.show()
'''

271
libs3/bhoreal.py Normal file
View file

@ -0,0 +1,271 @@
# coding=UTF-8
"""
Bhoreal
v0.7.0
Bhoreal Led matrix Handler
Start a dedicated thread to handle incoming events from launchpad.
Cls()
AllColor(color)
StarttBhoreal(port) : Start animation
Led Matrix can be access with X and Y coordinates and as midi note (0-63)
NoteOn(note,color)
NoteOff(note)
NoteOnXY(x,y,color):
NoteOffXY(x,y):
NoteXY(x,y):
gstt.BhorLeds[] array stores matrix current state
by Sam Neurohack
from /team/laser
"""
import time
from rtmidi.midiconstants import (CHANNEL_PRESSURE, CONTROLLER_CHANGE, NOTE_ON, NOTE_OFF,
PITCH_BEND, POLY_PRESSURE, PROGRAM_CHANGE)
from . import gstt, midi3
import sys
gstt.BhorLeds = [0]*65
Here = -1
from queue import Queue
def NoteOn(note,color):
print(("bhoreal noteon", note, color))
msg = [NOTE_ON, note, color]
midi3.send(msg,"Bhoreal")
gstt.BhorLeds[note]=color
def NoteOff(note):
msg = [NOTE_OFF, note, 0]
midi3.send(msg,"Bhoreal")
gstt.BhorLeds[note]=0
def NoteOnXY(x,y,color):
#print x,y
msg = [NOTE_ON, NoteXY(x,y), color]
midi3.send(msg,"Bhoreal")
gstt.BhorLeds[NoteXY(x,y)]=color
def NoteOffXY(x,y):
msg = [NOTE_OFF, NoteXY(x,y), 0]
midi3.send(msg,"Bhoreal")
gstt.BhorLeds[NoteXY(x,y)]=0
# Leds position are humans numbers 1-8. So -1 for pythonic array position 0-7
def NoteXY(x,y):
note = (x -1)+ (y-1) * 8
return note
def Index(note):
y=note/8
x=note%8
#print "Note : ",note
#print "BhorIndex : ", x+1,y+1
return int(x+1),int(y+1)
#
# Bhoreal Start anim
#
# AllColor for bhoreal on given port
def AllColor(port,color):
for led in range(0,64,1):
msg = [NOTE_ON, led, color]
midi3.send(msg,"Bhoreal")
# Cls for bhoreal on given port
def Cls(port):
for led in range(0,64,1):
msg = [NOTE_OFF, led, 0]
midi3.send(msg,"Bhoreal")
def StartBhoreal(port):
Cls(port)
time.sleep(0.2)
for color in range(0,126,1):
AllColor(port,color)
time.sleep(0.02)
time.sleep(0.2)
Cls(port)
def UpdateLine(line,newval):
if gstt.BhorealHere != -1:
for led in range(8):
NoteOffXY(led,line)
NoteOnXY(newval,line,64)
# Update Laser
def Noteon_Update(note):
'''
# forward new instruction ?
if gstt.MyLaser != gstt.Laser:
doit = jumplaser.get(gstt.Laser)
doit("/noteon",note)
'''
#
if note < 8:
pass
#
if note > 7 and note < 16:
pass
#
if note > 15 and note < 24:
pass
# change current simulator PL
if note > 23 and note < 32:
pass
if note == 57 or note == 58:
pass
if note > 58:
pass
'''
# todo 57 Color mode : Rainbow
# 58 Color mode : RGB
# Notes for Curve : 0-7
def UpdateCurve():
print ("New Curve :", gstt.Curve)
if gstt.BhorealHere != -1:
for led in range(0,8):
NoteOff(led)
NoteOn(gstt.Curve,20)
# Notes for set : 8-15
def UpdateSet():
print ("New Set :", gstt.Set)
if gstt.BhorealHere != -1:
for led in range(9,17):
NoteOff(led)
NoteOn(gstt.Set+8,10)
# Note for current laser : 16-23
def UpdateLaser():
print ("New Laser :", gstt.Laser)
if gstt.BhorealHere != -1:
for led in range(16,24):
NoteOff(led)
NoteOn(gstt.Laser+16,30)
# Note for PL displayed in pygame window : 24-31
def UpdateSimu():
print ("New simuPL :", gstt.simuPL)
if gstt.BhorealHere != -1:
for led in range(24,32):
NoteOff(led)
NoteOn(gstt.simuPL+24,40)
'''
#
# Events from Bhoreal handling
#
# Process events coming from Bhoreal in a separate thread.
def MidinProcess(bhorqueue):
#print()
#print("bhoreal midi in process started with queue", bhorqueue)
#print()
bhorqueue_get = bhorqueue.get
while True:
msg = bhorqueue_get()
# Bhoreal Led pressed
print(("Bhoreal Matrix : ", str(msg[1]), gstt.BhorLeds[msg[1]]))
if msg[0] == NOTE_ON and msg[2] == 64:
# led
NoteOn(msg[1],64)
# Bhoreal Led depressed
elif msg[0] == NOTE_ON and msg[2] == 0:
NoteOn(msg[1],0)
'''
print "Bhoreal Matrix : ", str(msg[1]), str(gstt.BhorLeds[msg[1]])
if msg[1]< 8:
gstt.Curve = msg[1]
UpdateCurve()
if msg[1]> 7 and msg[1] < 16:
gstt.Set = msg[1]-8
UpdateSet()
if msg[1]> 15 and msg[1] < 24:
gstt.Laser = msg[1]-16
UpdateLaser()
if msg[1]> 23 and msg[1] < 31:
gstt.simuPL = msg[1]-24
UpdateSimu()
#Bhoreal send back note on and off to light up the led.
if msg[1]> 56:
if gstt.BhorLeds[msg[1]] < 115:
gstt.BhorLeds[msg[1]] += 10
#midi3.NoteOn(msg[1],gstt.BhorLeds[msg[1]])
#time.sleep(0.1)
#midi3.NoteOff(msg[1])
'''
bhorqueue = Queue()
# New Bhoreal call back : new msg forwarded to Bhoreal queue
class AddQueue(object):
def __init__(self, portname):
self.portname = portname
self._wallclock = time.time()
def __call__(self, event, data=None):
message, deltatime = event
self._wallclock += deltatime
print(("[%s] @%0.6f %r" % (self.portname, self._wallclock, message)))
bhorqueue.put(message)
'''
# Old Bhoreal call back : new msg forwarded to Bhoreal queue
class AddQueue(object):
def __init__(self, port):
self.port = port
self._wallclock = time.time()
def __call__(self, event, data=None):
message, deltatime = event
self._wallclock += deltatime
print("[%s] @%0.6f %r" % (self.port, self._wallclock, message))
bhorqueue.put(message)
'''

164
libs3/bhorunicornhat.py Normal file
View file

@ -0,0 +1,164 @@
#!/usr/bin/python3
# -*- coding: utf-8 -*-
'''
Bhorunicornhat
v0.7.0
A library to replace unicornhat and unicorn_hat_sim to use
any unicorn hat python script with a bhoreal or a Launchpad mini.
2 things to do :
1/ Change import in a unicorn target python program
import unicornhat as unicorn
by :
import bhorunicornhat as unicorn
2/ Set target (bhoreal or launchpad) by calling unicornhat.dest(device,rotation)
or modify destination and rotation manually : see a few lines down.
by Sam Neurohack
from /team/laser
CC NC BY
'''
import colorsys,time
import midi3,bhoreal,launchpad
# For Launchpad mini
mididest = "launchpad"
rotangle = 270
# For Bhoreal
#mididest = "bhoreal"
#rotangle = 180
BhorLeds = [0] * 64
Bhuffer = [0] * 64
HAT = (8,8)
AUTO = (8,8)
# 'launchpad' with rotation 270
# 'bhoreal' with rotation 180
def dest(dest,rot):
global mididest, rotangle
mididest = dest
rotangle = rot
def set_layout(x):
pass
def rot90():
for notes in range(0,64):
Bhuffer[notes] = BhorLeds[notes]
for y in range(1,9):
for x in range(1,9):
#print x,y,9-y,x,bhoreal.NoteXY(9-y,x),bhoreal.NoteXY(x,y),BhorLeds[bhoreal.NoteXY(9-y,x)],Bhuffer[bhoreal.NoteXY(x,y)]
BhorLeds[bhoreal.NoteXY(9-y,x)]=Bhuffer[bhoreal.NoteXY(x,y)]
def rot180():
for notes in range(0,64):
Bhuffer[notes] = BhorLeds[notes]
for y in range(8,0,-1):
#print ""
for x in range(1,9):
#print x,y,9-y,bhoreal.NoteXY(x,9-y),bhoreal.NoteXY(x,y),BhorLeds[bhoreal.NoteXY(x,9-y)],Bhuffer[bhoreal.NoteXY(x,y)]
BhorLeds[bhoreal.NoteXY(x,9-y)]=Bhuffer[bhoreal.NoteXY(x,y)]
def rotation(angle):
if angle == 90:
rot90()
if angle == 180:
rot180()
if angle == 270:
rot180()
rot90()
def brightness(brightness):
#like 0.5
pass
def get_shape():
return 8,8
def clear():
off()
def hue(r,g,b):
h = int(127*colorsys.rgb_to_hsv(r,g,b)[0])
v = int(127*colorsys.rgb_to_hsv(r,g,b)[2])
if h == 0 and v != 0:
h=127
#should be variation of grey (v,v,v)
#v = int(127*colorsys.rgb_to_hsv(r,g,b)[2])
#print r,g,b,h
return h
def off():
for note in range(1,64):
BhorLeds[note] = 0
show()
def set_all(r,g,b):
for led in range(0,64):
BhorLeds[led] = hue(r,g,b)
def set_pixel(x,y,r,g,b):
#print x,y,r,g,b,colorsys.rgb_to_hsv(r,g,b)
note = (x-1)+ (y-1) * 8
#print int(127*colorsys.rgb_to_hsv(r,g,b)[0])
BhorLeds[note] = hue(r,g,b)
def set_pixels(pixels):
led = 0
for line in pixels:
#print line
for ledline in range(0,8):
#print line[ledline]
r,g,b = line[ledline][0],line[ledline][1],line[ledline][2]
BhorLeds[led] = hue(r,g,b)
led += 1
def clean_shutdown():
pass
def show():
# How turn off all leds
'''
if bhoreal.Here != -1:
bhoreal.Cls(0)
if launchpad.Here != -1:
launchpad.Cls()
'''
# Check if midi3 has been previously initiated
if len(midi3.OutDevice) == 0:
midi3.OutConfig()
if (mididest == 'launchpad' and launchpad.Here != -1) or (mididest == 'bhoreal' and bhoreal.Here != -1):
rotation(rotangle)
for note in range(1,65):
midi3.NoteOn(note-1,BhorLeds[note-1],mididest)
time.sleep(0.0001)
else:
print(mididest,'is connected ?')

160
libs3/cli.py Normal file
View file

@ -0,0 +1,160 @@
# coding=UTF-8
"""
LJay/LJ
v0.8
Command line arguments parser
by Sam Neurohack
from /team/laser
"""
from libs3 import gstt
import argparse
import subprocess
def handle():
#have to be done before importing bhorosc.py to get correct port assignment
argsparser = argparse.ArgumentParser(description="LJ v0.8")
argsparser.add_argument("-r","--redisIP",help="IP address to bind builtin servers (OSC and websocket) also must be the Redis server IP ",type=str)
argsparser.add_argument("-L","--Lasers",help="Number of lasers requested (Autodetected by default).",type=int)
argsparser.add_argument("-v","--verbose",help="Debug mode 0,1 or 2 (0 by default)",type=int)
argsparser.add_argument("-x","--invx",help="Invert laser 0 X axis again",action="store_true")
argsparser.add_argument("-y","--invy",help="Invert laser 0 Y axis again",action="store_true")
argsparser.add_argument("-d","--display",help="Point List number displayed in simulator",type=int)
argsparser.add_argument("-a","--align",help="Reset laser 0 alignement values",action="store_true")
argsparser.add_argument("-i","--iport",help="port number for builtin LJ OSC server (8002 by default)",type=int)
argsparser.add_argument("-n","--nozoidIP",help="IP for llstr' Nozoid OSC server port 8003 ('127.0.0.1' by default)",type=str)
argsparser.add_argument("-b","--bhoroscIP",help="IP for OSC output ('127.0.0.1' by default)",type=str)
argsparser.add_argument("-o","--oport",help="OSC output port number (8001 by default)",type=int)
argsparser.add_argument("-w","--webui",help="Regen the webui",action="store_true")
# Keep it ! if new features of cli.py is used in a monolaser program
# argsparser.add_argument("-l","--laser",help="Last digit of etherdream ip address 192.168.1.0/24 (4 by default). Localhost if digit provided is 0.",type=int)
args = argsparser.parse_args()
# Verbose = debug
if args.verbose != None:
#print "setting gstt.debug to", args.verbose
gstt.debug = args.verbose
else:
gstt.debug = 0
# Webui regen
if args.webui == True:
subprocess.call(['python','webui/build.py'])
# Ports arguments
if args.iport:
iport = args.iport
gstt.iport = iport
else:
iport = gstt.iport
if args.oport:
oport = args.oport
gstt.oport = oport
else:
oport = gstt.oport
if gstt.debug > 0:
print("Accept OSC on port",gstt.oport)
print("gstt.iport:",gstt.iport)
# X Y inversion arguments
if args.invx == True:
gstt.swapX[0] = -1 * gstt.swapX[0]
gstt.centerx[0] = 0
gstt.centery[0] = 0
#WriteSettings()
print("laser 0 X new invertion Asked")
if gstt.swapX[0] == 1:
print ("X not Inverted")
else:
print ("X Inverted")
if args.invy == True:
gstt.swapY[0] = -1 * gstt.swapY[0]
gstt.centerx[0] = 0
gstt.centery[0] = 0
#WriteSettings()
print("laser 0 Y new invertion Asked")
if gstt.swapY[0] == 1:
print ("Y not Inverted")
else:
print("Y inverted")
# Redis Computer IP
if args.redisIP != None:
gstt.LjayServerIP = args.redisIP
# Point list number used by simulator
if args.display != None:
gstt.simuPL = args.display
print("Display : " + str(gstt.simuPL))
# Lasers = number of laser connected otherwise will be autodetected with one minimum
if args.Lasers != None:
gstt.LaserNumber = args.Lasers
else:
gstt.LaserNumber = -1
if args.bhoroscIP != None:
gstt.oscIPin = args.bhoroscIP
else:
gstt.oscIPin = '127.0.0.1'
if args.nozoidIP != None:
gstt.nozoscIP = args.nozoidIP
else:
gstt.nozoscIP = '127.0.0.1'
# Etherdream target for mono laser program
'''
if args.laser != None:
lstdgtlaser = args.laser
if lstdgtlaser == 0:
etherIP = "127.0.0.1"
else:
etherIP = "192.168.1."+str(lstdgtlaser)
else:
etherIP = "192.168.1.4"
#print ("Laser 1 etherIP:",etherIP)
'''
# Reset alignment values
if args.align == True:
gstt.centerx[0] = 0
gstt.centery[0] = 0
gstt.zoomx[0] = 15
gstt.zoomy[0] = 15
gstt.sizex[0] = 32000
gstt.sizey[0] = 32000
gstt.finangle[0] = 0.0
gstt.swapx[0] = 1
gstt.swapy[0] = 1
#Settings.Write()
handle()

669
libs3/commands.py Normal file
View file

@ -0,0 +1,669 @@
# coding=UTF-8
"""
LJ OSC and Websockets laser commands
v0.7.0
LICENCE : CC
by Sam Neurohack, Loloster
from /team/laser
Commands reference. Use commands from websocket (webUI) or OSC, do not set values in redis directly except for /pl.
DAChecks()
UpdateAllwww()
/forwardui "htmlid args"
/scale/X/lasernumber value
/scale/Y/lasernumber value
/client or note on < 8 : change client displayed for Current Laser
23 < /noteon < 32 : PL number displayed on webUI simulator
/grid/lasernumber value (0 or 1) : switch given laser with grid display on or off
/black/lasernumber value (0 or 1) : set given laser to black on or off
/ip/lasernumber value : change given laser IP i.e '192.168.1.1'
/kpps/lasernumber value
Live change of kpps is not implemented in newdac.py. Change will effect next startup.
/angle/lasernumber value : increase/decrease angle correction for given laser by value
/intens/lasernumber value : increase/decrease intensity for given laser by value
/resampler/lasernumber lsteps : change resampling strategy (glitch art) for given laser
lsteps is a string like "[ (1.0, 8),(0.25, 3), (0.75, 3), (1.0, 10)]"
/mouse/lasernumber value (0 or 1)
/swap/X/lasernumber value (0 or 1)
/swap/Y/lasernumber value (0 or 1)
/loffset/X/lasernumber value : change X offset of given laser by value
/loffset/Y/lasernumber value : change Y offset of given laser by value
/order value : instruct tracer what to do.
0 : display user pointlist with current client key. See below for client key.
1 : pull in redis a new correction matrix (EDH)
2 : display black
3 : display grid
4 : resampler
5 : pull in redis a new client key
6 : Max Intensity Change = reread redis key /intensity
7 : kpps change = reread redis key /kpps
8 : color balance change = reread redis keys /red /green /blue
/planet will be forwarded to planetarium client.
/nozoid will be forwarded to nozoid client.
/scene/scenenumber/start 0 or 1
/regen : regen webui index html page.
/pl/clientnumber/lasernumber value : value is the pointlist to draw as string type. For string format see code in clients directory.
Example : client 0 send 2 point lists one for laser 0 and one for laser 1 by sending in redis :
/pl/0/0 and /pl/0/1
The "client key" when client 0 is selected to be displayed by lasers is "/pl/0/".
Each tracer pull its pointlist by using the current client key "/pl/0/"
and add its laser number at startup : /pl0/0 ant /pl/0/1
"Client" is a concept. Imagine in a demoparty there is 4 lasers.
John and Paul want to draw on all lasers.
Let's give John client 0, he will send points to /pl/0/0, /pl/0/1, /pl/0/2 and /pl/0/3.
Paul is client 1, so he will use /pl/1/0, /pl/1/1, /pl/1/2 and /pl/1/3.
Both can send their pointlists to redis server.
When John get the lasers switch to client 0, when it's Paul turn switch to client 1.
But say Bob and Lisa needs only 2 lasers each. Give them client 2.
Bob could use /pl/2/0 and /pl/2/1 and Lisa could use /pl/2/2 and /pl/2/3.
"""
import types, time, socket
from libs3 import gstt
import redis
from libs3 import settings, plugins, homographyp
r = redis.StrictRedis(host=gstt.LjayServerIP , port=6379, db=0)
#r = redis.StrictRedis(host=gstt.LjayServerIP , port=6379, db=0, password='-+F816Y+-')
GenericCommands = ["start","align","ljclient","scene","addest","deldest","dest","clientnumber","vcvrack","fft","mitraille","faceosc","midigen","viewgen","audiogen","noteon","cc","ljpong","ljwars","mouse","emergency","simu","status","run","nozoid","planet","live","words","ai","bank0","pose","lj","cycl","glyph","pong","maxw","custom1","square","regen","trckr","aurora","line1","ForwardUI","settings","debug","pl"]
def UserOn(laser):
print("User for laser ", laser)
plugins.sendWSall("/status User on laser " + str(laser))
r.set('/order/'+str(laser), 0)
def NewEDH(laser):
print("New EDH requested for laser ", laser)
plugins.sendWSall("/status New EDH on laser " + str(laser))
settings.Write()
print("Settings saving swapX ", gstt.swapX[laser])
print("Settings saving swapY ", gstt.swapY[laser])
homographyp.newEDH(laser)
def BlackOn(laser):
print("Black for laser ", laser)
plugins.sendWSall("/status Black on laser " + str(laser))
r.set('/order/'+str(laser), 2)
def GridOn(laser):
print("Grid for laser ", laser)
plugins.sendWSall("/status Grid on laser " + str(laser))
r.set('/order/'+str(laser), 3)
def Resampler(laser,args):
# lsteps is a string like : "[ (1.0, 8),(0.25, 3), (0.75, 3), (1.0, 10)]"
print("Resampler change for laser", laser, "[("+str(args[0])+","+str(args[1])+"),("+str(args[2])+","+str(args[3])+"),("+str(args[4])+","+str(args[5])+"),("+str(args[6])+","+str(args[7])+")]")
#r.set('/resampler/' + str(laser), lsteps)
r.set('/resampler/' + str(laser), "[("+str(args[0])+","+str(args[1])+"),("+str(args[2])+","+str(args[3])+"),("+str(args[4])+","+str(args[5])+"),("+str(args[6])+","+str(args[7])+")]")
r.set('/order/'+str(laser), 4)
def LasClientChange(clientnumber):
if r.get("/pl/"+str(clientnumber)+"/0") != None:
print("Switching to laser client", clientnumber)
gstt.SceneNumber = clientnumber
plugins.sendWSall("/status Client " + str(gstt.SceneNumber) + " laser " + str(gstt.Laser))
r.set('/clientkey', "/pl/"+str(clientnumber)+"/")
print("clientkey set to", "/pl/"+str(clientnumber)+"/")
for laserid in range(0,gstt.LaserNumber):
r.set('/order/'+str(laserid), 5)
else:
print("ERROR : Maximum number of scenes is set to ", gstt.MaxScenes)
def SceneChange(newscene):
print("Switching to scene", newscene)
gstt.SceneNumber = int(newscene)
plugins.sendWSall("/status Scene " + newscene)
r.set('/clientkey', "/pl/"+ newscene +"/")
print("clientkey set to", "/pl/" + newscene + "/")
for laserid in range(0,gstt.LaserNumber):
r.set('/order/'+str(laserid), 5)
plugins.sendWSall("/scene/" + str(laserid) + "/start 0")
plugins.sendWSall("/scene/" + newscene + "/start 1")
# Change current laser and send "/scim lasernumber to each plugin"
def NoteOn(note):
print("NoteOn", note)
# Change laser client
if note < 8:
LasClientChange(note)
# Change PL displayed on webui
if note > 23 and note < 32:
if note - 24 > gstt.LaserNumber -1:
print("Only",gstt.LaserNumber,"lasers asked, you dum ass !")
plugins.sendWSall("/redstatus No Laser"+str(note-24))
plugins.sendWSall("/laser "+str(gstt.LaserNumber-1))
else:
gstt.Laser = note -24
plugins.sendWSall("/status Laser " + str(gstt.Laser))
plugins.SendAll("/scim "+str(gstt.Laser))
print("Current Laser switched to", gstt.Laser)
def Scim(path, tags, args, source):
laser = int(args[0])
print("OSC /scim", laser)
# Change PL displayed on webui
if laser > 23 and laser < 32:
if laser - 24 > gstt.LaserNumber -1:
print("Only",gstt.LaserNumber,"lasers asked, you dum ass !")
plugins.sendWSall("/redstatus No Laser"+str(note-24))
plugins.sendWSall("/laser "+str(gstt.LaserNumber-1))
else:
gstt.Laser = laser -24
plugins.sendWSall("/status Laser " + str(gstt.Laser))
print("Current Laser switched to", gstt.Laser)
def Line1(path, tags, args, source):
line1 = args[0]
print("OSC /line1", line1)
plugins.sendWSall("/line1 " +"Fx "+line1)
# forward
def ForwardUI(path, tags, args, source):
line = args[0]
print("OSC /forwardui to WebUI :", line)
print('from path', path, 'args', args)
plugins.sendWSall(line)
def CC(number, value):
print("CC", note, value)
def Mouse(x1,y1,x2,y2):
print("Mouse", x1,y1,x2,y2)
def handler(oscpath, args):
print("OSC handler in commands.py got /"+ str(oscpath)+ " with args :",args)
if gstt.debug > 0:
print("OSC handler in commands.py got /"+ str(oscpath)+ " with args :",args)
# 2 incoming cases : generic or specific for a given lasernumber :
#
# Generic : Commands without a laser number
#
if oscpath[1] in GenericCommands:
if gstt.debug > 0:
print("GenericCommand :", oscpath[1], "with args", args)
if oscpath[1] == "ljclient":
#LasClientChange(int(args[0]))
SceneChange(args[0])
if oscpath[1] == "pl":
r.set(oscpath, args[0])
#/scene/scenenumber/start 0 or 1
if oscpath[1] == "scene":
print(oscpath[1], oscpath[2], args[0])
if args[0] == '1' and r.get("/pl/" + oscpath[2] + "/0") != None:
SceneChange(oscpath[2])
else:
print("ERROR : Maximum number of scenes is set to ", gstt.MaxScenes)
elif oscpath[1] == "noteon":
NoteOn(int(args[0]))
# regen index.html (python build.py)
elif oscpath[1] == "regen":
subprocess.Popen(["python", plugins.ljpath + "/webui/build.py"])
# todo
elif oscpath[1] == "CC":
CC(int(args[0]), int(args[1]))
elif oscpath[1] == "pong":
#print "LJ commands got pong from", args
if gstt.debug >0:
print(("/" + args[0] + "/start 1"))
print(("/status got pong from "+ args[0] +"."))
plugins.sendWSall("/" + args[0] + "/start 1")
#plugins.sendWSall("/status got pong from "+ args[0] +".")
elif oscpath[1] == "vcvrack":
pass
'''
#print "LJ commands got /vcvrack from", args
if oscpath[2] == "1" :
r.set('/vcvrack/1', args[0])
#print('/vcvrack/1', args[0])
if oscpath[2] == "2" :
r.set('/vcvrack/2', args[0])
#print('/vcvrack/2', args[0])
'''
elif oscpath[1] == "mouse":
Mouse(int(args[0]),int(args[1]),int(args[2]),int(args[3]))
# /emergency value (0 or 1)
elif oscpath[1] == "emergency":
if args[0] == "1":
for laser in range(gstt.lasernumber):
print("Black requested for laser ", laser)
BlackOn(laser)
print("EMERGENCY MODE")
plugins.sendWSall("/status EMERGENCY MODE")
else:
for laser in range(gstt.lasernumber):
print("Back to normal for laser ", laser)
UserOn(laser)
# Settings commands :
elif oscpath[1] == "settings":
if oscpath[2] == "lasers":
print()
print("new laser number",args[0])
print()
if oscpath[2] == "regen":
print()
print("Regen www pages...")
UpdateAllwww()
if oscpath[2] == "IP":
print()
print("new server IP for www regen",args[0])
gstt.wwwIP = args[0]
if oscpath[2] == "debug":
print()
print("Debug level",args[0])
print()
gstt.debug = int(args[0])
plugins.SendAll("/debug "+str(gstt.debug))
if oscpath[2] == "rescan":
print()
print("Rescanning DACs...")
DAChecks()
print("Done.")
if oscpath[2] == "rstrt":
print()
print("Restarting", args[0], "...")
if args[0] == "lj":
raise Restart(time.asctime())
else:
plugins.Restart(args[0])
print()
#
# Commands with a laser number
#
else:
pathlength = len(oscpath)
if gstt.debug > 0:
print("Non Generic Command :", oscpath[1], "with args", args)
#print "oscpath", oscpath
#print "pathlength", pathlength
#print "args", args
if pathlength == 3:
laser = int(oscpath[2])
else:
laser = int(oscpath[3])
#print "args[0] :",args[0]," ", type(args[0])
# /grid/lasernumber value (0 or 1)
if oscpath[1] == "grid":
if args[0] == "1":
print("Grid requested for laser ", laser)
GridOn(laser)
else:
print("No grid for laser ", laser)
UserOn(laser)
# /ip/lasernumber value
if oscpath[1] == "ip":
print("New IP for laser ", laser)
gstt.lasersIPS[laser]= args[0]
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", int(args[0]))
gstt.kpps[laser]= int(args[0])
settings.Write()
r.set('/kpps/' + str(laser), str(args[0]))
r.set('/order/'+str(laser), 7)
# /angle/lasernumber value
if oscpath[1] == "angle":
print("New Angle modification for laser ", oscpath[2], ":", float(args[0]))
gstt.finANGLE[laser] += float(args[0])
NewEDH(laser)
print("New angle", gstt.finANGLE[laser])
# /intens/lasernumber value
if oscpath[1] == "intens":
print("LJ2 : New intensity requested for laser ", laser, ":", int(args[0]))
plugins.sendWSall("/status Intensity " + str(args[0]))
r.set('/intensity/' + str(laser), str(args[0]))
r.set('/order/'+str(laser), 6)
# /resampler/lasernumber lsteps
# lsteps is a string like "[ (1.0, 8),(0.25, 3), (0.75, 3), (1.0, 10)]"
if oscpath[1] == "resampler":
#print"resampler with args", args
Resampler(laser,args)
# /mouse/lasernumber value (0 or 1)
if oscpath[1] == "mouse":
if args[0] == "1":
print("Mouse requested for laser ", oscpath[2])
gstt.Laser = oscpath[2]
else:
print("No mouse for laser ", oscpath[2])
# /swap/X/lasernumber value (0 or 1)
if oscpath[1] == "swap" and oscpath[2] == "X":
print("swapX was", gstt.swapX[laser])
if args[0] == "0":
print("swap X -1 for laser ", laser)
gstt.swapX[laser]= -1
NewEDH(laser)
else:
print("swap X 1 for laser ", laser)
gstt.swapX[laser]= 1
NewEDH(laser)
# /swap/Y/lasernumber value (0 or 1)
if oscpath[1] == "swap" and oscpath[2] == "Y":
print("swapY was", gstt.swapX[laser])
if args[0] == "0":
print("swap Y -1 for laser ", laser)
gstt.swapY[laser]= -1
NewEDH(laser)
else:
print("swap Y 1 for laser ", laser)
gstt.swapY[laser]= 1
NewEDH(laser)
# /loffset/X/lasernumber value
if oscpath[1] == "loffset" and oscpath[2] == "X":
print("offset/X laser", laser, "modified to", args[0])
gstt.centerX[laser] -= int(args[0])
NewEDH(laser)
# /loffset/Y/lasernumber value
if oscpath[1] == "loffset" and oscpath[2] == "Y":
print("offset/Y laser", laser, "modified to", args[0])
gstt.centerY[laser] -= int(args[0])
NewEDH(laser)
# /scale/X/lasernumber value
if oscpath[1] == "scale" and oscpath[2] == "X":
if gstt.zoomX[laser] + int(args[0]) > 0:
gstt.zoomX[laser] += int(args[0])
print("scale/X laser", laser , "modified to", gstt.zoomX[laser])
NewEDH(laser)
# /scale/Y/lasernumber value
if oscpath[1] == "scale" and oscpath[2] == "Y":
if gstt.zoomY[laser] + int(args[0]) > 0:
gstt.zoomY[laser] += int(args[0])
print("scale/Y laser", laser, "modified to", gstt.zoomY[laser])
NewEDH(laser)
#
# Different useful codes for some commands
#
def Updatewww(file_name):
print("updating", file_name)
f=open(file_name,"r+")
a=f.readlines()
for line in a:
if "var LJ = " in line == True:
p=a.index(line)
#so now we have the position of the line which to be modified
a[p]=" var LJ = 'ws://"+gstt.wwwIP+":9001/'\n"
#print(p, line, a[p])
f.seek(0)
f.truncate() #ersing all data from the file
f.close()
#so now we have an empty file and we will write the modified content now in the file
o=open(file_name,"w")
for i in a:
o.write(i)
o.close()
#now the modification is done in the file
# Change
def UpdateAllwww():
print("Updating all www pages...")
Updatewww(gstt.ljpath+"/www/LJ.js")
Updatewww(gstt.ljpath+"/www/trckr/trckrcam1.html")
Updatewww(gstt.ljpath+"/www/simu.html")
Updatewww(gstt.ljpath+"/www/align.html")
Updatewww(gstt.ljpath+"/www/gen0.html")
Updatewww(gstt.ljpath+"/www/aur0.html")
Updatewww(gstt.ljpath+"/www/aur0s.html")
Updatewww(gstt.ljpath+"/www/aur1.html")
Updatewww(gstt.ljpath+"/www/auralls.html")
Updatewww(gstt.ljpath+"/www/index.html")
def isOpen(ip):
dacksock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
dacksock.settimeout(1)
istate = False
try:
dacksock.connect((ip, 7765))
#s.shutdown(2)
istate = True
dacksock.shutdown(socket.SHUT_RDWR)
except:
time.sleep(1)
finally:
dacksock.close()
return istate
'''
def isconnected(IP):
ipup = False
for i in range(retry):
if isOpen(IP, 7765):
ipup = True
break
else:
time.sleep(delay)
return ipup
'''
# autodetect connected DACs. Will change gstt.LaserNumber. One at least
def DAChecks():
gstt.dacs = [-1, -1, -1, -1]
gstt.dacnumber = 0
print("Searching DACs...")
for dac in range(gstt.maxdacs):
if isOpen(gstt.lasersIPS[dac]):
print("DAC", dac, "at", gstt.lasersIPS[dac], ": UP")
gstt.dacs[gstt.dacnumber] = dac
gstt.dacnumber +=1
else:
print("DAC", dac, "at", gstt.lasersIPS[dac], ": DOWN")
# At least one.
if gstt.dacnumber == 0:
gstt.dacs = [0, -1, -1, -1]
gstt.dacnumber = 1
gstt.LaserNumber = gstt.dacnumber
'''
For reference values of EDH modifier if assign to keyboard keys (was alignp)
gstt.centerY[gstt.Laser] -= 20
gstt.centerY[gstt.Laser] += 20
gstt.zoomX[gstt.Laser]-= 0.1
gstt.zoomX[gstt.Laser] += 0.1
gstt.zoomY[gstt.Laser] -= 0.1
gstt.zoomY[gstt.Laser] += 0.1
gstt.sizeX[gstt.Laser] -= 50
gstt.sizeX[gstt.Laser] += 50
gstt.sizeY[gstt.Laser] -= 50
gstt.sizeY[gstt.Laser] += 50
gstt.finANGLE[gstt.Laser] -= 0.001
gstt.finANGLE[gstt.Laser] += 0.001
Code for bit analysis 2 bits / laser to encode order.
# Grid PL is Laser bit 0 = 1 and bit 1 = 1
#order = r.get('/order')
#neworder = order | (1<<laser*2)
#neworder = neworder | (1<< 1+laser*2)
#r.set('/order', str(neworder))
# Laser bit 0 = 0 and bit 1 = 0 : USER PL
#order = r.get('/order')
#neworder = order & ~(1<< laser*2)
#neworder = neworder & ~(1<< 1+ laser*2)
#r.set('/order', str(neworder))
# Laser bit 0 = 0 and bit 1 = 1 : New EDH
#order = r.get('/order')
#neworder = order & ~(1<< laser*2)
#neworder = neworder | (1<< 1+laser*2)
#r.set('/order', str(neworder))
# Black PL is Laser bit 0 = 1 and bit 1 = 0 :
#order = r.get('/order')
#neworder = order | (1<<laser*2)
#neworder = neworder & ~(1<< 1+laser*2)
'''

119
libs3/font1.py Normal file
View file

@ -0,0 +1,119 @@
# coding=UTF-8
"""
LJ Font 1
v0.7.0
LICENCE : CC
by Sam Neurohack
from /team/laser
"""
from libs3 import gstt
def DigitsDots(number,color):
dots =[]
#print ASCII_GRAPHICS[ord(char) - 48]
for dot in ASCII_GRAPHICS[number]:
#print dot
dots.append((gstt.xy_center[0]+dot[0],gstt.xy_center[1]+dot[1],color))
#self.point_list.append((xy + (c,)))
return dots
ASCII_GRAPHICS = [
#implementé
[(-50,30), (-30,-30), (30,-30), (10,30), (-50,30)], #0
[(-20,30), (0,-30), (-20,30)], #1
[(-30,-10), (0,-30), (30,-10), (30,0), (-30,30), (30,30)], #2
[(-30,-30), (0,-30), (30,-10), (0,0), (30,10), (0,30), (-30,30)], #3
[(30,10), (-30,10), (0,-30), (0,30)], #4
[(30,-30), (-30,-30), (-30,0), (0,0), (30,10), (0,30), (-30,30)], #5
[(30,-30), (0,-30), (-30,-10), (-30,30), (0,30), (30,10), (30,0), (-30,0)], #6
[(-30,-30), (30,-30), (-30,30)], #7
[(-30,30), (-30,-30), (30,-30), (30,30), (-30,30), (-30,0), (30,0)], #8
[(30,0), (-30,0), (-30,-10), (0,-30), (30,-30), (30,10), (0,30), (-30,30)], #9
# A implementer
[(-30,10), (30,-10), (30,10), (0,30), (-30,10), (-30,-10), (0,-30), (30,-10)], #:
[(-30,-10), (0,-30), (0,30)], [(-30,30), (30,30)], #;
[(-30,-10), (0,-30), (30,-10), (30,0), (-30,30), (30,30)], #<
[(-30,-30), (0,-30), (30,-10), (0,0), (30,10), (0,30), (-30,30)], #=
[(30,10), (-30,10), (0,-30), (0,30)], #>
[(30,-30), (-30,-30), (-30,0), (0,0), (30,10), (0,30), (-30,30)], #?
[(30,-30), (0,-30), (-30,-10), (-30,30), (0,30), (30,10), (30,0), (-30,0)], #@
# Implementé
[(-30,30), (-30,-30), (30,-30), (30,30), (30,0), (-30,0)], #A
[(-30,30), (-30,-30), (30,-30), (30,30), (30,0), (-30,0)], #A
[(-30,30), (-30,-30), (30,-30), (30,30), (-30,30), (-30,0), (30,0)], #B
[(30,30), (-30,30), (-30,-30), (30,-30)], #C
[(-30,30), (-30,-30), (30,-30), (30,30), (-30,30)], #D
[(30,30), (-30,30), (-30,-0), (30,0), (-30,0), (-30,-30), (30,-30)], #E
[(-30,30), (-30,-0), (30,0), (-30,0), (-30,-30), (30,-30)], #F
[(0,0), (30,0), (30,30), (-30,30), (-30,-30),(30,-30)], #G
[(-30,-30), (-30,30), (-30,0), (30,0), (30,30), (30,-30)], #H
[(0,30), (0,-30)], #I
[(-30,30), (0,-30), (0,-30), (-30,-30), (30,-30)], #J
[(-30,-30), (-30,30), (-30,0), (30,-30), (-30,0), (30,30)], #K
[(30,30), (-30,30), (-30,-30)], #L
[(-30,30), (-30,-30), (0,0), (30,-30), (30,30)], #M
[(-30,30), (-30,-30), (30,30), (30,-30)], #N
[(-30,30), (-30,-30), (30,-30), (30,30), (-30,30)], #O
[(-30,0), (30,0), (30,-30), (-30,-30), (-30,30)], #P
[(30,30), (30,-30), (-30,-30), (-30,30), (30,30),(35,35)], #Q
[(-30,30), (-30,-30), (30,-30), (30,0), (-30,0), (30,30)], #R
[(30,-30), (-30,-30), (-30,0), (30,0), (30,30), (-30,30)], #S
[(0,30), (0,-30), (-30,-30), (30,-30)], #T
[(-30,-30), (-30,30), (30,30), (30,-30)], #U
[(-30,-30), (0,30), (30,-30)], #V
[(-30,-30), (-30,30), (0,0), (30,30), (30,-30)], #W
[(-30,30), (30,-30)], [(-30,-30), (30,30)], #X
[(0,30), (0,0), (30,-30), (0,0), (-30,-30)], #Y
[(30,30), (-30,30), (30,-30), (-30,-30)], #Z
# A implementer
[(-30,-10), (0,-30), (0,30)], [(-30,30), (30,30)], #[
[(-30,-10), (0,-30), (30,-10), (30,0), (-30,30), (30,30)], #\
[(-30,-30), (0,-30), (30,-10), (0,0), (30,10), (0,30), (-30,30)], #]
[(30,10), (-30,10), (0,-30), (0,30)], #^
[(30,-30), (-30,-30), (-30,0), (0,0), (30,10), (0,30), (-30,30)], #_
[(30,-30), (0,-30), (-30,-10), (-30,30), (0,30), (30,10), (30,0), (-30,0)], #`
# Implementé
[(-20,20), (-20,-20), (20,-20), (20,20), (20,0), (-20,0)], #a
[(-20,20), (-20,-20), (20,-20), (20,20), (-20,20), (-20,0), (20,0)], #b
[(20,20), (-20,20), (-20,-20), (20,-20)], #c
[(-20,20), (-20,-20), (20,-20), (20,20), (-20,20)], #d
[(20,20), (-20,20), (-20,-0), (20,0), (-20,0), (-20,-20), (20,-20)], #e
[(-20,20), (-20,-0), (20,0), (-20,0), (-20,-20), (20,-20)], #f
[(0,0), (20,0), (20,20), (-20,20), (-20,-20),(20,-20)], #g
[(-20,-20), (-20,20), (-20,0), (20,0), (20,20), (20,-20)], #H
[(0,20), (0,-20)], #I
[(-20,20), (0,-20), (0,-20), (-20,-20), (20,-20)], #J
[(-20,-20), (-20,20), (-20,0), (20,-20), (-20,0), (20,20)], #K
[(20,20), (-20,20), (-20,-20)], #L
[(-20,20), (-20,-20), (0,0), (20,-20), (20,20)], #M
[(-20,20), (-20,-20), (20,20), (20,-20)], #N
[(-20,20), (-20,-20), (20,-20), (20,20), (-20,20)], #O
[(-20,0), (20,0), (20,-20), (-20,-20), (-20,20)], #P
[(20,20), (20,-20), (-20,-20), (-20,20), (20,20),(25,25)], #Q
[(-20,20), (-20,-20), (20,-20), (20,0), (-20,0), (20,20)], #R
[(20,-20), (-20,-20), (-20,0), (20,0), (20,20), (-20,20)], #S
[(0,20), (0,-20), (-20,-20), (20,-20)], #T
[(-20,-20), (-20,20), (20,20), (20,-20)], #U
[(-20,-20), (0,20), (20,-20)], #V
[(-20,-20), (-20,20), (0,0), (20,20), (20,-20)], #W
[(-20,20), (20,-20)], [(-20,-20), (20,20)], #X
[(0,20), (0,0), (20,-20), (0,0), (-20,-20)], #Y
[(20,20), (-20,20), (20,-20), (-20,-20)], #Z
[(-2,15), (2,15)] # Point a la place de {
]

152
libs3/gstt.py Normal file
View file

@ -0,0 +1,152 @@
# coding=UTF-8
'''
LJ Global state
v0.8.0
**
Almost all values here Will be overriden by data in LJ.conf at startup
**
LICENCE : CC BY
by Sam Neurohack, Loloster, pclf
from /team/laser
'''
#ConfigName = "setexample.conf"
ConfigName = "LJ.conf"
debug = 0
ljpath=''
anims= [[],[],[],[]]
# How many lasers are connected. Different that "currentlaser" and "dacnumber" (=autodetected)
LaserNumber = -1
# What laser client to listen at launch
SceneNumber = 0
MaxScenes = 3
screen_size = [400,400]
xy_center = [screen_size[0]/2,screen_size[1]/2]
LjayServerIP = '192.168.1.13'
oscIPin = '192.168.1.15'
nozoscip = '192.168.1.15'
wwwIP = '192.168.1.15'
# gstt.Laser select to what laser modifcation will occur.
# Can be changed with /noteon 16-23
Laser = 0
# gstt.simuPL select what point list number to display in webUI simulator
# Can be changed with /noteon 24-31
simuPL = 1
# gstt.laserIPS.
lasersIPS = ['192.168.1.5','192.168.1.6','192.168.1.3','192.168.1.4']
maxdacs = 4
# Autodetected by DAChecks() in main3 :
# Store connected dacs. Maybe laser 1 in LJ.conf is not connected but Laser 2 is.
dacs = [-1, -1, -1, -1]
# Actual number of connected DACs
dacnumber = 0
# gstt.kpps stores kpps for each laser.
# ** Will be overridden by LJ.conf file values **
kpps = [25000,25000,25000,25000]
lasertype = ["LOCAL","LOCAL","LOCAL","LOCAL"]
intensity = [-1,-1,-1,-1]
# gstt.GridDisplay : if = 1 Curve points actually sent to PL are replaced by a grid
GridDisplay = [0,0,0,0]
# Transformation Matrix for each laser
EDH = [[], [], [], []]
# Etherdreams reports
# ipconn is initial newdac to its etherdream
lstt_ipconn = [[-1], [-1], [-1], [-1]]
# dacstt is dac light engine state
lstt_dacstt = [[-1], [-1], [-1], [-1]]
# store last dac answers : ACK, not ACK,...
lstt_dacanswers = [[-1], [-1], [-1], [-1]]
# store last number of points sent to etherdreams buffer
lstt_points = [[0], [0], [0], [0]]
swapX = [1,1,1,-1]
swapY = [1,1,1,-1]
lsteps = [[],[],[],[]]
# For glitch art : change position and number of points added by tracer.py
# shortline is for distance with next point, 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)]
#stepslongline = [(0.25,1), (0.75, 1), (1.0, 1)]
#stepshortline = [(1.0, 8)]
#stepslongline = [(1.0, 1)]
#stepshortline = [(1.0, 1)]
point = [0,0,0]
cc = [0] * 256
lfo = [0] * 10
osc = [0] * 255
oscInUse = [0] * 255
knob = [0] * 33
# Viewer distance (cc 21)
cc[21]=60
viewer_distance = cc[21] * 8
# fov (cc 22)
cc[22]= 60
fov = 4 * cc[22]
JumpFlag =0
# OSC ports
#temporaray fix hack : iport=nozoport
iport = 8002 # LJ input port
oport = 8001 # LJ output port
noziport=8003 #nozosc.py receiving commands port
nozoport=8001 #nozosc.py sending port to LJay (main.py)
nozuport=0 #linux serial usb port connecting nozoid devices ACM0 by default
angleX = 0
angleY = 0
angleZ = 0
# multilasers arrays
centerX = [0,0,0,0]
centerY = [0,0,0,0]
zoomX = [0,0,0,0]
zoomY = [0,0,0,0]
sizeX = [0,0,0,0]
sizeY = [0,0,0,0]
finANGLE = [0,0,0,0]
warpdest = [[[ 1. , 0. , 0.],[ 0. , 1. , 0.],[ 0. , 0. , 1.]],
[[ 1. , 0. , 0.],[ 0. , 1. , 0.],[ 0. , 0. , 1.]],
[[ 1. , 0. , 0.],[ 0. , 1. , 0.],[ 0. , 0. , 1.]],
[[ 1. , 0. , 0.],[ 0. , 1. , 0.],[ 0. , 0. , 1.]]
]
BeatstepLayer = 1
BeatstepLayers = ['XY','Live',"Align","Zregulators"]
TouchOSCPort = 8101
TouchOSCIP = '192.168.2.67' # iPad 1
#TouchOSCIP = '192.168.2.156' # iPad mini
#TouchOSCIP = '192.168.43.146' # iPad mini @ fuzz
#TouchOSCIP = '192.168.151.213' # CCN
#TouchOSCIP = '127.0.0.1' # Localhost

252
libs3/homographyp.py Normal file
View file

@ -0,0 +1,252 @@
#!/usr/bin/python3
# -*- coding: utf-8 -*-
# -*- mode: Python -*-
'''
LJay/LJ
v0.7.0
LICENCE : CC
Sam Neurohack
Homographies for align + swap corrections and warp corrections
Align + swap homography if found with 4 original points and corrected coordinates
Warp correction is disabled for the moment. Should be computed at warp edition : set 1 curve 1
Use the :
########################################################################
# Module to compute homographies #
# #
# Author : Alexis Mignon #
# email : alexis.mignon@info.unicaen.fr #
# date : 10/03/2010 #
########################################################################
Module to compute homographies between two sets of 2D points
implemented functions :
- find_homography(points1,points2) : finds the homography between
two sets of 2D points
- find_affine_homography(points1,points2) : finds the affine
homography between two sets of 2D points
- apply_homography(H,points) : applies homography H to the set of
2D points 'points'
example :
>>> from homography import *
>>>
>>> points1 = np.array([[ 0., 0. ],
>>> [ 1., 0. ],
>>> [ 0., 1. ],
>>> [ 1., 1. ]])
>>>
>>> points2 = np.array([[ 0. , 0. ],
>>> [ 1. , 0. ],
>>> [ 0.25, 1. ],
>>> [ 0.75, 1. ]])
>>>
>>> points3 = np.array([[-1., 0.],
>>> [ 0.,-1.],
>>> [ 0., 1.],
>>> [ 1., 0.]])
>>>
>>> H1 = find_homography(points1,points2)
>>> print H1
>>> print apply_homography(H1,points1)
>>> H2 = find_affine_homography(points1,points3)
>>> print H2
>>> print apply_homography(H2,points1)
'''
import numpy as np
import math
from scipy.linalg import svd,lstsq
import ast
from libs3 import gstt
#from globalVars import xy_center
import redis
r = redis.StrictRedis(host=gstt.LjayServerIP, port=6379, db=0)
def find(points1,points2):
if points1.shape[0] != points2.shape[0] : raise ValueError("The number of input and output points mismatches")
if points1.shape[1] == 2 :
p1 = np.ones((len(points1),3),'float64')
p1[:,:2] = points1
elif points1.shape[1] == 3 : p1 = points1
else : raise ValueError("Bad shape for input points")
if points2.shape[1] == 2 :
p2 = np.ones((len(points2),3),'float64')
p2[:,:2] = points2
elif points2.shape[1] == 3 : p2 = points2
else : raise ValueError("Bad shape for output points")
npoints = len(points1)
A = np.zeros((3*npoints,9),'float64')
for i in range(npoints):
p1i = p1[i]
x2i,y2i,w2i = p2[i]
xpi = x2i*p1i
ypi = y2i*p1i
wpi = w2i*p1i
A[i*3 ,3:6] = -wpi
A[i*3 ,6:9] = ypi
A[i*3+1,0:3] = wpi
A[i*3+1,6:9] = -xpi
A[i*3+2,0:3] = -ypi
A[i*3+2,3:6] = xpi
U,s,Vt = svd(A,full_matrices = False, overwrite_a = True)
del U,s
h = Vt[-1]
H = h.reshape(3,3)
return H
def find_affine(points1,points2):
if points1.shape[0] != points2.shape[0] : raise ValueError("The number of input and output points mismatches")
if points1.shape[1] == 2 :
p1 = np.ones((len(points1),3),'float64')
p1[:,:2] = points1
elif points1.shape[1] == 3 : p1 = points1
else : raise ValueError("Bad shape for input points")
if points2.shape[1] == 2 :
p2 = np.ones((len(points2),3),'float64')
p2[:,:2] = points2
elif points2.shape[1] == 3 : p2 = points2
else : raise ValueError("Bad shape for output points")
npoints = len(points1)
A = np.zeros((3*npoints,6),'float64')
b = np.zeros((3*npoints,1),'float64')
for i in range(npoints):
p1i = p1[i]
x2i,y2i,w2i = p2[i]
xpi = x2i*p1i
ypi = y2i*p1i
wpi = w2i*p1i
A[i*3 ,3:6] = -wpi
A[i*3+1,0:3] = wpi
A[i*3+2,0:3] = -ypi
A[i*3+2,3:6] = xpi
b[i*3 ] = -y2i*p1i[2]
b[i*3+1] = x2i*p1i[2]
h = lstsq(A,b,overwrite_a = True, overwrite_b = True)[0]
H = np.zeros( (3,3) , 'float64' )
H[:2,:] = h.reshape(2,3)
H[2,2] = 1
return H
def apply(H,points):
p = np.ones((len(points),3),'float64')
p[:,:2] = points
pp = np.dot(p,H.T)
pp[:,:2]/=pp[:,2].reshape(len(p),1)
return pp[:,:2]
# Align and axis swap corrections
# Reference points
pointsref = np.array([(300.0, 400.0), (500.0, 400.0), (500.0, 200.0), (300.0, 200.0)])
def EDpoint(mylaser, xxx_todo_changeme):
#print "current point : ", pygamex, pygamey
(pygamex,pygamey) = xxx_todo_changeme
XX = pygamex - gstt.xy_center[0]
YY = pygamey - gstt.xy_center[1]
CosANGLE = math.cos(gstt.finANGLE[mylaser])
SinANGLE = math.sin(gstt.finANGLE[mylaser])
x = (gstt.xy_center[0] + ((XX * CosANGLE) - (YY * SinANGLE)) - gstt.xy_center[0]) * gstt.zoomX[mylaser] + gstt.centerX[mylaser]
y = (gstt.xy_center[1] + ((XX * SinANGLE) + (YY * CosANGLE)) - gstt.xy_center[1]) * gstt.zoomY[mylaser] + gstt.centerY[mylaser]
if gstt.debug >1:
#print "global center :", xy_center
print("EDpoint computing...")
print("Laser :", mylaser, "center at : ", gstt.centerX[mylaser], gstt.centerY[mylaser])
print("Pygame point",pygamex,",",pygamey)
'''
print "swaps : ", (gstt.swapX[mylaser]), str(gstt.swapY[mylaser])
print "zooms : ", gstt.zoomX[mylaser], gstt.zoomY[mylaser]
print "angles : ", gstt.finANGLE[mylaser]
'''
print("Result point : ", x * gstt.swapX[mylaser] , y * gstt.swapY[mylaser])
return [x * gstt.swapX[mylaser] , y * gstt.swapY[mylaser]]
'''
def EDpoint((pygamex,pygamey)):
XX = pygamex - xy_center[0]
YY = pygamey - xy_center[1]
CosANGLE = math.cos(finangle)
SinANGLE = math.sin(finangle)
# Multilaser style
x = (xy_center[0] + ((XX * CosANGLE) - (YY * SinANGLE)) - xy_center[0]) * zoomx + centerx
y = (xy_center[1] + ((XX * SinANGLE) + (YY * CosANGLE)) - xy_center[1]) * zoomy + centery
return [x*1, y*1]
'''
# New total homography from always the same reference points : ED (= align + swap) transform + warp transform.
# WARP IS DISABLED. Some bug tracking is needed !
def newEDH(mylaser):
EDpoints = []
for point in range(4):
EDpoints.append(EDpoint(mylaser,pointsref[point]))
# H matrix tansform pygame points in Etherdream system with align and swap correction,
H = find(pointsref, np.array(EDpoints))
# Computer Hwarp matrix with previously reference warped points in configuration file.
Hwarp = find(pointsref, gstt.warpdest[mylaser])
#Hwarp = np.identity(3, dtype = float)
# EDH matrix
gstt.EDH[mylaser] = H
# EDH matrix is H x Hwarp
#gstt.EDH[mylaser] = np.dot(H,Hwarp)
print("Tracer", 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)
print("reference points", pointsref)
print("laser EDpoints :", EDpoints)
print("-> Computed H :",H)
#print "warped points coordinates ", gstt.warpdest[mylaser]
#print "-> Computed Hwarp", Hwarp
#print "laser ", mylaser, "warpd ",ast.literal_eval(gstt.warpdest[gstt.Laser])
#print "laser ", mylaser, "Hwarp ", Hwarp
#print ""
print("-> new EDH :", gstt.EDH[mylaser])

72
libs3/kb.py Normal file
View file

@ -0,0 +1,72 @@
#!/usr/bin/python3
# -*- coding: utf-8 -*-
"""
typetext('hello')
tap(key)
Loosely found and reuse in LPHK from nimaid
https://github.com/nimaid/LPHK
mouse functions commented yet
"""
import keyboard
# import ms
media_keys = {"vol_up" : 57392, "vol_down" : 57390, "mute" : 57376, "play_pause" : 57378, "prev_track" : 57360, "next_track" : 57369}
#with mouse
#media_keys = {"vol_up" : 57392, "vol_down" : 57390, "mute" : 57376, "play_pause" : 57378, "prev_track" : 57360, "next_track" : 57369, "mouse_left" : "mouse_left","mouse_middle" : "mouse_middle", "mouse_right" : "mouse_right"}
pressed = set()
def sp(name):
try:
return keyboard.key_to_scan_codes(str(name))[0]
except:
try:
return media_keys[str(name)]
except:
return None
def press(key):
pressed.add(key)
if type(key) == str:
'''
if "mouse_" in key:
ms.press(key[6:])
return
'''
keyboard.press(key)
def release(key):
pressed.discard(key)
if type(key) == str:
'''
if "mouse_" in key:
ms.release(key[6:])
return
'''
keyboard.release(key)
def release_all():
for key in pressed.copy():
release(key)
def tap(key):
if type(key) == str:
'''
if "mouse_" in key:
ms.click(key[6:])
return
'''
press(key)
release(key)
def typetext(name):
#print(name)
for letter in name:
#print (letter)
tap(letter)

1417
libs3/launchpad.py Normal file

File diff suppressed because it is too large Load diff

Binary file not shown.

Binary file not shown.

BIN
libs3/link.cpython-38-darwin.so Executable file

Binary file not shown.

BIN
libs3/link.so Executable file

Binary file not shown.

1003
libs3/lj23layers.py Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,986 @@
#!/usr/bin/python3
# -*- coding: utf-8 -*-
# -*- mode: Python -*-
'''
lj23layers v0.7.6 for LJ v0.8+
LJ functions (API) for python plugins/clients
"layers" version :
- "PL" has been replaced by "layer"
- "Client"
Each program using LJ should declare itself by call lj23layers Config :
Config(redisIP, client number, name)
Basic Draw :
- PolyLineOneColor, rPolyLineOneColor, LineTo, Line
- PolyLineRGB, rPolyLineRGB, LineRGBTo, LineRGB
- rgb2int(r,g,b)
- Drawlayer (point list number) : once you stacked all wanted elements, like 2 polylines, send them to lasers.
- DrawDests(): Draw all requested destinations for each layer .
High level draw :
- Text(word, integercolor, layer , xpos, ypos, resize, rotx, roty, rotz) : Display a word
- TextRGB(word, red, green, blue, ...)
- Embeded font1
Laser objects (name and convenient group of parameters for one or several point lists)
- RelativeObject
- FixedObject
"Destinations" : Tell for given Layer a scene/Laser ("destination").
Each Layer can have different destination (i.e to display same stuff on different laser)
OSC and plugins functions :
SendLJ(adress,message) : LJ remote control. See commands.py
SendResol(address,message): Send OSC message to Resolume.
WebStatus(message) : display message on webui
Ljscene(client): Change scene number in redis keys
Ljlayer(layer): Change layer number in redis keys = laser target.
ClosePlugin(name): Send UI closing info of given plugin
OSCstart(): Start the OSC system.
OSCframe(): Handle incoming OSC message. Calling the right callback
OSCstop(): Properly close the OSC system
OSCping(): /ping Answer to LJ pings by sending /pong name
OSCquit(): /quit Exit calling script using name in terminal
OSCadddest(): layer , scene, laser Add a destination
OSCdeldest(): layer , scene, lasers delete a destination
OSCobj(): /name/obj objectname attribute value for automation
OSCvar(): /name/var variablename value for automation
Joystick management is removed. Get it back in todolist
setup_controls(joystick)
XboxController : getLeftHori, getLeftVert, getRightHori, getRightVert, getLeftTrigger, getRightTrigger
Ps3Controller : getLeftHori, getLeftVert, getRightHori, getRightVert, getLeftTrigger, getRightTrigger, getUp, getDown, getLeft, getRight, getFire1, getFire2(self):
MySaitekController : getLeftHori,getLeftVert, getRightHori,getRightVert, getLeftTrigger,getRightTrigger
MyThrustController : getLeftHori, getLeftVert, getRightHori, getRightVert, getLeftTrigger, getRightTrigger
CSLController : getLeftHori,getLeftVert,getRightHori, getRightVert,getLeftTrigger,getRightTrigger,getFire1,getFire2
my USB Joystick : getUp,getDown,getLeft,getRight,etLeftTrigger, getRightTrigger,getFire1, getFire2
Class management manuals:
https://stackoverflow.com/questions/739882/iterating-over-object-instances-of-a-given-class-in-python
https://stackoverflow.com/questions/8628123/counting-instances-of-a-class
http://effbot.org/pyfaq/how-do-i-get-a-list-of-all-instances-of-a-given-class.htm
LICENCE : CC
Sam Neurohack
'''
import math
import redis
import sys
import weakref
import struct
import numpy as np
import gstt
from multiprocessing import Process, Queue, TimeoutError
is_py2 = sys.version[0] == '2'
if is_py2:
from OSC import OSCServer, OSCClient, OSCMessage
#print ("Importing lj23 and OSC from libs...")
else:
from OSC3 import OSCServer, OSCClient, OSCMessage
#print ("Importing lj23 and OSC3 from libs...")
#redisIP = '127.0.0.1'
#r = redis.StrictRedis(host=redisIP, port=6379, db=0)
ClientNumber = 0
name = "noname"
oscrun = True
point_list = []
layers = [[],[],[],[],[],[],[],[],[],[]]
fft3Groups = [-1,-1,-1,-1]
Dests = dict()
oscIPresol = "127.0.0.1"
oscPORTresol = 7000
# 3D to 2D projection parameters
fov = 256
viewer_distance = 2.2
'''
Laser "objects"
set a name and convenient group of parameters for one or several point lists
RelativeObject is for point lists around 0,0 with builtin move/rotation.
How to init with object color, xpos,... :
osciObj = lj.RelativeObject('osciObj', True, 255, [], white, red, green,blue,0 , False, centerX , centerY , 1 , Xrot , Yrot , Zrot)
How to use in drawing functions : you're free to use 0, some or all of any laserobject attributes
- draw one or several pointlists with 'A' laserobject color and 'B' laserobject xpos ypos ?
- Change color of 'main' object and all other objects using it will change also
how to change attribute :
osciObj.resize = 2 or /pluginame/change 'OsciObj' 'resize' 2
'''
class RelativeObject:
kind = 'relative'
counter = 0
def __init__(self, name, active, intensity, xy, color, red, green, blue, layer , closed, xpos , ypos , resize , rotx , roty , rotz):
self.name = name
self.active = active # True/False
self.intensity = intensity
self.xy = [] # Dots list
self.color = color # RGB color in int
self.red = red
self.green = green
self.blue = blue
self.layer = layer
self.closed = closed
self.xpos = xpos
self.ypos = ypos
self.resize = resize
self.rotx = rotx
self.roty = roty
self.rotz = rotz
RelativeObject.counter += 1
#type(self).counter += 1
def __del__(self):
RelativeObject.counter -= 1
# Fixed Laser object : point list in 'pygame' space (top left = 0,0 / bottom right)
class FixedObject:
kind = 'fixed'
counter = 0
def __init__(self, name, intensity, active, xy, color, red, green, blue, layer , closed):
self.name = name
self.active = active # True/False
self.intensity = intensity
self.xy = []
self.color = color
self.red = red
self.green = green
self.blue = blue
self.layer = layer
self.closed = closed
FixedObject.counter += 1
def __del__(self):
FixedObject.counter -= 1
'''
class IterDest(type):
def __new__ (cls, name, bases, dct):
dct['_instances'] = []
return super().__new__(cls, name, bases, dct)
def __call__(cls, *args, **kwargs):
instance = super().__call__(*args, **kwargs)
cls._instances.append(instance)
return instance
def __iter__(cls):
return iter(cls._instances)
class DestObject():
# class Destinations(metaclass=IterDest):
__metaclass__ = IterDest
counter = 0
def __init__(self, name, number, active, layer , scene, laser):
self.name = name
self.number = number
self.active = active
self.layer = layer
self.scene = scene
self.laser = laser
DestObject.counter += 1
def __del__(self):
DestObject.counter -= 1
'''
class DestObject():
# class Destinations(metaclass=IterDest):
_instances = set()
counter = 0
def __init__(self, name, number, active, layer , scene, laser):
self.name = name
self.number = number
self.active = active
self.layer = layer
self.scene = scene
self.laser = laser
self._instances.add(weakref.ref(self))
DestObject.counter += 1
@classmethod
def getinstances(cls):
dead = set()
for ref in cls._instances:
obj = ref()
if obj is not None:
yield obj
else:
dead.add(ref)
cls._instances -= dead
def __del__(self):
DestObject.counter -= 1
def Config(redIP,client,myname):
global ClientNumber, name, redisIP, r
redisIP = redIP
r = redis.StrictRedis(host=redisIP, port=6379, db=0)
# ClientNumber 255 are not drawing anything like artnet
ClientNumber = client
#print ("client configured",ClientNumber)
name = myname
print ("lj23layers : Plugin declare its name :",name)
#print layer
return r
def LjClient(client):
global ClientNumber
ClientNumber = client
def Ljlayer(somelayer):
global layer
layer = somelayer
def fromRedis(n):
encoded = r.get(n)
#print("")
#print('fromredis key',n,":",encoded)
h, w = struct.unpack('>II',encoded[:8])
#print("fromredis array size",n,":",h,w)
a = np.frombuffer(encoded, dtype=np.int16, offset=8).reshape(h,w)
#print("fromredis array",n,":",a)
return a
# Store Numpy array 'a' in Redis key 'n'
# Write also in redis key 'a' numpy array, its 2 dimensions size : h time w values
def toRedis(n,a):
#print("array.shape", a.shape, len(a.shape) )
if len(a.shape) == 1:
h = a.shape[0]
w = 1
else:
h,w = a.shape
#print("toredis", n,"h",h,"w",w,"a",a)
shape = struct.pack('>II',h,w)
#shape = struct.pack('>II',len(a),1)
#print("toredis",n,a)
encoded = shape + a.tobytes()
# Store encoded data in Redis
return r.set(n,encoded)
#
# OSC functions
#
# OSC clients
def SendLJ(oscaddress,oscargs=''):
oscmsg = OSCMessage()
oscmsg.setAddress(oscaddress)
oscmsg.append(oscargs)
osclientlj = OSCClient()
osclientlj.connect((redisIP, 8002))
print("lj23layers for",name,"sending OSC message :", oscmsg, "to", redisIP, ":8002")
if gstt.debug >0:
print("lj23layers for",name,"sending OSC message :", oscmsg, "to", redisIP, ":8002")
try:
osclientlj.sendto(oscmsg, (redisIP, 8002))
oscmsg.clearData()
except:
print ('Connection to LJ refused : died ?')
pass
#time.sleep(0.001
# Resolume OSC Arena client.
# sendresol(oscaddress, [arg1, arg2,...])
# example : sendresol("/noteon",note)
def SendResol(oscaddress,oscargs):
oscmsg = OSCMessage()
oscmsg.setAddress(oscaddress)
oscmsg.append(oscargs)
osclientresol = OSCClient()
osclientresol.connect((oscIPresol, oscPORTresol))
print("lj23layers sending OSC message : ", oscmsg, "to Resolume", oscIPresol, ":", oscPORTresol)
try:
osclientresol.sendto(oscmsg, (oscIPresol, oscPORTresol))
oscmsg.clearData()
except:
print ('Connection to Resolume refused : died ?')
pass
def SendIntensity(laser, intensity):
r.set('/intensity/' + str(laser), str(intensity))
r.set('/order/'+str(laser), 6)
SendLJ("/kpps/" + str(layer)+ " " + str(int(args[1])))
def Sendkpps(laser, kpps):
r.set('/kpps/' + str(laser), str(kpps))
r.set('/order/'+str(laser), 7)
def WebStatus(message):
SendLJ("/status", message)
# Closing plugin messages to LJ
def ClosePlugin():
WebStatus(name+" Exiting")
SendLJ("/"+name+"/start",0)
# RAW OSC Frame available ?
def OSCframe():
# clear timed_out flag
#print "oscframe"
oscserver.timed_out = False
# handle all pending requests then return
while not oscserver.timed_out:
oscserver.handle_request()
# Answer to LJ pings with /pong value
def OSCping(path, tags, args, source):
#def OSCping():
if gstt.debug >0:
print(name, "lj23layers got /ping from LJ -> reply /pong", name)
SendLJ("/pong",name)
# Properly close the system. Todo
def OSCstop():
oscserver.close()
# /quit
def OSCquit(path, tags, args, source):
global oscrun
oscrun = False
print('lj23layers got /quit for',name)
#WebStatus(name + " quit.")
#SendLJ("/"+name+"/start",0)
#print("Stopping OSC...")
#OSCstop()
#sys.exit()
# default handler
def OSChandler(path, tags, args, source):
oscaddress = ''.join(path.split("/"))
print("lj23layers Default OSC Handler for",name,": msg from Client :" + str(source[0]),)
print("OSC address", path)
if len(args) > 0:
print("with args", args)
#oscIPout = str(source[0])
#osclient.connect((oscIPout, oscPORTout))
# for any laser object : /pluginame/obj objectname attribute value
# like : /pluginname/obj 'fft' 'xpos' 100
# attributes for all lj Objects: name, xy_list, c, layer
# + for RelativeObjects : closed, xpos , ypos , resize , rotx , roty , rotz
def OSCobj(path, tags, args, source):
obj = eval(args[0]+"."+ args[1])
obj = args[2]
def OSCvar(path, tags, args, source):
obj = eval(args[0])
obj = args[1]
def addOSCdefaults(server):
global oscserver
oscserver = server
oscserver.addMsgHandler( "default", OSChandler )
oscserver.addMsgHandler( "/ping", OSCping)
oscserver.addMsgHandler( "/quit", OSCquit)
oscserver.addMsgHandler( "/"+ name + "/adddest", OSCadddest)
oscserver.addMsgHandler( "/"+ name + "/deldest", OSCdeldest)
oscserver.addMsgHandler( "/"+ name + "/dest", OSCdest)
oscserver.addMsgHandler( "/"+ name + "/obj", OSCobj)
oscserver.addMsgHandler( "/"+ name + "/var", OSCvar)
#
# Color functions
#
# input hexcode = '0xff00ff'
def hex2rgb(hexcode):
hexcode = hexcode[2:]
return tuple(int(hexcode[i:i+2], 16) for i in (0, 2, 4))
#return tuple(map(ord,hexcode[1:].decode('hex')))
# input rgb=(255,0,255) output '0xff00ff'
#def rgb2hex(rgb):
# return '0x%02x%02x%02x' % tuple(rgb)
def rgb2hex(r, g, b):
return hex((r << 16) + (g << 8) + b)
#def rgb2int(rgb):
# return int('0x%02x%02x%02x' % tuple(rgb),0)
def rgb2int(r,g,b):
return int('0x%02x%02x%02x' % (r,g,b),0)
def int2rgb(intcode):
#hexcode = '0x{0:06X}'.format(intcode)
hexcode = '{0:06X}'.format(intcode)
return tuple(int(hexcode[i:i+2], 16) for i in (0, 2, 4))
#
# Drawing basic functions
#
# Lines
def Line(xy1, xy2, c, layer ):
LineTo(xy1, 0, layer )
LineTo(xy2, c , layer )
def rLine(xy1, xy2, c, layer , xpos = 0, ypos =0, resize =0.7, rotx =0, roty =0 , rotz=0):
rLineTo(xy1, 0, layer )
rLineTo(xy2, c , layer )
def LineRGB(xy1, xy2, red,green,blue, layer ):
LineTo(xy1, 0, layer )
LineTo(xy2, int('0x%02x%02x%02x' % (red,green,blue),0) , layer )
# Lineto
def LineTo(xy, c, layer ):
layers[layer].append((xy + (c,)))
def LineRGBTo(xy, red, green, blue, layer ):
LineTo(xy, int('0x%02x%02x%02x' % (red,green,blue),0), layer )
def rLineTo(xy, c, layer , xpos = 0, ypos =0, resize =0.7, rotx =0, roty =0 , rotz=0):
layers[layer ].append((Pointransf(xy, xpos, ypos, resize, rotx, roty, rotz) + (c,)))
# Polylines
def PolyLineOneColor(xy_list, c, layer , closed ):
#print "--"
#print "c",c
#print "xy_list",xy_list
#print "--"
xy0 = None
for xy in xy_list:
if xy0 is None:
xy0 = xy
#print "xy0:",xy0
LineTo(xy0,0, layer )
LineTo(xy0,c, layer )
else:
#print "xy:",xy
LineTo(xy,c, layer )
if closed:
LineTo(xy0,c, layer )
def PolyLineRGB(xy_list, red, green, blue, layer , closed ):
PolyLineOneColor(xy_list, int('0x%02x%02x%02x' % (red,green,blue),0), layer , closed )
# rPolylines
# Send 2D point list around 0,0 with 3D rotation resizing and reposition around xpos ypos
#def rPolyLineOneColor(self, xy_list, c, layer , closed, xpos = 0, ypos =0, resize =1, rotx =0, roty =0 , rotz=0):
def rPolyLineOneColor(xy_list, c, layer , closed, xpos = 0, ypos =0, resize =0.7, rotx =0, roty =0 , rotz=0):
xy0 = None
for xy in xy_list:
print(xy,xy0)
if xy0 is None:
xy0 = xy
LineTo(Pointransf(xy0, xpos, ypos, resize, rotx, roty, rotz), 0, layer )
LineTo(Pointransf(xy0, xpos, ypos, resize, rotx, roty, rotz), c, layer )
else:
LineTo(Pointransf(xy, xpos, ypos, resize, rotx, roty, rotz), c, layer )
if closed:
LineTo(Pointransf(xy0, xpos, ypos, resize, rotx, roty, rotz), c, layer )
def rPolyLineRGB(xy_list, red, green, blue, layer , closed, xpos = 0, ypos =0, resize =0.7, rotx =0, roty =0 , rotz=0):
rPolyLineOneColor(xy_list, int('0x%02x%02x%02x' % (red,green,blue),0), layer , closed, xpos = 0, ypos =0, resize =0.7, rotx =0, roty =0 , rotz=0)
# Computing points coordinates for rPolyline function from 3D and around 0,0 to pygame coordinates
def Pointransf(xy, xpos = 0, ypos =0, resize =1, rotx =0, roty =0 , rotz=0):
x = xy[0] * resize
y = xy[1] * resize
z = xy[2] * resize
rad = math.radians(rotx)
cosaX = math.cos(rad)
sinaX = math.sin(rad)
y2 = y
y = y2 * cosaX - z * sinaX
z = y2 * sinaX + z * cosaX
rad = math.radians(roty)
cosaY = math.cos(rad)
sinaY = math.sin(rad)
z2 = z
z = z2 * cosaY - x * sinaY
x = z2 * sinaY + x * cosaY
rad = math.radians(rotz)
cosZ = math.cos(rad)
sinZ = math.sin(rad)
x2 = x
x = x2 * cosZ - y * sinZ
y = x2 * sinZ + y * cosZ
#print xy, (x + xpos,y+ ypos)
#return (x + xpos, y + ypos)
#to understand why it get negative Y
""" Transforms this 3D point to 2D using a perspective projection. """
factor = fov / (viewer_distance + z)
x = x * factor + xpos
y = y * factor + ypos
#y = - y * factor + ypos
return (x, y)
def Lineslayer(layer):
print("Stupido !! your code is to old : use Drawlayer() instead of LinesPL()")
Drawlayer(layer )
def Draw(layer):
#print '/pl/0/'+str(layer), str(layers[layer])
if r.set('/pl/'+str(ClientNumber)+'/'+str(layer), str(layers[layer])) == True:
#print '/pl/'+str(ClientNumber)+'/'+str(layer), str(layers[layer])
layers[layer] = []
return True
else:
return False
def Resetlayer(self, layer):
layers[layer] = []
#
# "Destinations" management for layers
#
# Add a destination for a given layer
def Addest(layer, scene, laser):
print (name,'adding',layer,scene,laser,'?')
if Findest(layer, scene, laser) == -1:
newdest = DestsObjects.counter + 1
Dest0 = lj.DestObject(str(newdest), newdest, True, layer , scene, laser)
print("New destination added with number", newdest)
else:
print("Destination already existed")
# OSC add a destination for a given layer
# /pluginame/dest layer, scene, laser
def OSCadddest(path, tags, args, source):
Addests(int(args[0]),int(args[1]),int(args[2]))
# Find layer destination with its parameters in destinations dictionnary
def Findest(layer, scene, laser):
print(name, 'searching layer,scene,laser',layer,scene,laser)
for item in DestObjects.getinstances():
#print(item)
if item.layer == layer and item.scene == scene and item.laser == laser:
#Dests.append(item[0])
print('found number',item.number)
return item.number
else:
print('no destination found')
return -1
'''
#Dests = list()
allDests = Dests.items()
for item in allDests:
print(item)
if item[1] == layer and item[2] == scene and item[3] == laser:
#Dests.append(item[0])
return Dests[item[0]]
else:
return -1
'''
# Find and remove a layer destination with its parameters in destinations dictionnary
def Deldest(layer, scene, laser):
Destnumber = Findest(layer, scene, laser)
print(name,'deleting Destination layer, scene, laser', layer,scene, laser)
if Destnumber != -1:
print('found DestObject', Destnumber)
delattr(DestObjects, str(Destnumber))
print("Destination", Destnumber,"was removed")
else:
print("Destination was not found")
# OSC Delete a destination for a given layer
# /pluginame/deldests layer, scene, laser
def OSCdeldest(path, tags, args, source):
Deldests(args[0], args[1], args[2])
# pluginame/dest layer, scene, laser
def OSCdest(path, tags, args, source):
# For single layer plugin : add a new destination
Addest(0, args[0], args[1])
# For single layer plugin : remove a destination
# For multiple layers plugin : add or remove
# Replace Drawlayer if Destinations paradigm is implemented in plugin code
def DrawDests():
# Objects style
#print("DrawDest")
for destination in DestObject.getinstances():
#print(Dests[str(destination)])
#print('/pl/'+str(Dests[str(destination)]["scene"])+'/'+str(Dests[str(destination)]["laser"]), ":", str(layers[Dests[str(destination)]["PL"]]))
#print(len(layers[destination.layer]))
if destination.active == True:
if r.set('/pl/'+str(destination.scene)+'/'+str(destination.laser), str(layers[destination.layer])) == True:
#print ('layer', destination.layer, '/pl/'+str(destination.scene)+'/'+str(destination.laser), str(layers[destination.layer]))
pass
else:
print('Redis key modification failed')
# Maybe one layer can be sent to multiple destination so they are all reset *after* all sending.
for layerss in range(4):
layers[layerss] = []
'''
# Dictionnary style
#print(Dests)
for destination in range(len(Dests)):
#print(Dests[str(destination)])
#print('/pl/'+str(Dests[str(destination)]["scene"])+'/'+str(Dests[str(destination)]["laser"]), ":", str(layers[Dests[str(destination)]["layer"]]))
if r.set('/pl/'+str(Dests[str(destination)]["scene"])+'/'+str(Dests[str(destination)]["laser"]), str(layers[Dests[str(destination)]["layer"]])) == True:
#print '/pl/'+str(ClientNumber)+'/'+str(layer), str(layers[layer])
pass
else:
print('Redis key modification failed')
# Maybe one layer can be sent to multiple destination so they are all reset *after* all sending.
for destination in range(len(Dests)):
layers[Dests[str(destination)]["layer"]] = []
'''
'''
scenes = 4
def DrawDestslayer(layer):
for scene in range(scenes):
if Dests[laser]["scene"] != -1:
if r.set('/pl/'+str(Dests[laser]["scene"])+'/'+str(Dests[laser]["laser"]), str(layers[Dests[laser]["laser"]])) == True:
if r.set('/pl/'+str(ClientNumber)+'/'+str(layer), str(layers[layer])) == True:
#print '/pl/'+str(ClientNumber)+'/'+str(layer), str(layers[layer])
layers[Dests[laser]["laser"]] = []
return True
else:
return False
'''
#
# High level drawing functions
#
# Font1
ASCII_GRAPHICS = [
#implementé
[(-50,30), (-30,-30), (30,-30), (10,30), (-50,30)], # 0
[(-20,30), (0,-30), (-20,30)], # 1
[(-30,-10), (0,-30), (30,-10), (30,0), (-30,30), (30,30)], # 2
[(-30,-30), (0,-30), (30,-10), (0,0), (30,10), (0,30), (-30,30)], # 3
[(30,10), (-30,10), (0,-30), (0,30)], # 4
[(30,-30), (-30,-30), (-30,0), (0,0), (30,10), (0,30), (-30,30)], # 5
[(30,-30), (0,-30), (-30,-10), (-30,30), (0,30), (30,10), (30,0), (-30,0)], # 6
[(-30,-30), (30,-30), (-30,30)], # 7
[(-30,30), (-30,-30), (30,-30), (30,30), (-30,30), (-30,0), (30,0)], # 8
[(30,0), (-30,0), (-30,-10), (0,-30), (30,-30), (30,10), (0,30), (-30,30)], # 9
# A implementer
[(-30,10), (30,-10), (30,10), (0,30), (-30,10), (-30,-10), (0,-30), (30,-10)], #:
[(-30,-10), (0,-30), (0,30)], [(-30,30), (30,30)], # ;
[(-30,-10), (0,-30), (30,-10), (30,0), (-30,30), (30,30)], # <
[(-30,-30), (0,-30), (30,-10), (0,0), (30,10), (0,30), (-30,30)], # =
[(30,10), (-30,10), (0,-30), (0,30)], # >
[(30,-30), (-30,-30), (-30,0), (0,0), (30,10), (0,30), (-30,30)], # ?
[(30,-30), (0,-30), (-30,-10), (-30,30), (0,30), (30,10), (30,0), (-30,0)], # @
# Implementé 65-90
[(-30,30), (-30,-30), (30,-30), (30,30), (30,0), (-30,0)], # A
[(-30,30), (-30,-30), (30,-30), (30,30), (30,0), (-30,0)], # A
[(-30,30), (-30,-30), (30,-30), (30,30), (-30,30), (-30,0), (30,0)], # B
[(30,30), (-30,30), (-30,-30), (30,-30)], # C
[(-30,30), (-30,-30), (30,-30), (30,30), (-30,30)], # D
[(30,30), (-30,30), (-30,-0), (30,0), (-30,0), (-30,-30), (30,-30)], # E
[(-30,30), (-30,-0), (30,0), (-30,0), (-30,-30), (30,-30)], # F
[(0,0), (30,0), (30,30), (-30,30), (-30,-30),(30,-30)], # G
[(-30,-30), (-30,30), (-30,0), (30,0), (30,30), (30,-30)], # H
[(0,30), (0,-30)], # I
[(-30,30), (0,-30), (0,-30), (-30,-30), (30,-30)], # J
[(-30,-30), (-30,30), (-30,0), (30,-30), (-30,0), (30,30)], # K
[(30,30), (-30,30), (-30,-30)], # L
[(-30,30), (-30,-30), (0,0), (30,-30), (30,30)], # M
[(-30,30), (-30,-30), (30,30), (30,-30)], # N
[(-30,30), (-30,-30), (30,-30), (30,30), (-30,30)], # O
[(-30,0), (30,0), (30,-30), (-30,-30), (-30,30)], # P
[(30,30), (30,-30), (-30,-30), (-30,30), (30,30),(35,35)], # Q
[(-30,30), (-30,-30), (30,-30), (30,0), (-30,0), (30,30)], # R
[(30,-30), (-30,-30), (-30,0), (30,0), (30,30), (-30,30)], # S
[(0,30), (0,-30), (-30,-30), (30,-30)], # T
[(-30,-30), (-30,30), (30,30), (30,-30)], # U
[(-30,-30), (0,30), (30,-30)], # V
[(-30,-30), (-30,30), (0,0), (30,30), (30,-30)], # W
[(-30,30), (30,-30), (-30,-30), (30,30)], # X
[(0,30), (0,0), (30,-30), (0,0), (-30,-30)], # Y
[(30,30), (-30,30), (30,-30), (-30,-30)], # Z
# A implementer
[(-30,-10), (0,-30), (0,30)], [(-30,30), (30,30)], # [
[(-30,-10), (0,-30), (30,-10), (30,0), (-30,30), (30,30)], # \
[(-30,-30), (0,-30), (30,-10), (0,0), (30,10), (0,30), (-30,30)], # ]
[(30,10), (-30,10), (0,-30), (0,30)], # ^
[(30,-30), (-30,-30), (-30,0), (0,0), (30,10), (0,30), (-30,30)], # _
[(30,-30), (0,-30), (-30,-10), (-30,30), (0,30), (30,10), (30,0), (-30,0)], # `
# Implementé 97-122
[(-20,20), (-20,-20), (20,-20), (20,20), (20,0), (-20,0)], # a
[(-20,20), (-20,-20), (20,-20), (20,20), (-20,20), (-20,0), (20,0)], # b
[(20,20), (-20,20), (-20,-20), (20,-20)], # c
[(-20,20), (-20,-20), (20,-20), (20,20), (-20,20)], # d
[(20,20), (-20,20), (-20,-0), (20,0), (-20,0), (-20,-20), (20,-20)], # e
[(-20,20), (-20,-0), (20,0), (-20,0), (-20,-20), (20,-20)], # f
[(0,0), (20,0), (20,20), (-20,20), (-20,-20),(20,-20)], # g
[(-20,-20), (-20,20), (-20,0), (20,0), (20,20), (20,-20)], # h
[(0,20), (0,-20)], # i
[(-20,20), (0,-20), (0,-20), (-20,-20), (20,-20)], # j
[(-20,-20), (-20,20), (-20,0), (20,-20), (-20,0), (20,20)], # k
[(20,20), (-20,20), (-20,-20)], # l
[(-20,20), (-20,-20), (0,0), (20,-20), (20,20)], # m
[(-20,20), (-20,-20), (20,20), (20,-20)], # n
[(-20,20), (-20,-20), (20,-20), (20,20), (-20,20)], # o
[(-20,0), (20,0), (20,-20), (-20,-20), (-20,20)], # p
[(20,20), (20,-20), (-20,-20), (-20,20), (20,20),(25,25)], # q
[(-20,20), (-20,-20), (20,-20), (20,0), (-20,0), (20,20)], # r
[(20,-20), (-20,-20), (-20,0), (20,0), (20,20), (-20,20)], # s
[(0,20), (0,-20), (-20,-20), (20,-20)], # t
[(-20,-20), (-20,20), (20,20), (20,-20)], # u
[(-20,-20), (0,20), (20,-20)], # v
[(-20,-20), (-20,20), (0,0), (20,20), (20,-20)], # w
[(-20,20), (20,-20)], [(-20,-20), (20,20)], # x
[(0,20), (0,0), (20,-20), (0,0), (-20,-20)], # y
[(20,20), (-20,20), (20,-20), (-20,-20)], # z
# A implementer
[(-2,15), (2,15)], # Point a la place de {
[(-30,-10), (0,-30), (0,30)], [(-30,30), (30,30)], # |
[(-30,-10), (0,-30), (30,-10), (30,0), (-30,30), (30,30)], # }
[(-30,-30), (0,-30), (30,-10), (0,0), (30,10), (0,30), (-30,30)], #
[(30,10), (-30,10), (0,-30), (0,30)], # DEL
# Accents 128-151 a implementer
[(30,30), (-30,30), (-30,-30), (30,-30)], # C
[(-20,-20), (-20,20), (20,20), (20,-20)], # û
[(20,20), (-20,20), (-20,-0), (20,0), (-20,0), (-20,-20), (20,-20)], # é
[(-20,20), (-20,-20), (20,-20), (20,20), (20,0), (-20,0)], # â
[(-20,20), (-20,-20), (20,-20), (20,20), (20,0), (-20,0)], # ä
[(-20,20), (-20,-20), (20,-20), (20,20), (20,0), (-20,0)], # a
[(20,20), (-20,20), (-20,-20), (20,-20)], # c
[(20,20), (-20,20), (-20,-0), (20,0), (-20,0), (-20,-20), (20,-20)], # é
[(20,20), (-20,20), (-20,-0), (20,0), (-20,0), (-20,-20), (20,-20)], # é
[(20,20), (-20,20), (-20,-0), (20,0), (-20,0), (-20,-20), (20,-20)], # é
[(0,20), (0,-20)], # i
[(0,20), (0,-20)], # i
[(0,20), (0,-20)], # i
[(-30,30), (-30,-30), (30,-30), (30,30), (30,0), (-30,0)], # A
[(-30,30), (-30,-30), (30,-30), (30,30), (30,0), (-30,0)], # A
[(30,30), (-30,30), (-30,-0), (30,0), (-30,0), (-30,-30), (30,-30)], # E
[(-20,20), (-20,-20), (20,-20), (20,20), (20,0), (-20,0)], # a
[(-20,20), (-20,-20), (20,-20), (20,20), (20,0), (-20,0)], # a
[(-20,20), (-20,-20), (20,-20), (20,20), (-20,20)], # o
[(-20,20), (-20,-20), (20,-20), (20,20), (-20,20)], # o
[(-20,20), (-20,-20), (20,-20), (20,20), (-20,20)], # o
[(-20,-20), (-20,20), (20,20), (20,-20)], # u
[(-20,-20), (-20,20), (20,20), (20,-20)] # u
]
def DigitsDots(number,color):
dots =[]
for dot in ASCII_GRAPHICS[number]:
#print dot
dots.append((gstt.xy_center[0]+dot[0],gstt.xy_center[1]+dot[1],color))
#self.point_list.append((xy + (c,)))
return dots
def CharDots(char,color):
dots =[]
for dot in ASCII_GRAPHICS[ord(char)-46]:
dots.append((dot[0],dot[1],color))
return dots
def Text(message, c, layer, xpos, ypos, resize, rotx, roty, rotz):
dots =[]
l = len(message)
i= 0
#print()
# print (message)
for ch in message:
#print ""
# texte centre en x automatiquement selon le nombre de lettres l
x_offset = 26 * (- (0.9*l) + 3*i)
# Digits
if ord(ch)<58:
char_layer_list = ASCII_GRAPHICS[ord(ch) - 48]
# Uppercase
elif 64 < ord(ch) < 91 :
char_layer_list = ASCII_GRAPHICS[ord(ch) - 46]
# Lowercase
elif 96 < ord(ch) < 123 :
char_layer_list = ASCII_GRAPHICS[ord(ch) - 45]
char_draw = []
#dots.append((char_layer_list[0][0] + x_offset,char_layer_list[0][1],0))
for xy in char_layer_list:
char_draw.append((xy[0] + x_offset,xy[1],c))
i +=1
#print ch,char_layer_list,char_draw
rPolyLineOneColor(char_draw, c, layer , False, xpos, ypos, resize, rotx, roty, rotz)
#dots.append(char_draw)
def TextRGB(message,c, layer, xpos, ypos, resize, rotx, roty, rotz):
Text(message,int('0x%02x%02x%02x' % (red,green,blue),0), layer, xpos, ypos, resize, rotx, roty, rotz)

659
libs3/lj3.py Normal file
View file

@ -0,0 +1,659 @@
'''
lj3 v0.7.5 for LJ v0.8+
Some LJ functions useful for python clients
lj3 is deprecated use lj23
OSC functions commented, waiting working on OSC in python3
Config(redisIP, client number,name)
PolyLineOneColor
rPolyLineOneColor
Text(word, color, PL, xpos, ypos, resize, rotx, roty, rotz) : Display a word
SendLJ(adress,message) : LJ remote control. See commands.py
WebStatus(message) : display message on webui
DrawPL(point list number) : once you stacked all wanted elements, like 2 polylines, send them to lasers.
rgb2int(r,g,b)
LjClient(client): Change Client number in redis keys
LjPl(pl): Change pl number in redis keys = laser target.
ClosePlugin(name): Send UI closing info of given plugin
OSCstart(): Start the OSC system.
OSCframe(): Handle incoming OSC message. Calling the right callback
OSCstop(): Properly close the OSC system
OSCping(): Answer to LJ pings by sending /pong name
OSCquit(): Exit calling script using name in terminal
setup_controls(joystick)
XboxController : getLeftHori, getLeftVert, getRightHori, getRightVert, getLeftTrigger, getRightTrigger
Ps3Controller : getLeftHori, getLeftVert, getRightHori, getRightVert, getLeftTrigger, getRightTrigger, getUp, getDown, getLeft, getRight, getFire1, getFire2(self):
MySaitekController : getLeftHori,getLeftVert, getRightHori,getRightVert, getLeftTrigger,getRightTrigger
MyThrustController : getLeftHori, getLeftVert, getRightHori, getRightVert, getLeftTrigger, getRightTrigger
CSLController : getLeftHori,getLeftVert,getRightHori, getRightVert,getLeftTrigger,getRightTrigger,getFire1,getFire2
my USB Joystick : getUp,getDown,getLeft,getRight,etLeftTrigger, getRightTrigger,getFire1, getFire2
LICENCE : CC
Sam Neurohack
'''
import math
import redis
# Import needed modules from osc4py3
from osc4py3.as_eventloop import *
from osc4py3 import oscbuildparse
#from osc4py3 import oscmethod as osm
from osc4py3.oscmethod import *
#redisIP = '127.0.0.1'
#r = redis.StrictRedis(host=redisIP, port=6379, db=0)
print('Importing lj3 from libs...')
ClientNumber = 0
name = "noname"
point_list = []
pl = [[],[],[],[]]
#
# OSC interaction with LJ
#
def OSCstart():
# Start the system.
osc_startup()
#osc_udp_client(redisIP, 8002, "LJ 8002")
def OSCframe():
#print("OSCprocess")
osc_process()
# Properly close the system. Todo
def OSCstop():
osc_terminate()
def SendLJ(oscaddress,oscargs=''):
try:
msg = oscbuildparse.OSCMessage(oscaddress, None, [oscargs])
# print(msg)
print("lj3 sending OSC message to", redisIP, ":8002")
osc_send(msg, "LJ 8002")
OSCframe()
except:
print (oscaddress,'Connection to LJ refused : died ?')
pass
def WebStatus(message):
SendLJ("/status", message)
# Answer to LJ /ping 1 with /pong name
def OSCping(value):
print(name,"got /ping 1 from LJ -> reply /pong", name)
SendLJ("/pong",name)
# Closing plugin messages to LJ
def ClosePlugin():
WebStatus(name+" Exit")
SendLJ("/"+name+"/start",0)
print("Stopping OSC...")
OSCstop()
'''
# /quit
def OSCquit():
WebStatus(name + " quit.")
SendLJ("/"+name+"/start",0)
print("Stopping OSC...")
OSCstop()
sys.exit()
'''
'''
def handlerfunction(s, x, y):
# Will receive message data unpacked in s, x, y
pass
def handlerfunction2(address, s, x, y):
# Will receive message address, and message data flattened in s, x, y
pass
# Make server channels to receive packets.
osc_udp_server("127.0.0.1", 3721, "localhost")
osc_udp_server("0.0.0.0", 3724, "anotherserver")
'''
ASCII_GRAPHICS = [
# caracteres corrects
[(-50,30), (-30,-30), (30,-30), (10,30), (-50,30)], #0
[(-20,30), (0,-30), (-20,30)], #1
[(-30,-10), (0,-30), (30,-10), (30,0), (-30,30), (30,30)], #2
[(-30,-30), (0,-30), (30,-10), (0,0), (30,10), (0,30), (-30,30)], #3
[(30,10), (-30,10), (0,-30), (0,30)], #4
[(30,-30), (-30,-30), (-30,0), (0,0), (30,10), (0,30), (-30,30)], #5
[(30,-30), (0,-30), (-30,-10), (-30,30), (0,30), (30,10), (30,0), (-30,0)], #6
[(-30,-30), (30,-30), (-30,30)], #7
[(-30,30), (-30,-30), (30,-30), (30,30), (-30,30), (-30,0), (30,0)], #8
[(30,0), (-30,0), (-30,-10), (0,-30), (30,-30), (30,10), (0,30), (-30,30)], #9
# caracteres a implementer
[(-30,10), (30,-10), (30,10), (0,30), (-30,10), (-30,-10), (0,-30), (30,-10)], #:
[(-30,-10), (0,-30), (0,30)], [(-30,30), (30,30)], #;
[(-30,-10), (0,-30), (30,-10), (30,0), (-30,30), (30,30)], #<
[(-30,-30), (0,-30), (30,-10), (0,0), (30,10), (0,30), (-30,30)], #=
[(30,10), (-30,10), (0,-30), (0,30)], #>
[(30,-30), (-30,-30), (-30,0), (0,0), (30,10), (0,30), (-30,30)], #?
[(30,-30), (0,-30), (-30,-10), (-30,30), (0,30), (30,10), (30,0), (-30,0)], #@
# Caracteres corrects
[(-30,30), (-30,-30), (30,-30), (30,30), (30,0), (-30,0)], #A
[(-30,30), (-30,-30), (30,-30), (30,30), (30,0), (-30,0)], #A
[(-30,30), (-30,-30), (30,-30), (30,30), (-30,30), (-30,0), (30,0)], #B
[(30,30), (-30,30), (-30,-30), (30,-30)], #C
[(-30,30), (-30,-30), (30,-30), (30,30), (-30,30)], #D
[(30,30), (-30,30), (-30,-0), (30,0), (-30,0), (-30,-30), (30,-30)], #E
[(-30,30), (-30,-0), (30,0), (-30,0), (-30,-30), (30,-30)], #F
[(0,0), (30,0), (30,30), (-30,30), (-30,-30),(30,-30)], #G
[(-30,-30), (-30,30), (-30,0), (30,0), (30,30), (30,-30)], #H
[(0,30), (0,-30)], #I
[(-30,30), (0,-30), (0,-30), (-30,-30), (30,-30)], #J
[(-30,-30), (-30,30), (-30,0), (30,-30), (-30,0), (30,30)], #K
[(30,30), (-30,30), (-30,-30)], #L
[(-30,30), (-30,-30), (0,0), (30,-30), (30,30)], #M
[(-30,30), (-30,-30), (30,30), (30,-30)], #N
[(-30,30), (-30,-30), (30,-30), (30,30), (-30,30)], #O
[(-30,0), (30,0), (30,-30), (-30,-30), (-30,30)], #P
[(30,30), (30,-30), (-30,-30), (-30,30), (30,30),(35,35)], #Q
[(-30,30), (-30,-30), (30,-30), (30,0), (-30,0), (30,30)], #R
[(30,-30), (-30,-30), (-30,0), (30,0), (30,30), (-30,30)], #S
[(0,30), (0,-30), (-30,-30), (30,-30)], #T
[(-30,-30), (-30,30), (30,30), (30,-30)], #U
[(-30,-30), (0,30), (30,-30)], #V
[(-30,-30), (-30,30), (0,0), (30,30), (30,-30)], #W
[(-30,30), (30,-30)], [(-30,-30), (30,30)], #X
[(0,30), (0,0), (30,-30), (0,0), (-30,-30)], #Y
[(30,30), (-30,30), (30,-30), (-30,-30)], #Z
# A implementer
[(-30,-10), (0,-30), (0,30)], [(-30,30), (30,30)], #[
[(-30,-10), (0,-30), (30,-10), (30,0), (-30,30), (30,30)], #\
[(-30,-30), (0,-30), (30,-10), (0,0), (30,10), (0,30), (-30,30)], #]
[(30,10), (-30,10), (0,-30), (0,30)], #^
[(30,-30), (-30,-30), (-30,0), (0,0), (30,10), (0,30), (-30,30)], #_
[(30,-30), (0,-30), (-30,-10), (-30,30), (0,30), (30,10), (30,0), (-30,0)], #`
# Implementé
[(-20,20), (-20,-20), (20,-20), (20,20), (20,0), (-20,0)], #a
[(-20,20), (-20,-20), (20,-20), (20,20), (-20,20), (-20,0), (20,0)], #b
[(20,20), (-20,20), (-20,-20), (20,-20)], #c
[(-20,20), (-20,-20), (20,-20), (20,20), (-20,20)], #d
[(20,20), (-20,20), (-20,-0), (20,0), (-20,0), (-20,-20), (20,-20)], #e
[(-20,20), (-20,-0), (20,0), (-20,0), (-20,-20), (20,-20)], #f
[(0,0), (20,0), (20,20), (-20,20), (-20,-20),(20,-20)], #g
[(-20,-20), (-20,20), (-20,0), (20,0), (20,20), (20,-20)], #H
[(0,20), (0,-20)], #I
[(-20,20), (0,-20), (0,-20), (-20,-20), (20,-20)], #J
[(-20,-20), (-20,20), (-20,0), (20,-20), (-20,0), (20,20)], #K
[(20,20), (-20,20), (-20,-20)], #L
[(-20,20), (-20,-20), (0,0), (20,-20), (20,20)], #M
[(-20,20), (-20,-20), (20,20), (20,-20)], #N
[(-20,20), (-20,-20), (20,-20), (20,20), (-20,20)], #O
[(-20,0), (20,0), (20,-20), (-20,-20), (-20,20)], #P
[(20,20), (20,-20), (-20,-20), (-20,20), (20,20),(25,25)], #Q
[(-20,20), (-20,-20), (20,-20), (20,0), (-20,0), (20,20)], #R
[(20,-20), (-20,-20), (-20,0), (20,0), (20,20), (-20,20)], #S
[(0,20), (0,-20), (-20,-20), (20,-20)], #T
[(-20,-20), (-20,20), (20,20), (20,-20)], #U
[(-20,-20), (0,20), (20,-20)], #V
[(-20,-20), (-20,20), (0,0), (20,20), (20,-20)], #W
[(-20,20), (20,-20)], [(-20,-20), (20,20)], #X
[(0,20), (0,0), (20,-20), (0,0), (-20,-20)], #Y
[(20,20), (-20,20), (20,-20), (-20,-20)], #Z
[(-2,15), (2,15)] # Point a la place de {
]
def rgb2int(r,g,b):
return int('0x%02x%02x%02x' % (r,g,b),0)
def Config(redisIP,client,myname):
global ClientNumber, r, name
name = myname
print ("lj3 got a name to use :", name)
r = redis.StrictRedis(host=redisIP, port=6379, db=0)
ClientNumber = client
osc_udp_client(redisIP, 8002, "LJ 8002")
return r
def LjClient(client):
global ClientNumber
ClientNumber = client
def LjPl(pl):
global PL
PL = pl
def LineTo(xy, c, PL):
pl[PL].append((xy + (c,)))
def Line(xy1, xy2, c, PL):
LineTo(xy1, 0, PL)
LineTo(xy2, c , PL)
def PolyLineOneColor(xy_list, c, PL , closed ):
#print "--"
#print "c",c
#print "xy_list",xy_list
#print "--"
xy0 = None
for xy in xy_list:
if xy0 is None:
xy0 = xy
#print "xy0:",xy0
LineTo(xy0,0, PL)
LineTo(xy0,c, PL)
else:
#print "xy:",xy
LineTo(xy,c, PL)
if closed:
LineTo(xy0,c, PL)
# Computing points coordinates for rPolyline function from 3D and around 0,0 to pygame coordinates
def Pointransf(xy, xpos = 0, ypos =0, resize =1, rotx =0, roty =0 , rotz=0):
x = xy[0] * resize
y = xy[1] * resize
z = 0
rad = rotx * math.pi / 180
cosaX = math.cos(rad)
sinaX = math.sin(rad)
y2 = y
y = y2 * cosaX - z * sinaX
z = y2 * sinaX + z * cosaX
rad = roty * math.pi / 180
cosaY = math.cos(rad)
sinaY = math.sin(rad)
z2 = z
z = z2 * cosaY - x * sinaY
x = z2 * sinaY + x * cosaY
rad = rotz * math.pi / 180
cosZ = math.cos(rad)
sinZ = math.sin(rad)
x2 = x
x = x2 * cosZ - y * sinZ
y = x2 * sinZ + y * cosZ
#print xy, (x + xpos,y+ ypos)
return (x + xpos,y+ ypos)
'''
to understand why it get negative Y
# 3D to 2D projection
factor = 4 * gstt.cc[22] / ((gstt.cc[21] * 8) + z)
print xy, (x * factor + xpos, - y * factor + ypos )
return (x * factor + xpos, - y * factor + ypos )
'''
# Send 2D point list around 0,0 with 3D rotation resizing and reposition around xpos ypos
#def rPolyLineOneColor(self, xy_list, c, PL , closed, xpos = 0, ypos =0, resize =1, rotx =0, roty =0 , rotz=0):
def rPolyLineOneColor(xy_list, c, PL , closed, xpos = 0, ypos =0, resize =0.7, rotx =0, roty =0 , rotz=0):
xy0 = None
for xy in xy_list:
if xy0 is None:
xy0 = xy
LineTo(Pointransf(xy0, xpos, ypos, resize, rotx, roty, rotz),0, PL)
LineTo(Pointransf(xy0, xpos, ypos, resize, rotx, roty, rotz),c, PL)
else:
LineTo(Pointransf(xy, xpos, ypos, resize, rotx, roty, rotz),c, PL)
if closed:
LineTo(Pointransf(xy0, xpos, ypos, resize, rotx, roty, rotz),c, PL)
def LinesPL(PL):
print ("Stupido !! your code is to old : use DrawPL() instead of LinesPL()")
DrawPL(PL)
def DrawPL(PL):
#print '/pl/0/'+str(PL), str(pl[PL])
if r.set('/pl/'+str(ClientNumber)+'/'+str(PL), str(pl[PL])) == True:
pl[PL] = []
return True
else:
return False
def ResetPL(self, PL):
pl[PL] = []
def DigitsDots(number,color):
dots =[]
for dot in ASCII_GRAPHICS[number]:
#print dot
dots.append((gstt.xy_center[0]+dot[0],gstt.xy_center[1]+dot[1],color))
#self.point_list.append((xy + (c,)))
return dots
def CharDots(char,color):
dots =[]
for dot in ASCII_GRAPHICS[ord(char)-46]:
dots.append((dot[0],dot[1],color))
return dots
def Text(message,c, PL, xpos, ypos, resize, rotx, roty, rotz):
dots =[]
l = len(message)
i= 0
#print message
for ch in message:
#print ""
# texte centre en x automatiquement selon le nombre de lettres l
x_offset = 26 * (- (0.9*l) + 3*i)
#print i,x_offset
# if digit
if ord(ch)<58:
char_pl_list = ASCII_GRAPHICS[ord(ch) - 48]
else:
char_pl_list = ASCII_GRAPHICS[ord(ch) - 46 ]
char_draw = []
#dots.append((char_pl_list[0][0] + x_offset,char_pl_list[0][1],0))
for xy in char_pl_list:
char_draw.append((xy[0] + x_offset,xy[1],c))
i +=1
#print ch,char_pl_list,char_draw
rPolyLineOneColor(char_draw, c, PL , False, xpos, ypos, resize, rotx, roty, rotz)
#print ("laser",PL,"message",message)
#dots.append(char_draw)
import re
def setup_controls(joystick):
"""
Joystick wrapper.
"""
if re.search('playstation', joystick.get_name(), re.I):
return Ps3Controller(joystick)
elif re.search('X-box', joystick.get_name(), re.I):
return XboxController(joystick)
elif re.search('Saitek', joystick.get_name(), re.I):
return MySaitekController(joystick)
elif re.search('Thrustmaster dual analog 3.2', joystick.get_name(), re.I):
return MyThrustController(joystick)
elif re.search('2n1 USB', joystick.get_name(), re.I):
return CSLController(joystick)
elif re.search('Joystick', joystick.get_name(), re.I):
return USBController(joystick)
return Controller(joystick)
class Controller(object):
def __init__(self, joystick):
"""Pass a PyGame joystick instance."""
self.js = joystick
def getLeftHori(self):
return self.js.get_axis(2)
def getLeftVert(self):
return self.js.get_axis(3)
def getRightHori(self):
return self.js.get_axis(0)
def getRightVert(self):
return self.js.get_axis(1)
def getLeftTrigger(self):
return self.js.get_button(9)
def getRightTrigger(self):
return self.js.get_button(2)
class XboxController(Controller):
def __init__(self, joystick):
super(XboxController, self).__init__(joystick)
def getLeftHori(self):
return self.js.get_axis(0)
def getLeftVert(self):
return self.js.get_axis(1)
def getRightHori(self):
return self.js.get_axis(3)
def getRightVert(self):
return self.js.get_axis(4)
def getLeftTrigger(self):
return self.js.get_axis(2)
def getRightTrigger(self):
return self.js.get_button(11)
class Ps3Controller(Controller):
#up 4 _DOWN 6 left 7 right 5 croix 14 rond 13 triangle 12
def __init__(self, joystick):
super(Ps3Controller, self).__init__(joystick)
def getLeftHori(self):
return self.js.get_axis(0)
def getLeftVert(self):
return self.js.get_axis(1)
def getRightHori(self):
return self.js.get_axis(2)
def getRightVert(self):
return self.js.get_axis(3)
def getLeftTrigger(self):
# TODO: Verify
return self.js.get_button(8)
def getRightTrigger(self):
# TODO: Verify
return self.js.get_button(9)
def getUp(self):
return self.js.get_button(4)
def getDown(self):
return self.js.get_button(6)
def getLeft(self):
return self.js.get_button(7)
def getRight(self):
return self.js.get_button(5)
def getFire1(self):
return self.js.get_button(14)
def getFire2(self):
return self.js.get_button(13)
class MySaitekController(Controller):
def __init__(self, joystick):
super(MySaitekController, self).__init__(joystick)
def getLeftHori(self):
return self.js.get_axis(0)
def getLeftVert(self):
return self.js.get_axis(1)
def getRightHori(self):
return self.js.get_axis(3)
def getRightVert(self):
return self.js.get_axis(2)
def getLeftTrigger(self):
return self.js.get_button(6)
def getRightTrigger(self):
return self.js.get_button(7)
class MyThrustController(Controller):
def __init__(self, joystick):
super(MyThrustController, self).__init__(joystick)
def getLeftHori(self):
return self.js.get_axis(0)
def getLeftVert(self):
return self.js.get_axis(1)
def getRightHori(self):
return self.js.get_axis(2)
def getRightVert(self):
return self.js.get_axis(3)
def getLeftTrigger(self):
return self.js.get_button(5)
def getRightTrigger(self):
return self.js.get_button(7)
class CSLController(Controller):
def __init__(self, joystick):
super(CSLController, self).__init__(joystick)
def getLeftHori(self):
return self.js.get_axis(2)
def getLeftVert(self):
return self.js.get_axis(3)
def getRightHori(self):
return self.js.get_axis(0)
def getRightVert(self):
return self.js.get_axis(1)
def getLeftTrigger(self):
return self.js.get_button(6)
def getRightTrigger(self):
return self.js.get_button(7)
def getFire1(self):
return self.js.get_button(2)
def getFire2(self):
return self.js.get_button(1)
class USBController(Controller):
# my USB Joystick
#up axis 0 -1 DOWN axis 0 1 left axis 1 1 right axis 1 -1 bouton gauche 10 bouton droite 9
def __init__(self, joystick):
super(USBController, self).__init__(joystick)
def getUp(self):
if self.js.get_axis(0) == -1:
return 1
else:
return 0
def getDown(self):
if self.js.get_axis(0) > 0.9:
return 1
else:
return 0
def getLeft(self):
if self.js.get_axis(1) == 1:
return 1
else:
return 0
def getRight(self):
if self.js.get_axis(1) == -1:
return 1
else:
return 0
def getLeftTrigger(self):
return self.js.get_button(10)
def getRightTrigger(self):
return self.js.get_button(9)
def getFire1(self):
if self.js.get_button(10) == 1:
print ("fire 1")
return self.js.get_button(10)
def getFire2(self):
if self.js.get_button(9) == 1:
print ("fire 2")
return self.js.get_button(9)

43
libs3/log.py Normal file
View file

@ -0,0 +1,43 @@
#!/usr/bin/python3
# -*- coding: utf-8 -*-
# -*- mode: Python -*-
'''
Log in color from
https://stackoverflow.com/questions/287871/how-to-print-colored-text-in-terminal-in-python
usage :
import log
log.info("Hello World")
log.err("System Error")
'''
HEADER = '\033[95m'
OKBLUE = '\033[94m'
OKGREEN = '\033[92m'
WARNING = '\033[93m'
FAIL = '\033[91m'
ENDC = '\033[0m'
BOLD = "\033[1m"
def disable():
HEADER = ''
OKBLUE = ''
OKGREEN = ''
WARNING = ''
FAIL = ''
ENDC = ''
def infog( msg):
print(OKGREEN + msg + ENDC)
def info( msg):
print(OKBLUE + msg + ENDC)
def warn( msg):
print(WARNING + msg + ENDC)
def err( msg):
print(FAIL + msg + ENDC)

404
libs3/maxwellccs.py Normal file
View file

@ -0,0 +1,404 @@
#!/usr/bin/python3
# -*- coding: utf-8 -*-
"""
Maxwell Macros
v0.7.0
by Sam Neurohack
from /team/laser
Launchpad set a "current path"
"""
from OSC3 import OSCServer, OSCClient, OSCMessage
import time
import numpy as np
import rtmidi
from rtmidi.midiutil import open_midiinput
from threading import Thread
from rtmidi.midiconstants import (CHANNEL_PRESSURE, CONTROLLER_CHANGE, NOTE_ON, NOTE_OFF,
PITCH_BEND, POLY_PRESSURE, PROGRAM_CHANGE)
import os, json
import midi3
if os.uname()[1]=='raspberrypi':
pass
port = 8090
ip = "127.0.0.1"
mididest = 'Session 1'
djdest = 'Port'
midichannel = 1
computerIP = ['127.0.0.1','192.168.2.95','192.168.2.52','127.0.0.1',
'127.0.0.1','127.0.0.1','127.0.0.1','127.0.0.1']
computer = 0
# store current value for computer 1
cc1 =[0]*140
current = {
"patch": 0,
"prefixLeft": "/osc/left/X",
"prefixRight": "/osc/right/X",
"suffix": "/amp",
"path": "/osc/left/X/curvetype",
"pathLeft": "/osc/left/X/curvetype",
"pathRight": "/osc/left/X/curvetype",
"previousmacro": -1,
"LeftCurveType": 0,
"lfo": 1,
"rotator": 1,
"translator": 1
}
specificvalues = {
# Sine: 0-32, Tri: 33-64, Square: 65-96, Line: 96-127
"curvetype": {"sin": 0, "saw": 33, "squ": 95, "lin": 127},
"freqlimit": {"1": 0, "4": 26, "16": 52, "32": 80, "127": 127},
"amptype": {"constant": 0, "lfo1": 33, "lfo2": 95, "lfo3": 127},
"phasemodtype": {"linear": 0,"sin": 90},
"phaseoffsettype": {"manual": 0, "lfo1": 33, "lfo2": 95, "lfo3": 127},
"ampoffsettype": { "manual": 0, "lfo1": 33, "lfo2": 95, "lfo3": 127},
"inversion": {"off": 0, "on": 127},
"colortype": {"solid": 0, "lfo": 127},
"modtype": {"sin": 0,"linear": 127},
"switch": {"off": 0,"on": 127},
"operation": {"+": 0, "-": 50, "*": 127}
}
#
# Maxwell CCs
#
def FindCC(FunctionName):
for Maxfunction in range(len(maxwell['ccs'])):
if FunctionName == maxwell['ccs'][Maxfunction]['Function']:
#print(FunctionName, "is CC", Maxfunction)
return Maxfunction
def LoadCC():
global maxwell
print("Loading Maxwell CCs Functions...")
if os.path.exists('maxwell.json'):
#print('File maxwell.json exits')
f=open("maxwell.json","r")
else:
if os.path.exists('../maxwell.json'):
#print('File ../maxwell.json exits')
f=open("../maxwell.json","r")
s = f.read()
maxwell = json.loads(s)
print(len(maxwell['ccs']),"Functions")
print("Loaded.")
# /cc cc number value
def cc(ccnumber, value, dest=mididest):
#print('Output CC',[CONTROLLER_CHANGE+midichannel-1, ccnumber, value], dest)
midi3.MidiMsg([CONTROLLER_CHANGE+midichannel-1,ccnumber,value], dest)
def NoteOn(note,velocity, dest=mididest):
midi3.NoteOn(note,velocity, mididest)
def NoteOff(note, dest=mididest):
midi3.NoteOn(note, mididest)
def Send(oscaddress,oscargs=''):
oscmsg = OSCMessage()
oscmsg.setAddress(oscaddress)
oscmsg.append(oscargs)
osclient = OSCClient()
osclient.connect((ip, port))
print("sending OSC message : ", oscmsg, "to", ip, ":",port)
try:
osclient.sendto(oscmsg, (ip, port))
oscmsg.clearData()
return True
except:
print ('Connection to', ip, 'refused : died ?')
return False
def ssawtooth(samples,freq,phase):
t = np.linspace(0+phase, 1+phase, samples)
for ww in range(samples):
samparray[ww] = signal.sawtooth(2 * np.pi * freq * t[ww])
return samparray
def ssquare(samples,freq,phase):
t = np.linspace(0+phase, 1+phase, samples)
for ww in range(samples):
samparray[ww] = signal.square(2 * np.pi * freq * t[ww])
return samparray
def ssine(samples,freq,phase):
t = np.linspace(0+phase, 1+phase, samples)
for ww in range(samples):
samparray[ww] = np.sin(2 * np.pi * freq * t[ww])
return samparray
def MixerLeft(value):
if value == 127:
Send("/mixer/value", 0)
def MixerRight(value):
if value == 127:
Send("/mixer/value", 127)
def MixerTempo(tempo):
for counter in range(127):
Send("/mixer/value", counter)
# Jog send 127 to left and 1 to right
# increase or decrease current CC defined in current path
def jogLeft(value):
path = current["pathLeft"]
print("jog : path =",path, "CC :", FindCC(path), "value", value)
MaxwellCC = FindCC(current["pathLeft"])
if value == 127:
# decrease CC
if cc1[MaxwellCC] > 0:
cc1[MaxwellCC] -= 1
else:
if cc1[MaxwellCC] < 127:
cc1[MaxwellCC] += 1
#print("sending", cc1[MaxwellCC], "to CC", MaxwellCC )
cc(MaxwellCC, cc1[MaxwellCC] , dest ='to Maxwell 1')
#RotarySpecifics(MaxwellCC, path[path.rfind("/")+1:len(path)], value)
# Jog send 127 to left and 1 to right
# increase or decrease current CC defined in current path
def jogRight(value):
path = current["pathRight"]
print("jog : path =",path, "CC :", FindCC(path), "value", value)
MaxwellCC = FindCC(current["pathRight"])
if value == 127:
# decrease CC
if cc1[MaxwellCC] > 0:
cc1[MaxwellCC] -= 1
else:
if cc1[MaxwellCC] < 127:
cc1[MaxwellCC] += 1
#print("sending", cc1[MaxwellCC], "to CC", MaxwellCC )
cc(MaxwellCC, cc1[MaxwellCC] , dest ='to Maxwell 1')
#RotarySpecifics(MaxwellCC, path[path.rfind("/")+1:len(path)], value)
# Parameter change : to left 127 / to right 0 or 1
def RotarySpecifics( MaxwellCC, specificsname, value):
global maxwell
print("Maxwell CC :",MaxwellCC)
print("Current :",maxwell['ccs'][MaxwellCC]['init'])
print("Specifics :",specificvalues[specificsname])
print("midi value :", value)
elements = list(enumerate(specificvalues[specificsname]))
print(elements)
nextype = maxwell['ccs'][MaxwellCC]['init']
for count,ele in elements:
if ele == maxwell['ccs'][MaxwellCC]['init']:
if count > 0 and value == 127:
nextype = elements[count-1][1]
if count < len(elements)-1 and value < 2:
#print("next is :",elements[count+1][1])
nextype = elements[count+1][1]
print("result :", nextype, "new value :", specificvalues[specificsname][nextype], "Maxwell CC", MaxwellCC)
maxwell['ccs'][MaxwellCC]['init'] = nextype
cc(MaxwellCC, specificvalues[specificsname][nextype], dest ='to Maxwell 1')
# Change type : trig with only with midi value 127 on a CC event
def ButtonSpecifics127( MaxwellCC, specificsname, value):
global maxwell
print("Maxwell CC :",MaxwellCC)
print("Current :",maxwell['ccs'][MaxwellCC]['init'])
print("Specifics :",specificvalues[specificsname])
print("midi value :", value)
elements = list(enumerate(specificvalues[specificsname]))
print(elements)
nextype = maxwell['ccs'][MaxwellCC]['init']
for count,ele in elements:
if ele == maxwell['ccs'][MaxwellCC]['init']:
if count >0 and value == 127:
nextype = elements[count-1][1]
if count < len(elements)-1 and value < 2:
#print("next is :",elements[count+1][1])
nextype = elements[count+1][1]
print("result :", nextype, "new value :", specificvalues[specificsname][nextype], "Maxwell CC", MaxwellCC)
maxwell['ccs'][MaxwellCC]['init'] = nextype
cc(MaxwellCC, specificvalues[specificsname][nextype], dest ='to Maxwell 1')
# Left cue button 127 = on 0 = off
def PrevPatch(value):
global current
print('PrevPatch function')
if value == 127 and current['patch'] - 1 > -1:
cc(9, 127, dest=djdest)
time.sleep(0.1)
current['patch'] -= 1
print("Current patch is now :",current['patch'])
midi3.NoteOn(current['patch'], 127, 'to Maxwell 1')
cc(9, 0, dest=djdest)
# Right cue button 127 = on 0 = off
def NextPatch(value):
global current
print('NextPatch function', current["patch"])
if value == 127 and current["patch"] + 1 < 41:
cc(3, 127, dest = djdest)
current["patch"] += 1
#ModeNote(current["patch"], 127, 'to Maxwell 1')
midi3.NoteOn(current["patch"], 127, 'to Maxwell 1')
print("Current patch is now :",current["patch"])
time.sleep(0.1)
cc(3, 0, dest = djdest)
# increase/decrease a CC
def changeCC(value, path):
global current
#path = current["pathLeft"]
MaxwellCC = FindCC(path)
cc1[MaxwellCC] += value
print("Change Left CC : path =",path, "CC :", FindCC(path), "is now ", cc1[MaxwellCC])
cc(MaxwellCC, cc1[MaxwellCC] , dest ='to Maxwell 1')
def PlusTenLeft(value):
value = 10
changeCC(value, current["pathLeft"])
def MinusTenLeft(value):
value = -10
changeCC(value, current["pathLeft"])
def PlusOneLeft(value):
value = 1
changeCC(value, current["pathLeft"])
def MinusOneLeft(value):
value = -1
changeCC(value, current["pathLeft"])
def PlusTenRight(value):
value = 10
changeCC(value, current["pathRight"])
def MinusTenRight(value):
value = -10
changeCC(value, current["pathRight"])
def PlusOneRight(value):
value = 1
changeCC(value, current["pathRight"])
def MinusOneRight(value):
value = -1
changeCC(value, current["pathRight"])
def ChangeCurveLeft(value):
MaxwellCC = FindCC(current["prefixLeft"] + '/curvetype')
RotarySpecifics(MaxwellCC, "curvetype", value)
def ChangeFreqLimitLeft(value):
MaxwellCC = FindCC(current["prefixLeft"] + '/freqlimit')
RotarySpecifics(MaxwellCC, "curvetype", value)
def ChangeATypeLeft(value):
MaxwellCC = FindCC(current["prefixLeft"] + '/freqlimit')
RotarySpecifics(MaxwellCC, "curvetype", value)
def ChangePMTypeLeft(value):
MaxwellCC = FindCC(current["prefixLeft"] + '/phasemodtype')
RotarySpecifics(MaxwellCC, "curvetype", value)
def ChangePOTypeLeft(value):
MaxwellCC = FindCC(current["prefixLeft"] + '/phaseoffsettype')
RotarySpecifics(MaxwellCC, "curvetype", value)
def ChangeAOTypeLeft(value):
MaxwellCC = FindCC(current["prefixLeft"] + '/ampoffsettype')
RotarySpecifics(MaxwellCC, "curvetype", value)
def ChangeCurveRight(value):
MaxwellCC = FindCC(current["prefixRight"] + '/curvetype')
RotarySpecifics(MaxwellCC, "curvetype", value)
def ChangeCurveLFO(value):
MaxwellCC = FindCC('/lfo/'+ current["lfo"] +'/curvetype')
RotarySpecifics(MaxwellCC, "curvetype", value)
def ChangeCurveRot(value):
MaxwellCC = FindCC('/rotator/'+ current["rotator"] +'/curvetype')
RotarySpecifics(MaxwellCC, "curvetype", value)
def ChangeCurveTrans(value):
MaxwellCC = FindCC('/translator/'+ current["translator"] +'/curvetype')
RotarySpecifics(MaxwellCC, "curvetype", value)

452
libs3/midi.py Normal file
View file

@ -0,0 +1,452 @@
#!/usr/bin/python2.7
# -*- coding: utf-8 -*-
"""
LJay/LJ
v0.7.0
Midi Handler
Deprecated, see midi3
by Sam Neurohack
from /team/laser
"""
print "importing midi 0"
import time
import rtmidi
from rtmidi.midiutil import open_midiinput
from threading import Thread
from rtmidi.midiconstants import (CHANNEL_PRESSURE, CONTROLLER_CHANGE, NOTE_ON, NOTE_OFF,
PITCH_BEND, POLY_PRESSURE, PROGRAM_CHANGE)
import mido
from mido import MidiFile
#import mido
import sys
from serial.tools import list_ports
import serial
from sys import platform
import gstt
# import bhoroscp
import bhoreal
import launchpad
from OSC import OSCServer, OSCClient, OSCMessage
#import orbits
midiname = ["Name"] * 16
midiport = [rtmidi.MidiOut() for i in range(16) ]
is_py2 = sys.version[0] == '2'
if is_py2:
from Queue import Queue
else:
from queue import Queue
# max 16 midi port array
midinputsname = ["Name"] * 16
midinputsqueue = [Queue() for i in range(16) ]
midinputs = []
BhorealMidiName = "Bhoreal"
LaunchMidiName = "Launch"
BhorealPort, Midi1Port, Midi2Port, VirtualPort, MPort = -1,-1,-1, -1, -1
VirtualName = "LaunchPad Mini"
Mser = False
# Myxolidian 3 notes chords list
Myxo = [(59,51,54),(49,52,56),(49,52,56),(51,54,57),(52,56,59),(52,56,59),(54,57,48),(57,49,52)]
MidInsNumber = 0
try:
input = raw_input
except NameError:
# Python 3
StandardError = Exception
STATUS_MAP = {
'noteon': NOTE_ON,
'noteoff': NOTE_OFF,
'programchange': PROGRAM_CHANGE,
'controllerchange': CONTROLLER_CHANGE,
'pitchbend': PITCH_BEND,
'polypressure': POLY_PRESSURE,
'channelpressure': CHANNEL_PRESSURE
}
# OSC destination list for incoming midi
midi2OSC = {
"lj": {"oscip": "127.0.0.1", "oscport": 8002, "notes": False, "msgs": False},
"nozoid": {"oscip": "127.0.0.1", "oscport": 8003, "notes": False, "msgs": False},
"dump": {"oscip": "127.0.0.1", "oscport": 8040, "notes": True, "msgs": True}
}
#mycontroller.midiport[LaunchHere].send_message([CONTROLLER_CHANGE, LaunchTop[number-1], color])
def send(device,msg):
'''
# if device is the midi name
if device in midiname:
deviceport = midiname.index(device)
midiport[deviceport].send_message(msg)
'''
if device == "Launchpad":
#print LaunchHere
midiport[gstt.LaunchHere].send_message(msg)
if device == "Bhoreal":
midiport[gstt.BhorealHere].send_message(msg)
def NoteOn(note, color):
global MidInsNumber
gstt.note = note
gstt.velocity = color
for port in range(MidInsNumber):
if midiname[port].find(LaunchMidiName) == 0:
launchpad.PadNoteOn(note%64,color)
if midiname[port].find(BhorealMidiName) == 0:
gstt.BhorLeds[note%64]=color
midiport[port].send_message([NOTE_ON, note%64, color])
#bhorosc.sendosc("/bhoreal", [note%64 , color])
if midiname[port].find(BhorealMidiName) != 0 and midiname[port].find(LaunchMidiName) != 0:
midiport[port].send_message([NOTE_ON, note, color])
#virtual.send_message([NOTE_ON, note, color])
for OSCtarget in midi2OSC:
if midi2OSC[OSCtarget]['notes']:
pass
#OSCsend(OSCtarget, "/noteon", [note, color])
def NoteOff(note):
global MidInsNumber
gstt.note = note
gstt.velocity = 0
for port in range(MidInsNumber):
if midiname[port].find(LaunchMidiName) == 0:
launchpad.PadNoteOff(note%64)
if midiname[port].find(BhorealMidiName) == 0:
midiport[port].send_message([NOTE_OFF, note%64, 0])
gstt.BhorLeds[note%64]=0
#bhorosc.sendosc("/bhoreal", [note%64 , 0])
if midiname[port].find(BhorealMidiName) != 0 and midiname[port].find(LaunchMidiName) != 0:
midiport[port].send_message([NOTE_OFF, note, 0])
#virtual.send_message([NOTE_OFF, note, 0])
for OSCtarget in midi2OSC:
if midi2OSC[OSCtarget]["notes"]:
pass
#OSCsend(OSCtarget, "/noteoff", note)
def MidiMsg(midimsg):
print ("MidiMsg", midimsg)
for port in range(MidInsNumber):
if midiname[port].find(BhorealMidiName) != 0:
midiport[port].send_message(midimsg)
def OSCsend(name, oscaddress, oscargs =''):
ip = midi2OSC[name]["oscip"]
port = midi2OSC[name]["oscport"]
osclient = OSCClient()
osclient.connect((ip, port))
oscmsg = OSCMessage()
oscmsg.setAddress(oscaddress)
oscmsg.append(oscargs)
try:
if gstt.debug > 0:
print("Midi OSCSend : sending", oscmsg, "to", name, "at", gstt.LjayServerIP, ":", PluginPort)
osclient.sendto(oscmsg, (ip, port))
oscmsg.clearData()
#if gstt.debug >0:
# print oscaddress, oscargs, "was sent to",name
return True
except:
if gstt.debug > 0:
print ('Midi OSCSend : Connection to IP', ip ,':', port,'refused : died ?')
#sendWSall("/status No plugin.")
#sendWSall("/status " + name + " is offline")
#sendWSall("/" + name + "/start 0")
#PluginStart(name)
return False
def WebStatus(message):
OSCsend("lj","/status", message)
#
# MIDI Startup and handling
#
mqueue = Queue()
inqueue = Queue()
#
# Events from Generic MIDI Handling
#
def midinProcess(midiqueue):
midiqueue_get = midiqueue.get
while True:
msg = midiqueue_get()
print ("midin ", msg)
time.sleep(0.001)
# Event from Bhoreal or Launchpad
def MidinProcess(inqueue):
inqueue_get = inqueue.get
while True:
time.sleep(0.001)
msg = inqueue_get()
print ("Midinproces", msg[0])
# Note On
if msg[0]==NOTE_ON:
NoteOn(msg[1],msg[2])
#if bhorosc.oscdevice == 1:
WebStatus(''.join(("note ",msg[1]," to ",msg[2])))
# Note Off
if msg[0]==NOTE_OFF:
print ("noteoff")
NoteOff(msg[1],msg[2])
#if bhorosc.oscdevice == 1:
WebStatus(''.join(("note ",msg[1]," to ",msg[2])))
# Midi CC message
if msg[0] == CONTROLLER_CHANGE:
print ("CC :", msg[1], msg[2])
WebStatus("CC :" + str(msg[1]) + " " + str(msg[2]))
#orbits.RotX(msg[2])
for OSCtarget in midi2OSC:
if OSCtarget["notes"]:
pass
#OSCsend(OSCtarget, "/CC", note)
# other midi message
if msg[0] != NOTE_OFF and msg[0] != NOTE_ON:
MidiMsg(msg[0],msg[1],msg[2])
#if bhorosc.oscdevice == 1:
WebStatus(''.join(("msg : ",msg[0]," ",msg[1]," ",msg[2])))
# Generic call back : new msg forwarded to queue
class AddQueue(object):
def __init__(self, port):
self.port = port
print ("AddQueue", port)
self._wallclock = time.time()
def __call__(self, event, data=None):
message, deltatime = event
self._wallclock += deltatime
print("[%s] @%0.6f %r" % (self.port, self._wallclock, message))
inqueue.put(message)
#
# MIDI OUT Handling
#
def OutConfig():
global midiout, MidInsNumber
print("")
print("MIDIout...")
print("List and attach to available devices on host with IN port :")
# Display list of available midi IN devices on the host, create and start an OUT instance to talk to each of these Midi IN devices
midiout = rtmidi.MidiOut()
available_ports = midiout.get_ports()
for port, name in enumerate(available_ports):
midiname[port]=name
midiport[port].open_port(port)
print("Will send to [%i] %s" % (port, name))
#MidIns[port][1].open_port(port)
# Search for a Bhoreal
if name.find(BhorealMidiName) == 0:
print("Bhoreal start animation")
gstt.BhorealHere = port
bhoreal.StartBhoreal(port)
bhoreal.UpdateCurve()
bhoreal.UpdateSet()
bhoreal.UpdateLaser()
bhoreal.UpdateSimu()
time.sleep(0.2)
# Search for a LaunchPad
if name.find(LaunchMidiName) == 0:
print("Launchpad mini start animation")
gstt.LaunchHere = port
print(gstt.LaunchHere)
launchpad.StartLaunchPad(port)
time.sleep(0.2)
# Search for a Guitar Wing
if name.find("Livid") == 0:
print("Livid Guitar Wing start animation")
gstt.WingHere = port
print(gstt.WingHere)
#guitarwing.StartWing(port)
time.sleep(0.2)
print ("")
MidInsNumber = port+1
#
# MIDI IN Handling
# Create processing thread and queue for each device
#
def InConfig():
print("")
print("MIDIin...")
print("List and attach to available devices on host with OUT port :")
if platform == 'darwin':
mido.set_backend('mido.backends.rtmidi/MACOSX_CORE')
for port, name in enumerate(mido.get_input_names()):
#print (name)
midinputsname[port]=name
print(port,name)
# Bhoreal found ?
if name.find(BhorealMidiName) == 0:
# thread launch to handle all queued MIDI messages from Bhoreal device
thread = Thread(target=bhoreal.MidinProcess, args=(bhoreal.bhorqueue,))
thread.setDaemon(True)
thread.start()
try:
bhorealin, port_name = open_midiinput(port+1) # weird rtmidi call port number is not the same in mido enumeration and here
except (EOFError, KeyboardInterrupt):
sys.exit()
midinputs.append(bhorealin)
print("Attaching MIDI in callback handler to Bhoreal : ", name)
midinputs[port].set_callback(bhoreal.AddQueue(name))
print("Bhor",port,port_name)
# LaunchPad Mini Found ?
if name.find(LaunchMidiName) == 0:
# thread launch to handle all queued MIDI messages from LauchPad device
thread = Thread(target=launchpad.LaunchMidinProcess, args=(launchpad.launchqueue,))
thread.setDaemon(True)
thread.start()
try:
launchin, port_name = open_midiinput(port+1) # weird port number is not the same in mido enumeration and here
except (EOFError, KeyboardInterrupt):
sys.exit()
midinputs.append(launchin)
print ("Attaching MIDI in callback handler to Launchpad : ", name)
launchin.set_callback(launchpad.LaunchAddQueue(name))
#print "Launch",port,port_name
# all other devices
'''
port = mido.open_ioport(name,callback=AddQueue(name))
This doesn't work on OS X on French system "Réseau Session" has a bug with accent.
Todo : stop using different midi framework.
if name.find(BhorealMidiName) != 0 and name.find(LaunchMidiName) != 0:
thread = Thread(target=midinProcess, args=(midinputsqueue[port],))
thread.setDaemon(True)
thread.start()
try:
port = mido.open_ioport(name,callback=AddQueue(name))
#port_port, port_name = open_midiinput(port)
except (EOFError, KeyboardInterrupt):
sys.exit()
#midinputs.append(port_port)
print "Attaching MIDI in callback handler to : ", name
#midinputs[port].set_callback(AddQueue(name))
#MIDInport = mido.open_ioport("Laser",virtual=True,callback=MIDIn)
'''
if name.find(BhorealMidiName) != 0 and name.find(LaunchMidiName) != 0:
thread = Thread(target=midinProcess, args=(midinputsqueue[port],))
thread.setDaemon(True)
thread.start()
try:
port_port, port_name = open_midiinput(port)
except (EOFError, KeyboardInterrupt):
sys.exit()
midinputs.append(port_port)
print("Attaching MIDI in callback handler to : ", name)
midinputs[port].set_callback(AddQueue(name))
#MIDInport = mido.open_ioport("Laser",virtual=True,callback=MIDIn)
def End():
global midiout
#midiin.close_port()
midiout.close_port()
#del virtual
if gstt.LaunchHere != -1:
del gstt.LaunchHere
if gstt.BhorealHere != -1:
del gstt.BhorealHere
def listdevice(number):
return midiname[number]
def check():
InConfig()
OutConfig()
#return listdevice(255)

760
libs3/midi3.py Normal file
View file

@ -0,0 +1,760 @@
#!/usr/bin/python3
# -*- coding: utf-8 -*-
"""
Midi3
v0.7.0
Midi Handler :
- Hook to the MIDI host
- Enumerate connected midi devices and spawn a process/device to handle incoming events
- Provide sending functions to
- found midi devices with IN port
- OSC targets /noteon /noteoff /cc (see midi2OSC).
- Launchpad mini led matrix from/to, see launchpad.py
- Bhoreal led matrix from/to, see bhoreal.py
todo :
Midi macros : plusieurs parametres evoluant les uns apres les autres ou en meme temps.
cadence
by Sam Neurohack
from /team/laser
for python 2 & 3
Laser selection
one universe / laser
Plugin selection
bank change/scene/
"""
import time
import rtmidi
from rtmidi.midiutil import open_midiinput
from threading import Thread
from rtmidi.midiconstants import (CHANNEL_PRESSURE, CONTROLLER_CHANGE, NOTE_ON, NOTE_OFF,
PITCH_BEND, POLY_PRESSURE, PROGRAM_CHANGE)
import mido
from mido import MidiFile
import traceback
import weakref
import sys
from sys import platform
print()
print('Midi startup...')
import gstt, bhoreal, launchpad, LPD8
from queue import Queue
from OSC3 import OSCServer, OSCClient, OSCMessage
midiname = ["Name"] * 16
midiport = [rtmidi.MidiOut() for i in range(16) ]
OutDevice = []
InDevice = []
# max 16 midi port array
midinputsname = ["Name"] * 16
midinputsqueue = [Queue() for i in range(16) ]
midinputs = []
BhorealMidiName = "Bhoreal"
LaunchMidiName = "Launch"
BhorealPort, Midi1Port, Midi2Port, VirtualPort, MPort = -1,-1,-1, -1, -1
VirtualName = "LaunchPad Mini"
Mser = False
# Myxolidian 3 notes chords list
Myxo = [(59,51,54),(49,52,56),(49,52,56),(51,54,57),(52,56,59),(52,56,59),(54,57,48),(57,49,52)]
MidInsNumber = 0
clock = mido.Message(type="clock")
start = mido.Message(type ="start")
stop = mido.Message(type ="stop")
ccontinue = mido.Message(type ="continue")
reset = mido.Message(type ="reset")
songpos = mido.Message(type ="songpos")
mode = "maxwell"
'''
print("clock",clock)
print("start",start)
print("continue", ccontinue)
print("reset",reset)
print("sonpos",songpos)
'''
try:
input = raw_input
except NameError:
# Python 3
StandardError = Exception
STATUS_MAP = {
'noteon': NOTE_ON,
'noteoff': NOTE_OFF,
'programchange': PROGRAM_CHANGE,
'controllerchange': CONTROLLER_CHANGE,
'pitchbend': PITCH_BEND,
'polypressure': POLY_PRESSURE,
'channelpressure': CHANNEL_PRESSURE
}
# OSC targets list
midi2OSC = {
"lj": {"oscip": "127.0.0.1", "oscport": 8002, "notes": False, "msgs": False},
"nozoid": {"oscip": "127.0.0.1", "oscport": 8003, "notes": False, "msgs": False},
"dump": {"oscip": "127.0.0.1", "oscport": 8040, "notes": True, "msgs": True},
"maxwell": {"oscip": "127.0.0.1", "oscport": 8012, "notes": True, "msgs": True}
}
notes = ["C","C#","D","D#","E","F","F#","G","G#","A","A#","B"]
def midi2note(midinote):
print("midinote",midinote, "note", notes[midinote%12]+str(round(midinote/12)))
return notes[midinote%12]+str(round(midinote/12))
#mycontroller.midiport[LaunchHere].send_message([CONTROLLER_CHANGE, LaunchTop[number-1], color])
def send(msg,device):
'''
# if device is the midi name
if device in midiname:
deviceport = midiname.index(device)
midiport[deviceport].send_message(msg)
'''
if device == "Launchpad":
#print LaunchHere
midiport[launchpad.Here].send_message(msg)
if device == "Bhoreal":
midiport[bhoreal.Here].send_message(msg)
# mididest : all, launchpad, bhoreal, specificname
def NoteOn(note,color, mididest):
global MidInsNumber
gstt.note = note
gstt.velocity = color
for port in range(MidInsNumber):
# To Launchpad, if present.
if mididest == "launchpad" and midiname[port].find(LaunchMidiName) == 0:
launchpad.PadNoteOn(note%64,color)
# To Bhoreal, if present.
elif mididest == "bhoreal" and midiname[port].find(BhorealMidiName) == 0:
gstt.BhorLeds[note%64]=color
midiport[port].send_message([NOTE_ON, note%64, color])
#bhorosc.sendosc("/bhoreal", [note%64 , 0])
# To mididest
elif midiname[port].find(mididest) == 0:
midiport[port].send_message([NOTE_ON, note, color])
# To All
elif mididest == "all" and midiname[port].find(mididest) != 0 and midiname[port].find(BhorealMidiName) != 0 and midiname[port].find(LaunchMidiName) != 0:
midiport[port].send_message([NOTE_ON, note, color])
#virtual.send_message([NOTE_ON, note, color])
for OSCtarget in midi2OSC:
if (OSCtarget == mididest or mididest == 'all') and midi2OSC[OSCtarget]["notes"]:
OSCsend(OSCtarget, "/noteon", [note, color])
# mididest : all, launchpad, bhoreal, specificname
def NoteOff(note, mididest):
global MidInsNumber
gstt.note = note
gstt.velocity = 0
for port in range(MidInsNumber):
# To Launchpad, if present.
if mididest == "launchpad" and midiname[port].find(LaunchMidiName) == 0:
launchpad.PadNoteOff(note%64)
# To Bhoreal, if present.
elif mididest == "bhoreal" and midiname[port].find(BhorealMidiName) == 0:
midiport[port].send_message([NOTE_OFF, note%64, 0])
gstt.BhorLeds[note%64] = 0
#bhorosc.sendosc("/bhoreal", [note%64 , 0])
# To mididest
elif midiname[port].find(mididest) != -1:
midiport[port].send_message([NOTE_OFF, note, 0])
# To All
elif mididest == "all" and midiname[port].find(mididest) == -1 and midiname[port].find(BhorealMidiName) == -1 and midiname[port].find(LaunchMidiName) == -1:
midiport[port].send_message([NOTE_OFF, note, 0])
#virtual.send_message([NOTE_OFF, note, 0])
for OSCtarget in midi2OSC:
if (OSCtarget == mididest or mididest == 'all') and midi2OSC[OSCtarget]["notes"]:
OSCsend(OSCtarget, "/noteoff", note)
# mididest : all or specifiname, won't be sent to launchpad or Bhoreal.
def MidiMsg(midimsg, mididest):
#print("midi3 got MidiMsg", midimsg, "Dest", mididest)
desterror = -1
for port in range(MidInsNumber):
#print("port",port,"midiname", midiname[port])
# To mididest
if midiname[port].find(mididest) != -1:
#print("midi 3 sending to name", midiname[port], "port", port, ":", midimsg)
midiport[port].send_message(midimsg)
desterror = 0
# To All
elif mididest == "all" and midiname[port].find(mididest) == -1 and midiname[port].find(BhorealMidiName) == -1 and midiname[port].find(LaunchMidiName) == -1 and midiname[port].find(DJName) == -1:
#print("all sending to port",port,"name", midiname[port])
midiport[port].send_message(midimsg)
desterror = 0
for OSCtarget in midi2OSC:
if (OSCtarget == mididest or mididest == 'all') and midi2OSC[OSCtarget]["msgs"]:
OSCsend(OSCtarget, "/cc", [midimsg[1], midimsg[2]])
desterror = 0
if desterror == -1:
print ("** This midi or OSC destination doesn't exists **")
def OSCsend(name, oscaddress, oscargs =''):
ip = midi2OSC[name]["oscip"]
port = midi2OSC[name]["oscport"]
osclient = OSCClient()
osclient.connect((ip, port))
oscmsg = OSCMessage()
oscmsg.setAddress(oscaddress)
oscmsg.append(oscargs)
try:
if gstt.debug > 0:
print("Midi OSCSend : sending", oscmsg,"to", name, "at", ip , ":", port)
osclient.sendto(oscmsg, (ip, port))
oscmsg.clearData()
return True
except:
if gstt.debug > 0:
print('Midi OSCSend : Connection to IP', ip ,':', port,'refused : died ?')
#sendWSall("/status No plugin.")
#sendWSall("/status " + name + " is offline")
#sendWSall("/" + name + "/start 0")
#PluginStart(name)
return False
def Webstatus(message):
OSCsend("lj","/status", message)
#
# MIDI Startup and handling
#
mqueue = Queue()
inqueue = Queue()
#
# Events from Generic MIDI Handling
#
'''
def midinProcess(midiqueue):
midiqueue_get = midiqueue.get
while True:
msg = midiqueue_get()
print("midin ", msg)
time.sleep(0.001)
'''
# Event from Bhoreal or Launchpad
# Or it could be the midinprocess in launchpad.py or bhoreal.py
def MidinProcess(inqueue, portname):
inqueue_get = inqueue.get
mididest = "to Maxwell 1"
while True:
time.sleep(0.001)
msg = inqueue_get()
#print("Midinprocess", msg[0])
# Note On
if msg[0]==NOTE_ON:
print ("from", portname, "noteon", msg[1])
# NoteOn(msg[1],msg[2],mididest)
# Note Off
if msg[0]==NOTE_OFF:
print("from", portname,"noteoff")
# NoteOff(msg[1],msg[2], mididest)
# Midi CC message
if msg[0] == CONTROLLER_CHANGE:
print("from", portname,"CC :", msg[1], msg[2])
# other midi message
if msg[0] != NOTE_OFF and msg[0] != NOTE_ON and msg[0] != CONTROLLER_CHANGE:
print("from", portname,"other midi message")
MidiMsg(msg[0],msg[1],msg[2],mididest)
# Webstatus(''.join(("msg : ",msg[0]," ",msg[1]," ",msg[2])))
# Generic call back : new msg forwarded to queue
class AddQueue(object):
def __init__(self, portname, port):
self.portname = portname
self.port = port
#print("AddQueue", port)
self._wallclock = time.time()
def __call__(self, event, data=None):
message, deltatime = event
self._wallclock += deltatime
#print("inqueue : [%s] @%0.6f %r" % ( self.portname, self._wallclock, message))
midinputsqueue[self.port].put(message)
#
# MIDI OUT Handling
#
class OutObject():
_instances = set()
counter = 0
def __init__(self, name, kind, port):
self.name = name
self.kind = kind
self.port = port
self._instances.add(weakref.ref(self))
OutObject.counter += 1
print(self.name, "kind", self.kind, "port", self.port)
@classmethod
def getinstances(cls):
dead = set()
for ref in cls._instances:
obj = ref()
if obj is not None:
yield obj
else:
dead.add(ref)
cls._instances -= dead
def __del__(self):
OutObject.counter -= 1
def OutConfig():
global midiout, MidInsNumber
#
if len(OutDevice) == 0:
print("")
print("MIDIout...")
print("List and attach to available devices on host with IN port :")
# Display list of available midi IN devices on the host, create and start an OUT instance to talk to each of these Midi IN devices
midiout = rtmidi.MidiOut()
available_ports = midiout.get_ports()
for port, name in enumerate(available_ports):
midiname[port]=name
midiport[port].open_port(port)
#print("Will send to [%i] %s" % (port, name))
#MidIns[port][1].open_port(port)
# Search for a Bhoreal
if name.find(BhorealMidiName) == 0:
OutDevice.append(OutObject(name, "bhoreal", port))
print("Bhoreal start animation")
bhoreal.Here = port
bhoreal.StartBhoreal(port)
time.sleep(0.2)
# Search for a LaunchPad
elif name.find(LaunchMidiName) == 0:
OutDevice.append(OutObject(name, "launchpad", port))
print("Launchpad mini start animation")
launchpad.Here = port
launchpad.StartLaunchPad(port)
time.sleep(0.2)
# Search for a LPD8
elif name.find('LPD8') == 0:
OutDevice.append(OutObject(name, "LPD8", port))
#print("LPD8 mini start animation")
LPD8.Here = port
#LPD8.StartLPD8(port)
time.sleep(0.2)
# Search for a Guitar Wing
elif name.find("Livid") == 0:
OutDevice.append(OutObject(name, "livid", port))
print("Livid Guitar Wing start animation")
gstt.WingHere = port
print(gstt.WingHere)
#guitarwing.StartWing(port)
time.sleep(0.2)
else:
OutDevice.append(OutObject(name, "generic", port))
#print("")
print(len(OutDevice), "Out devices")
#ListOutDevice()
MidInsNumber = len(OutDevice)+1
def ListOutDevice():
for item in OutObject.getinstances():
print(item.name)
def FindOutDevice(name):
port = -1
for item in OutObject.getinstances():
#print("searching", name, "in", item.name)
if name == item.name:
#print('found port',item.port)
port = item.port
return port
def DelOutDevice(name):
Outnumber = Findest(name)
print('deleting OutDevice', name)
if Outnumber != -1:
print('found OutDevice', Outnumber)
delattr(OutObject, str(name))
print("OutDevice", Outnumber,"was removed")
else:
print("OutDevice was not found")
#
# MIDI IN Handling
# Create processing thread and queue for each device
#
class InObject():
_instances = set()
counter = 0
def __init__(self, name, kind, port, rtmidi):
self.name = name
self.kind = kind
self.port = port
self.rtmidi = rtmidi
self.queue = Queue()
self._instances.add(weakref.ref(self))
InObject.counter += 1
#print("Adding InDevice name", self.name, "kind", self.kind, "port", self.port,"rtmidi", self.rtmidi, "Queue", self.queue)
@classmethod
def getinstances(cls):
dead = set()
for ref in cls._instances:
obj = ref()
if obj is not None:
yield obj
else:
dead.add(ref)
cls._instances -= dead
def __del__(self):
InObject.counter -= 1
def InConfig():
print("")
print("MIDIin...")
print("List and attach to available devices on host with OUT port :")
if platform == 'darwin':
mido.set_backend('mido.backends.rtmidi/MACOSX_CORE')
genericnumber = 0
for port, name in enumerate(mido.get_input_names()):
#print()
# Maxwell midi IN & OUT port names are different
if name.find("from ") == 0:
#print ("name",name)
name = "to "+name[5:]
#print ("corrected to",name)
outport = FindOutDevice(name)
midinputsname[port]=name
#print("name",name, "Port",port, "Outport", outport)
'''
# New Bhoreal found ?
if name.find(BhorealMidiName) == 0:
try:
bhorealin, port_name = open_midiinput(outport) # weird rtmidi call port number is not the same in mido enumeration and here
BhoreralDevice = InObject(port_name, "bhoreal", outport, bhorealin)
print("BhorealDevice.queue",BhoreralDevice.queue )
# thread launch to handle all queued MIDI messages from Bhoreal device
thread = Thread(target=bhoreal.MidinProcess, args=(bhoreal.bhorqueue,))
thread.setDaemon(True)
thread.start()
print("Attaching MIDI in callback handler to Bhoreal : ", name, "port", port, "portname", port_name)
BhoreralDevice.rtmidi.set_callback(bhoreal.AddQueue(port_name))
except Exception:
traceback.print_exc()
'''
# Old Bhoreal found ?
if name.find(BhorealMidiName) == 0:
try:
bhorealin, port_name = open_midiinput(outport) # weird rtmidi call port number is not the same in mido enumeration and here
except (EOFError, KeyboardInterrupt):
sys.exit
#midinputs.append(bhorealin)
InDevice.append(InObject(name, "bhoreal", outport, bhorealin))
# thread launch to handle all queued MIDI messages from Bhoreal device
thread = Thread(target=bhoreal.MidinProcess, args=(bhoreal.bhorqueue,))
#thread = Thread(target=bhoreal.MidinProcess, args=(InDevice[port].queue,))
thread.setDaemon(True)
thread.start()
#print("midinputs[port]", midinputs[port])
print(name)
InDevice[port].rtmidi.set_callback(bhoreal.AddQueue(name))
#midinputs[port].set_callback(bhoreal.AddQueue(name))
'''
# New LaunchPad Mini Found ?
if name.find(LaunchMidiName) == 0:
try:
launchin, port_name = open_midiinput(outport)
except (EOFError, KeyboardInterrupt):
sys.exit()
LaunchDevice = InObject(port_name, "launchpad", outport, launchin)
thread = Thread(target=launchpad.MidinProcess, args=(launchpad.launchqueue,))
thread.setDaemon(True)
thread.start()
print("Attaching MIDI in callback handler to Launchpad : ", name, "port", port, "portname", port_name)
LaunchDevice.rtmidi.set_callback(launchpad.LaunchAddQueue(name))
'''
# Old LaunchPad Mini Found ?
if name.find(LaunchMidiName) == 0:
try:
launchin, port_name = open_midiinput(outport)
except (EOFError, KeyboardInterrupt):
sys.exit()
#midinputs.append(launchin)
InDevice.append(InObject(name, "launchpad", outport, launchin))
thread = Thread(target=launchpad.MidinProcess, args=(launchpad.launchqueue,))
#thread = Thread(target=launchpad.MidinProcess, args=(InDevice[port].queue,))
thread.setDaemon(True)
thread.start()
print(name, "port", port, "portname", port_name)
InDevice[port].rtmidi.set_callback(launchpad.LaunchAddQueue(name))
#launchin.set_callback(launchpad.LaunchAddQueue(name))
# LPD8 Found ?
if name.find('LPD8') == 0:
print()
print('LPD8 Found..')
try:
LPD8in, port_name = open_midiinput(outport)
except (EOFError, KeyboardInterrupt):
sys.exit()
#midinputs.append(LPD8in)
InDevice.append(InObject(name, "LPD8", outport, LPD8in))
print ("Launching LPD8 thread..")
thread = Thread(target=LPD8.MidinProcess, args=(LPD8.LPD8queue,))
#thread = Thread(target=LPD8.MidinProcess, args=(InDevice[port].queue,))
thread.setDaemon(True)
thread.start()
print(name, "port", port, "portname", port_name)
InDevice[port].rtmidi.set_callback(LPD8.LPD8AddQueue(name))
# Everything that is not Bhoreal or Launchpad
if name.find(BhorealMidiName) != 0 and name.find(LaunchMidiName) != 0 and name.find('LPD8') != 0:
try:
#print (name, name.find("RtMidi output"))
if name.find("RtMidi output") > -1:
print("No thread started for device", name)
else:
portin = object
port_name = ""
portin, port_name = open_midiinput(outport)
#midinputs.append(portin)
InDevice.append(InObject(name, "generic", outport, portin))
thread = Thread(target=MidinProcess, args=(midinputsqueue[port],port_name))
thread.setDaemon(True)
thread.start()
print(name, "port", port, "portname", port_name)
#midinputs[port].set_callback(AddQueue(name),midinputsqueue[port])
#midinputs[port].set_callback(AddQueue(name))
#genericnumber += 1
InDevice[port].rtmidi.set_callback(AddQueue(name,port))
except Exception:
traceback.print_exc()
#print("")
print(InObject.counter, "In devices")
#ListInDevice()
def ListInDevice():
for item in InObject.getinstances():
print(item.name)
def FindInDevice(name):
port = -1
for item in InObject.getinstances():
#print("searching", name, "in", item.name)
if name in item.name:
#print('found port',item.port)
port = item.port
return port
def DelInDevice(name):
Innumber = Findest(name)
print('deleting InDevice', name)
if Innumber != -1:
print('found InDevice', Innumber)
delattr(InObject, str(name))
print("InDevice", Innumber,"was removed")
else:
print("InDevice was not found")
# all other devices
'''
port = mido.open_ioport(name,callback=AddQueue(name))
This doesn't work on OS X on French system "Réseau Session" has a bug with accent.
Todo : stop using different midi framework.
if name.find(BhorealMidiName) != 0 and name.find(LaunchMidiName) != 0:
thread = Thread(target=midinProcess, args=(midinputsqueue[port],))
thread.setDaemon(True)
thread.start()
try:
port = mido.open_ioport(name,callback=AddQueue(name))
#port_port, port_name = open_midiinput(port)
except (EOFError, KeyboardInterrupt):
sys.exit()
#midinputs.append(port_port)
print "Attaching MIDI in callback handler to : ", name
#midinputs[port].set_callback(AddQueue(name))
#MIDInport = mido.open_ioport("Laser",virtual=True,callback=MIDIn)
'''
def End():
global midiout
#midiin.close_port()
midiout.close_port()
#del virtual
if launchpad.Here != -1:
del launchpad.Here
if bhoreal.Here != -1:
del bhoreal.Here
if LPD8.Here != -1:
del LPD8.Here
def listdevice(number):
return midiname[number]
def check():
OutConfig()
InConfig()
#return listdevice(255)

251
libs3/plugins.py Normal file
View file

@ -0,0 +1,251 @@
#!/usr/bin/python3
# -*- coding: utf-8 -*-
# -*- mode: Python -*-
'''
LJ Laser Server v0.8.1
Plugins Handler.
'''
from OSC3 import OSCServer, OSCClient, OSCMessage
from websocket_server import WebsocketServer
from libs3 import gstt
import os
import subprocess
import sys
def Init(wserver):
global WSserver
WSserver = wserver
def sendWSall(message):
#if gstt.debug >0:
#print("WS sending %s" % (message))
WSserver.send_message_to_all(message)
# What is plugin's OSC port ?
def Port(name):
data = gstt.plugins.get(name)
return data.get("OSC")
def Ping(name):
sendWSall("/"+ name + "/start 0")
return OSCsend(name,"/ping",1)
#return True
# How to start the plugin ?
def Command(name):
data = gstt.plugins.get(name)
return data.get("command")
# Get all plugin current state
def Data(name):
return gstt.plugins.get(name)
def Kill(name):
#data = Data(name)
print("Killing", name, "...")
OSCsend(name,"/quit")
'''
if data["process"] != None:
print name, "plugin is owned by LJ."
print "Killing plugin", name
OSCsend(name,"/quit")
#data["process"].terminate()
sendWSall("/status Killing "+ name +".")
else:
print "Killing asked but plugin is not owned by LJ"
sendWSall("/status Not own plugin")
'''
def Restart(name):
Kill(name)
Start(name)
# See LJ.conf data
def Start(name):
# get Plugin configuration.
command = Command(name)
sendWSall("/status Starting "+name+"...")
# Get LJ path
#ljpath = r'%s' % os.getcwd().replace('\\','/')
print("")
print("LJ is starting plugin :", name)
# Construct the command with absolute path.
PluginPath = command.split(" ")
# Launch as a subprocess
print("launch :", PluginPath[0], gstt.ljpath + "/" + PluginPath[1])
# without argument
if len(PluginPath) < 3:
PluginProcess = subprocess.Popen( [PluginPath[0], gstt.ljpath + "/" + PluginPath[1] ], env=os.environ)
# with 1 argument
else:
PluginProcess = subprocess.Popen( [PluginPath[0], gstt.ljpath + "/" + PluginPath[1] + " " + PluginPath[2]], env=os.environ)
#PluginProcess = os.execv([PluginPath[0], ljpath + "/" + PluginPath[1]])
if gstt.debug >0:
print("LJ path :", ljpath)
print("New process pid for ", name, ":", PluginProcess.pid)
'''
# Maybe it's not fully started
data = Data(name)
if command != "" and "pid" not in data :
sendWSall("/status Starting "+name+"...")
# Get LJ path
ljpath = r'%s' % os.getcwd().replace('\\','/')
print ""
print "LJ is starting plugin :", name
# Construct the command with absolute path.
PluginPath = command.split(" ")
# Launch as a subprocess
PluginProcess = subprocess.Popen([PluginPath[0], ljpath + "/" + PluginPath[1]])
if gstt.debug >0:
print "LJ path :", ljpath
print "New process pid for ", name, ":", PluginProcess.pid
data = Data(name)
data["pid"] = PluginProcess.pid
data["process"] = PluginProcess
# Process can be terminated with :
# PluginProcess.terminate()
'''
def OSCsend(name, oscaddress, oscargs =''):
#print "OSCsend in plugins got for", name, ": oscaddress", oscaddress, "oscargs :", oscargs
PluginPort = Port(name)
#sendWSall("/status Checking "+ name + "...")
osclientplugin = OSCClient()
osclientplugin.connect((gstt.LjayServerIP, PluginPort))
oscmsg = OSCMessage()
oscmsg.setAddress(oscaddress)
oscmsg.append(oscargs)
try:
if gstt.debug > 0:
print("Plugins manager : OSCsending", oscmsg,"to plugin", name, "at", gstt.LjayServerIP, ":", PluginPort)
osclientplugin.sendto(oscmsg, (gstt.LjayServerIP, PluginPort))
oscmsg.clearData()
if gstt.debug >0:
print(oscaddress, oscargs, "was sent to",name)
return True
except:
if gstt.debug > 0:
print('OSCSend : Connection to plugin IP', gstt.LjayServerIP ,':', PluginPort,'refused : died ?')
#sendWSall("/status No plugin.")
#sendWSall("/status " + name + " is offline")
#sendWSall("/" + name + "/start 0")
#PluginStart(name)
return False
# for each plugin will automatically add /pluginame before oscpath to send like /aurora/scim 1, if oscpath = "/scim 1"
def SendAll(oscpath):
if gstt.debug > 0:
print("Sending to all plugins ", oscpath)
for plugin in list(gstt.plugins.keys()):
if gstt.debug > 0:
print("sending ",oscpath,"to", plugin)
#sendWSall("/"+ plugin + "/start 0")
Send(plugin, "/"+plugin+oscpath)
# Send a command to given plugin. Will also start it if command contain /start 1
def Send(name, oscpath):
if oscpath[0].find(name) != -1:
# Plugin is online ?
if Ping(name):
# Light up the plugin button
#sendWSall("/" + name + "/start 1")
#sendWSall("/status " + name + " online")
if gstt.debug > 0:
print('')
print("Plugins manager got", oscpath, "for plugin", name, "currently online.")
# If start 0, try to kill plugin
if oscpath[0].find("start") != -1 and oscpath[1] == "0":
if gstt.debug >0:
print("start 0, so killing", name, "...")
Kill(name)
# Send osc command
elif len(oscpath) == 1:
OSCsend(name, oscpath[0], oscargs='noargs')
elif len(oscpath) == 2:
OSCsend(name, oscpath[0], oscargs=oscpath[1])
elif len(oscpath) == 3:
OSCsend(name, oscpath[0], oscargs=(oscpath[1], oscpath[2]))
elif name == "trckr":
#print("To trckr", name, oscpath, len(oscpath))
OSCsend(name, oscpath[0], oscpath[1:])
elif name == "aurora":
#print("To Aurora", oscpath, len(oscpath))
OSCsend(name, oscpath[0], oscpath[1:])
return True
# Plugin not online..
else:
if gstt.debug >0:
print("Plugin manager send says plugin " + name + " is offline.")
#print "Command", oscpath
sendWSall("/redstatus Plugin " + name + " offline")
sendWSall("/"+ name + "/start 0")
# Try to Start it if /start 1
if oscpath[0].find("start") != -1 and oscpath[1] == "1":
if gstt.debug >0:
print("Plugin Manager Trying to start", name, "...")
Start(name)
return False

82
libs3/pysimpledmx.py Normal file
View file

@ -0,0 +1,82 @@
import serial, sys
START_VAL = 0x7E
END_VAL = 0xE7
COM_BAUD = 57600
COM_TIMEOUT = 1
COM_PORT = 7
DMX_SIZE = 512
LABELS = {
'GET_WIDGET_PARAMETERS' :3, #unused
'SET_WIDGET_PARAMETERS' :4, #unused
'RX_DMX_PACKET' :5, #unused
'TX_DMX_PACKET' :6,
'TX_RDM_PACKET_REQUEST' :7, #unused
'RX_DMX_ON_CHANGE' :8, #unused
}
class DMXConnection(object):
def __init__(self, comport = None):
'''
On Windows, the only argument is the port number. On *nix, it's the path to the serial device.
For example:
DMXConnection(4) # Windows
DMXConnection('/dev/tty2') # Linux
DMXConnection("/dev/ttyUSB0") # Linux
'''
self.dmx_frame = [0] * DMX_SIZE
try:
self.com = serial.Serial(comport, baudrate = COM_BAUD, timeout = COM_TIMEOUT)
except:
com_name = 'COM%s' % (comport + 1) if type(comport) == int else comport
print("Could not open device %s. Quitting application." % com_name)
sys.exit(0)
print("Opened %s." % (self.com.portstr))
def setChannel(self, chan, val, autorender = False):
'''
Takes channel and value arguments to set a channel level in the local
DMX frame, to be rendered the next time the render() method is called.
'''
if not 1 <= chan-1 <= DMX_SIZE:
print('Invalid channel specified: %s' % chan-1)
return
# clamp value
val = max(0, min(val, 255))
self.dmx_frame[chan-1] = val
if autorender: self.render()
def clear(self, chan = 0):
'''
Clears all channels to zero. blackout.
With optional channel argument, clears only one channel.
'''
if chan == 0:
self.dmx_frame = [0] * DMX_SIZE
else:
self.dmx_frame[chan-1] = 0
def render(self):
''''
Updates the DMX output from the USB DMX Pro with the values from self.dmx_frame.
'''
packet = [
START_VAL,
LABELS['TX_DMX_PACKET'],
len(self.dmx_frame) & 0xFF,
(len(self.dmx_frame) >> 8) & 0xFF,
]
packet += self.dmx_frame
packet.append(END_VAL)
packet = list(map(chr, packet))
self.com.write(''.join(packet))
def close(self):
self.com.close()

803
libs3/scrolldisp.py Normal file
View file

@ -0,0 +1,803 @@
#!/usr/bin/python3
# -*- coding: utf-8 -*-
'''
ScrollDisp
v0.7.0
An example of an unicornhat hack for Launchpad Mini or Bhoreal.
This is a custom version of scrolldisp.py that display text on unicornhat
with use of bhorunicornhat to use with a Bhoreal or a Launchpad mini mk2
Default device is the launchpad.
Command line to display 2 chars:
To display 'OK' :
python3 scrolldisp.py OK
To display a rainbow :
python3 scrolldisp.py ~R
See the end of this script for more option like scrolling or use a bhoreal in command line.
As a Library :
Display(text, color=(255,255,255), delay=0.2, mididest ='launchpad')
DisplayScroll(text, color=(255,255,255), delay=0.2, mididest = 'launchpad')
Remember there is Cls functions
launchpad.Cls()
bhoreal.Cls()
'''
#from unicorn_hat_sim import unicornhat as u
import bhorunicornhat as u
import time, math, sys
class ScrollDisp:
columns = []
mappings = {'!': [" ",
"#",
"#",
"#",
"#",
" ",
"#",
" "],
'\'': [" ",
"#",
"#",
" ",
" ",
" ",
" ",
" "],
'(': [" ",
" #",
"# ",
"# ",
"# ",
"# ",
" #",
" "],
')': [" ",
"# ",
" #",
" #",
" #",
" #",
"# ",
" "],
',': [" ",
" ",
" ",
" ",
" ",
" ",
" #",
"# "],
'-': [" ",
" ",
" ",
" ",
"###",
" ",
" ",
" "],
'.': [" ",
" ",
" ",
" ",
" ",
" ",
"#",
" "],
'0': [" ",
" ## ",
"# #",
"# #",
"# #",
"# #",
" ## ",
" "],
'1': [" ",
" # ",
"## ",
" # ",
" # ",
" # ",
"###",
" "],
'2': [" ",
" ## ",
"# #",
" #",
" # ",
" # ",
"####",
" "],
'3': [" ",
"####",
" #",
" # ",
" #",
"# #",
" ## ",
" "],
'4': [" ",
"# #",
"# #",
"####",
" #",
" #",
" #",
" "],
'5': [" ",
"####",
"# ",
"### ",
" #",
"# #",
" ## ",
" "],
'6': [" ",
" ## ",
"# ",
"### ",
"# #",
"# #",
" ## ",
" "],
'7': [" ",
"####",
" #",
" # ",
" # ",
" # ",
" # ",
" "],
'8': [" ",
" ## ",
"# #",
" ## ",
"# #",
"# #",
" ## ",
" "],
'9': [" ",
" ## ",
"# #",
" ###",
" #",
" #",
" ## ",
" "],
':': [" ",
" ",
" ",
"#",
" ",
" ",
"#",
" "],
'A': [" ",
" ## ",
"# #",
"# #",
"####",
"# #",
"# #",
" "],
'B': [" ",
"### ",
"# #",
"### ",
"# #",
"# #",
"### ",
" "],
'C': [" ",
" ## ",
"# #",
"# ",
"# ",
"# #",
" ## ",
" "],
'D': [" ",
"### ",
"# #",
"# #",
"# #",
"# #",
"### ",
" "],
'E': [" ",
"####",
"# ",
"### ",
"# ",
"# ",
"####",
" "],
'F': [" ",
"####",
"# ",
"### ",
"# ",
"# ",
"# ",
" "],
'G': [" ",
" ## ",
"# #",
"# ",
"# ##",
"# #",
" ## ",
" "],
'H': [" ",
"# #",
"# #",
"####",
"# #",
"# #",
"# #",
" "],
'I': [" ",
"###",
" # ",
" # ",
" # ",
" # ",
"###",
" "],
'J': [" ",
" ###",
" #",
" #",
" #",
"# #",
" ## ",
" "],
'K': [" ",
"# #",
"# # ",
"## ",
"## ",
"# # ",
"# #",
" "],
'L': [" ",
"# ",
"# ",
"# ",
"# ",
"# ",
"###",
" "],
'M': [" ",
"# #",
"####",
"# #",
"# #",
"# #",
"# #",
" "],
'N': [" ",
"# #",
"## #",
"# ##",
"# #",
"# #",
"# #",
" "],
'O': [" ",
" ## ",
"# #",
"# #",
"# #",
"# #",
" ## ",
" "],
'P': [" ",
"### ",
"# #",
"### ",
"# ",
"# ",
"# ",
" "],
'Q': [" ",
" ## ",
"# #",
"# #",
"# #",
"# # ",
" # #",
" "],
'R': [" ",
"### ",
"# #",
"### ",
"## ",
"# # ",
"# #",
" "],
'S': [" ",
" ###",
"# ",
" # ",
" # ",
" #",
"### ",
" "],
'T': [" ",
"#####",
" # ",
" # ",
" # ",
" # ",
" # ",
" "],
'U': [" ",
"# #",
"# #",
"# #",
"# #",
"# #",
" ## ",
" "],
'V': [" ",
"# # ",
"# # ",
"# # ",
"# # ",
"# # ",
" # ",
" "],
'W': [" ",
"# #",
"# #",
"# #",
"# # #",
"## ##",
"# #",
" "],
'X': [" ",
"# #",
"# #",
" # ",
" # ",
"# #",
"# #",
" "],
'Y': [" ",
"# #",
"# #",
"# #",
" # ",
" # ",
" # ",
" "],
'Z': [" ",
"####",
" #",
" # ",
" # ",
"# ",
"####",
" "],
'[': [" ",
"##",
"# ",
"# ",
"# ",
"# ",
"##",
" "],
']': [" ",
"##",
" #",
" #",
" #",
" #",
"##",
" "],
'_': [" ",
" ",
" ",
" ",
" ",
" ",
" ",
"####"],
'a': [" ",
" ",
" ## ",
" #",
" ###",
"# #",
" ###",
" "],
'b': [" ",
"# ",
"# ",
"### ",
"# #",
"# #",
"### ",
" "],
'c': [" ",
" ",
" ## ",
"# #",
"# ",
"# #",
" ## ",
" "],
'd': [" ",
" #",
" #",
" ###",
"# #",
"# #",
" ###",
" "],
'e': [" ",
" ",
" ## ",
"# #",
"####",
"# ",
" ## ",
" "],
'f': [" ",
" ## ",
"# #",
"# ",
"## ",
"# ",
"# ",
" "],
'g': [" ",
" ",
" ## ",
"# #",
" ###",
" #",
"### ",
" "],
'h': [" ",
"# ",
"# ",
"# # ",
"## #",
"# #",
"# #",
" "],
'i': [" ",
" # ",
" ",
"## ",
" # ",
" # ",
"###",
" "],
'j': [" ",
" #",
" ",
" #",
" #",
" #",
"# #",
" # "],
'k': [" ",
"# ",
"# #",
"# # ",
"## ",
"# # ",
"# #",
" "],
'l': [" ",
"## ",
" # ",
" # ",
" # ",
" # ",
"###",
" "],
'm': [" ",
" ",
"## # ",
"# # #",
"# #",
"# #",
"# #",
" "],
'n': [" ",
" ",
"### ",
"# #",
"# #",
"# #",
"# #",
" "],
'o': [" ",
" ",
" ## ",
"# #",
"# #",
"# #",
" ## ",
" "],
'p': [" ",
" ",
"### ",
"# #",
"# #",
"### ",
"# ",
"# "],
'q': [" ",
" ",
" ###",
"# #",
"# #",
" ###",
" #",
" #"],
'r': [" ",
" ",
"# ##",
"## ",
"# ",
"# ",
"# ",
" "],
's': [" ",
" ",
" ###",
"# ",
" ## ",
" #",
"### ",
" "],
't': [" ",
" # ",
"####",
" # ",
" # ",
" # ",
" ##",
" "],
'u': [" ",
" ",
"# #",
"# #",
"# #",
"# #",
" ## ",
" "],
'v': [" ",
" ",
"# #",
"# #",
" # # ",
" # # ",
" # ",
" "],
'w': [" ",
" ",
"# #",
"# #",
"# # #",
"# # #",
" # # ",
" "],
'x': [" ",
" ",
"# #",
"# #",
" ## ",
"# #",
"# #",
" "],
'y': [" ",
" ",
"# #",
"# #",
" ###",
" #",
"# #",
" ## "],
'z': [" ",
" ",
"####",
" # ",
" # ",
"# ",
"####",
" "]
}
sharpnotes = {
'A': [" #",
" ## ",
"# #",
"# #",
"####",
"# #",
"# #",
" "],
'C': [" #",
" ## ",
"# #",
"# ",
"# ",
"# #",
" ## ",
" "],
'D': [" #",
"### ",
"# #",
"# #",
"# #",
"# #",
"### ",
" "],
'F': [" #",
"### ",
"# ",
"### ",
"# ",
"# ",
"# ",
" "],
'G': [" #",
" ## ",
"# #",
"# ",
"# ##",
"# #",
" ## ",
" "]
}
def append_mapping(self, char, color):
#self.append_space()
bitmap = self.mappings[char]
n = len(bitmap[0])
for x in range(n):
self.columns.append([(color if bitmap[i][x] == '#' else (0,0,0)) for i in range(8)])
def append_rainbow(self):
for x in range(8):
r = int((math.cos(x * math.pi / 4) + 1) * 127)
g = int((math.cos((x - 8.0 / 3.0) * math.pi / 4) + 1) * 127)
b = int((math.cos((x + 8.0 / 3.0) * math.pi / 4) + 1) * 127)
self.columns.append([(r,g,b) for i in range(8)])
def append_space(self, n=1):
for x in range(n):
self.columns.append([(0,0,0) for i in range(8)])
def append_buffer(self):
self.append_space(9)
def append_letter(self, char, color=None):
if char == ' ':
self.append_space(2)
elif char == 0:
self.append_rainbow()
elif char in self.mappings.keys():
self.append_mapping(char, color)
else:
self.columns.append([(255,255,255),(255,255,255),(255,255,255),(255,255,255),(255,255,255),(255,255,255),(255,255,255),(255,255,255)])
print("unknown char {0} ({1})".format(char, ord(char)))
def append_sharpnote(self, text, color=(255,255,255)):
# Note
# Should be a better test for A-G letter.
if text[0] in self.mappings.keys():
bitmap = self.sharpnotes[text[0]]
n = len(bitmap[0])
for x in range(n):
self.columns.append([(color if bitmap[i][x] == '#' else (0,0,0)) for i in range(8)])
# Octave
if text[2] in self.mappings.keys():
self.append_letter(text[2], color)
def append_string(self, text, color=(255,255,255)):
i = 0
while i < len(text):
if text[i] == '~':
i += 1
if text[i] == 'R': #rainbow
self.append_letter(0)
else:
self.append_letter(text[i], color)
i += 1
def set_text(self, text, color=(255,255,255)):
self.columns = []
#self.append_buffer()
if len(text) == 3 and text[1] == "#":
self.append_sharpnote(text)
else:
self.append_string(text)
self.append_buffer()
def __init__(self, text=""):
self.set_text(text)
def start(self, delay=0.1):
u.set_pixels(self.columns[0:8])
u.show()
time.sleep(delay)
def startScroll(self, delay=0.1):
for x in range(len(self.columns) - 8):
u.set_pixels(self.columns[x:x+8])
u.show()
time.sleep(delay)
def Display(text, color=(255,255,255), delay=0.2, mididest ='launchpad'):
disp = ScrollDisp()
#print(text)
if mididest == 'bhoreal':
u.dest(mididest,180)
else:
u.dest(mididest,270)
disp.set_text(text, color)
disp.start(delay)
def DisplayScroll(text, color=(255,255,255), delay=0.2, mididest = 'launchpad'):
disp = ScrollDisp()
if mididest == 'bhoreal':
u.dest(mididest,180)
else:
u.dest(mididest,270)
disp.set_text(text, color)
disp.startScroll(delay)
if __name__ == '__main__':
from libs import midi3
# Implemented for script compatibility but actually do nothing on supported devices
u.brightness(0.5)
# 2 chars with no scrolling
Display(' '.join(sys.argv[1:]))
# text with scrolling
# DisplayScroll(' '.join(sys.argv[1:]))
# To use with a Bhoreal just add mididest = 'bhoreal' in Display()
# or DisplayScroll()
# Display(' '.join(sys.argv[1:]), mididest = 'bhoreal')

82
libs3/settings.py Normal file
View file

@ -0,0 +1,82 @@
#!/usr/bin/python3
# -*- coding: utf-8 -*-
# -*- mode: Python -*-
'''
LJay/LJ
v0.7.0
Settings Handler
LICENCE : CC
'''
import configparser
from libs3 import gstt
import ast
import numpy as np
def Write():
config.set('General', 'lasernumber', str(gstt.LaserNumber))
config.set('General', 'ljayserverip', str(gstt.LjayServerIP))
config.set('General', 'wwwip', str(gstt.wwwIP))
config.set('General', 'bhoroscip', str(gstt.oscIPin))
config.set('General', 'nozoscip', str(gstt.nozoscIP))
config.set('General', 'debug', str(gstt.debug))
config.set('General', 'autostart', gstt.autostart)
for i in range(gstt.LaserNumber):
laser = 'laser' + str(i)
config.set(laser, 'ip', str(gstt.lasersIPS[i]))
config.set(laser, 'type', str(gstt.lasertype[i]))
config.set(laser, 'kpps', str(gstt.kpps[i]))
config.set(laser, 'centerx', str(gstt.centerX[i]))
config.set(laser, 'centery', str(gstt.centerY[i]))
config.set(laser, 'zoomx', str(gstt.zoomX[i]))
config.set(laser, 'zoomy', str(gstt.zoomY[i]))
config.set(laser, 'sizex', str(gstt.sizeX[i]))
config.set(laser, 'sizey', str(gstt.sizeY[i]))
config.set(laser, 'finangle', str(gstt.finANGLE[i]))
config.set(laser, 'swapx', str(gstt.swapX[i]))
config.set(laser, 'swapy', str(gstt.swapY[i]))
config.set(laser, 'warpdest', np.array2string(gstt.warpdest[i], precision=2, separator=',',suppress_small=True))
config.write(open(gstt.ConfigName,'w'))
def Read():
gstt.LaserNumber = config.getint('General', 'lasernumber')
gstt.LjayServerIP= config.get('General', 'ljayserverip')
gstt.wwwIP= config.get('General', 'wwwip')
gstt.oscIPin = config.get('General', 'bhoroscip')
gstt.nozoscip = config.get('General', 'nozoscip')
gstt.debug = config.get('General', 'debug')
gstt.plugins = ast.literal_eval(config.get('plugins', 'plugins'))
gstt.autostart = config.get('General', 'autostart')
for i in range(4):
laser = 'laser' + str(i)
gstt.lasersIPS[i]= config.get(laser, 'ip')
gstt.lasertype[i]= config.get(laser, 'type')
gstt.kpps[i] = config.getint(laser, 'kpps')
#gstt.lasersPLcolor[i] = config.getint(laser, 'color')
gstt.centerX[i]= config.getint(laser, 'centerx')
gstt.centerY[i] = config.getint(laser, 'centery')
gstt.zoomX[i] = config.getfloat(laser, 'zoomx')
gstt.zoomY[i] = config.getfloat(laser, 'zoomy')
gstt.sizeX[i] = config.getint(laser, 'sizex')
gstt.sizeY[i] = config.getint(laser, 'sizey')
gstt.finANGLE[i] = config.getfloat(laser, 'finangle')
gstt.swapX[i] = config.getint(laser, 'swapx')
gstt.swapY[i] = config.getint(laser, 'swapy')
gstt.lsteps[i] = ast.literal_eval(config.get(laser, 'lsteps'))
gstt.warpdest[i]= np.array(ast.literal_eval(config.get(laser, 'warpdest')))
config = configparser.ConfigParser()
config.read(gstt.ConfigName)

583
libs3/tracer3.py Normal file
View file

@ -0,0 +1,583 @@
#!/usr/bin/python3
# -*- coding: utf-8 -*-
# -*- mode: Python -*-
'''
Tracer v0.8.2
Etherdream DACs handler on network via Redis
LICENCE : CC
Sam Neurohack, pclf
Includes live conversion in etherdream coordinates, geometric corrections, color balance change, intensity limitation, grid display,...
One tracer process is launched per requested laser by LJ. Lasers parameters in LJ.conf.
Live I/O based on redis keys : inputs (Pointlists to draw,...) and outputs (DAC state, errors,..).
Keys are mostly read and set at each main loop.
This tracer include an enhanced version (support for several lasers) of the etherdream python library from j4cDAC.
* Redis keys reference *
- Drawing things :
/pl/Scene/lasernumber [(x,y,color),(x1,y1,color),...] The live list of drawn pygame points. Tracer continously ask redis for key /clientkey+lasernumber
/resampler/lasernumber [(1.0,8), (0.25,3),(0.75,3),(1.0,10)] : a string for resampling rules.
the first tuple (1.0,8) is for short line < 4000 in etherdream space
(0.25,3),(0.75,3),(1.0,10) for long line > 4000
i.e (0.25,3) means go at 25% position on the line, send 3 times this position to etherdream
/clientkey "/pl/SceneNumber/" What Scene to retrieve from redis
/EDH/lasernumber
- Tracer control :
/order 0-8 Set redis key with new value then issue the order number
0 : Draw Normal point list
1 : Get the new EDH = reread redis key /EDH/lasernumber
2 : Draw BLACK point list
3 : Draw GRID point list
4 : Resampler Change (longs and shorts lsteps)
5 : Client Key Change = reread redis key /clientkey
6 : Max Intensity Change = reread redis key /intensity
7 : kpps change = reread redis key /kpps
8 : color balance change = reread redis keys /red /green /blue
- Managing Etherdream DACs :
Discrete drawing values
/kpps 0- DAC output speed to laser, then order 7. Depends of actual angle
/intensity 0-255 Laser output power, then order 6 (for alignement,...)
/red 0-100 % of full red, then order 8
/green 0-100 % of full green, then order 8
/blue 0-100 % of full blue, then order 8
DAC status report
/lstt/lasernumber etherdream last_status.playback_state (0: idle 1: prepare 2: playing)
/cap/lasernumber number of empty points sent to fill etherdream buffer (up to 1799)
/lack/lasernumber "a": ACK "F": Full "I": invalid. 64 or 35 for no connection.
Geometric corrections
Doctodo
'''
import socket
import time
import struct
#from gstt import debug
from libs3 import gstt,log
import math
from itertools import cycle
#from globalVars import *
import pdb
import ast
import redis
from libs3 import homographyp
import numpy as np
import binascii
black_points = [(278.0,225.0,0),(562.0,279.0,0),(401.0,375.0,0),(296.0,454.0,0),(298.0,165.0,0)]
grid_points = [(300.0,200.0,0),(500.0,200.0,65280),(500.0,400.0,65280),(300.0,400.0,65280),(300.0,200.0,65280),(300.0,200.0,0),(200.0,100.0,0),(600.0,100.0,65280),(600.0,500.0,65280),(200.0,500.0,65280),(200.0,100.0,65280)]
r = redis.StrictRedis(host=gstt.LjayServerIP, port=6379, db=0)
# r = redis.StrictRedis(host=gstt.LjayServerIP , port=6379, db=0, password='-+F816Y+-')
ackstate = {'61': 'ACK', '46': 'FULL', '49': "INVALID", '21': 'STOP', '64': "NO CONNECTION ?", '35': "NO CONNECTION ?" , '97': 'ACK', '70': 'FULL', '73': "INVALID", '33': 'STOP', '100': "NOCONNECTION", '48': "NOCONNECTION", 'a': 'ACK', 'F': 'FULL', 'I': "INVALID", '!': 'STOP', 'd': "NO CONNECTION ?", '0': "NO CONNECTION ?"}
lstate = {'0': 'IDLE', '1': 'PREPARE', '2': "PLAYING", '64': "NOCONNECTION ?" }
def pack_point(laser, intensity, x, y, r, g, b, i = -1, u1 = 0, u2 = 0, flags = 0):
"""Pack some color values into a struct dac_point."""
#print("Tracer", laser,":", r,g,b,"intensity", intensity, "i", i)
if r > intensity:
r = intensity
if g > intensity:
g = intensity
if b > intensity:
b = intensity
if max(r, g, b) == 0:
i = 0
else:
i = intensity
x = int(x)
y = int(y)
#print("Tracer ", laser, ": packing", x, y, r, g, b, "intensity", intensity, "i", i)
if x < -32767:
x = -32767
if gstt.debug >1:
log.err("Tracer "+ str(laser) +" : x coordinates was below -32767")
if x > 32767:
x = 32767
if gstt.debug >1:
log.err("Tracer "+ str(laser) +" : x coordinates was bigger than 32767")
if y < -32767:
y = -32767
if gstt.debug >1:
log.err("Tracer "+ str(laser) +" : y coordinates was below -32767")
if y > 32767:
y = 32767
if gstt.debug >1:
log.err("Tracer "+ str(laser) +" : y coordinates was bigger than 32767")
return struct.pack("<HhhHHHHHH", flags, x, y, r, g, b, i, u1, u2)
#return struct.pack("<HhhHHHHHH", flags, round(x), round(y), r, g, b, i, u1, u2)
class ProtocolError(Exception):
"""Exception used when a protocol error is detected."""
pass
class Status(object):
"""Represents a status response from the DAC."""
def __init__(self, data):
"""Initialize from a chunk of data."""
self.protocol_version, self.le_state, self.playback_state, \
self.source, self.le_flags, self.playback_flags, \
self.source_flags, self.fullness, self.point_rate, \
self.point_count = \
struct.unpack("<BBBBHHHHII", data)
def dump(self, prefix = " - "):
"""Dump to a string."""
lines = [
""
"Host ",
"Light engine: state %d, flags 0x%x" %
(self.le_state, self.le_flags),
"Playback: state %d, flags 0x%x" %
(self.playback_state, self.playback_flags),
"Buffer: %d points" %
(self.fullness, ),
"Playback: %d kpps, %d points played" %
(self.point_rate, self.point_count),
"Source: %d, flags 0x%x" %
(self.source, self.source_flags)
]
if gstt.debug == 2:
print()
for l in lines:
print(prefix + l)
class BroadcastPacket(object):
"""Represents a broadcast packet from the DAC."""
def __init__(self, st):
"""Initialize from a chunk of data."""
self.mac = st[:6]
self.hw_rev, self.sw_rev, self.buffer_capacity, \
self.max_point_rate = struct.unpack("<HHHI", st[6:16])
self.status = Status(st[16:36])
def dump(self, prefix = " - "):
"""Dump to a string."""
lines = [
"MAC: " + ":".join(
"%02x" % (ord(o), ) for o in self.mac),
"HW %d, SW %d" %
(self.hw_rev, self.sw_rev),
"Capabilities: max %d points, %d kpps" %
(self.buffer_capacity, self.max_point_rate)
]
print()
for l in lines:
print(prefix + l)
if gstt.debug > 1:
self.status.dump(prefix)
class DAC(object):
"""A connection to a DAC."""
# "Laser point List" Point generator
# each points is yielded : Getpoints() call n times OnePoint()
def OnePoint(self):
while True:
#pdb.set_trace()
for indexpoint,currentpoint in enumerate(self.pl):
#print indexpoint, currentpoint
xyc = [currentpoint[0],currentpoint[1],currentpoint[2]]
self.xyrgb = self.EtherPoint(xyc)
#print(self.xyrgb[2:])
rgb = (round(self.xyrgb[2:][0] *self.intred/100), round(self.xyrgb[2:][1] *self.intgreen/100), round(self.xyrgb[2:][2] *self.intblue/100))
#print("rgb :", rgb)
#round(*self.intred/100)
#round(*self.intgreen/100)
#round(*self.intblue/100)
delta_x, delta_y = self.xyrgb[0] - self.xyrgb_prev[0], self.xyrgb[1] - self.xyrgb_prev[1]
#test adaptation selon longueur ligne
if math.hypot(delta_x, delta_y) < 4000:
# For glitch art : decrease lsteps
#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)]
l_steps = gstt.stepslongline
for e in l_steps:
step = e[0]
for i in range(0,e[1]):
self.xyrgb_step = (self.xyrgb_prev[0] + step*delta_x, self.xyrgb_prev[1] + step*delta_y) + rgb # + self.xyrgb_prev[2:]# + rgb
#print(self.xyrgb_step)
yield self.xyrgb_step
self.xyrgb_prev = self.xyrgb
def GetPoints(self, n):
d = [next(self.newstream) for i in range(n)]
#print d
return d
# Etherpoint all transform in one matrix, with warp !!
# xyc : x y color
def EtherPoint(self,xyc):
c = xyc[2]
#print("")
#print("pygame point",[(xyc[0],xyc[1],xyc[2])])
#gstt.EDH[self.mylaser]= np.array(ast.literal_eval(r.get('/EDH/'+str(self.mylaser))))
position = homographyp.apply(gstt.EDH[self.mylaser],np.array([(xyc[0],xyc[1])]))
#print("etherdream point",position[0][0], position[0][1], ((c >> 16) & 0xFF) << 8, ((c >> 8) & 0xFF) << 8, (c & 0xFF) << 8)
return (position[0][0], position[0][1], ((c >> 16) & 0xFF) << 8, ((c >> 8) & 0xFF) << 8, (c & 0xFF) << 8)
def read(self, l):
"""Read exactly length bytes from the connection."""
while l > len(self.buf):
self.buf += self.conn.recv(4096)
obuf = self.buf
self.buf = obuf[l:]
return obuf[:l]
def readresp(self, cmd):
"""Read a response from the DAC."""
data = self.read(22)
response = data[0]
gstt.lstt_dacanswers[self.mylaser] = response
cmdR = chr(data[1])
status = Status(data[2:])
r.set('/lack/'+str(self.mylaser), response)
if cmdR != cmd:
raise ProtocolError("expected resp for %r, got %r"
% (cmd, cmdR))
if response != ord('a'):
raise ProtocolError("expected ACK, got %r"
% (response, ))
self.last_status = status
return status
def __init__(self, mylaser, PL, port = 7765):
"""Connect to the DAC over TCP."""
socket.setdefaulttimeout(2)
self.mylaser = mylaser
self.clientkey = r.get("/clientkey").decode('ascii')
#log.info("Tracer "+str(self.mylaser)+" connecting to "+ gstt.lasersIPS[mylaser])
#print("DAC", self.mylaser, "Handler process, connecting to", gstt.lasersIPS[mylaser] )
self.conn = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.connstatus = self.conn.connect_ex((gstt.lasersIPS[mylaser], port))
if self.connstatus == 35 or self.connstatus == 64:
log.err("Tracer "+ str(self.mylaser)+" ("+ gstt.lasersIPS[mylaser]+"): "+ackstate[str(self.connstatus)])
else:
print("Tracer "+ str(self.mylaser)+" ("+ gstt.lasersIPS[mylaser]+"): "+ackstate[str(self.connstatus)])
# ipconn state is -1 at startup (see gstt) and modified here
r.set('/lack/'+str(self.mylaser), self.connstatus)
gstt.lstt_ipconn[self.mylaser] = self.connstatus
self.buf = b''
# Upper case PL is the Point List number
self.PL = PL
# Lower case pl is the actual point list coordinates
#pdb.set_trace()
self.pl = ast.literal_eval(r.get(self.clientkey + str(self.mylaser)).decode('ascii'))
if r.get('/EDH/'+str(self.mylaser)) == None:
#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)).decode('ascii')))
#print("Laser",self.mylaser,"found its EDH in redis")
#print gstt.EDH[self.mylaser]
self.xyrgb = self.xyrgb_prev = (0,0,0,0,0)
self.intensity = 65280
self.intred = 100
self.intgreen = 100
self.intblue = 100
self.newstream = self.OnePoint()
if gstt.debug >0:
print("Tracer",self.mylaser,"init asked for ckey", self.clientkey+str(self.mylaser))
if self.connstatus != 0:
#print(""
log.err("Connection ERROR " +str(self.connstatus)+" with laser "+str(mylaser)+" : "+str(gstt.lasersIPS[mylaser]))
#print("first 10 points in PL",self.PL, self.GetPoints(10)
else:
print("Connection status for DAC "+str(self.mylaser)+" : "+ str(self.connstatus))
# Reference points
# Read the "hello" message
first_status = self.readresp("?")
first_status.dump()
position = []
def begin(self, lwm, rate):
cmd = struct.pack("<cHI", b'b', lwm, rate)
print("Tracer", str(self.mylaser), "begin with PL : ", str(self.PL))
self.conn.sendall(cmd)
return self.readresp("b")
def update(self, lwm, rate):
print(("update", lwm, rate))
cmd = struct.pack("<cHI", b'u', lwm, rate)
self.conn.sendall(cmd)
return self.readresp("u")
def encode_point(self, point):
return pack_point(self.mylaser, self.intensity, *point)
def write(self, points):
epoints = list(map(self.encode_point, points))
cmd = struct.pack("<cH", b'd', len(epoints))
self.conn.sendall(cmd + b''.join(epoints))
return self.readresp('d')
def prepare(self):
self.conn.sendall(b'p')
return self.readresp('p')
def stop(self):
self.conn.sendall('s')
return self.readresp('s')
def estop(self):
self.conn.sendall("\xFF")
return self.readresp("\xFF")
def clear_estop(self):
self.conn.sendall("c")
return self.readresp("c")
def ping(self):
self.conn.sendall('?')
return self.readresp('?')
def play_stream(self):
#print("laser", self.mylaser, "Pb : ",self.last_status.playback_state)
# error if etherdream is already playing state (from other source)
if self.last_status.playback_state == 2:
raise Exception("already playing?!")
# if idle go to prepare state
elif self.last_status.playback_state == 0:
self.prepare()
started = 0
while True:
#print("laser", self.mylaser, "Pb : ",self.last_status.playback_state)
order = int(r.get('/order/'+str(self.mylaser)).decode('ascii'))
#print("tracer", str(self.mylaser),"order", order, type(order)
if order == 0:
# USER point list
self.pl = ast.literal_eval(r.get(self.clientkey+str(self.mylaser)).decode('ascii'))
#print("Tracer : laser", self.mylaser, " order 0 : pl : ",len(self.pl))
else:
# Get the new EDH
if order == 1:
print("Tracer",self.mylaser,"new EDH ORDER in redis")
gstt.EDH[self.mylaser]= np.array(ast.literal_eval(r.get('/EDH/'+str(self.mylaser)).decode('ascii')))
# Back to user point list
r.set('/order/'+str(self.mylaser), 0)
# BLACK point list
if order == 2:
print("Tracer",self.mylaser,"BLACK ORDER in redis")
self.pl = black_points
# GRID point list
if order == 3:
print("Tracer",self.mylaser,"GRID ORDER in redis")
self.pl = grid_points
# Resampler Change
if order == 4:
self.resampler = ast.literal_eval(r.get('/resampler/'+str(self.mylaser)).decode('ascii'))
print("Tracer", self.mylaser," : resetting lsteps for",self.resampler)
gstt.stepshortline = self.resampler[0]
gstt.stepslongline[0] = self.resampler[1]
gstt.stepslongline[1] = self.resampler[2]
gstt.stepslongline[2] = self.resampler[3]
# Back to user point list order
r.set('/order/'+str(self.mylaser), 0)
# Client Key change
if order == 5:
print("Tracer", self.mylaser, "new clientkey")
self.clientkey = r.get('/clientkey')
# Back to user point list order
r.set('/order/'+str(self.mylaser), 0)
# Intensity change
if order == 6:
self.intensity = int(r.get('/intensity/' + str(self.mylaser)).decode('ascii')) << 8
print("Tracer" , self.mylaser, "new Intensity", self.intensity)
gstt.intensity[self.mylaser] = self.intensity
r.set('/order/'+str(self.mylaser), "0")
# kpps change
if order == 7:
gstt.kpps[self.mylaser] = int(r.get('/kpps/' + str(self.mylaser)).decode('ascii'))
print("Tracer",self.mylaser,"new kpps", gstt.kpps[self.mylaser])
self.update(0, gstt.kpps[self.mylaser])
r.set('/order/'+str(self.mylaser), "0")
# color balance change
if order == 8:
self.intred = int(r.get('/red/' + str(self.mylaser)).decode('ascii'))
self.intgreen = int(r.get('/green/' + str(self.mylaser)).decode('ascii'))
self.intblue = int(r.get('/blue/' + str(self.mylaser)).decode('ascii'))
print("Tracer", self.mylaser, "new color balance", self.intred,"% ", self.intgreen, "% ",self.intblue,"% ")
r.set('/order/'+str(self.mylaser), "0")
r.set('/lstt/'+str(self.mylaser), self.last_status.playback_state)
# pdb.set_trace()
# How much room?
cap = 1799 - self.last_status.fullness
points = self.GetPoints(cap)
r.set('/cap/'+str(self.mylaser), cap)
if cap < 100:
time.sleep(0.001)
cap += 150
# print("Writing %d points" % (cap, ))
#t0 = time.time()
#print points
self.write(points)
#t1 = time.time()
# print("Took %f" % (t1 - t0, )
if not started:
print("Tracer", self.mylaser, "starting with", gstt.kpps[self.mylaser],"kpps")
self.begin(0, gstt.kpps[self.mylaser])
started = 1
# not used in LJ.
def find_dac():
"""Listen for broadcast packets."""
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.bind(("0.0.0.0", 7654))
while True:
data, addr = s.recvfrom(1024)
bp = BroadcastPacket(data)
print(("Packet from %s: " % (addr, )))
bp.dump()
'''
#Laser order bit 0 = 0
if not order & (1 << (self.mylaser*2)):
#print("laser",mylaser,"bit 0 : 0")
# Laser bit 0 = 0 and bit 1 = 0 : USER PL
if not order & (1 << (1+self.mylaser*2)):
#print("laser",mylaser,"bit 1 : 0")
self.pl = ast.literal_eval(r.get('/pl/'+str(self.mylaser)))
else:
# Laser bit 0 = 0 and bit 1 = 1 : New EDH
#print("laser",mylaser,"bit 1 : 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))))
# Back to USER PL
order = r.get('/order')
neworder = order & ~(1<< self.mylaser*2)
neworder = neworder & ~(1<< 1+ self.mylaser*2)
r.set('/order', str(neworder))
else:
# Laser bit 0 = 1
print("laser",mylaser,"bit 0 : 1")
# Laser bit 0 = 1 and bit 1 = 0 : Black PL
if not order & (1 << (1+self.mylaser*2)):
#print("laser",mylaser,"bit 1 : 0")
self.pl = black_points
else:
# Laser bit 0 = 1 and bit 1 = 1 : GRID PL
#print("laser",mylaser,"bit 1 : 1" )
self.pl = grid_points
'''

View file

@ -0,0 +1,542 @@
#!/usr/bin/python3
# -*- coding: utf-8 -*-
# -*- mode: Python -*-
'''
Tracer
for LJ v0.8.2
Enhanced version (support for several lasers) of the etherdream python library from j4cDAC.
One tracer process is launched per requested laser. I/O based on redis keys.
LICENCE : CC
Sam Neurohack, pclf
Uses redis keys value for live inputs (Pointlists to draw,...) and outputs (DAC state, errors,..).
Most of redis keys are read and set at each main loop.
Includes live conversion in etherdream coordinates, geometric corrections,...
Etherdream IP is found in conf file for given laser number. (LJ.conf)
Redis keys to draw things :
/order select some change to adjust. See below
/pl/lasernumber [(x,y,color),(x1,y1,color),...] A string of list of points list.
/resampler/lasernumber [(1.0,8), (0.25,3),(0.75,3),(1.0,10)] : a string for resampling rules.
the first tuple (1.0,8) is for short line < 4000 in etherdream space
(0.25,3),(0.75,3),(1.0,10) for long line > 4000
i.e (0.25,3) means go at 25% position on the line, send 3 times this position to etherdream
/clientkey
Redis keys for Etherdream DAC
- Control
/kpps see order 7
/intensity see order 6
/red see order 8
/green see order 8
/blue see order 8
- DAC status report
/lstt/lasernumber value etherdream last_status.playback_state (0: idle 1: prepare 2: playing)
/cap/lasernumber value number of empty points sent to fill etherdream buffer (up to 1799)
/lack/lasernumber value "a": ACK "F": Full "I": invalid. 64 or 35 for no connection.
Order
0 : Draw Normal point list
1 : Get the new EDH
2 : Draw BLACK point list
3 : Draw GRID point list
4 : Resampler Change (longs and shorts lsteps)
5 : Client Key change
6 : Intensity change
7 : kpps change
8 : color balance change
Geometric corrections :
Doctodo
'''
import socket
import time
import struct
#from gstt import debug
from libs3 import gstt,log
import math
from itertools import cycle
#from globalVars import *
import pdb
import ast
import redis
from libs3 import homographyp
import numpy as np
import binascii
black_points = [(278.0,225.0,0),(562.0,279.0,0),(401.0,375.0,0),(296.0,454.0,0),(298.0,165.0,0)]
grid_points = [(300.0,200.0,0),(500.0,200.0,65280),(500.0,400.0,65280),(300.0,400.0,65280),(300.0,200.0,65280),(300.0,200.0,0),(200.0,100.0,0),(600.0,100.0,65280),(600.0,500.0,65280),(200.0,500.0,65280),(200.0,100.0,65280)]
r = redis.StrictRedis(host=gstt.LjayServerIP, port=6379, db=0)
# r = redis.StrictRedis(host=gstt.LjayServerIP , port=6379, db=0, password='-+F816Y+-')
ackstate = {'61': 'ACK', '46': 'FULL', '49': "INVALID", '21': 'STOP', '64': "NO CONNECTION ?", '35': "NO CONNECTION ?" , '97': 'ACK', '70': 'FULL', '73': "INVALID", '33': 'STOP', '100': "NOCONNECTION", '48': "NOCONNECTION", 'a': 'ACK', 'F': 'FULL', 'I': "INVALID", '!': 'STOP', 'd': "NO CONNECTION ?", '0': "NO CONNECTION ?"}
lstate = {'0': 'IDLE', '1': 'PREPARE', '2': "PLAYING", '64': "NOCONNECTION ?" }
def pack_point(laser,intensity, x, y, r, g, b, i = -1, u1 = 0, u2 = 0, flags = 0):
"""Pack some color values into a struct dac_point."""
#print("Tracer", laser,":", r,g,b,"intensity", intensity, "i", i)
if r > intensity:
r = intensity
if g > intensity:
g = intensity
if b > intensity:
b = intensity
if max(r, g, b) == 0:
i = 0
else:
i = intensity
#print("Tracer", laser,":", r,g,b,"intensity", intensity, "i", i)
#print(x, type(x), int(x))
return struct.pack("<HhhHHHHHH", flags, int(x), int(y), r, g, b, i, u1, u2)
class ProtocolError(Exception):
"""Exception used when a protocol error is detected."""
pass
class Status(object):
"""Represents a status response from the DAC."""
def __init__(self, data):
"""Initialize from a chunk of data."""
self.protocol_version, self.le_state, self.playback_state, \
self.source, self.le_flags, self.playback_flags, \
self.source_flags, self.fullness, self.point_rate, \
self.point_count = \
struct.unpack("<BBBBHHHHII", data)
def dump(self, prefix = " - "):
"""Dump to a string."""
lines = [
""
"Host ",
"Light engine: state %d, flags 0x%x" %
(self.le_state, self.le_flags),
"Playback: state %d, flags 0x%x" %
(self.playback_state, self.playback_flags),
"Buffer: %d points" %
(self.fullness, ),
"Playback: %d kpps, %d points played" %
(self.point_rate, self.point_count),
"Source: %d, flags 0x%x" %
(self.source, self.source_flags)
]
if gstt.debug == 2:
print()
for l in lines:
print(prefix + l)
class BroadcastPacket(object):
"""Represents a broadcast packet from the DAC."""
def __init__(self, st):
"""Initialize from a chunk of data."""
self.mac = st[:6]
self.hw_rev, self.sw_rev, self.buffer_capacity, \
self.max_point_rate = struct.unpack("<HHHI", st[6:16])
self.status = Status(st[16:36])
def dump(self, prefix = " - "):
"""Dump to a string."""
lines = [
"MAC: " + ":".join(
"%02x" % (ord(o), ) for o in self.mac),
"HW %d, SW %d" %
(self.hw_rev, self.sw_rev),
"Capabilities: max %d points, %d kpps" %
(self.buffer_capacity, self.max_point_rate)
]
print()
for l in lines:
print(prefix + l)
if gstt.debug > 1:
self.status.dump(prefix)
class DAC(object):
"""A connection to a DAC."""
# "Laser point List" Point generator
# each points is yielded : Getpoints() call n times OnePoint()
def OnePoint(self):
while True:
#pdb.set_trace()
for indexpoint,currentpoint in enumerate(self.pl):
#print indexpoint, currentpoint
xyc = [currentpoint[0],currentpoint[1],currentpoint[2]]
self.xyrgb = self.EtherPoint(xyc)
delta_x, delta_y = self.xyrgb[0] - self.xyrgb_prev[0], self.xyrgb[1] - self.xyrgb_prev[1]
#test adaptation selon longueur ligne
if math.hypot(delta_x, delta_y) < 4000:
# For glitch art : decrease lsteps
#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)]
l_steps = gstt.stepslongline
for e in l_steps:
step = e[0]
for i in range(0,e[1]):
self.xyrgb_step = (self.xyrgb_prev[0] + step*delta_x, self.xyrgb_prev[1] + step*delta_y) + self.xyrgb[2:]
yield self.xyrgb_step
self.xyrgb_prev = self.xyrgb
def GetPoints(self, n):
d = [next(self.newstream) for i in range(n)]
#print d
return d
# Etherpoint all transform in one matrix, with warp !!
# xyc : x y color
def EtherPoint(self,xyc):
c = xyc[2]
#print("")
#print("pygame point",[(xyc[0],xyc[1],xyc[2])])
#gstt.EDH[self.mylaser]= np.array(ast.literal_eval(r.get('/EDH/'+str(self.mylaser))))
position = homographyp.apply(gstt.EDH[self.mylaser],np.array([(xyc[0],xyc[1])]))
#print("etherdream point",position[0][0], position[0][1], ((c >> 16) & 0xFF) << 8, ((c >> 8) & 0xFF) << 8, (c & 0xFF) << 8)
return (position[0][0], position[0][1], ((c >> 16) & 0xFF) << 8, ((c >> 8) & 0xFF) << 8, (c & 0xFF) << 8)
def read(self, l):
"""Read exactly length bytes from the connection."""
while l > len(self.buf):
self.buf += self.conn.recv(4096)
obuf = self.buf
self.buf = obuf[l:]
return obuf[:l]
def readresp(self, cmd):
"""Read a response from the DAC."""
data = self.read(22)
response = data[0]
gstt.lstt_dacanswers[self.mylaser] = response
cmdR = chr(data[1])
status = Status(data[2:])
r.set('/lack/'+str(self.mylaser), response)
if cmdR != cmd:
raise ProtocolError("expected resp for %r, got %r"
% (cmd, cmdR))
if response != ord('a'):
raise ProtocolError("expected ACK, got %r"
% (response, ))
self.last_status = status
return status
def __init__(self, mylaser, PL, port = 7765):
"""Connect to the DAC over TCP."""
socket.setdefaulttimeout(2)
self.mylaser = mylaser
self.clientkey = r.get("/clientkey").decode('ascii')
#log.info("Tracer "+str(self.mylaser)+" connecting to "+ gstt.lasersIPS[mylaser])
#print("DAC", self.mylaser, "Handler process, connecting to", gstt.lasersIPS[mylaser] )
self.conn = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.connstatus = self.conn.connect_ex((gstt.lasersIPS[mylaser], port))
if self.connstatus == 35 or self.connstatus == 64:
log.err("Tracer "+ str(self.mylaser)+" ("+ gstt.lasersIPS[mylaser]+"): "+ackstate[str(self.connstatus)])
else:
print("Tracer "+ str(self.mylaser)+" ("+ gstt.lasersIPS[mylaser]+"): "+ackstate[str(self.connstatus)])
# ipconn state is -1 at startup (see gstt) and modified here
r.set('/lack/'+str(self.mylaser), self.connstatus)
gstt.lstt_ipconn[self.mylaser] = self.connstatus
self.buf = b''
# Upper case PL is the Point List number
self.PL = PL
# Lower case pl is the actual point list coordinates
#pdb.set_trace()
self.pl = ast.literal_eval(r.get(self.clientkey + str(self.mylaser)).decode('ascii'))
if r.get('/EDH/'+str(self.mylaser)) == None:
#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)).decode('ascii')))
#print("Laser",self.mylaser,"found its EDH in redis")
#print gstt.EDH[self.mylaser]
self.xyrgb = self.xyrgb_prev = (0,0,0,0,0)
self.intensity = 65280
self.newstream = self.OnePoint()
if gstt.debug >0:
print("Tracer",self.mylaser,"init asked for ckey", self.clientkey+str(self.mylaser))
if self.connstatus != 0:
#print(""
log.err("Connection ERROR " +str(self.connstatus)+" with laser "+str(mylaser)+" : "+str(gstt.lasersIPS[mylaser]))
#print("first 10 points in PL",self.PL, self.GetPoints(10)
else:
print("Connection status for DAC "+str(self.mylaser)+" : "+ str(self.connstatus))
# Reference points
# Read the "hello" message
first_status = self.readresp("?")
first_status.dump()
position = []
def begin(self, lwm, rate):
cmd = struct.pack("<cHI", b'b', lwm, rate)
print("Tracer", str(self.mylaser), "begin with PL : ", str(self.PL))
self.conn.sendall(cmd)
return self.readresp("b")
def update(self, lwm, rate):
print(("update", lwm, rate))
cmd = struct.pack("<cHI", b'u', lwm, rate)
self.conn.sendall(cmd)
return self.readresp("u")
def encode_point(self, point):
return pack_point(self.mylaser, self.intensity, *point)
def write(self, points):
epoints = list(map(self.encode_point, points))
cmd = struct.pack("<cH", b'd', len(epoints))
self.conn.sendall(cmd + b''.join(epoints))
return self.readresp("d")
def prepare(self):
self.conn.sendall("p")
return self.readresp("p")
def stop(self):
self.conn.sendall("s")
return self.readresp("s")
def estop(self):
self.conn.sendall("\xFF")
return self.readresp("\xFF")
def clear_estop(self):
self.conn.sendall("c")
return self.readresp("c")
def ping(self):
self.conn.sendall("?")
return self.readresp("?")
def play_stream(self):
#print("laser", self.mylaser, "Pb : ",self.last_status.playback_state)
# error if etherdream is already playing state (from other source)
if self.last_status.playback_state == 2:
raise Exception("already playing?!")
# if idle go to prepare state
elif self.last_status.playback_state == 0:
self.prepare()
started = 0
while True:
#print("laser", self.mylaser, "Pb : ",self.last_status.playback_state)
order = int(r.get('/order/'+str(self.mylaser)).decode('ascii'))
#print("tracer", str(self.mylaser),"order", order, type(order)
if order == 0:
# USER point list
self.pl = ast.literal_eval(r.get(self.clientkey+str(self.mylaser)).decode('ascii'))
#print("Tracer : laser", self.mylaser, " order 0 : pl : ",len(self.pl))
else:
# Get the new EDH
if order == 1:
print("Tracer",self.mylaser,"new EDH ORDER in redis")
gstt.EDH[self.mylaser]= np.array(ast.literal_eval(r.get('/EDH/'+str(self.mylaser)).decode('ascii')))
# Back to user point list
r.set('/order/'+str(self.mylaser), 0)
# BLACK point list
if order == 2:
print("Tracer",self.mylaser,"BLACK ORDER in redis")
self.pl = black_points
# GRID point list
if order == 3:
print("Tracer",self.mylaser,"GRID ORDER in redis")
self.pl = grid_points
# Resampler Change
if order == 4:
self.resampler = ast.literal_eval(r.get('/resampler/'+str(self.mylaser)).decode('ascii'))
print("Tracer", self.mylaser," : resetting lsteps for",self.resampler)
gstt.stepshortline = self.resampler[0]
gstt.stepslongline[0] = self.resampler[1]
gstt.stepslongline[1] = self.resampler[2]
gstt.stepslongline[2] = self.resampler[3]
# Back to user point list order
r.set('/order/'+str(self.mylaser), 0)
# Client Key change
if order == 5:
print("Tracer", self.mylaser, "new clientkey")
self.clientkey = r.get('/clientkey')
# Back to user point list order
r.set('/order/'+str(self.mylaser), 0)
# Intensity change
if order == 6:
self.intensity = int(r.get('/intensity/' + str(self.mylaser)).decode('ascii')) << 8
print("Tracer" , self.mylaser, "new Intensity", self.intensity)
gstt.intensity[self.mylaser] = self.intensity
r.set('/order/'+str(self.mylaser), "0")
# kpps change
if order == 7:
gstt.kpps[self.mylaser] = int(r.get('/kpps/' + str(self.mylaser)).decode('ascii'))
print("Tracer",self.mylaser,"new kpps", gstt.kpps[self.mylaser])
self.update(0, gstt.kpps[self.mylaser])
r.set('/order/'+str(self.mylaser), "0")
# color balance change
if order == 8:
gstt.red[self.mylaser] = int(r.get('/red/' + str(self.mylaser)).decode('ascii'))
gstt.green[self.mylaser] = int(r.get('/green/' + str(self.mylaser)).decode('ascii'))
gstt.blue[self.mylaser] = int(r.get('/blue/' + str(self.mylaser)).decode('ascii'))
print("Tracer",self.mylaser,"new color balance", gstt.red[self.mylaser], gstt.green[self.mylaser], gstt.blue[self.mylaser])
r.set('/order/'+str(self.mylaser), "0")
r.set('/lstt/'+str(self.mylaser), self.last_status.playback_state)
# pdb.set_trace()
# How much room?
cap = 1799 - self.last_status.fullness
points = self.GetPoints(cap)
r.set('/cap/'+str(self.mylaser), cap)
if cap < 100:
time.sleep(0.001)
cap += 150
# print("Writing %d points" % (cap, ))
#t0 = time.time()
#print points
self.write(points)
#t1 = time.time()
# print("Took %f" % (t1 - t0, )
if not started:
print("Tracer", self.mylaser, "starting with", gstt.kpps[self.mylaser],"kpps")
self.begin(0, gstt.kpps[self.mylaser])
started = 1
# not used in LJay.
def find_dac():
"""Listen for broadcast packets."""
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.bind(("0.0.0.0", 7654))
while True:
data, addr = s.recvfrom(1024)
bp = BroadcastPacket(data)
print(("Packet from %s: " % (addr, )))
bp.dump()
'''
#Laser order bit 0 = 0
if not order & (1 << (self.mylaser*2)):
#print("laser",mylaser,"bit 0 : 0")
# Laser bit 0 = 0 and bit 1 = 0 : USER PL
if not order & (1 << (1+self.mylaser*2)):
#print("laser",mylaser,"bit 1 : 0")
self.pl = ast.literal_eval(r.get('/pl/'+str(self.mylaser)))
else:
# Laser bit 0 = 0 and bit 1 = 1 : New EDH
#print("laser",mylaser,"bit 1 : 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))))
# Back to USER PL
order = r.get('/order')
neworder = order & ~(1<< self.mylaser*2)
neworder = neworder & ~(1<< 1+ self.mylaser*2)
r.set('/order', str(neworder))
else:
# Laser bit 0 = 1
print("laser",mylaser,"bit 0 : 1")
# Laser bit 0 = 1 and bit 1 = 0 : Black PL
if not order & (1 << (1+self.mylaser*2)):
#print("laser",mylaser,"bit 1 : 0")
self.pl = black_points
else:
# Laser bit 0 = 1 and bit 1 = 1 : GRID PL
#print("laser",mylaser,"bit 1 : 1" )
self.pl = grid_points
'''