|
|
|
@ -7,16 +7,20 @@ import logging |
|
|
|
from pprint import pprint |
|
|
|
from pprint import pprint |
|
|
|
import argparse |
|
|
|
import argparse |
|
|
|
import colorsys |
|
|
|
import colorsys |
|
|
|
|
|
|
|
import gpiozero |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# Set up basic vars and logging |
|
|
|
logging.basicConfig(format='%(asctime)s - %(threadName)s ø %(name)s - ' |
|
|
|
logging.basicConfig(format='%(asctime)s - %(threadName)s ø %(name)s - ' |
|
|
|
'%(levelname)s - %(message)s') |
|
|
|
'%(levelname)s - %(message)s') |
|
|
|
log = logging.getLogger("osc") |
|
|
|
log = logging.getLogger("osc") |
|
|
|
log.setLevel(logging.INFO) |
|
|
|
log.setLevel(logging.INFO) |
|
|
|
clientname="ledserver" |
|
|
|
clientname="ledserver" |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# Get the normalised RGB values for the HSV (i.e, the colour float designating hue from OSC) |
|
|
|
def hsv2rgb(h,s,v): |
|
|
|
def hsv2rgb(h,s,v): |
|
|
|
return tuple(round(i*255) for i in colorsys.hsv_to_rgb(h,s,v)) |
|
|
|
return tuple(round(i*255) for i in colorsys.hsv_to_rgb(h,s,v)) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# Some little helper methods to set colours in the terminal output |
|
|
|
def termrgb(rgb): |
|
|
|
def termrgb(rgb): |
|
|
|
return "\033[38;2;{};{};{}m".format(rgb[0],rgb[1],rgb[2]) |
|
|
|
return "\033[38;2;{};{};{}m".format(rgb[0],rgb[1],rgb[2]) |
|
|
|
def termendc(): |
|
|
|
def termendc(): |
|
|
|
@ -25,80 +29,113 @@ def output_led_colour(rgb): |
|
|
|
return termrgb(rgb)+"█"+termendc() |
|
|
|
return termrgb(rgb)+"█"+termendc() |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# Our main class, the LED |
|
|
|
|
|
|
|
|
|
|
|
class LED: |
|
|
|
class LED: |
|
|
|
|
|
|
|
# These should be largely self-explanatory |
|
|
|
|
|
|
|
# Float, from OSC FX param 2 |
|
|
|
brightness=0 |
|
|
|
brightness=0 |
|
|
|
|
|
|
|
# RGB array, currently as int 0-255, but will probably end up float 0-1.0, converted from hue |
|
|
|
colour=[0,0,0] |
|
|
|
colour=[0,0,0] |
|
|
|
|
|
|
|
#Float, from OSC FX param 1 |
|
|
|
hue=0 |
|
|
|
hue=0 |
|
|
|
state=True |
|
|
|
# Bool, Intial state, off. Should always align to whether the LED is on or off, brightness being >0 |
|
|
|
|
|
|
|
state=False |
|
|
|
|
|
|
|
# The pin number of the LED |
|
|
|
pin=None |
|
|
|
pin=None |
|
|
|
mode=["off","on"] |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# List for logging state |
|
|
|
|
|
|
|
mode=["off","on"] |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# Initialize the object, set up GPIO |
|
|
|
def __init__(self,pin): |
|
|
|
def __init__(self,pin): |
|
|
|
self.pin=pin |
|
|
|
self.pin=pin |
|
|
|
|
|
|
|
self.led=gpiozero.PWMLED(pin) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# Set the brightness, this only triggers on a delta threshold so that we don't go crazy |
|
|
|
def set_brightness(self,value): |
|
|
|
def set_brightness(self,value): |
|
|
|
if abs(self.brightness-value) > .005: |
|
|
|
if abs(self.brightness-value) > .005: |
|
|
|
self.brightness=value |
|
|
|
self.brightness=value |
|
|
|
|
|
|
|
self.led.value=self.state*self.brightness |
|
|
|
print("Setting brightness for LED on pin {} to {}".format(self.pin, self.brightness)) |
|
|
|
print("Setting brightness for LED on pin {} to {}".format(self.pin, self.brightness)) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# Set the colour, only on a delta threshold |
|
|
|
def set_colour(self,value): |
|
|
|
def set_colour(self,value): |
|
|
|
if abs(self.hue-value) > .01: |
|
|
|
if abs(self.hue-value) > .01: |
|
|
|
self.colour=hsv2rgb(value,1,1) |
|
|
|
self.colour=hsv2rgb(value,1,1) |
|
|
|
self.hue=value |
|
|
|
self.hue=value |
|
|
|
log.info("Setting colour for LED on pin {} to {} ({})".format(self.pin,self.colour,output_led_colour(self.colour))) |
|
|
|
log.info("Setting colour for LED on pin {} to {} ({})".format(self.pin,self.colour,output_led_colour(self.colour))) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# Set an absolute state, on or off. Brightness is set regardless, so we can turn it on with 40% brightness if desired |
|
|
|
def set_state(self,value): |
|
|
|
def set_state(self,value): |
|
|
|
self.state=value |
|
|
|
self.state=value |
|
|
|
|
|
|
|
self.led.value=self.state*self.brightness |
|
|
|
log.info("Turning LED {} {}".format(self.pin, self.mode[self.state])) |
|
|
|
log.info("Turning LED {} {}".format(self.pin, self.mode[self.state])) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# Toggle on or off, edge detecting. I.e, send True to turn on, send True again to turn off. False is ignored |
|
|
|
def toggle_state(self,value): |
|
|
|
def toggle_state(self,value): |
|
|
|
if (value): |
|
|
|
if (value): |
|
|
|
self.state=not self.state |
|
|
|
self.state=not self.state |
|
|
|
|
|
|
|
self.led.value=self.state*self.brightness |
|
|
|
log.info("Turning LED {} {}".format(self.pin, self.mode[self.state])) |
|
|
|
log.info("Turning LED {} {}".format(self.pin, self.mode[self.state])) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# Set the colour of the LEDs. This only does the first LED, but when the REAPER controls are finalised, it'll be more controllable |
|
|
|
def led_colour(value): |
|
|
|
def led_colour(value): |
|
|
|
leds[0].set_colour(value) |
|
|
|
leds[0].set_colour(value) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# Set the brightness of all LEDs. As above, when REAPER is finalised it'll be controllable (or everything, as required) |
|
|
|
def led_brightness(value): |
|
|
|
def led_brightness(value): |
|
|
|
leds[0].set_brightness(value) |
|
|
|
for led in leds: |
|
|
|
|
|
|
|
led.set_brightness(value) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# Turn an individual LED on or off based on FX param 3+ |
|
|
|
def led_state(addr,value): |
|
|
|
def led_state(addr,value): |
|
|
|
led=addr.split("/")[6] |
|
|
|
led=addr.split("/")[6] |
|
|
|
leds[int(led)-3].toggle_state(bool(value)) |
|
|
|
leds[int(led)-3].toggle_state(bool(value)) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# Set up command line arguments |
|
|
|
parser = argparse.ArgumentParser(description='OSC listener for LEDs') |
|
|
|
parser = argparse.ArgumentParser(description='OSC listener for LEDs') |
|
|
|
parser.add_argument('--listen',action='store',default='127.0.0.1:9000') |
|
|
|
parser.add_argument('--listen',action='store',default='127.0.0.1:9000',help="Listen on a specific IP/port. Defaults to 127.0.0,.1:9000") |
|
|
|
parser.add_argument('--debug-osc',action='store_true',default=False) |
|
|
|
parser.add_argument('--debug-osc',action='store_true',default=False,help="Debug OSC messages. Very verbose") |
|
|
|
args=parser.parse_args() |
|
|
|
args=parser.parse_args() |
|
|
|
|
|
|
|
|
|
|
|
# Start the system. |
|
|
|
# Start the system. |
|
|
|
log.info("Starting OSC...") |
|
|
|
log.info("Starting OSC...") |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
pprint(args) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# Log OSC stuff if requested |
|
|
|
if args.debug_osc: |
|
|
|
if args.debug_osc: |
|
|
|
osc_startup(logger=log) |
|
|
|
osc_startup(logger=log) |
|
|
|
else: |
|
|
|
else: |
|
|
|
osc_startup() |
|
|
|
osc_startup() |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# Split up port/address from command line |
|
|
|
listen_address=args.listen.split(':')[0] |
|
|
|
listen_address=args.listen.split(':')[0] |
|
|
|
listen_port=args.listen.split(':')[1] |
|
|
|
listen_port=args.listen.split(':')[1] |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# Start up the OSC server |
|
|
|
osc_udp_server(listen_address,listen_port, clientname) |
|
|
|
osc_udp_server(listen_address,listen_port, clientname) |
|
|
|
|
|
|
|
|
|
|
|
# Set up osc paths |
|
|
|
# Set up osc paths |
|
|
|
|
|
|
|
# Each FX param has a callback function that is triggered whenever a message for that path is received |
|
|
|
log.info("Binding targets...") |
|
|
|
log.info("Binding targets...") |
|
|
|
osc_method("/track/2/fx/1/fxparam/1/value",led_colour) |
|
|
|
osc_method("/track/2/fx/1/fxparam/1/value",led_colour) |
|
|
|
osc_method("/track/2/fx/1/fxparam/2/value",led_brightness) |
|
|
|
osc_method("/track/2/fx/1/fxparam/2/value",led_brightness) |
|
|
|
osc_method("/track/2/fx/1/fxparam/[!12]/value",led_state,argscheme=OSCARG_ADDRESS+OSCARG_DATAUNPACK) |
|
|
|
osc_method("/track/2/fx/1/fxparam/[!12]/value",led_state,argscheme=OSCARG_ADDRESS+OSCARG_DATAUNPACK) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
log.info("Creating LEDs...") |
|
|
|
log.info("Creating LEDs...") |
|
|
|
|
|
|
|
|
|
|
|
# Create LEDs |
|
|
|
# Create LEDs |
|
|
|
leds=[LED(i) for i in range(0,4)] |
|
|
|
leds=[LED(i) for i in [16,20,21]] |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# Do a massive loop listening for messages. This should really be cleaner... |
|
|
|
finished=False |
|
|
|
finished=False |
|
|
|
log.info("Starting loop") |
|
|
|
log.info("Starting loop") |
|
|
|
while not finished: |
|
|
|
while not finished: |
|
|
|
|