Last active
November 2, 2020 00:15
-
-
Save rbnpi/d7d99bb9cd06680aed468ec90273b4d9 to your computer and use it in GitHub Desktop.
Control Sonic PI 3 from a wireless PS3 games controller See video at https://youtu.be/8HcHKyhZaSM
This file contains hidden or 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
#!/usr/bin/env python3 | |
#ps3 wireless controller interface by Robin Newman July 2017 | |
#converts controller inputs to OSC messages | |
#which can be output to Sonic Pi 3, running on the local computer, or on an external machine | |
#rtested with ps3 "afterglow" wireless controller, dongle in Pi usb socket | |
#needs sudo apt-get install joystick after sudo apt-get update | |
#also needs sudo pip3 install python-osc | |
#Version 2. Modified to ensure clean exit on Ubuntu | |
import subprocess,pygame,sys | |
from signal import pause | |
from pythonosc import osc_message_builder | |
from pythonosc import udp_client | |
import argparse | |
from time import sleep | |
pygame.display.init() | |
pygame.joystick.init() | |
clock=pygame.time.Clock() | |
ps3 = pygame.joystick.Joystick(0) | |
ps3.init() | |
def control(spip): | |
gate=0.1 | |
sender=udp_client.SimpleUDPClient(spip,4559) #sender set up for specified IP | |
while True: | |
try: | |
print("ps3.py: Python ps3-> OSC interface") | |
print ("Specify external Sonic Pi with ./ps3.py --sp [SP3 IP ADDRESS] on command line") | |
print("For local Sonic Pi 3 just use ./ps3.py") | |
print ("Ctrl-C to exit") | |
pygame.event.pump() | |
lud=ps3.get_axis(1) | |
llr=ps3.get_axis(0) | |
rud=ps3.get_axis(3) | |
rlr=ps3.get_axis(2) | |
if abs(rud) > gate: | |
sender.send_message('/rud',-rud) | |
if abs(lud) > gate: | |
sender.send_message('/lud',-lud) | |
if abs(rlr) > gate: | |
sender.send_message('/rlr',rlr) | |
if abs(llr) > gate: | |
sender.send_message('/llr',llr) | |
b0=ps3.get_button(0) | |
if b0>0: | |
sender.send_message('/b0',b0) | |
b1=ps3.get_button(1) | |
if b1>0: | |
sender.send_message('/b1',b1) | |
b2=ps3.get_button(2) | |
if b2>0: | |
sender.send_message('/b2',b2) | |
b3=ps3.get_button(3) | |
if b3>0: | |
sender.send_message('/b3',b3) | |
b4=ps3.get_button(4) | |
if b4>0: | |
sender.send_message('/b4',b4) | |
b5=ps3.get_button(5) | |
if b5>0: | |
sender.send_message('/b5',b5) | |
b6=ps3.get_button(6) | |
if b6>0: | |
sender.send_message('/b6',b6) | |
b7=ps3.get_button(7) | |
if b7>0: | |
sender.send_message('/b7',b7) | |
b8=ps3.get_button(8) | |
if b8>0: | |
sender.send_message('/b8',b8) | |
b9=ps3.get_button(9) | |
if b9>0: | |
sender.send_message('/b9',b9) | |
b10=ps3.get_button(10) | |
if b10>0: | |
sender.send_message('/b10',b10) | |
b11=ps3.get_button(11) | |
if b11>0: | |
sender.send_message('/b11',b11) | |
b12=ps3.get_button(12) | |
if b12>0: | |
sender.send_message('/b12',b12) | |
hat=ps3.get_hat(0) | |
if hat[0]!=0 and hat[1]!=0: | |
sender.send_message('/hat',hat) | |
clock.tick(20) | |
subprocess.call("clear") | |
except KeyboardInterrupt: | |
print("\nExiting") | |
sys.exit() | |
if __name__=="__main__": | |
parser = argparse.ArgumentParser() | |
#This arg gets the server IP address to use. 127.0.0.1 or | |
#The local IP address of the Pi, required when using external Sonic Pi | |
parser.add_argument("--sp", | |
default="127.0.0.1", help="The ip to listen on") | |
args = parser.parse_args() | |
spip=args.sp | |
print("Sonic Pi on ip",spip) | |
sleep(2) | |
control(spip) |
This file contains hidden or 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
#Demo program showing Sonic Pi 3 and PS3 control | |
#written by Robin Newman, July 2017 | |
#uses a python script on the Raspberry Pi to | |
#interface to an "AfterGlow" wireless ps3 controller | |
#The script sends OSC messages with the PS3 data to Sonic Pi | |
#Which can be running on the Pi, or on a remote machine | |
# inputs are from: | |
#osc messages from the following | |
#llr and lud values for left left-right and left uo-down jopystick -1->+1 | |
#rlr and rud values for right -left-right and right up-down joystick -1->+1 | |
#hat values from the hat control [a,b] where a is -1 or 1 and b is -1 or 1 | |
#b0 to b12 values for the 13 buttons. (1) | |
#sender ignores 0 values and doesn't send them (threshold +-0.1 for joysticks) | |
#this cuts down a huge amount of OSC messages | |
#sender program polls values in a loop and sends when not 0 or > threshold | |
use_debug false | |
use_cue_logging false | |
#initialise set values. These are used to communicate with the various live_loops | |
set :vinc,1 #used to hold controlled volume level | |
set :ncontrol,72 #used to hold controlled note for "long note" | |
set :kill,0 #used to kill "long note" | |
set :mutevol,1 #used to mute/unmute "long note" | |
set :cutoff_val,80 #cutoff value for "long note" | |
set :opt_in, 1 #lets ps3 buttons opt in/out of controlled volume | |
set :syn_pointer, 0 #pints to current synth for buttons and "long note" | |
set :syn,:tri #current synth name for buttons and "long note" | |
define :pdec2 do |n| #for nice printing | |
return (n.to_f*100).round.to_f/100 | |
end | |
###################### CONTROL SECTION ADJUSTING VOLUME, SYNTHS, MUTING ETC ################ | |
define :vv do |a| #function to switch control of amp setting according to "a" | |
if a==1 | |
return get(:vinc) | |
else | |
return 1 | |
end | |
end | |
live_loop :hat do #used to adjust controlled volume level | |
use_real_time | |
b=sync "/osc/hat" | |
val=b[1] | |
set :vinc,[get(:vinc)+0.1,2].min if b[1]==1 | |
set :vinc,[get(:vinc)-0.11,0].max if b[1]==-1 | |
puts "volume set",pdec2(get(:vinc)) | |
end | |
live_loop :kill do #used to kill "long" note (and set vol to 0 for remainder) | |
b=sync "/osc/b12" | |
set :kill,1 | |
sleep 1 | |
set :kill,0 | |
end | |
live_loop :long_pitch do #used to change pitch of "long" note | |
use_real_time | |
b= sync "/osc/rud" | |
#puts "long_pitch",pdec2((b[0]*36+72)),"cutoff" | |
set :ncontrol,b[0]*36+72 | |
puts "Long Note pitch:",pdec2(get(:ncontrol)),"Cutoff:",pdec2(get(:cutoff_val)) | |
end | |
live_loop :mute do #used to temporarily mute "long note" | |
use_real_time | |
b=sync "/osc/b8" | |
set :mutevol,0 | |
end | |
live_loop :unmute do #used to unmute "long note" | |
use_real_time | |
b=sync "/osc/b9" | |
set :mutevol,1 | |
end | |
live_loop :control_btn_vols do #osed to opt buttons in/out of controlled volume | |
use_real_time | |
b=sync "/osc/b10" | |
set(:opt_in,get(:opt_in)*-1) | |
if get(:opt_in) ==1 | |
puts "Button volume controlled" | |
else | |
puts "Button volume NOT controlled" | |
end | |
sleep 0.4 | |
end | |
live_loop :rlr do #used to adjust "long note" cutoff | |
use_real_time | |
b= sync "/osc/rlr" | |
set :cutoff_val,b[0]*40+80 | |
puts "Long Note pitch:",pdec2(get(:ncontrol)),"Cutoff:",pdec2(get(:cutoff_val)) | |
end | |
live_loop :llr do #used to select synth for buttons | |
use_real_time | |
slist=[:sine,:tri,:saw,:prophet,:tb303,:fm,:mod_saw,:mod_fm] | |
b= sync "/osc/llr" | |
p=get(:syn_pointer) | |
p=[p+1,slist.length-1].min if b[0] > 0.4 | |
p=[p-1,0].max if b[0] < -0.4 | |
set :syn_pointer,p | |
set(:syn,slist[p]) | |
puts "syn is",get(:syn) | |
sleep 0.4 | |
end | |
############################ PLAYING SECTION BELOW ############################ | |
with_fx :reverb,room: 0.99 do #start playing section here inside reverb | |
live_loop :continuous_note do #setup up "long note" and control it | |
use_real_time | |
x= sync "/osc/b11" #start the "long" note | |
use_synth get(:syn) | |
n= play 72,sustain: 200,amp: 0 #start "long note" with zero volume | |
control n,note: 72 | |
2000.times do | |
control n,note: get(:ncontrol),note_slide: 0.1,amp: get(:mutevol)*get(:vinc),amp_slide: 0.1,cutoff: get(:cutoff_val),cutoff_slide: 0.1 | |
sleep 0.1 | |
break if get(:kill)==1 #if kill set abort loop and set vol of note to 0 | |
end | |
control n, amp: 0 | |
end | |
live_loop :lud do #adjust pitch of :sine synth with joystick | |
use_real_time | |
f = sync "/osc/lud" | |
synth :sine,note: f[0]*24+60,attack: 0.15,release: 0.2,amp: get(:vinc) #or use synth get(:syn) | |
end | |
live_loop :bt0 do #play set note :c4 with chosen synth | |
use_real_time | |
b= sync "/osc/b0" | |
synth get(:syn),note: :c4,attack: 0.1,release: 0.2,amp: vv(get(:opt_in)) if b[0]==1 | |
end | |
live_loop :bt1 do #remaining buttons similar with pitch increased each button | |
use_real_time | |
b= sync "/osc/b1" | |
synth get(:syn),note: :d4,attack: 0.1,release: 0.2,amp: vv(get(:opt_in)) if b[0]==1 | |
end | |
live_loop :bt2 do | |
use_real_time | |
b= sync "/osc/b2" | |
synth get(:syn),note: :e4,attack: 0.1,release: 0.2,amp: vv(get(:opt_in)) if b[0]==1 | |
end | |
live_loop :bt3 do | |
use_real_time | |
b= sync "/osc/b3" | |
synth get(:syn),note: :f4,attack: 0.1,release: 0.2,amp: vv(get(:opt_in)) if b[0]==1 | |
end | |
live_loop :bt4 do | |
use_real_time | |
b= sync "/osc/b4" | |
synth get(:syn),note: :g4,attack: 0.1,release: 0.2,amp: vv(get(:opt_in)) if b[0]==1 | |
end | |
live_loop :bt5 do | |
b= sync "/osc/b5" | |
use_real_time | |
synth get(:syn),note: :a4,attack: 0.1,release: 0.2,amp: vv(get(:opt_in)) if b[0]==1 | |
end | |
live_loop :bt6 do | |
use_real_time | |
b= sync "/osc/b6" | |
synth get(:syn),note: :b4,attack: 0.1,release: 0.2,amp: vv(get(:opt_in)) if b[0]==1 | |
end | |
live_loop :bt7 do | |
use_real_time | |
b= sync "/osc/b7" | |
synth get(:syn),note: :c5,attack: 0.1,release: 0.2,amp: vv(get(:opt_in)) if b[0]==1 | |
end | |
end #fx |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment