c935c15fb8bf2bf773c5644f3abf7c408b494d43
[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 import logging
16
17 PORT = 42001
18 DEFAULT_HOST = '127.0.0.1'
19 BUFFER_SIZE = 240 #used to be 100
20 SOCKET_TIMEOUT = 1
21 DEVICES = ['/dev/ttyACM0', '/dev/ttyACM1','/dev/ttyACM3']
22 #DEVICES = ['/dev/ttyACM0','/dev/ttyACM1']
23 #DRUM_DEVICE = '/dev/ttyACM0'
24 #GUITAR_DEVICE = '/dev/ttyUSB1'
25 #MARACAS_DEVICE = '/dev/ttyACM1'
26 ARDUINO_BAUD_RATE = 57600
27
28 BROADCAST_NAMES = {'guitar': 'guitar',
29 'drum': {0: 'cymbal',
30 1: 'hihat',
31 2: 'slowdrum',
32 3: 'snare',
33 4: 'tomtom'},
34 'maracas': 'maracas'}
35
36 SENSOR_NAMES = {'guitar': 'guitar_pitch'}
37
38 logging.basicConfig(level = logging.INFO)
39 #logging.basicConfig(level = logging.DEBUG)
40
41 class MyError(Exception):
42 def __init__(self, value):
43 self.value = value
44
45 def __str__(self):
46 return repr(self.value)
47
48 class ScratchSender(threading.Thread):
49 def __init__(self, socket):
50 threading.Thread.__init__(self)
51 self.scratch_socket = socket
52 self._stop = threading.Event()
53
54 def join(self,timeout=None):
55 self._stop.set()
56 threading.Thread.join(self, timeout)
57
58 def stopped(self):
59 return self._stop.isSet()
60
61 def run(self):
62 while not self.stopped():
63 time.sleep(0.01) # be kind to cpu - not certain why :)
64
65 def send_scratch_command(self, cmd):
66 n = len(cmd)
67 a = array('c')
68 a.append(chr((n >> 24) & 0xFF))
69 a.append(chr((n >> 16) & 0xFF))
70 a.append(chr((n >> 8) & 0xFF))
71 a.append(chr(n & 0xFF))
72 self.scratch_socket.send(a.tostring() + cmd)
73
74
75 class ArduinoListener(threading.Thread):
76 def __init__(self, device, speed, sender, instruments, values):
77 threading.Thread.__init__(self)
78 self.arduino_device = serial.Serial(device, speed, timeout=0.5)
79 self._stop = threading.Event()
80 self.scratch_sender = sender
81 self.instruments = instruments
82 self.values = values
83 logging.info("Started listener on port %s" % device)
84
85 def join(self,timeout=None):
86 self._stop.set()
87 threading.Thread.join(self, timeout)
88
89 def stopped(self):
90 return self._stop.isSet()
91
92 def run(self):
93 self.arduino_device.readline() # discard the first (partial) line
94 while not self.stopped():
95 logging.debug('Thread waiting for a signal')
96 try:
97 device_line = self.arduino_device.readline()
98 if device_line :
99 instrument, instrument_value_string = device_line.rstrip().split(',', 1)
100 instrument_value = int(instrument_value_string)
101 logging.info('Instrument: %s, Value: %d' % (instrument, instrument_value))
102 if instrument in self.values:
103 try:
104 logging.info("sensor-update %s %d" % (self.values[instrument], (instrument_value * 100) / 1024))
105 self.scratch_sender.send_scratch_command("sensor-update %s %d" % (self.values[instrument], (instrument_value * 100) / 1024))
106 except KeyError:
107 # Do nothing
108 pass
109 if instrument in self.instruments:
110 if isinstance(self.instruments[instrument], dict):
111 broadcast = self.instruments[instrument][instrument_value]
112 else:
113 broadcast = self.instruments[instrument]
114 try:
115 logging.info("broadcast %s" % broadcast)
116 self.scratch_sender.send_scratch_command('broadcast %s' % broadcast)
117 except KeyError:
118 # Do nothing
119 pass
120
121 except serial.SerialException:
122 logging.error('Serial exception')
123 logging.debug('Thread run() exiting')
124
125
126 def create_socket(host, port):
127 while True:
128 try:
129 logging.info('Connecting to Scratch')
130 scratch_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
131 scratch_sock.connect((host, port))
132 break
133 except socket.error:
134 logging.warning("There was an error connecting to Scratch!")
135 logging.warning("I couldn't find a Mesh session at host: %s, port: %s" % (host, port))
136 time.sleep(3)
137 return scratch_sock
138
139 def cleanup_threads(threads):
140 logging.debug("Stopping %d threads" % len(threads))
141 for thread in threads:
142 thread.join()
143 logging.debug("Threads joined")
144
145 if __name__ == '__main__':
146 if len(sys.argv) > 1:
147 host = sys.argv[1]
148 else:
149 host = DEFAULT_HOST
150
151 cycle_trace = 'start'
152 while True:
153 if (cycle_trace == 'disconnected'):
154 logging.info("Scratch disconnected")
155 cleanup_threads(listeners + sender)
156 time.sleep(1)
157 cycle_trace = 'start'
158
159 if (cycle_trace == 'start'):
160 # open the socket
161 logging.info('Connecting to Scratch...')
162 the_socket = create_socket(host, PORT)
163 logging.info('Connected to Scratch')
164 the_socket.settimeout(SOCKET_TIMEOUT)
165 sender = ScratchSender(the_socket)
166 listeners = [ArduinoListener(device, ARDUINO_BAUD_RATE, sender, BROADCAST_NAMES, SENSOR_NAMES) for device in DEVICES]
167 cycle_trace = 'running'
168 logging.info("Listeners running....")
169 sender.start()
170 for listener in listeners:
171 listener.start()
172
173 # wait for ctrl+c
174 try:
175 #just pause
176 time.sleep(0.1)
177 except KeyboardInterrupt:
178 logging.warning("Interrrupted")
179 cleanup_threads(listeners + [sender])
180 sys.exit()
181