annotate visualclient/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 4a7fde8ff0fd
children
rev   line source
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