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
) :
20 pos
= self
.logfile
.tell()
22 ret
= m(self
, *args
, **kw
)
23 self
.logfile
.seek(pos
)
27 class LogFileReader(object) :
29 classe utilitaire pour l'accès aux données d'un fichier de log MinWii.
32 def __init__(self
, logfile
) :
33 """ logfile : chemin d'accès au fichier de log MinWii.
34 le format supporté est actuellement la version 1.0 uniquement.
36 if isinstance(logfile
, str) :
37 self
.logfile
= open(logfile
, 'r')
39 self
.logfile
= logfile
41 firstline
= self
.next()
42 assert firstline
== SUPPORTED_FILE_HEADER
46 def getSongFile(self
) :
47 "retourne le chemin d'accès au fichier musicxml de la chanson"
49 if l
.startswith('APP chanson :') :
51 songfile
= l
.split(':', 1)[1].strip()
55 def getSoundFontFile(self
) :
56 "retourne le chemin d'accès au fichier de la soundfont (*.sf2)"
58 if l
.startswith('ENV soundfont :') :
60 soundFontFile
= l
.split(':', 1)[1].strip()
65 "retourne le paramètre bank du synthétiseur (entier)"
67 if l
.startswith('APP bank :') :
69 bank
= l
.split(':', 1)[1].strip()
74 "retourne le paramètre preset du synthétiseur (entier)"
76 if l
.startswith('APP preset :') :
78 preset
= l
.split(':', 1)[1].strip()
82 def getScreenResolution(self
) :
83 "retourne la résolution écran (tuple de deux entiers)"
85 if l
.startswith('ENV résolution écran :') :
87 screenResolution
= eval(l
.split(':', 1)[1].strip())
88 return screenResolution
92 "retourne le niveau de difficulté"
94 if l
.startswith('APP mode :') :
97 mode
= l
.split(':', 1)[1].strip()
101 def getFirstEventTicks(self
) :
102 "retourne le timecode du premier événement (entier)"
104 if l
.startswith('EVT ') :
106 firstTicks
= int(l
.split(None, 2)[1])
116 line
= self
.logfile
.next().strip()
119 def getEventsIterator(self
) :
120 """ Retourne un itérateur sur les événements.
121 Chaque itération retourne un tuple de 3 éléments :
122 (timecode, nom_événement, données) avec le typage :
123 (entier, chaîne, chaîne)
129 except StopIteration :
132 if not l
.startswith('EVT ') :
135 ticks
, eventName
, message
= l
.split(None, 3)[1:]
137 yield ticks
, eventName
, message
139 ticks
, eventName
= l
.split(None, 3)[1:]
141 yield ticks
, eventName
, ''
144 class LogFilePlayer(PlayingScreenBase
) :
146 ré-exécution d'une chanson sur la base de son fichier de log.
149 def __init__(self
, logfile
) :
150 lfr
= self
.lfr
= LogFileReader(logfile
)
151 songFile
= lfr
.getSongFile()
152 soundFontFile
= lfr
.getSoundFontFile()
153 sfPath
= lfr
.getSoundFontFile()
155 preset
= lfr
.getPreset()
156 synth
= Synth(sfPath
=sfPath
)
157 synth
.program_select(0, bank
, preset
)
158 self
.song
= musicXml2Song(songFile
)
159 screenResolution
= lfr
.getScreenResolution()
161 pygame
.display
.set_mode(screenResolution
)
163 super(LogFilePlayer
, self
).__init
__(synth
, self
.song
.distinctNotes
)
167 clock
= pygame
.time
.Clock()
168 pygame
.display
.flip()
169 pygame
.mouse
.set_visible(False)
171 previousTicks
= self
.lfr
.getFirstEventTicks()
172 eIter
= self
.lfr
.getEventsIterator()
174 for ticks
, eventName
, message
in eIter
:
175 t0
= pygame
.time
.get_ticks()
176 if eventName
== 'COLSTATECHANGE' :
177 parts
= message
.split(None, 4)
180 index
, state
, midi
, name
, syllabus
= parts
183 state
= state
== 'True'
184 col
= self
.columns
[midi
]
185 col
.update(state
, syllabus
=syllabus
.decode('utf-8'))
187 elif eventName
== 'NOTEON':
188 chan
, key
, vel
= [int(v
) for v
in message
.split(None, 2)]
189 self
.synth
.noteon(chan
, key
, vel
)
191 elif eventName
== 'NOTEOFF':
192 chan
, key
= [int(v
) for v
in message
.split(None, 1)]
193 self
.synth
.noteoff(chan
, key
)
195 elif eventName
.startswith('COL') :
196 pos
= [int(n
) for n
in message
.split(None, 4)[-1].strip('()').split(',')]
197 self
.cursor
.setPosition(pos
)
202 dirty
= self
.draw(pygame
.display
.get_surface())
203 pygame
.display
.update(dirty
)
204 execTime
= pygame
.time
.get_ticks() - t0
206 delay
= ticks
- previousTicks
- execTime
208 pygame
.time
.wait(delay
)
210 previousTicks
= ticks