gyorgyf@30
|
1 #!/usr/bin/env python
|
gyorgyf@30
|
2 # encoding: utf-8
|
gyorgyf@30
|
3 """
|
gyorgyf@30
|
4 visclient.py
|
gyorgyf@30
|
5
|
gyorgyf@30
|
6 Created by George Fazekas on 2012-06-17.
|
gyorgyf@30
|
7 Copyright (c) 2012 . All rights reserved.
|
gyorgyf@30
|
8 """
|
gyorgyf@30
|
9
|
gyorgyf@30
|
10 import sys,os,math,time,copy
|
gyorgyf@30
|
11 import pygame as pg
|
gyorgyf@30
|
12 from pygame.locals import *
|
gyorgyf@30
|
13 import httplib as ht
|
gyorgyf@30
|
14 from pygame import midi
|
gyorgyf@30
|
15
|
gyorgyf@30
|
16 import gradients
|
gyorgyf@30
|
17 from gradients import genericFxyGradient
|
gyorgyf@30
|
18
|
gyorgyf@30
|
19 from threading import Thread
|
gyorgyf@30
|
20 from random import random
|
gyorgyf@30
|
21 import numpy as np
|
gyorgyf@30
|
22
|
gyorgyf@30
|
23 import colorsys as cs
|
gyorgyf@30
|
24
|
gyorgyf@30
|
25 NOTE_OFF_CH0 = [[[0xb0,0x7b,0],0]]
|
gyorgyf@30
|
26
|
gyorgyf@30
|
27 # 1 HONKY TONK 0x1 / 0x4 (5)
|
gyorgyf@30
|
28 # 2 SITAR 0x9 / 0x0 (1)
|
gyorgyf@30
|
29 # 3 Melancholia
|
gyorgyf@30
|
30 # 4 Rock GITAR 0x3 / 0x2C (45)
|
gyorgyf@30
|
31
|
gyorgyf@30
|
32 IMAP = {1 : (0x1,0x4), 2: (0x9,0x0), 3:(0x8,0xb), 4:(0x3,0x2c) }
|
gyorgyf@30
|
33
|
gyorgyf@30
|
34
|
gyorgyf@30
|
35 # from pytagcloud import create_tag_image, make_tags
|
gyorgyf@30
|
36 # from pytagcloud.lang.counter import get_tag_counts
|
gyorgyf@30
|
37
|
gyorgyf@30
|
38 # YOUR_TEXT = "A tag cloud is a visual representation for text data, typically\
|
gyorgyf@30
|
39 # used to depict keyword metadata on websites, or to visualize free form text."
|
gyorgyf@30
|
40 #
|
gyorgyf@30
|
41 # tags = make_tags(get_tag_counts(YOUR_TEXT), maxsize=120)
|
gyorgyf@30
|
42 #
|
gyorgyf@30
|
43 # create_tag_image(tags, 'cloud_large.png', size=(900, 600), fontname='Lobster')
|
gyorgyf@30
|
44
|
gyorgyf@30
|
45 scol = (0,255,0,255)
|
gyorgyf@30
|
46 ecol = (0,0,0,255)
|
gyorgyf@30
|
47
|
gyorgyf@30
|
48 # X,Y=1140,900
|
gyorgyf@30
|
49 # X,Y = 600,400
|
gyorgyf@30
|
50 X,Y = 800,600
|
gyorgyf@30
|
51
|
gyorgyf@30
|
52 # Fullscreen resolution:
|
gyorgyf@30
|
53 # XF,YF = 1280,900
|
gyorgyf@30
|
54 # XF,YF = 1440,900
|
gyorgyf@30
|
55 XF,YF = 1344,900
|
gyorgyf@30
|
56 # display calibrated
|
gyorgyf@30
|
57
|
gyorgyf@30
|
58 # detect display resolution
|
gyorgyf@30
|
59 import subprocess
|
gyorgyf@30
|
60 screenres = subprocess.Popen('xrandr | grep "\*" | cut -d" " -f4',shell=True, stdout=subprocess.PIPE).communicate()[0]
|
gyorgyf@30
|
61 screenres = map(lambda x: int(x.strip()), screenres.split('x'))
|
gyorgyf@30
|
62 XF,YF = screenres
|
gyorgyf@30
|
63 print "Screen resolution: ",XF,YF
|
gyorgyf@30
|
64
|
gyorgyf@30
|
65 NBLOBS = 18
|
gyorgyf@30
|
66 BLOBSIZE = 25
|
gyorgyf@30
|
67 G=110
|
gyorgyf@30
|
68 FADE = 15
|
gyorgyf@30
|
69 DIST = 0.15 # blob equivalence tolerance
|
gyorgyf@30
|
70 FRAMERATE = 60
|
gyorgyf@30
|
71
|
gyorgyf@30
|
72 # Connection:
|
gyorgyf@30
|
73 # IP = "127.0.0.1:8030"
|
gyorgyf@30
|
74 # IP = "192.168.2.158:8030"
|
gyorgyf@30
|
75 IP = "138.37.95.215"
|
gyorgyf@30
|
76 HTTP_TIMEOUT = 3
|
gyorgyf@30
|
77 SERVER_UPDATE_INTERVAL = 0.8
|
gyorgyf@30
|
78
|
gyorgyf@30
|
79
|
gyorgyf@30
|
80 class Indicator(object):
|
gyorgyf@30
|
81
|
gyorgyf@30
|
82 off_color = pg.Color(110,0,0)
|
gyorgyf@30
|
83 on_color = pg.Color(0,120,0)
|
gyorgyf@30
|
84
|
gyorgyf@30
|
85 def __init__(self,bg,pos):
|
gyorgyf@30
|
86 self.visible = True
|
gyorgyf@30
|
87 self.ison = True
|
gyorgyf@30
|
88 self.x,self.y = pos
|
gyorgyf@30
|
89 self.xs = int(self.x * X)
|
gyorgyf@30
|
90 self.ys = int(Y - (self.y * Y))
|
gyorgyf@30
|
91 self.c = self.off_color
|
gyorgyf@30
|
92 self.size = 6
|
gyorgyf@30
|
93 self.bg = bg
|
gyorgyf@30
|
94
|
gyorgyf@30
|
95 def reinit(self,bg):
|
gyorgyf@30
|
96 self.bg = bg
|
gyorgyf@30
|
97 self.xs = int(self.x * X)
|
gyorgyf@30
|
98 self.ys = int(Y - (self.y * Y))
|
gyorgyf@30
|
99
|
gyorgyf@30
|
100 def draw(self):
|
gyorgyf@30
|
101 if self.visible :
|
gyorgyf@30
|
102 pg.draw.circle(self.bg, self.c, (self.xs,self.ys),self.size,0)
|
gyorgyf@30
|
103
|
gyorgyf@30
|
104 def toggle(self):
|
gyorgyf@30
|
105 if self.ison == True :
|
gyorgyf@30
|
106 self.off()
|
gyorgyf@30
|
107 else :
|
gyorgyf@30
|
108 self.on()
|
gyorgyf@30
|
109 return self
|
gyorgyf@30
|
110
|
gyorgyf@30
|
111 def on(self):
|
gyorgyf@30
|
112 self.c = self.on_color
|
gyorgyf@30
|
113 self.ison = True
|
gyorgyf@30
|
114 return self
|
gyorgyf@30
|
115
|
gyorgyf@30
|
116 def off(self):
|
gyorgyf@30
|
117 self.c = self.off_color
|
gyorgyf@30
|
118 self.ison = False
|
gyorgyf@30
|
119 return self
|
gyorgyf@30
|
120
|
gyorgyf@30
|
121
|
gyorgyf@30
|
122 class Blob(object):
|
gyorgyf@30
|
123
|
gyorgyf@30
|
124 def __init__(self,bg,x,y,color=(255,255,255),mood=None,fade=FADE):
|
gyorgyf@30
|
125 # print x,y
|
gyorgyf@30
|
126 self.x = x
|
gyorgyf@30
|
127 self.y = y
|
gyorgyf@30
|
128 self.quadrant = self.get_quadrant_number(x,y)
|
gyorgyf@30
|
129 self.xs = x * X
|
gyorgyf@30
|
130 self.ys = Y - (y * Y)
|
gyorgyf@30
|
131 self.bg = bg
|
gyorgyf@30
|
132 self.size = BLOBSIZE
|
gyorgyf@30
|
133 self.time = time.time()
|
gyorgyf@30
|
134 self.alpha = 255
|
gyorgyf@30
|
135 self.c = color
|
gyorgyf@30
|
136 self.count = 1
|
gyorgyf@30
|
137 self.visible = True
|
gyorgyf@30
|
138 self.FADE = fade
|
gyorgyf@30
|
139 if mood and mood.color :
|
gyorgyf@30
|
140 self.c = mood.color
|
gyorgyf@30
|
141
|
gyorgyf@30
|
142 def get_quadrant_number(self,x,y):
|
gyorgyf@30
|
143 if x > 0.5 and y > 0.5 :
|
gyorgyf@30
|
144 return 1
|
gyorgyf@30
|
145 if x > 0.5 and y < 0.5 :
|
gyorgyf@30
|
146 return 2
|
gyorgyf@30
|
147 if x < 0.5 and y < 0.5 :
|
gyorgyf@30
|
148 return 3
|
gyorgyf@30
|
149 if x < 0.5 and y > 0.5 :
|
gyorgyf@30
|
150 return 4
|
gyorgyf@30
|
151
|
gyorgyf@30
|
152
|
gyorgyf@30
|
153 def __cmp__(self,other):
|
gyorgyf@30
|
154 d = math.sqrt( math.pow((self.x-other.x),2) + math.pow((self.y-other.y),2) )
|
gyorgyf@30
|
155 if d < DIST :
|
gyorgyf@30
|
156 return 0
|
gyorgyf@30
|
157 else :
|
gyorgyf@30
|
158 return -1
|
gyorgyf@30
|
159
|
gyorgyf@30
|
160 def draw(self):
|
gyorgyf@30
|
161 if not self.visible : return
|
gyorgyf@30
|
162 d=int(self.size)
|
gyorgyf@30
|
163 self.bg.blit(gradients.radial(self.size, (self.c[0],self.c[1],self.c[2],self.alpha), (0,0,0,self.alpha)), (self.xs-d,self.ys-d))
|
gyorgyf@30
|
164 self.alpha = 255 - int(self.age()*self.FADE)
|
gyorgyf@30
|
165 if self.alpha < 5 :
|
gyorgyf@30
|
166 self.alpha = 1
|
gyorgyf@30
|
167 self.visible = False
|
gyorgyf@30
|
168
|
gyorgyf@30
|
169 def age(self):
|
gyorgyf@30
|
170 return time.time() - self.time
|
gyorgyf@30
|
171
|
gyorgyf@30
|
172 def increment(self,count):
|
gyorgyf@30
|
173 self.time = time.time()
|
gyorgyf@30
|
174 self.count = count
|
gyorgyf@30
|
175 # self.size = int(BLOBSIZE * int(self.count/1.5))
|
gyorgyf@30
|
176 self.to = int(BLOBSIZE * int(self.count/1.5))
|
gyorgyf@30
|
177 self.start_animate()
|
gyorgyf@30
|
178
|
gyorgyf@30
|
179 def get_distance(self,x,y):
|
gyorgyf@30
|
180 return math.sqrt( math.pow((self.x-x),2) + math.pow((self.y-y),2) )
|
gyorgyf@30
|
181
|
gyorgyf@30
|
182 def fade(self,fade):
|
gyorgyf@30
|
183 if not fade : self.alpha = 255
|
gyorgyf@30
|
184 self.FADE = fade
|
gyorgyf@30
|
185
|
gyorgyf@30
|
186 def reset_time(self):
|
gyorgyf@30
|
187 self.time = time.time()
|
gyorgyf@30
|
188
|
gyorgyf@30
|
189 def start_animate(self):
|
gyorgyf@30
|
190 self.thread = Thread(target = self.animate)
|
gyorgyf@30
|
191 self.thread.daemon = True
|
gyorgyf@30
|
192 self.thread.start()
|
gyorgyf@30
|
193
|
gyorgyf@30
|
194 def animate(self):
|
gyorgyf@30
|
195 '''Animate the way bubbles are grown.'''
|
gyorgyf@30
|
196 while self.size < self.to :
|
gyorgyf@30
|
197 self.size += 1
|
gyorgyf@30
|
198 time_inc = 20.0 / (pow(1.2, self.to-self.size) * 200.0)
|
gyorgyf@30
|
199 time.sleep(0.02+time_inc)
|
gyorgyf@30
|
200
|
gyorgyf@30
|
201
|
gyorgyf@30
|
202
|
gyorgyf@30
|
203
|
gyorgyf@30
|
204 class Mood():
|
gyorgyf@30
|
205 def __init__(self,word,x,y):
|
gyorgyf@30
|
206 self.word = word
|
gyorgyf@30
|
207 self.x = float(x)
|
gyorgyf@30
|
208 self.y = float(y)
|
gyorgyf@30
|
209 self.color = []
|
gyorgyf@30
|
210
|
gyorgyf@30
|
211 def get_distance(self,x,y):
|
gyorgyf@30
|
212 return math.sqrt( math.pow((self.x-x),2) + math.pow((self.y-y),2) )
|
gyorgyf@30
|
213
|
gyorgyf@30
|
214
|
gyorgyf@30
|
215
|
gyorgyf@30
|
216 class VisualClient(object):
|
gyorgyf@30
|
217 '''Main visualisation client.'''
|
gyorgyf@30
|
218
|
gyorgyf@30
|
219 def __init__(self):
|
gyorgyf@30
|
220 # self.conn = ht.HTTPConnection("192.168.2.184:8030")
|
gyorgyf@30
|
221 # self.conn = ht.HTTPConnection("138.37.95.215")
|
gyorgyf@30
|
222 self.s_age = 10
|
gyorgyf@30
|
223 self.s_dist = DIST
|
gyorgyf@30
|
224 self.s_ninp = 18
|
gyorgyf@30
|
225
|
gyorgyf@30
|
226 pg.init()
|
gyorgyf@30
|
227
|
gyorgyf@30
|
228 # fontObj = pg.font.Font("freesansbold.ttf",18)
|
gyorgyf@30
|
229 white = ( 255, 255, 255)
|
gyorgyf@30
|
230 black = ( 0,0,0)
|
gyorgyf@30
|
231
|
gyorgyf@30
|
232 self.fpsClock = pg.time.Clock()
|
gyorgyf@30
|
233 self.screen = pg.display.set_mode((X, Y))
|
gyorgyf@30
|
234 pg.display.set_caption('Mood Conductor')
|
gyorgyf@30
|
235 self.bg = pg.Surface(self.screen.get_size())
|
gyorgyf@30
|
236 self.bg = self.bg.convert()
|
gyorgyf@30
|
237 self.bg.fill((0,0,0))
|
gyorgyf@30
|
238 pg.display.set_gamma(100.0)
|
gyorgyf@30
|
239
|
gyorgyf@30
|
240
|
gyorgyf@30
|
241 self.scol = (0,255,0,255)
|
gyorgyf@30
|
242 self.ecol = (0,0,0,255)
|
gyorgyf@30
|
243 coordstxt = "test"
|
gyorgyf@30
|
244
|
gyorgyf@30
|
245 self.blobs = []
|
gyorgyf@30
|
246 self.moods = []
|
gyorgyf@30
|
247 self.read_mood_data()
|
gyorgyf@30
|
248
|
gyorgyf@30
|
249 self.FADE = FADE
|
gyorgyf@30
|
250
|
gyorgyf@30
|
251 self.indicators = {
|
gyorgyf@30
|
252 "conn":Indicator(self.bg,(0.98,0.02)),
|
gyorgyf@30
|
253 "update":Indicator(self.bg,(0.96,0.02)),
|
gyorgyf@30
|
254 "data":Indicator(self.bg,(0.94,0.02)),
|
gyorgyf@30
|
255 "receive":Indicator(self.bg,(0.92,0.02))}
|
gyorgyf@30
|
256
|
gyorgyf@30
|
257 self.thread = None
|
gyorgyf@30
|
258 self.running = False
|
gyorgyf@30
|
259 self.fullscreen = False
|
gyorgyf@30
|
260 self.init_reconnect = False
|
gyorgyf@30
|
261
|
gyorgyf@30
|
262 pg.midi.init()
|
gyorgyf@30
|
263
|
gyorgyf@30
|
264 print pg.midi.get_device_info(4)
|
gyorgyf@30
|
265 self.midi_out = pg.midi.Output(4,0)
|
gyorgyf@30
|
266 self.midi_out.set_instrument(6)
|
gyorgyf@30
|
267
|
gyorgyf@30
|
268 # self.midi_out.note_on(23,128,1)
|
gyorgyf@30
|
269 # self.midi_out.write([[[0xc0,0,0],20000],[[0x90,60,100],20500]])
|
gyorgyf@30
|
270
|
gyorgyf@30
|
271 self.active_quadrant = 1
|
gyorgyf@30
|
272 self.prev_quadrant = 1
|
gyorgyf@30
|
273
|
gyorgyf@30
|
274 pass
|
gyorgyf@30
|
275
|
gyorgyf@30
|
276
|
gyorgyf@30
|
277 def read_mood_data(self):
|
gyorgyf@30
|
278 '''Read the mood position and color information form csv file.'''
|
gyorgyf@30
|
279 with open('moods.csv') as mf:
|
gyorgyf@30
|
280 data = mf.readlines()[1:]
|
gyorgyf@30
|
281 for line in data :
|
gyorgyf@30
|
282 l = line.split(',')
|
gyorgyf@30
|
283 mood = Mood(l[0],l[1],l[2])
|
gyorgyf@30
|
284 self.moods.append(mood)
|
gyorgyf@30
|
285 with open('colors.txt') as ff:
|
gyorgyf@30
|
286 data = ff.readlines()[1:]
|
gyorgyf@30
|
287 data = map(lambda x: x.split(','),data)
|
gyorgyf@30
|
288 for mood in self.moods :
|
gyorgyf@30
|
289 d = cd = sys.float_info.max
|
gyorgyf@30
|
290 for colors in data :
|
gyorgyf@30
|
291 d = mood.get_distance(float(colors[0]),float(colors[1]))
|
gyorgyf@30
|
292 if d < cd :
|
gyorgyf@30
|
293 cd = d
|
gyorgyf@30
|
294 # mood.color = tuple(map(lambda x: int(pow(math.atan((float(x)/7.0)),12.5)),(colors[2],colors[3],colors[4])))
|
gyorgyf@30
|
295 mood.color = self.set_color(tuple(map(lambda x: int(x),(colors[2],colors[3],colors[4]))))
|
gyorgyf@30
|
296 return True
|
gyorgyf@30
|
297
|
gyorgyf@30
|
298 def set_color(self,color):
|
gyorgyf@30
|
299 '''Move to HLS colour space and manipulate saturation there.'''
|
gyorgyf@30
|
300 # TODO: ideally, we need a non-linear compressor of the lightness and saturation values
|
gyorgyf@30
|
301 r,g,b = map(lambda x: (1.0*x/255.0), color)
|
gyorgyf@30
|
302 h,l,s = cs.rgb_to_hls(r,g,b)
|
gyorgyf@30
|
303 s = 1.0 #1.0 - (1.0 / pow(50.0,s))
|
gyorgyf@30
|
304 l = 1.0 - (1.0 / pow(20.0,l)) #0.6
|
gyorgyf@30
|
305 r,g,b = map(lambda x: int(x*255), cs.hls_to_rgb(h,l,s))
|
gyorgyf@30
|
306 return r,g,b
|
gyorgyf@30
|
307
|
gyorgyf@30
|
308 def start_update_thread(self):
|
gyorgyf@30
|
309 '''Start the thread that reads data from the server.'''
|
gyorgyf@30
|
310 self.running = True
|
gyorgyf@30
|
311 self.thread = Thread(target = self.update_thread)
|
gyorgyf@30
|
312 self.thread.daemon = True
|
gyorgyf@30
|
313 self.thread.start()
|
gyorgyf@30
|
314
|
gyorgyf@30
|
315 def stop_update_thread(self):
|
gyorgyf@30
|
316 '''Stop the thread and allow some time fot the connections to close.'''
|
gyorgyf@30
|
317 self.running = False
|
gyorgyf@30
|
318 try :
|
gyorgyf@30
|
319 self.thread.join(2)
|
gyorgyf@30
|
320 except :
|
gyorgyf@30
|
321 print "No update thread to join."
|
gyorgyf@30
|
322
|
gyorgyf@30
|
323 def update_thread(self):
|
gyorgyf@30
|
324 '''The server update thread'''
|
gyorgyf@30
|
325 while self.running :
|
gyorgyf@30
|
326 try :
|
gyorgyf@30
|
327 self.update()
|
gyorgyf@30
|
328 # self.indicators["update"].visible = True
|
gyorgyf@30
|
329 except Exception, e:
|
gyorgyf@30
|
330 if str(e).strip() : print "Exception: ", str(e), type(e), len(str(e).strip())
|
gyorgyf@30
|
331 self.indicators["conn"].off()
|
gyorgyf@30
|
332 # self.indicators["update"].visible = False
|
gyorgyf@30
|
333 time.sleep(SERVER_UPDATE_INTERVAL)
|
gyorgyf@30
|
334
|
gyorgyf@30
|
335
|
gyorgyf@30
|
336 def update(self):
|
gyorgyf@30
|
337 '''Update the blob list from the server. This should be in a thread.'''
|
gyorgyf@30
|
338
|
gyorgyf@30
|
339 # indicate connection health by toggling an indictor
|
gyorgyf@30
|
340 self.indicators["update"].toggle()
|
gyorgyf@30
|
341
|
gyorgyf@30
|
342 # delete invisibles
|
gyorgyf@30
|
343 for blob in self.blobs :
|
gyorgyf@30
|
344 if not blob.visible :
|
gyorgyf@30
|
345 self.blobs.remove(blob)
|
gyorgyf@30
|
346
|
gyorgyf@30
|
347 # get new coordinates from the server
|
gyorgyf@30
|
348 self.conn.putrequest("GET","/moodconductor/result", skip_host=True)
|
gyorgyf@30
|
349 self.conn.putheader("Host", "www.isophonics.net")
|
gyorgyf@30
|
350 self.conn.endheaders()
|
gyorgyf@30
|
351 res = self.conn.getresponse()
|
gyorgyf@30
|
352 data = res.read()
|
gyorgyf@30
|
353 data = eval(data)
|
gyorgyf@30
|
354 if not data :
|
gyorgyf@30
|
355 self.conn.close()
|
gyorgyf@30
|
356 self.indicators["data"].toggle()
|
gyorgyf@30
|
357 return False
|
gyorgyf@30
|
358 for d in data :
|
gyorgyf@30
|
359 # coordstxt = "x:%s y:%s c:%s" %d
|
gyorgyf@30
|
360 x,y,count = d
|
gyorgyf@30
|
361 self.add_blob(x,y,count)
|
gyorgyf@30
|
362 self.indicators["receive"].toggle()
|
gyorgyf@30
|
363 self.conn.close()
|
gyorgyf@30
|
364 self.blobs = self.blobs[:NBLOBS]
|
gyorgyf@30
|
365 self.compute_quadrant_weighting()
|
gyorgyf@30
|
366 self.self_change_instrument()
|
gyorgyf@30
|
367 return True
|
gyorgyf@30
|
368
|
gyorgyf@30
|
369 def compute_quadrant_weighting(self):
|
gyorgyf@30
|
370 quadrant_dict = {1:[],2:[],3:[],4:[]}
|
gyorgyf@30
|
371 # sort blobs into quadrants
|
gyorgyf@30
|
372 for blob in self.blobs :
|
gyorgyf@30
|
373 quadrant_dict[blob.quadrant].append(blob)
|
gyorgyf@30
|
374 # get weight for each
|
gyorgyf@30
|
375 quadrant_weights = []
|
gyorgyf@30
|
376 for q,blob_list in quadrant_dict.iteritems() :
|
gyorgyf@30
|
377 quadrant_weights.append(sum(map(lambda x: x.alpha * x.size,blob_list)))
|
gyorgyf@30
|
378 self.active_quadrant = np.argmax(quadrant_weights) + 1
|
gyorgyf@30
|
379 print self.active_quadrant
|
gyorgyf@30
|
380 return self.active_quadrant
|
gyorgyf@30
|
381
|
gyorgyf@30
|
382 def self_change_instrument(self):
|
gyorgyf@30
|
383 if self.active_quadrant != self.prev_quadrant :
|
gyorgyf@30
|
384 self.prev_quadrant = self.active_quadrant
|
gyorgyf@30
|
385 args = IMAP[self.active_quadrant]
|
gyorgyf@30
|
386 self.send_midi_patch_change_GR20(*args)
|
gyorgyf@30
|
387 print args
|
gyorgyf@30
|
388
|
gyorgyf@30
|
389 def add_blob(self,x,y,count=1):
|
gyorgyf@30
|
390 '''Insert a blob to the list of blobs'''
|
gyorgyf@30
|
391 # find mood correxponding to x,y
|
gyorgyf@30
|
392 cmood = None
|
gyorgyf@30
|
393 d = cd = sys.float_info.max
|
gyorgyf@30
|
394 for mood in self.moods :
|
gyorgyf@30
|
395 d = mood.get_distance(x,y)
|
gyorgyf@30
|
396 if d < cd :
|
gyorgyf@30
|
397 cd = d
|
gyorgyf@30
|
398 cmood = mood
|
gyorgyf@30
|
399 # create new blob or increase click count on existing one
|
gyorgyf@30
|
400 new = Blob(self.bg,x,y,mood=cmood,fade=self.FADE)
|
gyorgyf@30
|
401 if not new in self.blobs :
|
gyorgyf@30
|
402 self.blobs.insert(0,new)
|
gyorgyf@30
|
403 # self.send_midi()
|
gyorgyf@30
|
404 elif count > self.blobs[self.blobs.index(new)].count:
|
gyorgyf@30
|
405 self.blobs[self.blobs.index(new)].increment(count)
|
gyorgyf@30
|
406 pass
|
gyorgyf@30
|
407
|
gyorgyf@30
|
408 def send_midi(self):
|
gyorgyf@30
|
409 # self.midi_out.write([[[0xc0,0,0],20000],[[0x90,60,100],20500]])
|
gyorgyf@30
|
410 self.midi_out.write([[[0x90,60,100],0],[[0x90,60,100],500]])
|
gyorgyf@30
|
411
|
gyorgyf@30
|
412 def send_midi_patch_change_GR20(self,bank,instrument):
|
gyorgyf@30
|
413 '''PIANO = BANK 1, Patch 5.. bank starts from 1, patch starts from 0 so I have to substract one...'''
|
gyorgyf@30
|
414 self.midi_out.write([[[0xB0,0x0,bank],0],[[0xC0,instrument],100]])
|
gyorgyf@30
|
415 # Control change (B) followed by patch change (C):
|
gyorgyf@30
|
416 # midi_out.write([[[0xB0,0x0,0x9],0],[[0xC0,0x0],100]])
|
gyorgyf@30
|
417 # midi_out.write([[[0xB0,0x0,int(bank)],0],[[0xC0,int(instrument)-1],100]])
|
gyorgyf@30
|
418 # 1 HONKY TONK 0x1 / 0x4 (5)
|
gyorgyf@30
|
419 # 2 SITAR 0x9 / 0x0 (1)
|
gyorgyf@30
|
420 # 3 Melancholia
|
gyorgyf@30
|
421 # 4 Rock GITAR 0x3 / 0x2C (45)
|
gyorgyf@30
|
422
|
gyorgyf@30
|
423
|
gyorgyf@30
|
424 def draw(self):
|
gyorgyf@30
|
425 self.bg.fill((0,0,0))
|
gyorgyf@30
|
426 # self.bg.blit(gradients.radial(19, self.scol, self.ecol), (rect_x,rect_y))
|
gyorgyf@30
|
427 l = copy.copy(self.blobs)
|
gyorgyf@30
|
428 l.reverse()
|
gyorgyf@30
|
429 for blob in l :
|
gyorgyf@30
|
430 blob.draw()
|
gyorgyf@30
|
431
|
gyorgyf@30
|
432 # axis
|
gyorgyf@30
|
433 pg.draw.line(self.bg, (G,G,G), (int(X/2.0),0),(int(X/2.0),Y), 1)
|
gyorgyf@30
|
434 pg.draw.line(self.bg, (G,G,G), (0,int(Y/2.0)),(X,int(Y/2.0)), 1)
|
gyorgyf@30
|
435
|
gyorgyf@30
|
436 # indicators
|
gyorgyf@30
|
437 for i in self.indicators.itervalues() :
|
gyorgyf@30
|
438 i.draw()
|
gyorgyf@30
|
439
|
gyorgyf@30
|
440 def read_keys(self):
|
gyorgyf@30
|
441 '''Read keys'''
|
gyorgyf@30
|
442 for event in pg.event.get() :
|
gyorgyf@30
|
443 # Quit (event)
|
gyorgyf@30
|
444 if event.type == QUIT:
|
gyorgyf@30
|
445 self.midi_out.write(NOTE_OFF_CH0)
|
gyorgyf@30
|
446 self.quit()
|
gyorgyf@30
|
447 elif event.type == KEYDOWN :
|
gyorgyf@30
|
448 # Post Quit: Esc, q
|
gyorgyf@30
|
449 if event.key == K_ESCAPE or event.key == K_q :
|
gyorgyf@30
|
450 pg.event.post(pg.event.Event(QUIT))
|
gyorgyf@30
|
451 # Reset: Space
|
gyorgyf@30
|
452 elif event.key == K_SPACE :
|
gyorgyf@30
|
453 self.blobs = []
|
gyorgyf@30
|
454 # Random : r
|
gyorgyf@30
|
455 elif event.key == K_r :
|
gyorgyf@30
|
456 self.add_blob(random(),random(),count=5)
|
gyorgyf@30
|
457 # Connection : c
|
gyorgyf@30
|
458 elif event.key == K_c :
|
gyorgyf@30
|
459 self.init_reconnect = True
|
gyorgyf@30
|
460 self.indicators["conn"].off()
|
gyorgyf@30
|
461 # Fullscreen: f
|
gyorgyf@30
|
462 elif event.key == K_f :
|
gyorgyf@30
|
463 # pg.display.toggle_fullscreen()
|
gyorgyf@30
|
464 self.toggle_screen_mode()
|
gyorgyf@30
|
465 # Toggle fade: s
|
gyorgyf@30
|
466 elif event.key == K_s :
|
gyorgyf@30
|
467 if self.FADE :
|
gyorgyf@30
|
468 print "fade off"
|
gyorgyf@30
|
469 self.indicators["conn"].off()
|
gyorgyf@30
|
470 self.FADE = 0
|
gyorgyf@30
|
471 for blob in self.blobs :
|
gyorgyf@30
|
472 blob.fade(0)
|
gyorgyf@30
|
473 else:
|
gyorgyf@30
|
474 print "fade on"
|
gyorgyf@30
|
475 self.indicators["conn"].on()
|
gyorgyf@30
|
476 self.FADE = 15
|
gyorgyf@30
|
477 for blob in self.blobs :
|
gyorgyf@30
|
478 blob.fade(15)
|
gyorgyf@30
|
479 blob.reset_time()
|
gyorgyf@30
|
480 # inc age
|
gyorgyf@30
|
481 elif event.key == K_1 :
|
gyorgyf@30
|
482 self.s_age += 1
|
gyorgyf@30
|
483 self.update_server_config(self.s_age,self.s_dist,self.s_ninp)
|
gyorgyf@30
|
484 # dec age
|
gyorgyf@30
|
485 elif event.key == K_2 :
|
gyorgyf@30
|
486 self.s_age -= 1
|
gyorgyf@30
|
487 self.update_server_config(self.s_age,self.s_dist,self.s_ninp)
|
gyorgyf@30
|
488 # inc dist
|
gyorgyf@30
|
489 elif event.key == K_3 :
|
gyorgyf@30
|
490 self.s_dist += 0.025
|
gyorgyf@30
|
491 self.update_server_config(self.s_age,self.s_dist,self.s_ninp)
|
gyorgyf@30
|
492 # dec dist
|
gyorgyf@30
|
493 elif event.key == K_4 :
|
gyorgyf@30
|
494 self.s_dist -= 0.025
|
gyorgyf@30
|
495 if self.s_dist < 0.025 : self.s_dist = 0.025
|
gyorgyf@30
|
496 self.update_server_config(self.s_age,self.s_dist,self.s_ninp)
|
gyorgyf@30
|
497 # inc ninp
|
gyorgyf@30
|
498 elif event.key == K_5 :
|
gyorgyf@30
|
499 self.s_ninp += 1
|
gyorgyf@30
|
500 self.update_server_config(self.s_age,self.s_dist,self.s_ninp)
|
gyorgyf@30
|
501 # dec ninp
|
gyorgyf@30
|
502 elif event.key == K_6 :
|
gyorgyf@30
|
503 self.s_ninp -= 1
|
gyorgyf@30
|
504 if self.s_ninp < 2 : self.s_ninp = 2
|
gyorgyf@30
|
505 self.update_server_config(self.s_age,self.s_dist,self.s_ninp)
|
gyorgyf@30
|
506
|
gyorgyf@30
|
507 pass
|
gyorgyf@30
|
508 pass
|
gyorgyf@30
|
509
|
gyorgyf@30
|
510 def toggle_screen_mode(self):
|
gyorgyf@30
|
511 '''Go back and forth between full screen mode.'''
|
gyorgyf@30
|
512 if self.fullscreen == False:
|
gyorgyf@30
|
513 globals()['_X'] = globals()['X']
|
gyorgyf@30
|
514 globals()['_Y'] = globals()['Y']
|
gyorgyf@30
|
515 globals()['X'] = XF
|
gyorgyf@30
|
516 globals()['Y'] = YF
|
gyorgyf@30
|
517 self.screen = pg.display.set_mode((X, Y))
|
gyorgyf@30
|
518 # self.screen = pg.display.set_mode((0,0),pg.FULLSCREEN)
|
gyorgyf@30
|
519 self.fullscreen = True
|
gyorgyf@30
|
520 self.bg = pg.Surface(self.screen.get_size())
|
gyorgyf@30
|
521 self.bg = self.bg.convert()
|
gyorgyf@30
|
522 self.bg.fill((0,0,0))
|
gyorgyf@30
|
523 for i in self.indicators.itervalues() :
|
gyorgyf@30
|
524 i.reinit(self.bg)
|
gyorgyf@30
|
525 i.draw()
|
gyorgyf@30
|
526 else :
|
gyorgyf@30
|
527 globals()['X'] = globals()['_X']
|
gyorgyf@30
|
528 globals()['Y'] = globals()['_Y']
|
gyorgyf@30
|
529 self.screen = pg.display.set_mode((X, Y))
|
gyorgyf@30
|
530 self.fullscreen = False
|
gyorgyf@30
|
531 self.bg = pg.Surface(self.screen.get_size())
|
gyorgyf@30
|
532 self.bg = self.bg.convert()
|
gyorgyf@30
|
533 self.bg.fill((0,0,0))
|
gyorgyf@30
|
534 for i in self.indicators.itervalues() :
|
gyorgyf@30
|
535 i.reinit(self.bg)
|
gyorgyf@30
|
536 i.draw()
|
gyorgyf@30
|
537 pg.display.toggle_fullscreen()
|
gyorgyf@30
|
538
|
gyorgyf@30
|
539
|
gyorgyf@30
|
540
|
gyorgyf@30
|
541 def run(self):
|
gyorgyf@30
|
542
|
gyorgyf@30
|
543 # setup connection
|
gyorgyf@30
|
544 self.connect()
|
gyorgyf@30
|
545
|
gyorgyf@30
|
546 # main loop
|
gyorgyf@30
|
547 while True :
|
gyorgyf@30
|
548 # pg.draw.circle(screen, pg.Color(255,0,0), (300,50),20,0)
|
gyorgyf@30
|
549 # screen.blit(gradients.radial(99, scol, ecol), (401, 1))
|
gyorgyf@30
|
550
|
gyorgyf@30
|
551 self.read_keys()
|
gyorgyf@30
|
552
|
gyorgyf@30
|
553 # Draw
|
gyorgyf@30
|
554 self.draw()
|
gyorgyf@30
|
555
|
gyorgyf@30
|
556 # update display
|
gyorgyf@30
|
557 self.screen.blit(self.bg, (0, 0))
|
gyorgyf@30
|
558 pg.display.flip()
|
gyorgyf@30
|
559 self.fpsClock.tick(FRAMERATE)
|
gyorgyf@30
|
560
|
gyorgyf@30
|
561 if self.init_reconnect:
|
gyorgyf@30
|
562 self.reconnect()
|
gyorgyf@30
|
563
|
gyorgyf@30
|
564 return True
|
gyorgyf@30
|
565
|
gyorgyf@30
|
566 def configure_server(self):
|
gyorgyf@30
|
567 '''Send the server some configuration data.'''
|
gyorgyf@30
|
568 # age = 10.0
|
gyorgyf@30
|
569 # dist = DIST
|
gyorgyf@30
|
570 # ninp = 18
|
gyorgyf@30
|
571 self.update_server_config(self.s_age,self.s_dist,self.s_ninp)
|
gyorgyf@30
|
572
|
gyorgyf@30
|
573
|
gyorgyf@30
|
574 def update_server_config(self,age,dist,ninp,retry = 3):
|
gyorgyf@30
|
575 '''Send the server some configuration data.'''
|
gyorgyf@30
|
576 try :
|
gyorgyf@30
|
577 self.conn.putrequest("GET","/moodconductor/config?age=%(age)s&dist=%(dist)s&ninp=%(ninp)s" %locals(), skip_host=True)
|
gyorgyf@30
|
578 self.conn.putheader("Host", "www.isophonics.net")
|
gyorgyf@30
|
579 self.conn.endheaders()
|
gyorgyf@30
|
580 res = self.conn.getresponse()
|
gyorgyf@30
|
581 if not res.status == 200 :
|
gyorgyf@30
|
582 print "Server response:", res.status, res.reason
|
gyorgyf@30
|
583 self.indicators["conn"].off()
|
gyorgyf@30
|
584 time.sleep(0.5)
|
gyorgyf@30
|
585 self.conn.putrequest("GET","/moodconductor/getconf", skip_host=True)
|
gyorgyf@30
|
586 self.conn.putheader("Host", "www.isophonics.net")
|
gyorgyf@30
|
587 self.conn.endheaders()
|
gyorgyf@30
|
588 res = self.conn.getresponse()
|
gyorgyf@30
|
589 if not res.status == 200 :
|
gyorgyf@30
|
590 print "Server response:", res.status, res.reason
|
gyorgyf@30
|
591 self.indicators["conn"].off()
|
gyorgyf@30
|
592 print "Server configuration:", res.read()
|
gyorgyf@30
|
593 except:
|
gyorgyf@30
|
594 time.sleep(2)
|
gyorgyf@30
|
595 retry -= 1
|
gyorgyf@30
|
596 self.update_server_config(age,dist,ninp,retry)
|
gyorgyf@30
|
597
|
gyorgyf@30
|
598
|
gyorgyf@30
|
599 def connect(self,retry = 5):
|
gyorgyf@30
|
600 '''Connect to the server and test connection.'''
|
gyorgyf@30
|
601 if not retry :
|
gyorgyf@30
|
602 print "Server unreachable"
|
gyorgyf@30
|
603 pg.quit()
|
gyorgyf@30
|
604 raise SystemExit
|
gyorgyf@30
|
605 if retry < 5 :
|
gyorgyf@30
|
606 time.sleep(3)
|
gyorgyf@30
|
607 try :
|
gyorgyf@30
|
608 self.conn = ht.HTTPConnection(IP,timeout=HTTP_TIMEOUT)
|
gyorgyf@30
|
609 # self.start_update_thread()
|
gyorgyf@30
|
610 self.indicators["conn"].on()
|
gyorgyf@30
|
611 except :
|
gyorgyf@30
|
612 self.indicators["conn"].off()
|
gyorgyf@30
|
613 self.connect(retry = retry-1)
|
gyorgyf@30
|
614
|
gyorgyf@30
|
615 try:
|
gyorgyf@30
|
616 self.conn.putrequest("GET","/moodconductor/index.html", skip_host=True)
|
gyorgyf@30
|
617 self.conn.putheader("Host", "www.isophonics.net")
|
gyorgyf@30
|
618 self.conn.endheaders()
|
gyorgyf@30
|
619 res = self.conn.getresponse()
|
gyorgyf@30
|
620 if res.status == 200 :
|
gyorgyf@30
|
621 self.indicators["conn"].on()
|
gyorgyf@30
|
622 else :
|
gyorgyf@30
|
623 print "Server response:", res.status, res.reason
|
gyorgyf@30
|
624 self.indicators["conn"].off()
|
gyorgyf@30
|
625 if not hasattr(self,"noretry") and raw_input("Go offline? ") in ['y',''] :
|
gyorgyf@30
|
626 return False
|
gyorgyf@30
|
627 else :
|
gyorgyf@30
|
628 self.noretry = None
|
gyorgyf@30
|
629 self.connect(retry = retry-1)
|
gyorgyf@30
|
630 except :
|
gyorgyf@30
|
631 print "Exception while testing connection."
|
gyorgyf@30
|
632 self.indicators["conn"].off()
|
gyorgyf@30
|
633 # comment out in offline mode
|
gyorgyf@30
|
634 if not hasattr(self,"noretry") and raw_input("Go offline? ") in ['y',''] :
|
gyorgyf@30
|
635 return False
|
gyorgyf@30
|
636 else :
|
gyorgyf@30
|
637 self.noretry = None
|
gyorgyf@30
|
638 self.connect(retry = retry-1)
|
gyorgyf@30
|
639 self.configure_server()
|
gyorgyf@30
|
640 self.start_update_thread()
|
gyorgyf@30
|
641 return True
|
gyorgyf@30
|
642
|
gyorgyf@30
|
643
|
gyorgyf@30
|
644
|
gyorgyf@30
|
645 def reconnect(self):
|
gyorgyf@30
|
646 '''Called when c is pressed.'''
|
gyorgyf@30
|
647 self.init_reconnect = False
|
gyorgyf@30
|
648 # self.indicators["conn"].off().draw()
|
gyorgyf@30
|
649 # self.screen.blit(self.bg, (0, 0))
|
gyorgyf@30
|
650 # pg.display.flip()
|
gyorgyf@30
|
651
|
gyorgyf@30
|
652 self.stop_update_thread()
|
gyorgyf@30
|
653 time.sleep(1)
|
gyorgyf@30
|
654 try :
|
gyorgyf@30
|
655 self.conn.close()
|
gyorgyf@30
|
656 except :
|
gyorgyf@30
|
657 self.indicators["conn"].off()
|
gyorgyf@30
|
658 try :
|
gyorgyf@30
|
659 self.conn = ht.HTTPConnection(IP,timeout=HTTP_TIMEOUT)
|
gyorgyf@30
|
660 self.conn.connect()
|
gyorgyf@30
|
661 self.indicators["conn"].on()
|
gyorgyf@30
|
662 print "Reconnected."
|
gyorgyf@30
|
663 except :
|
gyorgyf@30
|
664 self.indicators["conn"].off()
|
gyorgyf@30
|
665 print "Error while reconnecting."
|
gyorgyf@30
|
666 self.start_update_thread()
|
gyorgyf@30
|
667
|
gyorgyf@30
|
668
|
gyorgyf@30
|
669 def quit(self):
|
gyorgyf@30
|
670 print "Quitting.."
|
gyorgyf@30
|
671 self.indicators["conn"].off()
|
gyorgyf@30
|
672 self.stop_update_thread()
|
gyorgyf@30
|
673 self.conn.close()
|
gyorgyf@30
|
674 pg.quit()
|
gyorgyf@30
|
675 sys.exit()
|
gyorgyf@30
|
676
|
gyorgyf@30
|
677
|
gyorgyf@30
|
678
|
gyorgyf@30
|
679
|
gyorgyf@30
|
680
|
gyorgyf@30
|
681 def main():
|
gyorgyf@30
|
682
|
gyorgyf@30
|
683 v = VisualClient()
|
gyorgyf@30
|
684 v.run()
|
gyorgyf@30
|
685
|
gyorgyf@30
|
686
|
gyorgyf@30
|
687 if __name__ == '__main__':
|
gyorgyf@30
|
688 pass
|
gyorgyf@30
|
689 main()
|
gyorgyf@30
|
690
|