Skip to content

Instantly share code, notes, and snippets.

@rbnpi

rbnpi/PULSE.rb

Last active Aug 11, 2017
Embed
What would you like to do?
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
#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()
#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
You can’t perform that action at this time.