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