├── README.md ├── oscolarecorder.py ├── touchoscpixelcontrol.touchosc └── touchoscpixelplaybackonly.touchosc /README.md: -------------------------------------------------------------------------------- 1 | # OscOlaRecorder 2 | 3 | A simple python script i hacked together to allow recording, playback, loop and shuffle of dmx data via OLA on a raspberry pi or compatible linux device. 4 | 5 | I made it so i could easily record, trigger and run content to an LED installation, with an autopilot/shuffle function so i didnt have to keep my expensive latop at the event. 6 | 7 | Runs great on a raspberry pi 3b+, receiving content VIA artnet from Resolume then sending sacn data to a pixlite 4 to control the LEDs. 8 | 9 | It's super hacky as i dont know much about coding, but it works very well and is super responsive. If anyone would like to clean up my code or fork it and make it better then be my guest! 10 | 11 | ####INSTRUCTIONS#### 12 | - Install ola - https://github.com/OpenLightingProject/ola 13 | - set up OLA with up to 15 universes with Artnet in and sacn out or vice versa depending on your rig 14 | - make sure python is installed on your pi 15 | - install pyosc - https://github.com/ptone/pyosc 16 | - copy oscolarecorder.py file to your Pi in the home directory. 17 | type: 18 | sudo python oscolarecorder.py 19 | 20 | then you should be able to trigger recording, playback, loop and shuffle of the incoming and outgoing artnet/sacn data using Touchosc or any osc application with the following messages: 21 | 22 | record - /1/toggle1, /1/toggle2, /1/toggle3 etc 23 | 24 | playback - /2/toggle1, /2/toggle2, /2/toggle3 etc 25 | 26 | stop all playback - /3/stopallplay 27 | 28 | stop all recording - /3/stopallrec 29 | 30 | play all recorded files in series on loop - /3/playall 31 | 32 | shuffle all recorded files on loop - /3/shuffle 33 | 34 | set duration of how long a recording plays before moving onto the next one - /3/loopduration 35 | 36 | I have uploaded my templates for Touchosc, The main one has 3 pages, record, playback and control, the other has only playback functionality. 37 | 38 | Things to fix: 39 | 40 | Make IP address automatic 41 | 42 | Make it so loop duration can be changed on the fly (currently you need to stop and start shuffle playback to affect loop duration due to how i have implemented it. 43 | 44 | Make it so you can playback multiple recordings at a time, but not so many that it overloads the CPU. ( i have manually ran 6 recordings at once and OLA merges the data nicely, it wouldnt be a difficult addition but would be a nice one and allow layering of DMX data) 45 | 46 | Impliment OSC feedback messages so touchosc buttons can change colour when a particular pattern is playing or recording - i think this is possible but havent looked into it yet. 47 | 48 | 49 | have fun! 50 | 51 | Lightsteed / Beam Hacker 52 | -------------------------------------------------------------------------------- /oscolarecorder.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | from OSC import OSCServer,OSCClient, OSCMessage, ThreadingOSCServer 4 | import sys 5 | from time import sleep 6 | import time 7 | import types 8 | import os 9 | import subprocess 10 | import threading 11 | from threading import Thread 12 | from os import listdir 13 | from os.path import isfile, join 14 | from random import shuffle 15 | from random import sample 16 | 17 | # TODO: add code to create "/home/pi/myoscrecs" folder if not present. 18 | os.chdir('/home/pi/myoscrecs') 19 | time.sleep(5) 20 | 21 | # TODO: Add code to retreive ip address automatically (ethernet IP prioritized, wifi IP if ethernet not connected) 22 | server = ThreadingOSCServer( ("10.1.1.143", 7002) )# the ip adress of your pi/device goes here and the incoming OSC port you would like to use, i used 7002 for the port - the incoming port should be matched on your TouchOSC / OSC app 23 | client = OSCClient() 24 | 25 | #def handle_timeout(self): 26 | # print ("I'm IDLE") 27 | #This here is just to do something while the script recieves no information.... 28 | #server.handle_timeout = types.MethodType(handle_timeout, server) 29 | 30 | # Record Global Var 31 | r = None 32 | # Playback Global Var 33 | p = None 34 | # Stop Recording Global Var 35 | sr = 0 36 | #Stop Playback Global Var 37 | sp = 0 38 | #Set to loop Global Var 39 | loop = 1 40 | #Duration Time Global Var 41 | loopd = 10 42 | #Set to Shuffle Global Var 43 | shuffleloop = 1 44 | 45 | #Function to record DMX input using ola_recorder program and save into banks numbered 1-XX - Recording starts and stops when triggered via OSC. 46 | def record(path, tags, args, source): 47 | split = path.split("/1/toggle") 48 | x = split.pop() 49 | state=int(args[0]) 50 | print "Record",x 51 | global sr 52 | global r 53 | global sp 54 | global p 55 | if state == 1: 56 | if sr == 0: 57 | if sp == 0: 58 | r = subprocess.Popen(['ola_recorder', '-r', x, '-u 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20'], stdout=subprocess.PIPE) 59 | sr = 1 60 | else: 61 | p.kill() 62 | r = subprocess.Popen(['ola_recorder', '-r', x, '-u 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20'], stdout=subprocess.PIPE) 63 | sr = 1 64 | sp = 0 65 | else: 66 | if sp == 0: 67 | r.kill() 68 | r = subprocess.Popen(['ola_recorder', '-r', x, '-u 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20'], stdout=subprocess.PIPE) 69 | sr = 1 70 | else: 71 | p.kill() 72 | r = subprocess.Popen(['ola_recorder', '-r', x, '-u 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20'], stdout=subprocess.PIPE) 73 | sr = 1 74 | sp = 0 75 | 76 | else: 77 | if sr == 0: 78 | None 79 | else: 80 | r.kill() 81 | sr = 0 82 | 83 | 84 | #Function to Playback recorded DMX clips using ola_recorder program - Playback starts and stops when triggered via OSC. 85 | def playback(path, tags, args, source): 86 | split = path.split("/2/push") 87 | y = split.pop() 88 | state=int(args[0]) 89 | print "Playback:",y 90 | global sp 91 | global p 92 | global sr 93 | global r 94 | if state == 1: 95 | if sp == 0: 96 | if sr == 0: 97 | p = subprocess.Popen(['ola_recorder', '-p', y, '-i 0'], stdout=subprocess.PIPE) 98 | sp = 1 99 | else: 100 | r.kill() 101 | p = subprocess.Popen(['ola_recorder', '-p', y, '-i 0'], stdout=subprocess.PIPE) 102 | sp = 1 103 | sr = 0 104 | else: 105 | if sr == 0: 106 | p.kill() 107 | p = subprocess.Popen(['ola_recorder', '-p', y, '-i 0'], stdout=subprocess.PIPE) 108 | sp = 1 109 | else: 110 | p.kill() 111 | r.kill() 112 | p = subprocess.Popen(['ola_recorder', '-p', y, '-i 0'], stdout=subprocess.PIPE) 113 | sp = 1 114 | sr = 0 115 | 116 | # Function to kill all playback 117 | def stopallplay(path, tags, args, source): 118 | state=int(args[0]) 119 | print "Stop All Playback:", state; 120 | global sp 121 | global p 122 | if state == 1: 123 | p.kill() 124 | sp = 0 125 | 126 | #Function to kill all recording 127 | def stopallrec(path, tags, args, source): 128 | state=int(args[0]) 129 | print "Stop All Recording:", state; 130 | global sr 131 | global r 132 | if state == 1: 133 | r.kill() 134 | sr = 0 135 | 136 | #Function to set loop duration 137 | def loopduration(path, tags, args, source): 138 | global loopd 139 | loopd=int(args[0]) 140 | # print "Loop Duration", loopd; 141 | 142 | #Function to play all recorded clips in sequence 143 | def playall(path, tags, args, source): 144 | global loop 145 | state = int(args[0]) 146 | loop=state 147 | if loop == 1: 148 | playallloop() 149 | 150 | #Function to shuffle order of clips playing back 151 | def shuffler(path, tags, args, source): 152 | global shuffleloop 153 | state = int(args[0]) 154 | shuffleloop=state 155 | #if shuffle == 1: 156 | 157 | #Function to loop single file 158 | def playallloop(): 159 | global sp 160 | global p 161 | global sr 162 | global r 163 | global loop 164 | global loopd 165 | global shuffleloop 166 | while loop == 1: 167 | if loop == 0: 168 | p.kill() 169 | break 170 | print "Play on Loop"; 171 | file_list = os.listdir(r"/home/pi/myoscrecs") 172 | #file_list_sorted = sorted([file_list, key=int) 173 | file_shuffle = sample(file_list, len(file_list)) 174 | if shuffleloop == 0: 175 | files = sorted(file_list) 176 | else: 177 | files = file_shuffle 178 | for i in files: 179 | if loop == 0: 180 | p.kill() 181 | break 182 | #i = "" 183 | #i = str(x) 184 | print "Loop Duration", loopd; 185 | if sp == 0: 186 | if sr == 0: 187 | p = subprocess.Popen(['ola_recorder', '-p', i, '-i 0'], stdout=subprocess.PIPE) 188 | sp = 1 189 | else: 190 | r.kill() 191 | p = subprocess.Popen(['ola_recorder', '-p', i, '-i 0'], stdout=subprocess.PIPE) 192 | sp = 1 193 | sr = 0 194 | else: 195 | if sr == 0: 196 | p.kill() 197 | p = subprocess.Popen(['ola_recorder', '-p', i, '-i 0'], stdout=subprocess.PIPE) 198 | sp = 1 199 | else: 200 | p.kill() 201 | r.kill() 202 | p = subprocess.Popen(['ola_recorder', '-p', i, '-i 0'], stdout=subprocess.PIPE) 203 | sp = 1 204 | sr = 0 205 | time.sleep(loopd) 206 | 207 | #OSC Client message handling -You can increase the amount of record and playback banks by increasing the number from 70 to whatever in the below lines. 208 | for x in range(1,70): 209 | server.addMsgHandler("/1/"+"toggle"+`x`, record) 210 | 211 | for y in range(1,70): 212 | server.addMsgHandler("/2/"+"push"+`y`, playback) 213 | 214 | server.addMsgHandler("/3/stopallplay", stopallplay) 215 | server.addMsgHandler("/3/stopallrec", stopallrec) 216 | server.addMsgHandler("/3/playall", playall) 217 | server.addMsgHandler("/3/loopduration", loopduration) 218 | server.addMsgHandler("/3/shuffle", shuffler) 219 | 220 | 221 | #The way that the MSG Handlers work is by taking the values from set accessory, then it puts them into a function 222 | #The function then takes the values and separates them according to their class (args, source, path, and tags) 223 | 224 | while True: 225 | server.handle_request() 226 | 227 | server.close() 228 | #This will kill the server when the program ends 229 | 230 | 231 | -------------------------------------------------------------------------------- /touchoscpixelcontrol.touchosc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lightsteed/OscOlaRecorder/51c790aaba9f084f89c48367a78b73e3a32caf38/touchoscpixelcontrol.touchosc -------------------------------------------------------------------------------- /touchoscpixelplaybackonly.touchosc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lightsteed/OscOlaRecorder/51c790aaba9f084f89c48367a78b73e3a32caf38/touchoscpixelplaybackonly.touchosc --------------------------------------------------------------------------------