Last active
August 11, 2017 21:46
-
-
Save rbnpi/5ec34a5fda41da67caf1c850ce96efb7 to your computer and use it in GitHub Desktop.
Sonic PI3 drives a pi-topPULSE display, using an intermediary python oscserver to receive and process OSC messages. Full article at https://rbnrpi.wordpress.com/pi-toppulse-controlled-by-sonic-pi-3/ and video at https://youtu.be/z6Ykx2lI2Dk
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#oscserver to communicate with Sonic Pi | |
#messages received control the leds on an attached pi-topPULSE | |
#run with: python3 oscserver.py | |
#or to use with a remote Sonic Pi computer use ip address of THIS PI | |
#instead of "localhost" 127.0.0.1 eg: | |
#python3 oscserver.py --ip <ip-address-of-his-raspberry-pi> | |
import argparse | |
from time import sleep | |
from ptpulse import ledmatrix | |
from pythonosc import dispatcher | |
from pythonosc import osc_server | |
delta = 0.04 #timing correction for RPi3 to sync leds and sound with PULSE | |
def handle_led(unused_addr,args, x,y,r,g,b): | |
sleep(delta) | |
print("Set Pixel",x,y,"to",r,g,b) | |
ledmatrix.set_pixel(x,y,r,g,b) | |
ledmatrix.show() | |
def handle_clear(unused_addr): | |
sleep(delta) | |
print("Clearing Leds") | |
ledmatrix.off() | |
def handle_setall(unused_addr,args, r,g,b): | |
sleep(delta) | |
print("Set All Leds",r,g,b) | |
ledmatrix.set_all(r,g,b) | |
ledmatrix.show() | |
def handle_bright(unused_addr,args, b): | |
sleep(delta) | |
print("Set Brightness %4.2f" % (b)) #adjust to 2 decimal places | |
ledmatrix.brightness(b) | |
if __name__ == "__main__": | |
parser = argparse.ArgumentParser() | |
parser.add_argument("--ip", | |
default="127.0.0.1", help="The ip to listen on") | |
parser.add_argument("--port", | |
type=int, default=8000, help="The port to listen on") | |
args = parser.parse_args() | |
dispatcher = dispatcher.Dispatcher() | |
dispatcher.map("/pulse/xyrgb",handle_led,"x","y","r","g","b") | |
dispatcher.map("/pulse/clear",handle_clear) | |
dispatcher.map("/pulse/setall",handle_setall,"r","g","b") | |
dispatcher.map("/pulse/bright",handle_bright,"b") | |
server = osc_server.ThreadingOSCUDPServer( | |
(args.ip, args.port), dispatcher) | |
print("Serving on {}".format(server.server_address)) | |
server.serve_forever() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#Sonic Pi3 PULSE-leds workout by Robin Newman, August 2017 | |
#requires the python script osc-server.py to be running to communicate with PULSE-leds | |
#This requires the python library python-osc to be loaded | |
#Which can be done with the command: sudo pip3 install python-osc | |
#Then run the server with python3 oscserver.py | |
#Finally start this program running in Sonic Pi3 | |
#Each vertical column of LEDS corresponds to notes with a different synth | |
#The higher the pitch of the note, the higher up the column is the illuminated LED | |
#Every so often the LEDS are "cleared" to a different background colour | |
#The LEDs are controlled by the oscserver program, which receives OSC commands | |
#sent from Sonic Pi to tell it what to do. | |
#There are four such commands | |
#"/pulse/clear" which clear leds to current RGB colour | |
#"/pulse/xyrgb" which sends x,y and r,g,b values | |
#"/pulse/setall" which sets all LEDs to the r,g,b values specified | |
#"/pulse/bright" which sends the brightness value b to be used. | |
#All of these are sent to "localhost" at port 8000 which is where the python oscserver is listening | |
use_osc_logging true #show osc messages in the log | |
use_random_seed 2017 #change for different patterns | |
use_osc "localhost",8000 | |
#use_real_time | |
osc "/pulse/clear" | |
sleep 0.004 #make sure LEDs cleared | |
load_sample :drum_bass_hard | |
load_sample :drum_snare_hard | |
load_sample :drum_cymbal_closed | |
set :stopflag, false | |
at [0,20,40,60],[1.0,0.5,0.25,0.125] do |delay| | |
set :t,delay | |
end | |
at 85 do | |
set :stopflag, true | |
end | |
live_loop :pulse do | |
#use_real_time | |
r=rrand_i(40,255) | |
g=rrand_i(40,255) | |
b=rrand_i(40,255) | |
a=(r+g+b)/200 - 1 #nb integer division here | |
bright=[0.6,0.8,1][a] | |
osc "/pulse/bright",bright | |
sleep 0.004 #make sure brightness sent first | |
x=rand_i(7);y=rand_i(7) | |
use_synth [:saw,:tri,:mod_pulse,:piano,:prophet,:pulse,:pluck][x] | |
tick | |
play 48+y*2,amp: (a+1).to_f/3,release: get(:t) if look%20!=0 or look == 0 | |
osc "/pulse/xyrgb",x,y,r,g,b if look%20!=0 or look == 0 | |
osc "/pulse/setall",r,g,b if look%20==0 and look > 0 | |
sleep get(:t)-0.004 #compensate for brighness delay | |
osc "/pulse/clear" if look%80 == 0 and look > 0 | |
if get(:stopflag) == true | |
osc "/pulse/clear" | |
cue :endphase #cue lastnotes and LED phase | |
stop #this live_loop | |
end | |
end | |
set :bass_rhythm, ring(9, 0, 9, 0, 0, 0, 0, 0, 9, 0, 0, 3, 0, 0, 0, 0) | |
set :snare_rhythm, ring(0, 0, 0, 0, 9, 0, 0, 2, 0, 1, 0, 0, 9, 0, 0, 1) | |
set :hat_rhythm, ring(5, 0, 5, 0, 5, 0, 5, 0, 5, 0, 5, 0, 5, 0, 5, 0) | |
live_loop :drums, auto_sync: :pulse do | |
#use_real_time | |
sf = 0.05 | |
sample :drum_bass_hard, amp: sf*get(:bass_rhythm).tick | |
sample :drum_snare_hard, amp: sf*get(:snare_rhythm).look | |
sample :drum_cymbal_closed,amp: sf*get(:hat_rhythm).look | |
tval= get(:t) | |
#puts tval | |
if tval <0.3 | |
sleep tval | |
elsif tval <= 0.5 | |
sleep tval/2 | |
else | |
sleep tval/4 | |
end | |
stop if get(:stopflag) == true #this liveloop | |
end | |
live_loop :lastnotes ,sync: :endphase do | |
#use_real_time | |
use_synth :pulse | |
n=scale(:c4,:minor_pentatonic,num_octaves:3).tick | |
if look<32 | |
play n,release: 0.1 | |
sleep 0.1 | |
else | |
play [72,84,89,96],sustain: 1.8,release:0.1 | |
sleep 1.9 | |
end | |
stop if look==32 #this live look | |
end | |
#This section fades the LEDs up and down several times before | |
#leaving them all white for the final chord | |
#They fade out and are blanked when the music ends | |
sync :endphase | |
osc "/pulse/bright",1 #set full brightness | |
osc"/pulse/clear" | |
10.times do |x| | |
v=(255*x/10).to_i | |
osc "/pulse/setall",0,v,0 | |
sleep 0.05 | |
end | |
2.times do | |
10.times do |x| | |
v=(255*(x+1)/10).to_i | |
osc "/pulse/setall",v,255-v,v | |
sleep 0.05 | |
end | |
10.times do |x| | |
v=(255*(9-x)/10).to_i | |
osc "/pulse/setall",v,255-v,v | |
sleep 0.05 | |
end | |
end | |
10.times do |x| | |
v=(255*x/10).to_i | |
osc "/pulse/setall",0,255-v,0 | |
sleep 0.05 | |
end | |
osc "/pulse/setall",255,255,255 #set all white | |
sleep 2 | |
100.times do |x| | |
osc "/pulse/bright",(99-x).to_f/100 | |
sleep 0.01 | |
osc "/pulse/setall",255,255,255 | |
sleep 0.01 | |
end | |
osc "/pulse/clear" #clear all LEDs | |
puts "FINISHED!" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment