1 # -*- coding: utf-8 -*-
3 Module de lecture des fichiers de log minwii
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
16 SUPPORTED_FILE_HEADER
= 'ENV winwii log format version : 1.0'
19 def readinplace(self
, *args
, **kw
) :
21 ret
= m(self
, *args
, **kw
)
26 class LogFileReader(object) :
28 classe utilitaire pour l'accès aux données d'un fichier de log MinWii.
31 def __init__(self
, logfile
) :
32 """ logfile : chemin d'accès au fichier de log MinWii.
33 le format supporté est actuellement la version 1.0 uniquement.
35 if isinstance(logfile
, str) :
36 self
.logfile
= open(logfile
, 'r')
38 self
.logfile
= logfile
42 firstline
= self
.next()
43 assert firstline
== SUPPORTED_FILE_HEADER
46 self
.__pos
= self
.logfile
.tell()
50 self
.logfile
.seek(self
.__pos
)
53 def getSongFile(self
) :
54 "retourne le chemin d'accès au fichier musicxml de la chanson"
56 if l
.startswith('APP chanson :') :
58 songfile
= l
.split(':', 1)[1].strip()
62 def getSoundFontFile(self
) :
63 "retourne le chemin d'accès au fichier de la soundfont (*.sf2)"
65 if l
.startswith('ENV soundfont :') :
67 soundFontFile
= l
.split(':', 1)[1].strip()
72 "retourne le paramètre bank du synthétiseur (entier)"
74 if l
.startswith('APP bank :') :
76 bank
= l
.split(':', 1)[1].strip()
81 "retourne le paramètre preset du synthétiseur (entier)"
83 if l
.startswith('APP preset :') :
85 preset
= l
.split(':', 1)[1].strip()
89 def getScreenResolution(self
) :
90 "retourne la résolution écran (tuple de deux entiers)"
92 if l
.startswith('ENV résolution écran :') :
94 screenResolution
= eval(l
.split(':', 1)[1].strip())
95 return screenResolution
99 "retourne le niveau de difficulté"
101 if l
.startswith('APP mode :') :
104 mode
= l
.split(':', 1)[1].strip()
108 def getFirstEventTicks(self
) :
109 "retourne le timecode du premier événement (entier)"
111 if l
.startswith('EVT ') :
113 firstTicks
= int(l
.split(None, 2)[1])
123 line
= self
.logfile
.next().strip()
126 def getEventsIterator(self
) :
127 """ Retourne un itérateur sur les événements.
128 Chaque itération retourne un tuple de 3 éléments :
129 (timecode, nom_événement, données) avec le typage :
130 (entier, chaîne, chaîne)
136 except StopIteration :
139 if not l
.startswith('EVT ') :
142 ticks
, eventName
, message
= l
.split(None, 3)[1:]
144 yield ticks
, eventName
, message
146 ticks
, eventName
= l
.split(None, 3)[1:]
148 yield ticks
, eventName
, ''
151 class LogFilePlayer(PlayingScreenBase
) :
153 ré-exécution d'une chanson sur la base de son fichier de log.
156 def __init__(self
, logfile
) :
157 lfr
= self
.lfr
= LogFileReader(logfile
)
158 songFile
= lfr
.getSongFile()
159 soundFontFile
= lfr
.getSoundFontFile()
160 sfPath
= lfr
.getSoundFontFile()
162 preset
= lfr
.getPreset()
163 synth
= Synth(sfPath
=sfPath
)
164 synth
.program_select(0, bank
, preset
)
165 self
.song
= musicXml2Song(songFile
)
166 screenResolution
= lfr
.getScreenResolution()
168 pygame
.display
.set_mode(screenResolution
)
170 super(LogFilePlayer
, self
).__init
__(synth
, self
.song
.distinctNotes
)
174 clock
= pygame
.time
.Clock()
175 pygame
.display
.flip()
176 pygame
.mouse
.set_visible(False)
178 previousTicks
= self
.lfr
.getFirstEventTicks()
179 eIter
= self
.lfr
.getEventsIterator()
181 for ticks
, eventName
, message
in eIter
:
182 t0
= pygame
.time
.get_ticks()
183 if eventName
== 'COLSTATECHANGE' :
184 parts
= message
.split(None, 4)
187 index
, state
, midi
, name
, syllabus
= parts
190 state
= state
== 'True'
191 col
= self
.columns
[midi
]
192 col
.update(state
, syllabus
=syllabus
.decode('utf-8'))
194 elif eventName
== 'NOTEON':
195 chan
, key
, vel
= [int(v
) for v
in message
.split(None, 2)]
196 self
.synth
.noteon(chan
, key
, vel
)
198 elif eventName
== 'NOTEOFF':
199 chan
, key
= [int(v
) for v
in message
.split(None, 1)]
200 self
.synth
.noteoff(chan
, key
)
202 elif eventName
.startswith('COL') :
203 pos
= [int(n
) for n
in message
.split(None, 4)[-1].strip('()').split(',')]
204 self
.cursor
.setPosition(pos
)
209 dirty
= self
.draw(pygame
.display
.get_surface())
210 pygame
.display
.update(dirty
)
211 execTime
= pygame
.time
.get_ticks() - t0
213 delay
= ticks
- previousTicks
- execTime
215 pygame
.time
.wait(delay
)
217 previousTicks
= ticks