premier jet sur le replay d'une chanson à partir d'un fichier de log.
[minwii.git] / src / app / logfilereader.py
1 # -*- coding: utf-8 -*-
2 """
3 Module de lecture des fichiers de log minwii
4
5 $Id$
6 $URL$
7 """
8
9 from widgets.playingscreen import PlayingScreenBase
10 from eventutils import EventDispatcher
11 from events import eventCodes
12 from synth import Synth
13 from musicxml import musicXml2Song
14 import pygame
15
16 SUPPORTED_FILE_HEADER = 'ENV winwii log format version : 1.0-alpha'
17
18 class LogFileReader(object) :
19
20 def __init__(self, logfile) :
21 if isinstance(logfile, str) :
22 self.logfile = open(logfile, 'r')
23 else :
24 self.logfile = logfile
25
26 firstline = self.next()
27 assert firstline == SUPPORTED_FILE_HEADER
28
29
30 def getSongFile(self) :
31 f = self.logfile
32 pos = f.tell()
33
34 f.seek(0)
35 for l in self :
36 if l.startswith('APP chanson :') :
37 break
38 songfile = l.split(':', 1)[1].strip()
39 f.seek(pos)
40 return songfile
41
42 def getSoundFontFile(self) :
43 f = self.logfile
44 pos = f.tell()
45 f.seek(0)
46 for l in self :
47 if l.startswith('ENV soundfont :') :
48 break
49 soundFontFile = l.split(':', 1)[1].strip()
50 return soundFontFile
51
52 def getScreenResolution(self) :
53 f = self.logfile
54 pos = f.tell()
55 f.seek(0)
56 for l in self :
57 if l.startswith('ENV résolution écran :') :
58 break
59 screenResolution = eval(l.split(':', 1)[1].strip())
60 return screenResolution
61
62 def getFirstEventTicks(self) :
63 f = self.logfile
64 pos = f.tell()
65 f.seek(0)
66 for l in self :
67 if l.startswith('EVT ') :
68 break
69 firstTicks = int(l.split(None, 2)[1])
70 return firstTicks
71
72 def __del__(self) :
73 self.logfile.close()
74
75 def __iter__(self) :
76 return self
77
78 def next(self) :
79 line = self.logfile.next().strip()
80 return line
81
82 def getEventsIterator(self) :
83 while True :
84 try :
85 l = self.next()
86 except StopIteration :
87 break
88
89 if not l.startswith('EVT ') :
90 continue
91 try :
92 ticks, eventName, message = l.split(None, 3)[1:]
93 yield ticks, eventName, message
94 except ValueError :
95 ticks, eventName = l.split(None, 3)[1:]
96 yield ticks, eventName, ''
97
98
99 class LogFilePlayer(PlayingScreenBase) :
100 """
101 ré-exécution d'une chanson sur la base de son fichier de log.
102 """
103
104 def __init__(self, logfile) :
105 lfr = self.lfr = LogFileReader(logfile)
106 songFile = lfr.getSongFile()
107 soundFontFile = lfr.getSoundFontFile()
108 sfPath = lfr.getSoundFontFile()
109 synth = Synth(sfPath=sfPath)
110 self.song = musicXml2Song(songFile)
111 screenResolution = lfr.getScreenResolution()
112
113 pygame.display.set_mode(screenResolution)
114
115 super(LogFilePlayer, self).__init__(synth, self.song.distinctNotes)
116
117 def run(self):
118 self._running = True
119 clock = pygame.time.Clock()
120 pygame.display.flip()
121 pygame.mouse.set_visible(False)
122
123 previousTicks = self.lfr.getFirstEventTicks()
124 eIter = self.lfr.getEventsIterator()
125
126 for ticks, eventName, message in eIter :
127 ticks = int(ticks)
128 if eventName == 'COLSTATECHANGE' :
129 parts = message.split(None, 4)
130 if len(parts) == 4 :
131 parts.append('')
132 index, state, midi, name, syllabus = parts
133 index = int(index)
134 midi = int(midi)
135 state = state == 'True'
136 col = self.columns[midi]
137 col.update(state, syllabus=syllabus.decode('utf-8'))
138
139 pygame.event.clear() # à virer
140 #EventDispatcher.dispatchEvents()
141
142 dirty = self.draw(pygame.display.get_surface())
143 pygame.display.update(dirty)
144 execTime = clock.tick()
145
146 delay = ticks - previousTicks - execTime
147 if delay > 0 :
148 pygame.time.wait(delay)
149
150 previousTicks = ticks
151 #print ticks, eventName, message
152
153 #while self._running :
154 # EventDispatcher.dispatchEvents()
155 # dirty = self.draw(pygame.display.get_surface())
156 # pygame.display.update(dirty)
157 # clock.tick()
158
159