More plugins, more doc,...

This commit is contained in:
leduc 2019-08-06 03:08:54 +02:00
parent 4a2d1a5773
commit 0bb0049f02
105 changed files with 15152 additions and 2757 deletions

BIN
libs/.DS_Store vendored Normal file

Binary file not shown.

289
libs/LPD8.py Normal file
View file

@ -0,0 +1,289 @@
#!/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 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
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))
myIP = "127.0.0.1"
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...")
f=open("macros.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
libs/OSC3.py Executable file

File diff suppressed because it is too large Load diff

0
libs/__init__.py Normal file
View file

BIN
libs/__init__.pyc Normal file

Binary file not shown.

364
libs/artnet.py Normal file
View file

@ -0,0 +1,364 @@
#!/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
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 lj23 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 ("")
print ("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("")
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...")
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 xrange(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
libs/audio.py Executable 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
libs/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)
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)
'''

BIN
libs/bhoreal.pyc Normal file

Binary file not shown.

164
libs/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 ?')

157
libs/cli.py Normal file
View file

@ -0,0 +1,157 @@
# coding=UTF-8
"""
LJay/LJ
v0.8
Command line arguments parser
by Sam Neurohack
from /team/laser
"""
import gstt
import argparse
print "-h will display help"
print ""
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 connected (4 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)
# 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
# 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
if args.Lasers != None:
gstt.LaserNumber = args.Lasers
else:
gstt.LaserNumber = 4
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()

BIN
libs/cli.pyc Normal file

Binary file not shown.

448
libs/commands.py Normal file
View file

@ -0,0 +1,448 @@
# 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.
/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.
/planet will be forwarded to planetarium client.
/nozoid will be forwarded to nozoid client.
/scene/scenenumber/start 0 or 1
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
/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.
"""
from __future__ import absolute_import
import types, time
from libs import gstt
import redis
from libs import settings, plugins, homographyp
r = redis.StrictRedis(host=gstt.LjayServerIP , port=6379, db=0)
GenericCommands = ["start","align","ljclient","scene","addest","deldest","clientnumber","vcvrack","fft","midigen","viewgen","audiogen","noteon","cc","ljpong","ljwars","mouse","emergency","simu","status","run","nozoid","planet","live","words","ai","bank0","pose","lj","cycl","glyph","pong"]
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,lsteps):
# lsteps is a string like : "[ (1.0, 8),(0.25, 3), (0.75, 3), (1.0, 10)]"
print "Resampler change for laser ", laser
r.set('/resampler/' + str(laser), lsteps)
r.set('/order/'+str(laser), 4)
def 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 xrange(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 xrange(0,gstt.LaserNumber):
r.set('/order/'+str(laserid), 5)
plugins.sendWSall("/scene/" + str(laserid) + "/start 0")
plugins.sendWSall("/scene/" + newscene + "/start 1")
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("/status Not Enough Lasers")
else:
gstt.Laser = note -24
plugins.sendWSall("/status Scene " + str(gstt.SceneNumber) + " laser " + str(gstt.Laser))
print "Current Laser switched to", gstt.Laser
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 ""
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])
#/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]))
elif oscpath[1] == "CC":
CC(int(args[0]), int(args[1]))
elif oscpath[1] == "pong":
#print "LJ commands got pong from", args
print("/" + args[0] + "/start 1")
plugins.sendWSall("/" + args[0] + "/start 1")
print("/status got pong from "+ args[0] +".")
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)
# Commands with a laser number
else:
pathlength = len(oscpath)
print("oscpath", oscpath)
print("pathlength", pathlength)
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()
# /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 "New intensity requested for laser ", laser, ":", int(args[0])
print "Change not implemented yet"
# /resampler/lasernumber lsteps
# lsteps is a string like "[ (1.0, 8),(0.25, 3), (0.75, 3), (1.0, 10)]"
if oscpath[1] == "resampler":
Resampler(laser,args[0])
# /mouse/lasernumber value (0 or 1)
if oscpath[1] == "mouse":
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)
'''
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)
'''

BIN
libs/commands.pyc Normal file

Binary file not shown.

119
libs/font1.py Executable file
View file

@ -0,0 +1,119 @@
# coding=UTF-8
"""
LJ Font 1
v0.7.0
LICENCE : CC
by Sam Neurohack
from /team/laser
"""
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 {
]

BIN
libs/font1.pyc Normal file

Binary file not shown.

130
libs/gstt.py Normal file
View file

@ -0,0 +1,130 @@
# coding=UTF-8
'''
LJ Global state
v0.8.0
**
Almost all values here Will be overriden by LJ.conf file data
**
LICENCE : CC
by Sam Neurohack, Loloster, pclf
from /team/laser
'''
#ConfigName = "setexample.conf"
ConfigName = "LJ.conf"
debug = 0
anims= [[],[],[],[]]
# How many lasers are connected. Different that "currentlaser".
LaserNumber = 2
# 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'
# 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']
# gstt.kpps stores kpps for each laser.
# ** Will be overridden by LJ.conf file values **
kpps = [25000,25000,25000,25000]
# gstt.GridDisplay : if = 1 Curve points actually sent to PL are replaced by a grid
GridDisplay = [0,0,0,0]
# 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]
# 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.]]
]

BIN
libs/gstt.pyc Normal file

Binary file not shown.

252
libs/homographyp.py Executable file
View file

@ -0,0 +1,252 @@
#!/usr/bin/python2.7
# -*- 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
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 xrange(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 xrange(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,(pygamex,pygamey)):
#print "current point : ", pygamex, pygamey
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 xrange(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 "Laser",mylaser,"New EDH computed, sending to redis..."
if r.set('/EDH/'+str(mylaser), np.array2string(gstt.EDH[mylaser], separator=',')) == True:
r.set('/order/'+str(mylaser), 1)
print "New EDH sent."
else:
print "New EDH not sent."
'''
# Laser bit 0 = 0 and bit 1 = 1 : New EDH
order = r.get('/order')
print order
neworder = order & ~(1<< mylaser*2)
neworder = neworder | (1<< 1+mylaser*2)
r.set('/order', str(neworder))
'''
if gstt.debug >1:
print ""
print "laser ", mylaser
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]

BIN
libs/homographyp.pyc Normal file

Binary file not shown.

883
libs/launchpad.py Normal file
View file

@ -0,0 +1,883 @@
#!/usr/bin/python3
# -*- coding: utf-8 -*-
"""
Launchpad
v0.7.0
Maunchpad mini Handler.
Start a dedicated thread to handle incoming events from launchpad.
Cls()
AllColorPad(color)
StartLaunchPad(port) : Start animation
Led Matrix can be access with X and Y coordinates and as midi note (0-63)
PadNoteOn(note,color)
PadNoteOff(note)
PadNoteOnXY(x,y,color):
PadNoteOffXY(x,y):
PadNoteXY(x,y):
PadLeds[], PadTops[] and PadRights arrays stores matrix current state
Top raw and right column leds are numbered humanly 1-8. So -1 is for pythonic arrays position 0-7
PadTopOn(number,color)
PadTopOff(number)
PadRightOn(number)
PadRightOff(number):
by Sam Neurohack
from /team/laser
for python 2 & 3
"""
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 midi3
#import midimacros, maxwellmacros
import traceback
from queue import Queue
import scrolldisp
#from libs import macros
import json, subprocess
from OSC3 import OSCServer, OSCClient, OSCMessage
import socket
print('Launchpad 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))
myIP = "127.0.0.1"
print('Used IP', myIP)
OSCinPort = 8080
monomePort = 8000
maxwellatorPort = 8090
launchqueue = Queue()
mode = "maxwell"
mididest = 'Session 1'
midichannel = 1
CChannel = 0
CCvalue = 0
PadLeds = [0] * 64
PadTops= [0] * 8
PadRights= [0] * 8
Here = -1
ModeCallback = ''
# midi notes
LaunchLedMatrix = [(0,1,2,3,4,5,6,7),(16,17,18,19,20,21,22,23),(32,33,34,35,36,37,38,39),(48,49,50,51,52,53,54,55),(64,65,66,67,68,69,70,71),(80,81,82,83,84,85,86,87),(96,97,98,99,100,101,102,103),(112,113,114,115,116,117,118,119)]
# Notes
LaunchRight = (8,24,40,56,72,88,104,120)
# CC
LaunchTop = (104,105,106,107,108,109,110,111)
PadTop = [0,0,0,0,0,0,0,0]
PadRight = [0,0,0,0,0,0,0,0]
PadMatrix = [0] * 64
TopSelection = [0] *8
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 Disp(text,device = 'Launchpad Mini'):
print(device,midi3.FindInDevice(device))
if (device == "Launchpad Mini" or device =='launchpad') and midi3.FindInDevice(device) != -1:
scrolldisp.Display(text, color=(255,255,255), delay=0.2, mididest = 'launchpad')
if device == 'bhoreal' and midi3.FindInDevice('Bhoreal'):
scrolldisp.Display(text, color=(255,255,255), delay=0.2, mididest = device)
def PadNoteOn(note,color):
(x,y) = BhorIndex(note)
#print(note,x,y)
PadNoteOnXY(x,y,color)
def PadNoteOff(note):
(x,y) = BhorIndex(note)
PadNoteOffXY(x,y)
def PadNoteOnXY(x,y,color):
msg= [NOTE_ON, PadNoteXY(x,y), color]
#print msg
midi3.send(msg,"Launchpad")
PadLeds[BhorNoteXY(x,y)]=color
def PadNoteOffXY(x,y):
msg= [NOTE_OFF, PadNoteXY(x,y), 0]
midi3.send(msg,"Launchpad")
PadLeds[BhorNoteXY(x,y)]=0
def PadNoteXY(x,y):
note = LaunchLedMatrix[int(y-1)][int(x-1)]
return note
def PadIndex(note):
y=note/16
x=note%16
return int(x+1),int(y+1)
def BhorIndex(note):
y=note/8
x=note%8
#print "Note : ",note
#print "BhorIndex : ", x+1,y+1
return int(x+1),int(y+1)
def BhorNoteXY(x,y):
note = (x -1)+ (y-1) * 8
return note
# top raw and right column leds are numbered humanly 1-8. So -1 is for pythonic arrays position 0-7
def PadTopOn(number,color):
msg= [CONTROLLER_CHANGE, LaunchTop[number-1], color]
midi3.send(msg,"Launchpad")
PadTops[number-1]=color
def PadTopOff(number):
msg= [CONTROLLER_CHANGE, LaunchTop[number-1], 0]
midi3.send(msg,"Launchpad")
PadTops[number-1]=0
def PadRightOn(number,color):
msg= [NOTE_ON, LaunchRight[number-1], color]
midi3.send(msg,"Launchpad")
PadRights[number-1]=color
def PadRightOff(number):
msg= [NOTE_OFF, LaunchRight[number-1], 0]
midi3.send(msg,"Launchpad")
PadRights[number-1]=0
def TopUpdate(button,color):
#print(PadTop)
PadTop = [0,0,0,0,0,0,0,0]
PadTop[button] = color
for pad in range(7):
PadTopOn(pad+1,PadTop[pad])
def RightUpdate():
for pad in range(9):
PadRightOn(pad,PadRight[pad])
def MatrixUpdate():
for pad in range(64):
PadNoteOn(pad,PadMatrix[pad])
def MatrixSelect():
MatrixUpdate()
return
def ComputerUpdate(comput):
global computer
computer = comput
PadRightOn(computer+1,127)
# Client to export buttons actions from Launchpad 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
#
# LaunchPad start anim
#
# AllColor for bhoreal on given port
def AllColorPad(color):
for led in range(0,64,1):
PadNoteOn(led,color)
'''
for line in LaunchLedMatrix:
for led in line:
midiport[port].send_message([NOTE_ON, led, color])
'''
for rightled in range(8):
PadRightOn(rightled+1,color)
for topled in range(8):
PadTopOn(topled+1,color)
#midiport[port].send_message([CONTROLLER_CHANGE, topled, color])
def ClsMatrix():
for led in range(0,64,1):
PadNoteOff(led)
def ClsTop():
for topled in range(8):
PadTopOff(topled+1)
def ClsRight():
for rightled in range(8):
PadRightOff(rightled+1)
def Cls():
ClsMatrix()
ClsTop()
ClsRight()
ComputerUpdate(computer)
'''
for line in LaunchLedMatrix:
for led in line:
midiport[port].send_message([NOTE_OFF, led, 0])
'''
def StartLaunchPad(port):
#ClsPad(port)
#time.sleep(0.3)
AllColorPad(20)
time.sleep(0.6)
Cls()
time.sleep(0.3)
#
# Events from Launchpad Handling
#
# Process events coming from Launchpad in a separate thread.
def MidinProcess(launchqueue):
global computer
while True:
launchqueue_get = launchqueue.get
msg = launchqueue_get()
#print (msg)
if msg[0]==NOTE_ON:
(x,y) = PadIndex(msg[1])
# MATRIX = macros, notes, channels,...
if x < 9:
msg[1]= BhorNoteXY(x,y)
macroname = "m"+str(y)+str(x)
# Run Macro with matrix location and velocity
Run(macroname, macroargs = int(msg[2]))
# RIGHT = computer, this host or other computer
if x == 9:
print("Right Button : ", y)
macroname = "r"+str(y)
print(macroname)
ClsRight()
PadRightOn(y,127)
print("Destination computer",y)
computer = y
#time.sleep(0.1)
#PadRightOff(y)
# TOP = Mode Note, CC, Os, Monome,..
if msg[0]==CONTROLLER_CHANGE:
print("Pad Top Button : ", str(msg[1]-103), "value",msg[2])
TopUpdate(msg[1]-104,20)
macroname = "t"+str(msg[1]-103)
#print(macroname)
Run(macroname, macroargs = (msg[1]-103,msg[2]))
launchqueue = Queue()
ModeCallback = "ModeNo"
# LaunchPad Mini call back : new msg forwarded to Launchpad queue
class LaunchAddQueue(object):
def __init__(self, port):
self.port = port
#print("LaunchAddQueue", 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))
launchqueue.put(message)
#
# Modes : Top lines functions
#
# Load Matrix only macros (for the moment) in macros.json
def LoadMacros():
global macros
print()
print("Loading Launchpad Macros...")
f=open("macros.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
# Default top buttons : maxwell macros
def TopMacro(arg):
topbutton, value = arg
print ("topmacro", topbutton, "value", value)
if value == 127:
TopUpdate(topbutton-1,20)
Disp("Ma")
Disp('cr', 'bhoreal')
ModeCallback = TopCallback
def TopCallback(arg):
ClsMatrix()
x,y,velocity = arg
PadNoteOnXY(x,y,20)
#print ('Macros OS', BhorNoteXY(x,y), "velocity", velocity )
macroname = 'm'+str(y)+str(x)
macronumber = findMacros(macroname,'Maxwell')
if macronumber != -1:
#print("code : ",macros['OS'][macronumber]["code"])
eval(macros['Maxwell'][macronumber]["code"])
else:
print("no Code yet")
#
# Notes Macros
#
def ModeNote(arg):
global ModeCallback
topbutton, value = arg
if value == 127:
TopUpdate(topbutton-1,20)
Disp("No")
Disp('te', 'bhoreal')
print("ModeNote")
else:
ClsMatrix()
ModeCallback = "NoteCallback"
def NoteCallback(arg):
#ClsMatrix()
x,y,velocity = arg
notename = midi3.midi2note(BhorNoteXY(x,y))
print('computer',computer)
# todo : decide whether its 0 or 1 !!!
if computer == 0 or computer == 1:
midi3.NoteOn(BhorNoteXY(x,y),velocity,'AutoTonic MIDI In')
else:
SendOSC(computerIP[computer-1],maxwellatorPort,'/note',[BhorNoteXY(x,y),velocity])
if velocity == 127:
PadNoteOnXY(x,y,20)
#print ('NoteON', BhorNoteXY(x,y),notename , "velocity", velocity )
#Disp(notename)
else:
PadNoteOnXY(x,y,0)
#print ('NoteOFF', BhorNoteXY(x,y),notename , "velocity", velocity )
#
# CC Macros
#
def ModeCC(arg):
global ModeCallback
topbutton, value = arg
if value == 127:
TopUpdate(topbutton-1,20)
Disp('CC')
Disp(' ', 'bhoreal')
print("Mode CC")
ModeCallback = "CCSelect"
print("Please enter CC Channel")
#ClsMatrix()
Disp('Ch')
def CCSelect(arg):
global ModeCallback, CChannel
x,y, velocity = arg
PadNoteOnXY(x,y,20)
#print ('in CC channel callback x',x,'y',y)
if velocity == 127:
CChannel = BhorNoteXY(x,y)
print("CC Channel", CChannel)
print("Please enter CC Value")
ModeCallback = "CCValue"
Disp('Va')
def CCValue(arg):
#ClsMatrix()
x,y, velocity = arg
PadNoteOnXY(x,y,20)
#print ('in CC value callback x',x,'y',y)
if velocity == 127:
CCvalue = BhorNoteXY(x,y) * 2
print("CC Channel", CChannel,"CC Value", CCvalue)
#
# OS Macros
#
def ModeOS(arg):
global ModeCallback
topbutton, value = arg
if value == 127:
Disp('Os')
Disp('Ma', 'bhoreal')
TopUpdate(topbutton-1,20)
ModeCallback = "OSCallback"
else:
ClsMatrix()
def OSCallback(arg):
ClsMatrix()
x,y,velocity = arg
PadNoteOnXY(x,y,20)
#print ('Macros OS', BhorNoteXY(x,y), "velocity", velocity )
macroname = 'm'+str(y)+str(x)
macronumber = findMacros(macroname,'OS')
if macronumber != -1:
#print("code : ",macros['OS'][macronumber]["code"])
eval(macros['OS'][macronumber]["code"])
else:
print("no Code yet")
#
# Monome emulation
#
prefix = '/box'
def ModeMonome(arg):
global ModeCallback
topbutton, value = arg
if value == 127:
TopUpdate(topbutton-1,20)
Disp('Mo')
Disp('me', 'bhoreal')
ModeCallback = "MonomeCallback"
else:
ClsMatrix()
def MonomeCallback(arg):
ClsMatrix()
x,y,velocity = arg
#PadNoteOnXY(x,y,20)
SendOSC('127.0.0.1', monomePort, prefix+'/press', (x,y,1))
SendOSC('127.0.0.1', monomePort, prefix+'/grid/key', (x,y,1))
#
# StartMode
#
def ModeNo(arg):
x,y,velocity = arg
PadNoteOnXY(x,y,20)
print ('Mode No x',x,'y',y,"note", PadNoteXY(x,y))
'''
def Mode(mode):
global macros
if mode == "maxwell":
print("Launchpad in Maxwell mode")
macros = maxwellmacros.buttons
if mode == "generic":
print("Launchpad in generic mode")
macros = generic
'''
#
# Right column functions
#
def RightMacro(number):
print ("rightmacro",number)
#
# Default Pad macros
#
launchmacros = {
"t": {"command": TopMacro, "default": -1},
"t1": {"command": ModeNote, "default": ''},
"t2": {"command": ModeCC, "default": ''},
"t3": {"command": ModeOS, "default": ''},
"t4": {"command": ModeMonome, "default": ''},
"t5": {"command": TopMacro, "default": 5},
"t6": {"command": TopMacro, "default": 6},
"t7": {"command": TopMacro, "default": 7},
"t8": {"command": TopMacro, "default": 8},
"r1": {"command": RightMacro, "default": 1},
"r2": {"command": RightMacro, "default": 2},
"r3": {"command": RightMacro, "default": 3},
"r4": {"command": RightMacro, "default": 4},
"r5": {"command": RightMacro, "default": 5},
"r6": {"command": RightMacro, "default": 6},
"r7": {"command": RightMacro, "default": 7},
"r8": {"command": RightMacro, "default": 8}
}
#Mode("generic")
def Run(macroname, macroargs=''):
#print ("macroargs", macroargs)
# Matrix button -> parameters sent to current Function in ModeCallback
if macroname.find("m") == 0:
doit = eval(ModeCallback)
doit((int(macroname[2]),int(macroname[1]), macroargs))
#eval(ModeCallback)((int(macroname[2]),int(macroname[1]), macroargs),)
# Otherwise do the macro
else:
doit = launchmacros[macroname]["command"]
if macroargs=='':
macroargs = launchmacros[macroname]["default"]
#print("Running", doit, "with args", macroargs )
doit(macroargs)
#ComputerUpdate(computer)
LoadMacros()
'''
Docs Community About
monome
osc : opensound control / serialosc protocol
what is serialosc? how does it work?
discovering and connecting to serialosc devices
serialosc server listens on port 12002.
when devices are connected, serialosc spawns new ports for each device. querying the server allows you to discover the port number for each device. (this supersedes the zeroconf method, which is still in place for legacy compatibility).
messages sent to serialosc server
/serialosc/list si <host> <port>
request a list of the currently connected devices, sent to host:port
/serialosc/notify si <host> <port>
request that next device change (connect/disconnect) is sent to host:port. to keep receiving the notifications, send another message to /serialosc/notify from the notify handler.
messages received from serialosc server
/serialosc/device ssi <id> <type> <port>
currently connected device id and type, at this port
/serialosc/add s <id>
device added
/serialosc/remove s <id>
device removed
to serialosc device
sys
these messages can be sent to a serialosc device to change settings.
/sys/port i <port>
change computer port
/sys/host s <host>
change computer host
/sys/prefix s <prefix>
change message prefix (filtering)
/sys/rotation i <degrees>
rotate the monome by degrees, where degrees is one of 0, 90, 180, 270. this replaces /cable
/sys/info si <host> <port>
/sys/info i <port>
/sys/info
info
request information (settings) about this device
/info can take the following arguments:
/info si <host> <port> (send /sys/info messages to host:port)
/info i <port> (send to localhost:port)
/info (send to current computer application's host:port)
example:
to serialosc:
/sys/info localhost 9999
from serialosc to localhost:9999:
/sys/id m0000045
/sys/size 8 16
/sys/host localhost
/sys/port 23849
/sys/prefix /nubs
/sys/rotation 270
from serialosc
these messages are sent from serialosc to the computer port.
the messages below are sent after a /sys/info request is received.
sys
/sys/port i report computer port
/sys/host s report computer host
/sys/id s report device id
/sys/prefix s report prefix
/sys/rotation i report grid device rotation
/sys/size ii report grid device size
to device
grid
/grid/led/set x y s
set led at (x,y) to state s (0 or 1).
/grid/led/all s
set all leds to state s (0 or 1).
/grid/led/map x_offset y_offset s[8]
Set a quad (8×8, 64 buttons) in a single message.
Each number in the list is a bitmask of the buttons in a row, one number in the list for each row. The message will fail if the list doesnt have 8 entries plus offsets.
taken apart:
(/grid/led/map) <- the message/route
(8 8) <- the offsets
(1 2 4 8 16 32 64 128) <- the bitmasks for each row
examples
/grid/led/map 0 0 4 4 4 4 8 8 8 8
/grid/led/map 0 0 254 253 125 247 239 36 191 4
Offsets must be mutliples of 8.
/grid/led/row x_offset y s[..]
Set a row in a quad in a single message.
Each number in the list is a bitmask of the buttons in a row, one number in the list for each row being updated.
examples (for 256)
/grid/led/row 0 0 255 255
/grid/led/row 8 5 255
examples (for 64)
/grid/led/row 0 0 232
/grid/led/row 0 3 129
Offsets must be mutliples of 8. Offsets for monome64 should always be zero.
/grid/led/col x y_offset s[..]
Set a column in a quad in a single message.
Each number in the list is a bitmask of the buttons in a column, one number in the list for each row being updated.
examples (for 256)
/grid/led/col 0 0 255 255 (updates quads 1 and 3)
/grid/led/col 13 8 255 (updates quad 4 due to offset.)
examples (for 64)
/grid/led/col 0 0 232
/grid/led/col 6 0 155
Offsets must be mutliples of 8. Offsets for monome64 should always be zero.
/grid/led/intensity i
variable brightness:
Valid values for l below are in the range [0, 15].
January 2011 devices only support four intensity levels (off + 3 brightness levels). The value passed in /level/ messages will be rounded down to the lowest available intensity as below:
[0, 3] - off
[4, 7] - low intensity
[8, 11] - medium intensity
[12, 15] - high intensity
June 2012 devices allow the full 16 intensity levels.
/grid/led/level/set x y l
/grid/led/level/all l
/grid/led/level/map x_off y_off l[64]
/grid/led/level/row x_off y l[..]
/grid/led/level/col x y_off l[..]
tilt
/tilt/set n s
set active state of tilt sensor n to s (0 or 1, 1 = active, 0 = inactive).
arc
led 0 is north. clockwise increases led number. These can be viewed and tested in the browser at http://nomeist.com/osc/arc/
/ring/set n x l
set led x (0-63) on encoder n (0-1 or 0-3) to level l (0-15)
/ring/all n l
set all leds on encoder n (0-1 or 0-3) to level l (0-15)
/ring/map n l[64]
set all leds on encoder n (0-1 or 0-3) to 64 member array l[64]
/ring/range n x1 x2 l
set leds on encoder n (0-1 or 0-3) between (inclusive) x1 and x3 to level l (0-15). direction of set is always clockwise, with wrapping.
from device
grid
/grid/key x y s
key state change at (x,y) to s (0 or 1, 1 = key down, 0 = key up).
tilt
/tilt n x y z
position change on tilt sensor n, integer (8-bit) values (x, y, z)
arc
/enc/delta n d
position change on encoder n by value d (signed). clockwise is positive.
/enc/key n s
key state change on encoder n to s (0 or 1, 1 = key down, 0 = key up)
Info@monome.org
'''

BIN
libs/launchpad.pyc Normal file

Binary file not shown.

359
libs/lj.py Normal file
View file

@ -0,0 +1,359 @@
# coding=UTF-8
'''
lj v0.7.5 for LJ v0.8+
Some LJ functions useful for python 2.7 clients
Functions and documentation here is low priority as python 2 support will stop soon.
Better code your plugin with python 3 and lj3.py.
Config
PolyLineOneColor
rPolyLineOneColor
Text
SendLJ : remote control
LjClient :
LjPl :
DrawPL
WebStatus
LICENCE : CC
Sam Neurohack
'''
import math
import redis
from OSC import OSCServer, OSCClient, OSCMessage
print "Importing lj from libs..."
#redisIP = '127.0.0.1'
#r = redis.StrictRedis(host=redisIP, port=6379, db=0)
ClientNumber = 0
name = "noname"
oscrun = True
point_list = []
pl = [[],[],[],[]]
def SendLJ(oscaddress,oscargs=''):
oscmsg = OSCMessage()
oscmsg.setAddress(oscaddress)
oscmsg.append(oscargs)
osclientlj = OSCClient()
osclientlj.connect((redisIP, 8002))
print "lj is 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
def WebStatus(message):
SendLJ("/status", message)
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 {
]
def Config(redIP,client,myname):
global ClientNumber, name, redisIP
redisIP = redIP
r = redis.StrictRedis(host=redisIP, port=6379, db=0)
ClientNumber = client
#print "client configured",ClientNumber
name = myname
print "Plugin declare its name",name
def LjClient(client):
global ClientNumber
ClientNumber = client
def LjPl(pl):
global PL
PL = pl
# Answer to LJ pings with /pong value
def OSCping(path, tags, args, source):
print name,"got /ping from LJ -> reply /pong", name
SendLJ("/pong",name)
# Closing plugin messages to LJ
def ClosePlugin():
WebStatus(name+" Exiting")
SendLJ("/"+name+"/start",0)
# /quit
def OSCquit(path, tags, args, source):
global oscrun
oscrun = False
print('lj got /quit for',name)
#WebStatus(name + " quit.")
#SendLJ("/"+name+"/start",0)
#print("Stopping OSC...")
#OSCstop()
#sys.exit()
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 = 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
# 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:
#print '/pl/'+str(ClientNumber)+'/'+str(PL), str(pl[PL])
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)
# Digits
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)
#dots.append(char_draw)

892
libs/lj23.py Normal file
View file

@ -0,0 +1,892 @@
# coding=UTF-8
'''
lj23 v0.7.6 for LJ v0.8+
Some LJ functions useful for python clients
Class management :
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
Config(redisIP, client number,name)
Basic Draw :
- PolyLineOneColor, rPolyLineOneColor, LineTo, Line
- PolyLineRGB, rPolyLineRGB, LineRGBTo, LineRGB
- rgb2int(r,g,b)
- DrawPL(point list number) : once you stacked all wanted elements, like 2 polylines, send them to lasers.
- DrawDests(): Draw all requested destinations for each PL.
High level draw :
- Text(word, integercolor, PL, 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
PL "Destinations" : tells Live what PL to draw and to what scene/Laser ("destination") to send it.
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
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(): /ping Answer to LJ pings by sending /pong name
OSCquit(): /quit Exit calling script using name in terminal
OSCadddest(): PL, scene, laser Add a destination
OSCdeldest(): PL, scene, lasers delete a destination
OSCobj(): /name/obj objectname attribute value for automation
OSCvar(): /name/var variablename value for automation
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 sys
import weakref
import struct
import numpy as np
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 = []
pl = [[],[],[],[]]
fft3Groups = [-1,-1,-1,-1]
Dests = dict()
oscIPresol = "127.0.0.1"
oscPORTresol = 7000
'''
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, PL , 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.PL = PL
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, PL , 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.PL = PL
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, PL , scene, laser):
self.name = name
self.number = number
self.active = active
self.PL = PL
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, PL , scene, laser):
self.name = name
self.number = number
self.active = active
self.PL = PL
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 ("Plugin declare its name",name)
#print pl
return r
def LjClient(client):
global ClientNumber
ClientNumber = client
def LjPl(pl):
global PL
PL = pl
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("lj23 in",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("lj 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 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():
print(name, "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('lj23 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("Default OSC Handler in",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, PL
# + 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 + "/obj", OSCobj)
oscserver.addMsgHandler( "/"+ name + "/var", OSCvar)
#
# Drawing basic functions
#
def rgb2int(r,g,b):
return int('0x%02x%02x%02x' % (r,g,b),0)
def LineTo(xy, c, PL):
pl[PL].append((xy + (c,)))
def rLineTo(xy, c, PL, xpos = 0, ypos =0, resize =0.7, rotx =0, roty =0 , rotz=0):
pl[PL].append((Pointransf(xy0, xpos, ypos, resize, rotx, roty, rotz) + (c,)))
def Line(xy1, xy2, c, PL):
LineTo(xy1, 0, PL)
LineTo(xy2, c , PL)
def rLine(xy1, xy2, c, PL, xpos = 0, ypos =0, resize =0.7, rotx =0, roty =0 , rotz=0):
rLineTo(xy1, 0, PL)
rLineTo(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 = 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
# 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 )
'''
def rLineTo(xy, c, PL, xpos = 0, ypos =0, resize =0.7, rotx =0, roty =0 , rotz=0):
pl[PL].append((Pointransf(xy0, xpos, ypos, resize, rotx, roty, rotz) + (c,)))
def rLine(xy1, xy2, c, PL, xpos = 0, ypos =0, resize =0.7, rotx =0, roty =0 , rotz=0):
LineTo(Pointransf(xy1, xpos, ypos, resize, rotx, roty, rotz),0, PL)
LineTo(Pointransf(xy2, xpos, ypos, resize, rotx, roty, rotz),c, PL)
# 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 LineRGBTo(xy, red, green, blue, PL):
LineTo(xy, int('0x%02x%02x%02x' % (red,green,blue),0), PL)
def LineRGB(xy1, xy2, red,green,blue, PL):
LineTo(xy1, 0, PL)
LineTo(xy2, int('0x%02x%02x%02x' % (red,green,blue),0) , PL)
def PolyLineRGB(xy_list, red, green, blue, PL , closed ):
PolyLineOneColor(xy_list, int('0x%02x%02x%02x' % (red,green,blue),0), PL , closed )
def rPolyLineRGB(xy_list, red, green, blue, PL , 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), PL , closed, xpos = 0, ypos =0, resize =0.7, rotx =0, roty =0 , rotz=0)
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:
#print '/pl/'+str(ClientNumber)+'/'+str(PL), str(pl[PL])
pl[PL] = []
return True
else:
return False
def ResetPL(self, PL):
pl[PL] = []
#
# "Destinations" management for PLs
#
# Add a destination for a given PL
def Addest(PL, scene, laser):
print (name,'adding',PL,scene,laser,'?')
if Findest(PL, scene, laser) == -1:
newdest = DestsObjects.counter + 1
Dest0 = lj.DestObject(str(newdest), newdest, True, PL , scene, laser)
print("New destination added with number", newdest)
else:
print("Destination already existed")
# OSC add a destination for a given PL
# /pluginame/dest PL, scene, laser
def OSCadddest(path, tags, args, source):
Addests(int(args[0]),int(args[1]),int(args[2]))
# Find PL destination with its parameters in destinations dictionnary
def Findest(PL, scene, laser):
print(name, 'searching PL,scene,laser',PL,scene,laser)
for item in DestObjects.getinstances():
#print(item)
if item.PL == PL 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] == PL and item[2] == scene and item[3] == laser:
#Dests.append(item[0])
return Dests[item[0]]
else:
return -1
'''
# Find and remove a PL destination with its parameters in destinations dictionnary
def Deldest(PL, scene, laser):
Destnumber = Findest(PL, scene, laser)
print(name,'deleting Destination PL, scene, laser', PL,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 PL
# /pluginame/deldests PL, scene, laser
def OSCdeldest(path, tags, args, source):
Deldests(args[0],args[1],args[2])
# Replace DrawPL if Destinations paradigm is implemented in plugin code
def DrawDests():
# Objects style
#print("DrawDest")
for destination in DestObject.getinstances():
#print (destination.name, destination.number, destination.active, destination.PL, destination.scene, destination.laser, pl[destination.PL] )
#print(Dests[str(destination)])
#print('/pl/'+str(Dests[str(destination)]["scene"])+'/'+str(Dests[str(destination)]["laser"]), ":", str(pl[Dests[str(destination)]["PL"]]))
#print(len(pl[destination.PL]))
if destination.active == True:
if r.set('/pl/'+str(destination.scene)+'/'+str(destination.laser), str(pl[destination.PL])) == True:
#print ('pl', destination.PL, '/pl/'+str(destination.scene)+'/'+str(destination.laser), str(pl[destination.PL]))
pass
else:
print('Redis key modification failed')
# Maybe one PL can be sent to multiple destination so they are all reset *after* all sending.
for pls in range(4):
pl[pls] = []
'''
# 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(pl[Dests[str(destination)]["PL"]]))
if r.set('/pl/'+str(Dests[str(destination)]["scene"])+'/'+str(Dests[str(destination)]["laser"]), str(pl[Dests[str(destination)]["PL"]])) == True:
#print '/pl/'+str(ClientNumber)+'/'+str(PL), str(pl[PL])
pass
else:
print('Redis key modification failed')
# Maybe one PL can be sent to multiple destination so they are all reset *after* all sending.
for destination in range(len(Dests)):
pl[Dests[str(destination)]["PL"]] = []
'''
'''
scenes = 4
def DrawDestsPL(PL):
for scene in range(scenes):
if Dests[laser]["scene"] != -1:
if r.set('/pl/'+str(Dests[laser]["scene"])+'/'+str(Dests[laser]["laser"]), str(pl[Dests[laser]["laser"]])) == True:
if r.set('/pl/'+str(ClientNumber)+'/'+str(PL), str(pl[PL])) == True:
#print '/pl/'+str(ClientNumber)+'/'+str(PL), str(pl[PL])
pl[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é
[(-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 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()
# 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_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)
#dots.append(char_draw)
def TextRGB(message,c, PL, xpos, ypos, resize, rotx, roty, rotz):
Text(message,int('0x%02x%02x%02x' % (red,green,blue),0), PL, xpos, ypos, resize, rotx, roty, rotz)

890
libs/lj23layers.py Normal file
View file

@ -0,0 +1,890 @@
# coding=UTF-8
'''
lj23layers v0.7.6 for LJ v0.8+
Some LJ functions useful for python clients
"layers" version : "PL" has been replaced by layer
Class management :
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
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
layer "Destinations" : tells Live what layer to draw and to what scene/Laser ("destination") to send it.
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
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 sys
import weakref
import struct
import numpy as np
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
'''
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 ("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("lj23 in",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("lj 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 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():
print(name, "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('lj23 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("Default OSC Handler in",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 + "/obj", OSCobj)
oscserver.addMsgHandler( "/"+ name + "/var", OSCvar)
#
# Drawing basic functions
#
def rgb2int(r,g,b):
return int('0x%02x%02x%02x' % (r,g,b),0)
def LineTo(xy, c, layer ):
layers[layer].append((xy + (c,)))
def rLineTo(xy, c, layer , xpos = 0, ypos =0, resize =0.7, rotx =0, roty =0 , rotz=0):
layers[layer ].append((Pointransf(xy0, xpos, ypos, resize, rotx, roty, rotz) + (c,)))
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 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 )
# 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 = 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
# 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 )
'''
def rLineTo(xy, c, layer , xpos = 0, ypos =0, resize =0.7, rotx =0, roty =0 , rotz=0):
layers[layer ].append((Pointransf(xy0, xpos, ypos, resize, rotx, roty, rotz) + (c,)))
def rLine(xy1, xy2, c, layer , xpos = 0, ypos =0, resize =0.7, rotx =0, roty =0 , rotz=0):
LineTo(Pointransf(xy1, xpos, ypos, resize, rotx, roty, rotz),0, layer )
LineTo(Pointransf(xy2, xpos, ypos, resize, rotx, roty, rotz),c, layer )
# 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:
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 LineRGBTo(xy, red, green, blue, layer ):
LineTo(xy, int('0x%02x%02x%02x' % (red,green,blue),0), 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 )
def PolyLineRGB(xy_list, red, green, blue, layer , closed ):
PolyLineOneColor(xy_list, int('0x%02x%02x%02x' % (red,green,blue),0), layer , closed )
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)
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])
# Replace Drawlayer if Destinations paradigm is implemented in plugin code
def DrawDests():
# Objects style
#print("DrawDest")
for destination in DestObject.getinstances():
#print (destination.name, destination.number, destination.active, destination.layer, destination.scene, destination.laser, layers[destination.layer] )
#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é
[(-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 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]
else:
char_layer_list = ASCII_GRAPHICS[ord(ch) - 46]
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)

657
libs/lj3.py Normal file
View file

@ -0,0 +1,657 @@
'''
lj3 v0.7.5 for LJ v0.8+
Some LJ functions useful for python clients
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)

669
libs/maxwell.json Normal file
View file

@ -0,0 +1,669 @@
{
"ccs": [
{
"_comment": "Oscillator LEFT X Functions",
"Function": "/osc/left/X/curvetype",
"init": "sin"
},
{
"Function": "/osc/left/X/freq",
"init": "1"
},
{
"Function": "/osc/left/X/freqlimit",
"init": "127"
},
{
"Function": "/osc/left/X/amp",
"init": "100"
},
{
"Function": "/osc/left/X/amplimit",
"init": "constant"
},
{
"Function": "/osc/left/X/phasemod",
"init": "64"
},
{
"Function": "/osc/left/X/phasemodlimit",
"init": "linear"
},
{
"Function": "/osc/left/X/phaseoffset",
"init": "64"
},
{
"Function": "/osc/left/X/phaseoffsetlimit",
"init": "manual"
},
{
"Function": "/osc/left/X/ampoffset",
"init": "64"
},
{
"Function": "/osc/left/X/ampoffsetlimit",
"init": "manual"
},
{
"Function": "/osc/left/X/inversion",
"init": "off"
},
{
"_comment": "Oscillator LEFT Y Functions",
"Function": "/osc/left/Y/curvetype",
"init": "sin"
},
{
"Function": "/osc/left/Y/freq",
"init": "1"
},
{
"Function": "/osc/left/Y/freqlimit",
"init": "127"
},
{
"Function": "/osc/left/Y/amp",
"init": "100"
},
{
"Function": "/osc/left/Y/amplimit",
"init": "constant"
},
{
"Function": "/osc/left/Y/phasemod",
"init": "64"
},
{
"Function": "/osc/left/Y/phasemodlimit",
"init": "linear"
},
{
"Function": "/osc/left/Y/phaseoffset",
"init": "64"
},
{
"Function": "/osc/left/Y/phaseoffsetlimit",
"init": "manual"
},
{
"Function": "/osc/left/Y/ampoffset",
"init": "64"
},
{
"Function": "/osc/left/Y/ampoffsetlimit",
"init": "manual"
},
{
"Function": "/osc/left/Y/inversion",
"init": "off"
},
{
"_comment": "Oscillator LEFT Z Functions",
"Function": "/osc/left/Z/curvetype",
"init": "sin"
},
{
"Function": "/osc/left/Z/freq",
"init": "1"
},
{
"Function": "/osc/left/Z/freqlimit",
"init": "127"
},
{
"Function": "/osc/left/Z/amp",
"init": "100"
},
{
"Function": "/osc/left/Z/amplimit",
"init": "constant"
},
{
"Function": "/osc/left/Z/phasemod",
"init": "64"
},
{
"Function": "/osc/left/Z/phasemodlimit",
"init": "linear"
},
{
"Function": "/osc/left/Z/phaseoffset",
"init": "64"
},
{
"Function": "/osc/left/Z/phaseoffsetlimit",
"init": "manual"
},
{
"Function": "/osc/left/Z/ampoffset",
"init": "64"
},
{
"Function": "/osc/left/Z/ampoffsetlimit",
"init": "manual"
},
{
"Function": "/osc/left/Z/inversion",
"init": "off"
},
{
"_comment": "Oscillator RIGHT X Functions",
"Function": "/osc/right/X/curvetype",
"init": "sin"
},
{
"Function": "/osc/right/X/freq",
"init": "1"
},
{
"Function": "/osc/right/X/freqlimit",
"init": "127"
},
{
"Function": "/osc/right/X/amp",
"init": "100"
},
{
"Function": "/osc/right/X/amplimit",
"init": "constant"
},
{
"Function": "/osc/right/X/phasemod",
"init": "64"
},
{
"Function": "/osc/right/X/phasemodlimit",
"init": "linear"
},
{
"Function": "/osc/right/X/phaseoffset",
"init": "64"
},
{
"Function": "/osc/right/X/phaseoffsetlimit",
"init": "manual"
},
{
"Function": "/osc/right/X/ampoffset",
"init": "64"
},
{
"Function": "/osc/right/X/ampoffsetlimit",
"init": "manual"
},
{
"Function": "/osc/right/X/inversion",
"init": "off"
},
{
"_comment": "Oscillator RIGHT Y Functions",
"Function": "/osc/right/Y/curvetype",
"init": "sin"
},
{
"Function": "/osc/right/Y/freq",
"init": "1"
},
{
"Function": "/osc/right/Y/freqlimit",
"init": "127"
},
{
"Function": "/osc/right/Y/amp",
"init": "100"
},
{
"Function": "/osc/right/Y/amplimit",
"init": "constant"
},
{
"Function": "/osc/right/Y/phasemod",
"init": "64"
},
{
"Function": "/osc/right/Y/phasemodlimit",
"init": "linear"
},
{
"Function": "/osc/right/Y/phaseoffset",
"init": "64"
},
{
"Function": "/osc/right/Y/phaseoffsetlimit",
"init": "manual"
},
{
"Function": "/osc/right/Y/ampoffset",
"init": "64"
},
{
"Function": "/osc/right/Y/ampoffsetlimit",
"init": "manual"
},
{
"Function": "/osc/right/Y/inversion",
"init": "off"
},
{
"_comment": "Oscillator RIGHT Z Functions",
"Function": "/osc/right/Z/curvetype",
"init": "sin"
},
{
"Function": "/osc/right/Z/freq",
"init": "1"
},
{
"Function": "/osc/right/Z/freqlimit",
"init": "127"
},
{
"Function": "/osc/right/Z/amp",
"init": "100"
},
{
"Function": "/osc/right/Z/amplimit",
"init": "constant"
},
{
"Function": "/osc/right/Z/phasemod",
"init": "64"
},
{
"Function": "/osc/right/Z/phasemodlimit",
"init": "linear"
},
{
"Function": "/osc/right/Z/phaseoffset",
"init": "64"
},
{
"Function": "/osc/right/Z/phaseoffsetlimit",
"init": "manual"
},
{
"Function": "/osc/right/Z/ampoffset",
"init": "64"
},
{
"Function": "/osc/right/Z/ampoffsetlimit",
"init": "manual"
},
{
"Function": "/osc/right/Z/inversion",
"init": "off"
},
{
"_comment": "LFO 1 Functions",
"Function": "/lfo/1/curvetype",
"init": "sin"
},
{
"Function": "/lfo/1/freq",
"init": "64"
},
{
"Function": "/lfo/1/freqlimit",
"init": "127"
},
{
"Function": "/lfo/1/phase",
"init": "64"
},
{
"Function": "/lfo/1/inversion",
"init": "off"
},
{
"_comment": "LFO 2 Functions",
"Function": "/lfo/2/curvetype",
"init": "sin"
},
{
"Function": "/lfo/2/freq",
"init": "64"
},
{
"Function": "/lfo/2/freqlimit",
"init": "127"
},
{
"Function": "/lfo/2/phase",
"init": "64"
},
{
"Function": "/lfo/2/inversion",
"init": "off"
},
{
"_comment": "LFO 3 Functions",
"Function": "/lfo/3/curvetype",
"init": "sin"
},
{
"Function": "/lfo/3/freq",
"init": "64"
},
{
"Function": "/lfo/3/freqlimit",
"init": "127"
},
{
"Function": "/lfo/3/phase",
"init": "64"
},
{
"Function": "/lfo/3/inversion",
"init": "off"
},
{
"_comment": "Duplicator Functions",
"Function": "/duplicator/num",
"init": "0"
},
{
"Function": "/duplicator/offset",
"init": "0"
},
{
"_comment": "Mixer Functions",
"Function": "/mixer/operation",
"init": "+"
},
{
"Function": "/mixer/value",
"init": "0"
},
{
"_comment": "Intensity Functions",
"Function": "/intensity/mod",
"init": "0"
},
{
"Function": "/intensity/freq",
"init": "0"
},
{
"_comment": "Scaler Functions",
"Function": "/scaler/curvetype",
"init": "sin"
},
{
"Function": "/scaler/speed",
"init": "64"
},
{
"Function": "/scaler/switch",
"init": "off"
},
{
"Function": "/scaler/width",
"init": "64"
},
{
"Function": "/scaler/amt",
"init": "64"
},
{
"Function": "/scaler/scale",
"init": "64"
},
{
"_comment": "Rotator X Functions",
"Function": "/rotator/X/curvetype",
"init": "sin"
},
{
"Function": "/rotator/X/speed",
"init": "64"
},
{
"Function": "/rotator/X/lfo/switch",
"init": "off"
},
{
"Function": "/rotator/X/direct",
"init": "64"
},
{
"_comment": "Rotator Y Functions",
"Function": "/rotator/Y/curvetype",
"init": "sin"
},
{
"Function": "/rotator/Y/speed",
"init": "64"
},
{
"Function": "/rotator/Y/lfo/switch",
"init": "off"
},
{
"Function": "/rotator/Y/direct",
"init": "64"
},
{
"_comment": "Rotator Z Functions",
"Function": "/rotator/Z/curvetype",
"init": "sin"
},
{
"Function": "/rotator/Z/speed",
"init": "64"
},
{
"Function": "/rotator/Z/lfo/switch",
"init": "off"
},
{
"Function": "/rotator/Z/direct",
"init": "64"
},
{
"_comment": "Translator X Functions",
"Function": "/translator/X/curvetype",
"init": "sin"
},
{
"Function": "/translator/X/speed",
"init": "64"
},
{
"Function": "/translator/X/lfo/switch",
"init": "off"
},
{
"Function": "/translator/X/amt",
"init": "64"
},
{
"_comment": "Translator Y Functions",
"Function": "/translator/Y/curvetype",
"init": "sin"
},
{
"Function": "/translator/Y/speed",
"init": "64"
},
{
"Function": "/translator/Y/lfo/switch",
"init": "off"
},
{
"Function": "/translator/Y/amt",
"init": "64"
},
{
"_comment": "Translator Z Functions",
"Function": "/translator/Z/curvetype",
"init": "sin"
},
{
"Function": "/translator/Z/speed",
"init": "64"
},
{
"Function": "/translator/Z/lfo/switch",
"init": "off"
},
{
"Function": "/translator/Z/amt",
"init": "64"
},
{
"_comment": "Colors Functions",
"Function": "/color/colortype",
"init": "solid"
},
{
"Function": "/color/huewidth",
"init": "0"
},
{
"Function": "/color/hueoff",
"init": "0"
},
{
"Function": "/color/huemod",
"init": "0"
},
{
"Function": "/color/huerot",
"init": "0"
},
{
"Function": "/color/intwidth",
"init": "0"
},
{
"Function": "/color/intoff",
"init": "0"
},
{
"Function": "/color/intmod",
"init": "0"
},
{
"Function": "/color/intfreq",
"init": "0"
},
{
"Function": "/color/satwidth",
"init": "0"
},
{
"Function": "/color/satmod",
"init": "0"
},
{
"Function": "/color/saturation",
"init": "127"
},
{
"Function": "/color/modtype",
"init": "sin"
}
]
}

452
libs/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)

BIN
libs/midi.pyc Normal file

Binary file not shown.

760
libs/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("MidiMsg", midimsg, "Dest", mididest)
for port in range(MidInsNumber):
##print("port",port,"midiname", midiname[port])
# To mididest
if midiname[port].find(mididest) != -1:
#print("sending to name", midiname[port],midimsg)
midiport[port].send_message(midimsg)
# To All
elif mididest == "all" and midiname[port].find(mididest) == -1 and midiname[port].find(BhorealMidiName) == -1 and midiname[port].find(LaunchMidiName) == -1:
#print("all sending to port",port,"name", midiname[port])
midiport[port].send_message(midimsg)
for OSCtarget in midi2OSC:
if (OSCtarget == mididest or mididest == 'all') and midi2OSC[OSCtarget]["msgs"]:
OSCsend(OSCtarget, "/cc", [midimsg[1], midimsg[2]])
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)
# Webstatus(''.join(("note ",msg[1]," to ",msg[2])))
# Note Off
if msg[0]==NOTE_OFF:
print("from", portname,"noteoff")
NoteOff(msg[1],msg[2], mididest)
# Webstatus(''.join(("note ",msg[1]," to ",msg[2])))
# Midi CC message
if msg[0] == CONTROLLER_CHANGE:
print("from", portname,"CC :", msg[1], msg[2])
'''
Webstatus("CC :" + str(msg[1]) + " " + str(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 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)

212
libs/plugins.py Normal file
View file

@ -0,0 +1,212 @@
#!/usr/bin/python2.7
# -*- coding: utf-8 -*-
# -*- mode: Python -*-
'''
LJ Laser Server v0.8.1
Plugins Handler.
'''
from OSC import OSCServer, OSCClient, OSCMessage
from websocket_server import WebsocketServer
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")
# 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)
# 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
PluginProcess = subprocess.Popen([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
def Ping(name):
sendWSall("/"+ name + "/start 0")
return OSCsend(name,"/ping",1)
#return True
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")
'''
# 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]))
return True
# Plugin not online..
else:
if gstt.debug >0:
print "Plugin manager send says plugin " + name + " is offline."
#print "Command", oscpath
sendWSall("/status 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

BIN
libs/plugins.pyc Normal file

Binary file not shown.

803
libs/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')

77
libs/settings.py Normal file
View file

@ -0,0 +1,77 @@
#!/usr/bin/python2.7
# -*- coding: utf-8 -*-
# -*- mode: Python -*-
'''
LJay/LJ
v0.7.0
Settings Handler
LICENCE : CC
'''
import ConfigParser
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', 'bhoroscip', str(gstt.oscIPin))
config.set('General', 'nozoscip', str(gstt.nozoscIP))
config.set('General', 'debug', str(gstt.debug))
for i in range(gstt.LaserNumber):
laser = 'laser' + str(i)
config.set(laser, 'ip', str(gstt.lasersIPS[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.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'))
print ""
for i in range(4):
laser = 'laser' + str(i)
gstt.lasersIPS[i]= config.get(laser, 'ip')
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.warpdest[i]= np.array(ast.literal_eval(config.get(laser, 'warpdest')))
print "* Reading", gstt.ConfigName, "setup file.*"
config = ConfigParser.ConfigParser()
config.read(gstt.ConfigName)

BIN
libs/settings.pyc Normal file

Binary file not shown.

467
libs/tracer.py Normal file
View file

@ -0,0 +1,467 @@
#!/usr/bin/python2.7
# -*- coding: utf-8 -*-
# -*- mode: Python -*-
'''
LJay/LJ v0.8.0
tracer.py (was newdacp.py)
Enhanced version (support for several lasers) of the etherdream python library from j4cDAC.
LICENCE : CC
Sam Neurohack, pclf
Conversion in etherdream coordinates, geometric corrections,...
Init call with a laser number and which point list to draw. Etherdream IP is found in conf file for given laser number
Uses redis keys value for live inputs/outputs
These redis keys are read and set at each main loop.
Redis keys pulled to draw things :
/order select some change to adjust
/pl/lasernumber [(x,y,color),(x1,y1,color),...] A string of list of pygame 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
Etherdream status reports in redis keys:
/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.
Geometric corrections :
Doctodo
'''
import socket
import time
import struct
#from gstt import debug
import gstt
import math
from itertools import cycle
#from globalVars import *
import pdb
import ast
import redis
import homographyp
import numpy as np
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)
def pack_point(x, y, r, g, b, i = -1, u1 = 0, u2 = 0, flags = 0):
"""Pack some color values into a struct dac_point.
Values must be specified for x, y, r, g, and b. If a value is not
passed in for the other fields, i will default to max(r, g, b); the
rest default to zero.
"""
if i < 0:
i = max(r, g, b)
return struct.pack("<HhhHHHHHH", flags, x, 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 debug == 2:
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)
]
for l in lines:
print prefix + l
if 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 xrange(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 = [self.newstream.next() for i in xrange(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
#print ''
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]
#print "laser response", self.mylaser, response
gstt.lstt_dacanswers[self.mylaser] = response
cmdR = 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 != "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)
#print "init"
self.mylaser = mylaser
self.clientkey = r.get("/clientkey")
#print "Laser",self.mylaser,"Got clientkey", self.clientkey
#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))
#print "Connection status for", self.mylaser,":", 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 = ""
# Upper case PL is the Point List number
self.PL = PL
# Lower case pl is the actual point list coordinates
self.pl = ast.literal_eval(r.get(self.clientkey + str(self.mylaser)))
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))))
#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.newstream = self.OnePoint()
if gstt.debug >0:
print "Init laser",self.mylaser,"asked for ckey", self.clientkey+str(self.mylaser)
if self.connstatus != 0:
#print ""
print "Connection ERROR",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", self.mylaser,":", 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", lwm, rate)
#print "Begin newdac : Laser ", str(self.mylaser), " PL : ", str(self.PL)
self.conn.sendall(cmd)
return self.readresp("b")
def update(self, lwm, rate):
cmd = struct.pack("<cHI", "u", lwm, rate)
self.conn.sendall(cmd)
return self.readresp("u")
def encode_point(self, point):
return pack_point(*point)
def write(self, points):
epoints = map(self.encode_point, points)
cmd = struct.pack("<cH", "d", len(epoints))
self.conn.sendall(cmd + "".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)))
if order == 0:
# USER point list
self.pl = ast.literal_eval(r.get(self.clientkey+str(self.mylaser)))
#print "laser", self.mylaser, " order 0 : pl : ",len(self.pl)
else:
# Get the new EDH
if order == 1:
print "Laser",self.mylaser,"new EDH ORDER in redis"
gstt.EDH[self.mylaser]= np.array(ast.literal_eval(r.get('/EDH/'+str(self.mylaser))))
# Back to user point list
r.set('/order/'+str(self.mylaser), 0)
# BLACK point list
if order == 2:
print "Laser",self.mylaser,"BLACK ORDER in redis"
self.pl = black_points
# GRID point list
if order == 3:
print "Laser",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)))
print "tracer resetting lsteps for", self.mylaser, ":",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 "Laser",self.mylaser,"new clientkey",
self.clientkey = r.get('/clientkey')
# Back to user point list order
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 "starting laser", self.mylaser, "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
'''

BIN
libs/tracer.pyc Normal file

Binary file not shown.