Create python and scratch source
[miniband.git] / raspberrypi / music-maker-handler.py
1 # This code is copyright ...... under the GPL v2.
2 # This code is derived from scratch_gpio_handler by Simon Walters, which
3 # is derived from scratch_handler by Thomas Preston
4 # Version 0.1: It's kind of working.
5
6 from array import *
7 import threading
8 import socket
9 import time
10 import sys
11 import struct
12 import serial
13 import io
14 import datetime as dt
15
16 '''
17 from Tkinter import Tk
18 from tkSimpleDialog import askstring
19 root = Tk()
20 root.withdraw()
21 '''
22
23 PORT = 42001
24 DEFAULT_HOST = '127.0.0.1'
25 BUFFER_SIZE = 240 #used to be 100
26 SOCKET_TIMEOUT = 1
27 DRUM_DEVICE = '/dev/ttyACM0'
28 GUITAR_DEVICE = '/dev/ttyUSB1'
29 MARACAS_DEVICE = '/dev/ttyACM1'
30 ARDUINO_BAUD_RATE = 57600
31
32 DRUM_INSTRUMENT_NAMES = {0: 'cymbal',
33 1: 'hihat',
34 2: 'slowdrum',
35 3: 'snare',
36 4: 'tomtom'}
37
38 DRUM_VALUE_NAMES = {0: 'drum-volume',
39 1: 'drum-volume',
40 2: 'drum-volume',
41 3: 'drum-volume',
42 4: 'drum-volume'}
43
44 GUITAR_INSTRUMENT_NAMES = {0: 'guitar'}
45 GUITAR_VALUE_NAMES = {0: 'guitar_pitch'}
46
47 GUITAR_INSTRUMENT_NAMES = {0: 'guitar'}
48 GUITAR_VALUE_NAMES = {0: 'guitar_pitch'}
49
50 MARACAS_INSTRUMENT_NAMES = {0: 'maracas', 2: 'maracas'}
51 MARACAS_VALUE_NAMES = {0: 'maracas_vigour', 2: 'maracas_vigour'}
52
53
54 class MyError(Exception):
55 def __init__(self, value):
56 self.value = value
57
58 def __str__(self):
59 return repr(self.value)
60
61 class ScratchSender(threading.Thread):
62 def __init__(self, socket):
63 threading.Thread.__init__(self)
64 self.scratch_socket = socket
65 self._stop = threading.Event()
66
67
68 def stop(self):
69 self._stop.set()
70
71 def stopped(self):
72 return self._stop.isSet()
73
74 def run(self):
75 # Detect sensors here
76
77 while not self.stopped():
78 time.sleep(0.01) # be kind to cpu - not certain why :)
79
80 def send_scratch_command(self, cmd):
81 n = len(cmd)
82 a = array('c')
83 a.append(chr((n >> 24) & 0xFF))
84 a.append(chr((n >> 16) & 0xFF))
85 a.append(chr((n >> 8) & 0xFF))
86 a.append(chr(n & 0xFF))
87 self.scratch_socket.send(a.tostring() + cmd)
88
89
90 class ArduinoListener(threading.Thread):
91 def __init__(self, device, speed, sender, instruments, values):
92 threading.Thread.__init__(self)
93 self.arduino_device = serial.Serial(device, speed)
94 self._stop = threading.Event()
95 self.scratch_sender = sender
96 self.instruments = instruments
97 self.values = values
98
99 def stop(self):
100 self._stop.set()
101
102 def stopped(self):
103 return self._stop.isSet()
104
105 def run(self):
106 self.arduino_device.readline() # discard the first (partial) line
107 while not self.stopped():
108 try:
109 pin_string, pin_value_string = self.arduino_device.readline().rstrip().split(',', 1)
110 pin = int(pin_string)
111 pin_value = int(pin_value_string)
112 print dt.datetime.now(), 'Pin: %d, Value: %d' % (pin, pin_value)
113 try:
114 print "sensor-update %s %d" % (self.values[pin], (pin_value * 100) / 1024)
115 self.scratch_sender.send_scratch_command("sensor-update %s %d" % (self.values[pin], (pin_value * 100) / 1024))
116 except KeyError:
117 # Do nothing
118 pass
119 try:
120 print "broadcast %s" % self.instruments[pin]
121 self.scratch_sender.send_scratch_command('broadcast %s' % self.instruments[pin])
122 except KeyError:
123 # Do nothing
124 pass
125
126 except serial.SerialException:
127 print 'Serial exception'
128
129
130
131 def create_socket(host, port):
132 while True:
133 try:
134 print 'Trying'
135 scratch_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
136 scratch_sock.connect((host, port))
137 break
138 except socket.error:
139 print "There was an error connecting to Scratch!"
140 print "I couldn't find a Mesh session at host: %s, port: %s" % (host, port)
141 time.sleep(3)
142 #sys.exit(1)
143
144 return scratch_sock
145
146 def cleanup_threads(threads):
147 for thread in threads:
148 thread.stop()
149
150 for thread in threads:
151 thread.join()
152
153 if __name__ == '__main__':
154 if len(sys.argv) > 1:
155 host = sys.argv[1]
156 else:
157 host = DEFAULT_HOST
158
159
160 cycle_trace = 'start'
161 while True:
162 if (cycle_trace == 'disconnected'):
163 print "Scratch disconnected"
164 cleanup_threads((listener, sender))
165 time.sleep(1)
166 cycle_trace = 'start'
167
168 if (cycle_trace == 'start'):
169 # open the socket
170 print 'Starting to connect...' ,
171 the_socket = create_socket(host, PORT)
172 print 'Connected!'
173 the_socket.settimeout(SOCKET_TIMEOUT)
174 ## data = the_socket.recv(BUFFER_SIZE)
175 ## print "Discard 1st data buffer" , data[4:].lower()
176 sender = ScratchSender(the_socket)
177 drum_listener = ArduinoListener(DRUM_DEVICE, ARDUINO_BAUD_RATE, sender, DRUM_INSTRUMENT_NAMES, DRUM_VALUE_NAMES)
178 guitar_listener = ArduinoListener(GUITAR_DEVICE, ARDUINO_BAUD_RATE, sender, GUITAR_INSTRUMENT_NAMES, GUITAR_VALUE_NAMES)
179 maracas_listener = ArduinoListener(MARACAS_DEVICE, ARDUINO_BAUD_RATE, sender, MARACAS_INSTRUMENT_NAMES, MARACAS_VALUE_NAMES)
180 cycle_trace = 'running'
181 print "Running...."
182 sender.start()
183 drum_listener.start()
184 guitar_listener.start()
185 maracas_listener.start()
186
187 #pin = 1
188 #pin_value = 512
189 #print 'Sending:' "sensor-update %s %d" % (VOLUME_NAMES[pin], (pin_value * 100) / 1024)
190 #sender.send_scratch_command("sensor-update %s %d" % (VOLUME_NAMES[pin], (pin_value * 100) / 1024))
191 #print 'Sending:' 'broadcast %s' % INSTRUMENT_NAMES[pin]
192 #sender.send_scratch_command('broadcast %s' % INSTRUMENT_NAMES[pin])
193
194 #sender.send_scratch_command("sensor-update guitar-pitch 10")
195 #sender.send_scratch_command("broadcast cymbal")
196 #time.sleep(1)
197 #sender.send_scratch_command("broadcast snare")
198 #time.sleep(1)
199 #sender.send_scratch_command("broadcast hihat")
200 #time.sleep(1)
201 #sender.send_scratch_command("broadcast slowdrum")
202 #time.sleep(1)
203 #sender.send_scratch_command("broadcast tomtom")
204 #time.sleep(1)
205
206 # wait for ctrl+c
207 try:
208 #just pause
209
210 # else:
211 time.sleep(0.1)
212 except KeyboardInterrupt:
213 cleanup_threads((sender, drum_listener, guitar_listener, maracas_listener))
214 sys.exit()
215