JukeBox script to run programs in Sonic Pi using a TouchOSC frontend, and associated scripts to start Sonic Pi headless. See comment to load index.html file Article at https://rbnrpi.wordpress.com/a-touchosc-jukebox-for-sonicpi/ video at https://youtu.be/G8DOquunrFc
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
@reboot /home/pi/Desktop/startsp.sh |
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
<?xml version="1.0" encoding="UTF-8"?><layout version="15" mode="0" orientation="horizontal"><tabpage name="amI=" scalef="0.0" scalet="1.0" ><control name="ZjI=" x="13" y="134" w="290" h="18" color="gray" scalef="0.0" scalet="1.0" type="push" local_off="false" ></control><control name="ZjE=" x="13" y="100" w="290" h="18" color="gray" scalef="0.0" scalet="1.0" type="push" local_off="false" ></control><control name="ZjEw" x="15" y="410" w="290" h="18" color="gray" scalef="0.0" scalet="1.0" type="push" local_off="false" ></control><control name="Zjk=" x="15" y="375" w="290" h="18" color="gray" scalef="0.0" scalet="1.0" type="push" local_off="false" ></control><control name="Zjg=" x="15" y="341" w="290" h="18" color="gray" scalef="0.0" scalet="1.0" type="push" local_off="false" ></control><control name="Zjc=" x="14" y="306" w="290" h="18" color="gray" scalef="0.0" scalet="1.0" type="push" local_off="false" ></control><control name="ZjQ=" x="17" y="203" w="290" h="18" color="gray" scalef="0.0" scalet="1.0" type="push" local_off="false" ></control><control name="ZjU=" x="15" y="237" w="290" h="18" color="gray" scalef="0.0" scalet="1.0" type="push" local_off="false" ></control><control name="ZjM=" x="15" y="168" w="290" h="18" color="gray" scalef="0.0" scalet="1.0" type="push" local_off="false" ></control><control name="ZjY=" x="15" y="272" w="290" h="18" color="gray" scalef="0.0" scalet="1.0" type="push" local_off="false" ></control><control name="bjE=" x="13" y="100" w="290" h="18" color="yellow" type="labelh" text="" size="14" background="false" outline="true" ></control><control name="bjI=" x="13" y="133" w="290" h="20" color="yellow" type="labelh" text="" size="14" background="false" outline="true" ></control><control name="bjM=" x="16" y="168" w="290" h="20" color="yellow" type="labelh" text="" size="14" background="false" outline="true" ></control><control name="bjQ=" x="16" y="202" w="290" h="20" color="yellow" type="labelh" text="" size="14" background="false" outline="true" ></control><control name="bjU=" x="15" y="236" w="290" h="20" color="yellow" type="labelh" text="" size="14" background="false" outline="true" ></control><control name="bjc=" x="15" y="305" w="290" h="20" color="yellow" type="labelh" text="" size="14" background="false" outline="true" ></control><control name="bjg=" x="15" y="340" w="290" h="20" color="yellow" type="labelh" text="" size="14" background="false" outline="true" ></control><control name="bjk=" x="15" y="374" w="290" h="20" color="yellow" type="labelh" text="" size="14" background="false" outline="true" ></control><control name="bjEw" x="15" y="409" w="290" h="20" color="yellow" type="labelh" text="" size="14" background="false" outline="true" ></control><control name="bjY=" x="15" y="271" w="290" h="20" color="yellow" type="labelh" text="" size="14" background="false" outline="true" ></control><control name="bGFiZWw0" x="83" y="62" w="20" h="25" color="red" type="labelh" text="LQ==" size="14" background="true" outline="false" ></control><control name="cHJldg==" x="12" y="59" w="33" h="33" color="blue" scalef="0.0" scalet="1.0" type="push" local_off="false" ></control><control name="c3RvcA==" x="144" y="59" w="33" h="33" color="red" scalef="0.0" scalet="1.0" type="push" local_off="false" ></control><control name="bmV4dA==" x="269" y="59" w="33" h="33" color="green" scalef="0.0" scalet="1.0" type="push" local_off="false" ></control><control name="dGl0bGU=" x="48" y="10" w="224" h="20" color="yellow" type="labelh" text="U29uaWMgUGkgSnVrZWJveCBJbnRlcmZhY2U=" size="18" background="true" outline="false" ></control><control name="c3RhcnQ=" x="49" y="64" w="39" h="24" color="red" type="labelh" text="" size="14" background="false" outline="true" ></control><control name="ZmluaXNo" x="98" y="64" w="39" h="24" color="red" type="labelh" text="" size="14" background="false" outline="true" ></control><control name="dG90YWw=" x="226" y="64" w="39" h="24" color="red" type="labelh" text="" size="14" background="false" outline="true" ></control><control name="bGFiZWw1" x="179" y="63" w="46" h="25" color="red" type="labelh" text="dG90YWw=" size="14" background="true" outline="false" ></control><control name="bGVkMQ==" x="305" y="101" w="15" h="15" color="red" scalef="0.0" scalet="1.0" type="led" ></control><control name="bGVkMg==" x="305" y="135" w="15" h="15" color="red" scalef="0.0" scalet="1.0" type="led" ></control><control name="bGVkMw==" x="305" y="169" w="15" h="15" color="red" scalef="0.0" scalet="1.0" type="led" ></control><control name="bGVkNA==" x="305" y="204" w="15" h="15" color="red" scalef="0.0" scalet="1.0" type="led" ></control><control name="bGVkNQ==" x="305" y="238" w="15" h="15" color="red" scalef="0.0" scalet="1.0" type="led" ></control><control name="bGVkNg==" x="305" y="273" w="15" h="15" color="red" scalef="0.0" scalet="1.0" type="led" ></control><control name="bGVkNw==" x="305" y="307" w="15" h="15" color="red" scalef="0.0" scalet="1.0" type="led" ></control><control name="bGVkOA==" x="305" y="342" w="15" h="15" color="red" scalef="0.0" scalet="1.0" type="led" ></control><control name="bGVkOQ==" x="305" y="376" w="15" h="15" color="red" scalef="0.0" scalet="1.0" type="led" ></control><control name="bGVkMTA=" x="305" y="411" w="15" h="15" color="red" scalef="0.0" scalet="1.0" type="led" ></control><control name="bGFiZWw2" x="11" y="35" w="94" h="20" color="purple" type="labelh" text="RW5hYmxlIEF1ZGlvIElu" size="12" background="true" outline="false" ></control><control name="YXVkaW8taW4=" x="14" y="7" w="25" h="25" color="purple" scalef="0.0" scalet="1.0" type="toggle" local_off="false" ></control><control name="Y2xlYXI=" x="277" y="7" w="25" h="25" color="purple" scalef="0.0" scalet="1.0" type="push" local_off="false" ></control><control name="bGFiZWw3" x="213" y="35" w="89" h="20" color="purple" type="labelh" text="Q2xlYXIgU2FtcGxlcw==" size="12" background="true" outline="false" ></control></tabpage></layout> |
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 Pi init file | |
# Code in here will be evaluated on launch. | |
system('/home/pi/Desktop/jb.py --ip 172.24.1.89 --tip 172.24.1.127 >/dev/null 2>&1 &') | |
play 72,sustain: 2 |
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
#!/usr/bin/env python3 | |
#TouchOSC jukebox interface for Sonic Pi by Robin | |
#Newman, Aug 2017 | |
##################USER CONFIGURATION######################### | |
#specify full folder path containing playable SP3 files | |
SPfolder="/home/pi/Documents/SPfromXML/" #with trailing / | |
#specify full run path for sonic_pi Command Line Interface | |
c="/usr/local/bin/sonic_pi " | |
#################END OF USER CONFIGURATION############ | |
from pythonosc import osc_message_builder | |
from pythonosc import udp_client | |
from pythonosc import dispatcher | |
from pythonosc import osc_server | |
import os,time | |
from os.path import isfile,join | |
import argparse | |
import sys | |
#optional addition to getserver IP addres automatically | |
import socket | |
import signal | |
AF_INET=2 | |
SOCK_DGRAM=2 | |
def my_ip(): | |
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) | |
s.connect(('172.24.1.1',1027)) | |
return s.getsockname()[0] | |
#Read file names from selected folder, ignorgin subfolders and hidden files | |
list=sorted([f for f in os.listdir(SPfolder) if not f.startswith('.') if isfile(join(SPfolder, f))], key=lambda f: f.lower()) | |
print("\nTouchOSC controlled Jukebox server for Sonic Pi") | |
print("Written by Robin Newman, August 2017\n") | |
print("Folder is at ",SPfolder) | |
print("Nunber of files in folder ",len(list)) | |
smax=len(list) #max setting for start | |
start=0;finish=9 #initialise start/finish pointers | |
audioFlag=0 | |
def getargs(): #do getargs as a function which can be called from this namespace and from __main___ | |
try: | |
#first set up and deal with input args when program starts | |
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 TouchOSC | |
parser.add_argument("--ip", | |
default="127.0.0.1", help="The ip to listen on") | |
#This is the port on which the server listens. Usually 8000 is OK | |
#but you can specify a different one | |
parser.add_argument("--sport", | |
type=int, default=8000, help="The port the server listens on") | |
#This is the IP address of the machine running TouchOSC if remote | |
#or you can omit if using TouchOSC on the local Pi (very unlikely!!). | |
parser.add_argument("--tip", | |
default="127.0.0.1", help="The ip TouchOSC is on") | |
#This is the port that TouchOSC is listening on. Usually 9000 is OK | |
#but you can specify a differnt one | |
parser.add_argument("--tport", | |
type=int, default=9000, help="The port TouchOSC listens on") | |
args = parser.parse_args() | |
#return args #return without further processing if called | |
if args.ip=="127.0.0.1" and args.tip !="127.0.0.1": | |
#You must specify the local IP address of the Pi if trying to use | |
#the program with a remote TouchOSC on an external computer | |
raise AttributeError("--ip arg must specify actual local machine ip if using remote TouchOSC, not 127.0.0.1") | |
#Provide feed back to the user on the setup being used | |
if args.tip == "127.0.0.1": | |
touchip=args.tip | |
print("local machine used for TouchOSC: unlikely this is what you want",touchip) | |
else: | |
touchip=args.tip | |
#print("remote_host for TouchOSC is",args.tip) | |
touchport=args.tport | |
#print("remote TouchOSC listent on port",touchport) | |
#first two values needed by server, last two by sender | |
return args#(args.ip,args.sport,touchip,tport) | |
#Used the AttributeError to specify problems with the local ip address | |
except AttributeError as err: | |
print(err.args[0]) | |
sys.exit(0) | |
vals=getargs() #call getargs here to get touchip and touchport values for sender | |
touchip=(vals.tip) | |
#optional adjust if using auto select for server IP address | |
#you can uncomment and amend next four lines to suit your situation | |
##if my_ip()=='172.24.1.10': | |
## touchip='172.24.1.130' #adjust for my iPhone client | |
##elif my_ip()=='172.24.1.89: | |
## touchip='172.24.1.127' #adjust for my iPad client | |
touchport=int(vals.tport) | |
print("Sending to TouchOSC on ('{}', {})".format(touchip,touchport)) | |
sender=udp_client.SimpleUDPClient(touchip,touchport) #to send data to TouchOSC | |
sender.send_message('/jb/audio-in',0) | |
def update(): #updates display after one of the buttons is pushed | |
i=1 | |
for n in range(start,start+10): | |
#print('/jb/f'+str(i),[list[n]]) | |
if n<=smax-1: | |
sender.send_message('/jb/n'+str(i),[list[n]]) #print valid file name | |
else: | |
sender.send_message('/jb/n'+str(i)," ") #print blank filename | |
i +=1 | |
time.sleep(0.01) | |
sender.send_message('/jb/start',start+1) #update numeric values | |
sender.send_message('/jb/finish',finish+1) | |
sender.send_message('/jb/total',smax) | |
def updateleds(n): #update leds to reflect which file is playing | |
for x in range(1,11): | |
if x==n: | |
sender.send_message('/jb/led'+str(x),1) | |
else: | |
sender.send_message('/jb/led'+str(x),0) | |
time.sleep(1) | |
update() | |
updateleds(0) | |
def handle_next(unused_addr,args,n): #deal with next button pushed | |
global start,finish | |
if n == 1: #only act on push, not release | |
if start < smax-11: #inc start if more than 10 left | |
start +=10 | |
finish = start+9 #update finish | |
finish = min(finish,smax-1) #check if reached last file | |
print("next",n,start,finish) #print updated values on terminal | |
update() #update display | |
updateleds(0) #clear all leds | |
def handle_prev(unused_addr,args,p): #deal with previous button pushed | |
global start,finish | |
if p ==1: #only react to push, not release | |
start -= 10 #set new start value | |
start = max(start,0) #check not back at the beginning of list | |
finish=start+9 #set new finish value | |
finish = min(finish,smax-1) | |
print("prev",p,start,finish) #print new values on terminal | |
update() #update display | |
updateleds(0) #clear all leds | |
def handle_stop(unused_addr,args,s): # send stop signal to SP | |
if s ==1: #only on press, not release | |
os.system(c+"stop") | |
os.system(c+"midi_sound_off")#in case of any oprphan midi notes | |
updateleds(0) #clear all leds | |
print("stopped") #on terminal window | |
def handle_f1(unused_addr,args,n): #deal with button f1 (under first filename) pushed | |
global audioFlag | |
if n == 1: #only push not release | |
if start <= smax: #check there is a file there | |
if audioFlag==0: | |
os.system(c+"stop") #stop previous file (if any) | |
updateleds(1) #set led for position 1 | |
time.sleep(0.2) | |
if audioFlag==1: | |
os.system(c+"\'run_code \"with_fx :compressor,amp: 2 do;live_audio :sin;end\"\'") | |
f=SPfolder+list[start] #get full filename | |
os.system(c+"\'"+"run_file"+"\""+f+"\"'") #send run_file command via sonic_pi cli | |
def handle_f2(unused_addr,args,n): # as per other buttons adjust for position 2 | |
global audioFlag | |
if n == 1: | |
if start+1 <= smax: #is it valid? | |
if audioFlag==0: | |
os.system(c+"stop") #stop previous file (if any) | |
updateleds(2) | |
time.sleep(0.2) | |
if audioFlag==1: | |
os.system(c+"\'run_code \"with_fx :compressor,amp: 2 do;live_audio :sin;end\"\'") | |
f=SPfolder+list[start+1] | |
os.system(c+"\'"+"run_file"+"\""+f+"\"'") | |
def handle_f3(unused_addr,args,n): | |
global audioFlag | |
if n == 1: | |
if start+2 <= smax: | |
if audioFlag==0: | |
os.system(c+"stop") #stop previous file (if any) | |
updateleds(3) | |
time.sleep(0.2) | |
if audioFlag==1: | |
os.system(c+"\'run_code \"with_fx :compressor,amp: 2 do;live_audio :sin;end\"\'") | |
f=SPfolder+list[start+2] | |
os.system(c+"\'"+"run_file"+"\""+f+"\"'") | |
def handle_f4(unused_addr,args,n): | |
global audioFlag | |
if n == 1: | |
if start+3 <= smax: | |
if audioFlag==0: | |
os.system(c+"stop") #stop previous file (if any) | |
updateleds(4) | |
time.sleep(0.2) | |
if audioFlag==1: | |
os.system(c+"\'run_code \"with_fx :compressor,amp: 2 do;live_audio :sin;end\"\'") | |
f=SPfolder+list[start+3] | |
os.system(c+"\'"+"run_file"+"\""+f+"\"'") | |
def handle_f5(unused_addr,args,n): | |
global audioFlag | |
if n == 1: | |
if start+4 <= smax: | |
if audioFlag==0: | |
os.system(c+"stop") #stop previous file (if any) | |
updateleds(5) | |
time.sleep(0.2) | |
if audioFlag==1: | |
os.system(c+"\'run_code \"with_fx :compressor,amp: 2 do;live_audio :sin;end\"\'") | |
f=SPfolder+list[start+4] | |
os.system(c+"\'"+"run_file"+"\""+f+"\"'") | |
def handle_f6(unused_addr,args,n): | |
global audioFlag | |
if n == 1: | |
if start+5 <= smax: | |
if audioFlag==0: | |
os.system(c+"stop") #stop previous file (if any) | |
updateleds(6) | |
time.sleep(0.2) | |
if audioFlag==1: | |
os.system(c+"\'run_code \"with_fx :compressor,amp: 2 do;live_audio :sin;end\"\'") | |
f=SPfolder+list[start+5] | |
os.system(c+"\'"+"run_file"+"\""+f+"\"'") | |
def handle_f7(unused_addr,args,n): | |
global audioFlag | |
if n == 1: | |
if start+6 <= smax: | |
if audioFlag==0: | |
os.system(c+"stop") #stop previous file (if any) | |
updateleds(7) | |
time.sleep(0.2) | |
if audioFlag==1: | |
os.system(c+"\'run_code \"with_fx :compressor,amp: 2 do;live_audio :sin;end\"\'") | |
f=SPfolder+list[start+6] | |
os.system(c+"\'"+"run_file"+"\""+f+"\"'") | |
def handle_f8(unused_addr,args,n): | |
global audioFlag | |
if n == 1: | |
if start+7 <= smax: | |
if audioFlag==0: | |
os.system(c+"stop") #stop previous file (if any) | |
updateleds(8) | |
time.sleep(0.2) | |
if audioFlag==1: | |
os.system(c+"\'run_code \"with_fx :compressor,amp: 2 do;live_audio :sin;end\"\'") | |
f=SPfolder+list[start+7] | |
os.system(c+"\'"+"run_file"+"\""+f+"\"'") | |
def handle_f9(unused_addr,args,n): | |
global audioFlag | |
if n == 1: | |
if start+8 <= smax: | |
if audioFlag==0: | |
os.system(c+"stop") #stop previous file (if any) | |
updateleds(9) | |
time.sleep(0.2) | |
if audioFlag==1: | |
os.system(c+"\'run_code \"with_fx :compressor,amp: 2 do;live_audio :sin;end\"\'") | |
f=SPfolder+list[start+8] | |
os.system(c+"\'"+"run_file"+"\""+f+"\"'") | |
def handle_f10(unused_addr,args,n): | |
global audioFlag | |
if n == 1: | |
if start+9 <= smax: | |
if audioFlag==0: | |
os.system(c+"stop") #stop previous file (if any) | |
updateleds(10) | |
time.sleep(0.2) | |
if audioFlag==1: | |
os.system(c+"\'run_code \"with_fx :compressor,amp: 2 do;live_audio :sin;end\"\'") | |
f=SPfolder+list[start+9] | |
os.system(c+"\'"+"run_file"+"\""+f+"\"'") | |
def handle_audioIn(unused_addr,args,a): | |
global audioFlag | |
if a == 1: | |
audioFlag=1 | |
print("Audio Flag is ",audioFlag) | |
if a == 0: | |
audioFlag=0 | |
print("Audio Flag is ",audioFlag) | |
os.system(c+"\'run_code \"live_audio :sin,:stop\"\'") | |
def handle_clear(unused_addr,args,cl): | |
if cl == 1: #only on press, not release | |
print("Clearing..") | |
com="clear;sample_free_all" | |
os.system(c+"\'"+"run_code"+"\""+com+"\"'") | |
#The main routine called when the program starts up follows | |
if __name__ == "__main__": | |
try: #use try...except to handle possible errors | |
args=getargs() #call args parsing to get ip and port for server | |
sip=args.ip;sport=int(args.sport) #extract required itens | |
#dispatcher reacts to incoming OSC messages and then allocates | |
#different handler routines to deal with them | |
dispatcher = dispatcher.Dispatcher() | |
#set up the handler calls | |
dispatcher.map("/jb/prev",handle_prev,"p") | |
dispatcher.map("/jb/next",handle_next,"n") | |
dispatcher.map("/jb/stop",handle_stop,"s") | |
dispatcher.map("/jb/f1",handle_f1,"n") | |
dispatcher.map("/jb/f2",handle_f2,"n") | |
dispatcher.map("/jb/f3",handle_f3,"n") | |
dispatcher.map("/jb/f4",handle_f4,"n") | |
dispatcher.map("/jb/f5",handle_f5,"n") | |
dispatcher.map("/jb/f6",handle_f6,"n") | |
dispatcher.map("/jb/f7",handle_f7,"n") | |
dispatcher.map("/jb/f8",handle_f8,"n") | |
dispatcher.map("/jb/f9",handle_f9,"n") | |
dispatcher.map("/jb/f10",handle_f10,"n") | |
dispatcher.map("/jb/audio-in",handle_audioIn,"a") | |
dispatcher.map("/jb/clear",handle_clear,"cl") | |
#The following handler responds to the OSC message /testprint | |
#and prints it plus any arguments (data) sent with the message | |
#can be used for testing without doing anything | |
dispatcher.map("/testprint",print) | |
#Now set up and run the OSC server | |
#optionally determine IP address of server: uncomment next line | |
##sip=my_ip() #overwrites sip value obtained from input args | |
server = osc_server.ThreadingOSCUDPServer( | |
(sip, sport), dispatcher) | |
print("Serving messages from TouchOSC on {}".format(server.server_address)) | |
#run the server "forever" (till stopped by pressing ctrl-C) | |
server.serve_forever() | |
#deal with some error events | |
except KeyboardInterrupt: | |
print("\nServer stopped") #stop program with ctrl+C | |
#handle errors generated by the server | |
except OSError as err: | |
print("OSC server error", err.args) | |
#anything else falls through |
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
#!/bin/sh | |
Xvfb :1 & xvfb-run /usr/bin/sonic-pi 2 >/dev/null & |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
The file index.xml needs to be converted to a format which is used by TouchOSC. To do this, download the raw file and save it as index.html Then turn it into a zip file. On the Mac do this by right clicking the index.xml file in Finder, and choosing the compress option. This will produce a file named index.xml.zip Now rename this file to jukebox.touchosc (NB touchosc is the file extension). This file can then be loaded into the TouchOSC editor which comes with TouchOSC and downloaded to the TouchOSC app running on an iPad/iPhone/Android device in the usual way.