annotate visualclient2/midiclient.py @ 54:c0b34039917a tip

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