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'
18 class LogFileReader(object) :
20 classe utilitaire pour l'accès aux données d'un fichier de log MinWii.
23 def __init__(self
, logfile
) :
24 """ logfile : chemin d'accès au fichier de log MinWii.
25 le format supporté est actuellement la version 1.0 uniquement.
27 if isinstance(logfile
, str) :
28 self
.logfile
= open(logfile
, 'r')
30 self
.logfile
= logfile
32 firstline
= self
.next()
33 assert firstline
== SUPPORTED_FILE_HEADER
36 def getSongFile(self
) :
37 "retourne le chemin d'accès au fichier musicxml de la chanson"
43 if l
.startswith('APP chanson :') :
45 songfile
= l
.split(':', 1)[1].strip()
49 def getSoundFontFile(self
) :
50 "retourne le chemin d'accès au fichier de la soundfont (*.sf2)"
55 if l
.startswith('ENV soundfont :') :
57 soundFontFile
= l
.split(':', 1)[1].strip()
62 "retourne le paramètre bank du synthétiseur (entier)"
67 if l
.startswith('APP bank :') :
70 bank
= l
.split(':', 1)[1].strip()
74 "retourne le paramètre preset du synthétiseur (entier)"
79 if l
.startswith('APP preset :') :
82 preset
= l
.split(':', 1)[1].strip()
85 def getScreenResolution(self
) :
86 "retourne la résolution écran (tuple de deux entiers)"
91 if l
.startswith('ENV résolution écran :') :
93 screenResolution
= eval(l
.split(':', 1)[1].strip())
95 return screenResolution
98 "retourne le niveau de difficulté"
102 if l
.startswith('APP mode :') :
105 mode
= l
.split(':', 1)[1].strip()
109 def getFirstEventTicks(self
) :
110 "retourne le timecode du premier événement (entier)"
115 if l
.startswith('EVT ') :
117 firstTicks
= int(l
.split(None, 2)[1])
128 line
= self
.logfile
.next().strip()
131 def getEventsIterator(self
) :
132 """ Retourne un itérateur sur les événements.
133 Chaque itération retourne un tuple de 3 éléments :
134 (timecode, nom_événement, données) avec le typage :
135 (entier, chaîne, chaîne)
141 except StopIteration :
144 if not l
.startswith('EVT ') :
147 ticks
, eventName
, message
= l
.split(None, 3)[1:]
149 yield ticks
, eventName
, message
151 ticks
, eventName
= l
.split(None, 3)[1:]
153 yield ticks
, eventName
, ''
156 class LogFilePlayer(PlayingScreenBase
) :
158 ré-exécution d'une chanson sur la base de son fichier de log.
161 def __init__(self
, logfile
) :
162 lfr
= self
.lfr
= LogFileReader(logfile
)
163 songFile
= lfr
.getSongFile()
164 soundFontFile
= lfr
.getSoundFontFile()
165 sfPath
= lfr
.getSoundFontFile()
167 preset
= lfr
.getPreset()
168 synth
= Synth(sfPath
=sfPath
)
169 synth
.program_select(0, bank
, preset
)
170 self
.song
= musicXml2Song(songFile
)
171 screenResolution
= lfr
.getScreenResolution()
173 pygame
.display
.set_mode(screenResolution
)
175 super(LogFilePlayer
, self
).__init
__(synth
, self
.song
.distinctNotes
)
179 clock
= pygame
.time
.Clock()
180 pygame
.display
.flip()
181 pygame
.mouse
.set_visible(False)
183 previousTicks
= self
.lfr
.getFirstEventTicks()
184 eIter
= self
.lfr
.getEventsIterator()
186 for ticks
, eventName
, message
in eIter
:
187 t0
= pygame
.time
.get_ticks()
188 if eventName
== 'COLSTATECHANGE' :
189 parts
= message
.split(None, 4)
192 index
, state
, midi
, name
, syllabus
= parts
195 state
= state
== 'True'
196 col
= self
.columns
[midi
]
197 col
.update(state
, syllabus
=syllabus
.decode('utf-8'))
199 elif eventName
== 'NOTEON':
200 chan
, key
, vel
= [int(v
) for v
in message
.split(None, 2)]
201 self
.synth
.noteon(chan
, key
, vel
)
203 elif eventName
== 'NOTEOFF':
204 chan
, key
= [int(v
) for v
in message
.split(None, 1)]
205 self
.synth
.noteoff(chan
, key
)
207 elif eventName
.startswith('COL') :
208 pos
= [int(n
) for n
in message
.split(None, 4)[-1].strip('()').split(',')]
209 self
.cursor
.setPosition(pos
)
214 dirty
= self
.draw(pygame
.display
.get_surface())
215 pygame
.display
.update(dirty
)
216 execTime
= pygame
.time
.get_ticks() - t0
218 delay
= ticks
- previousTicks
- execTime
220 pygame
.time
.wait(delay
)
222 previousTicks
= ticks