Skip to content

Instantly share code, notes, and snippets.

@rbnpi
Last active May 30, 2022 08:24
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save rbnpi/d6fea3334facf7fd224359f42359eab2 to your computer and use it in GitHub Desktop.
Save rbnpi/d6fea3334facf7fd224359f42359eab2 to your computer and use it in GitHub Desktop.
Sonic Pi Theremin utilising two ultrasonic sensors. See post at https://rbnrpi.wordpress.com/2018/05/17/dual-sensor-theremin-for-sonic-pi/ and video at https://youtu.be/Kk9KO6Stqp4 for details.
#!/usr/bin/env python3
#program sets up two distance sensors and exports readings using OSC to localhost port 4560 (previously 4559)
#using gpiozero and python-osc libraries
#written by Robin Newman, May 2018 (port updated for latest SP to 4560)
#
from gpiozero import DistanceSensor
from time import sleep
from pythonosc import osc_message_builder
from pythonosc import udp_client
import argparse
import sys
def control(spip):
sensor = DistanceSensor(echo=17, trigger=4,threshold_distance=0.5)
sensor2 = DistanceSensor(echo=23, trigger=24,threshold_distance=0.5)
sender = udp_client.SimpleUDPClient(spip,4560)
while True:
try:
r1 = (sensor.distance* 100) #send numbers in range 0->100
r2 = (sensor2.distance* 100)
sender.send_message('/play_this',[r1,r2]) #sends list of 2 numbers
print("r1:",round(r1,2),"r2:",round(r2,2))
sleep(0.06)
except KeyboardInterrupt:
print("\nExiting")
sys.exit()
if __name__=="__main__":
parser = argparse.ArgumentParser()
parser.add_argument("--sp",
default="127.0.0.1", help="The ip onic Pi listens on")
args = parser.parse_args()
spip=args.sp
print("Sonic Pi on ip",spip)
sleep(2)
control(spip)
#Theremin plus, controlling pitch and ixi_techno phase by Robin Newman May 2018
#/osc/ changed to /osc*/ in line 24 to work with latest Sonic Pi Aug 2021
#I control a continously playing note (length 2000)
use_debug false
define :putsPretty do |n,p|
num=(n*10**p).round/(10**p).to_f
return num
end
define :scalev do |v,l,h|
return (l+v).to_f*(h-l)/100
end
with_fx :reverb,room: 0.8,mix: 0.8 do
with_fx :ixi_techno,phase: 4,phase_offset: 1,mix: 0.8 do |p|
set :p, p
use_synth :subpulse
k=play octs(0,2),sustain: 10000,amp: 0
set :k,k
live_loop :theremin do
use_real_time
b = sync "/osc*/play_this"
r1=scalev(b[0],30,100)
r2=scalev(b[1],0.1,1)
puts putsPretty(r1,2),putsPretty(r2,2)
if r1 < 60 then #adjust note pitch, and restore volume to 0.7
control get(:k),note: octs(r1+12,2),note_slide: 0.06 ,amp: 0.7,amp_slide: 0.2
else #set output vol to 0
control get(:k),amp: 0,amp_slide: 0.2
end
if r2 < 0.8 then #adjust phase modulation rate, and restore mix to 0.8
control get(:p),phase: r2,phase_slide: 0.06,mix: 0.8,mix_slide: 0.2
else #switch off phase modulation by setting mix to zero
control get(:p),mix: 0,mix_slide: 0.2
end
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment