1 # -*- coding: utf-8 -*-
3 Module d'analyse des fichiers de log minwii.
9 from minwii
.logfilereader
import LogFileReader
10 from pprint
import pprint
11 from minwii
.musicxml
import musicXml2Song
12 from minwii
.globals import PLAYING_MODES
13 from statlib
import stats
14 from datetime
import timedelta
15 from xml
.etree
import ElementTree
18 PLAYING_MODES
= dict(PLAYING_MODES
)
20 DEFAULT_STATS
= (#'geometricmean',
21 ('harmonicmean', 'Moyenne harmonique'),
23 ('median', 'Médiane'),
27 ('variation', 'Variation'),
29 ('kurtosis', 'Kurtosis'),
37 def computeList(self
):
40 for name
, label
in DEFAULT_STATS
:
41 results
.append('%s : %s' % (label
, getattr(stats
, name
)(l
)))
42 return '\n'.join(results
)
43 computeList
.__name
__ = m
.__name
__
44 computeList
.__doc
__ = m
.__doc
__
47 class LogFileAnalyser(LogFileReader
) :
49 POSSIBLE_ANALYSES
= {'BEGINNER' : ('songDuration',
51 'noteEndNoteOnLatency',
53 ,'EASY' : ('songDuration',
55 'noteEndNoteOnLatency',
58 ,'NORMAL' : ('songDuration',
62 ,'ADVANCED' : ('songDuration',
66 ,'EXPERT' : ('songDuration',
76 self
.mode
= mode
= self
.getMode()
77 results
.append(('Mode de jeu', PLAYING_MODES
.get(mode
, mode
)))
79 self
.songTitle
= LogFileAnalyser
.getSongTitle(self
.getSongFile())
80 results
.append(('Chanson', self
.songTitle
))
82 for name
in self
.POSSIBLE_ANALYSES
[mode
] :
83 meth
= getattr(self
, name
)
84 results
.append((meth
.__doc
__, meth()))
91 def getSongTitle(file) :
92 if os
.path
.exists(file) :
93 it
= ElementTree
.iterparse(file, ['start', 'end'])
97 if el
.tag
== 'credit' :
99 if el
.tag
== 'credit-words' and creditFound
:
101 if el
.tag
== 'part-list' :
102 # plus de chance de trouver un titre
103 return os
.path
.basename(file)
105 return os
.path
.basename(file)
107 def _toTimeDelta(self
, milliseconds
) :
108 duration
= milliseconds
/ 1000.
109 duration
= int(round(duration
, 0))
110 return str(timedelta(seconds
=duration
))
112 def playingDuration(self
) :
114 #retourne la durée écoulée entre le premier et de dernier message
115 #de type événement : correspond à la durée d'interprétation.
117 last
= self
.getLastEventTicks()
118 first
= self
.getFirstEventTicks()
119 return self
._toTimeDelta
(last
- first
)
122 def songDuration(self
) :
123 'Durée de référence de la chanson'
124 #retourne la durée de référence de la chanson
125 #en prenant en compte le tempo présent dans la transcription
126 #et en effectuant toutes les répétitions des couplets / refrains.
128 songFile
= self
.getSongFile()
129 song
= musicXml2Song(songFile
)
131 for note
, verseIndex
in song
.iterNotes() :
132 duration
= duration
+ note
.duration
133 duration
= duration
* song
.quarterNoteDuration
# en milisecondes
134 return self
._toTimeDelta
(duration
)
137 def noteEndNoteOnLatency(self
) :
139 eIter
= self
.getEventsIterator()
143 for ticks
, eventName
, message
in eIter
:
144 if eventName
== 'NOTEEND':
146 if eventName
== 'NOTEON' and lastnoteEndT
:
147 latencies
.append(ticks
- lastnoteEndT
)
151 def noteOnCount(self
) :
152 "retourne le nombre d'événements NOTEON"
154 eIter
= self
.getEventsIterator()
157 for ticks
, eventName
, message
in eIter
:
158 if eventName
== 'NOTEON' :
163 def realisationRate(self
) :
164 'Taux de réalisation'
165 #taux de réalisation en nombre de note
166 #peut être supérieur à 100 % car la chanson
169 songFile
= self
.getSongFile()
170 song
= musicXml2Song(songFile
)
172 for note
, verseIndex
in song
.iterNotes() :
173 songNoteCpt
= songNoteCpt
+ 1
175 return round(self
.noteOnCount() / float(songNoteCpt
) * 100, 1)
177 def missCount(self
) :
179 eIter
= self
.getEventsIterator()
181 if self
.mode
in ('EASY', 'NORMAL') :
183 for ticks
, eventName
, message
in eIter
:
184 if eventName
== 'COLDOWN' :
185 colState
= message
.split(None, 2)[1]
186 colState
= colState
== 'True'
192 elif eventName
== 'NOTEON' :
194 elif eventName
== 'COLUP' and catchColUp
:
197 for ticks
, eventName
, message
in eIter
:
198 if eventName
== 'COLDOWN' :
199 colState
= message
.split(None, 2)[1]
200 colState
= colState
== 'True'
211 from optparse
import OptionParser
212 usage
= "%prog logfile"
213 op
= OptionParser(usage
)
214 options
, args
= op
.parse_args()
216 op
.error("incorrect number of arguments")
219 lfa
= LogFileAnalyser(args
[0])
220 pprint(lfa
.analyse())
222 if __name__
== "__main__" :
223 from os
.path
import realpath
, sep
225 minwiipath
= realpath(__file__
).split(sep
)
226 minwiipath
= minwiipath
[:-2]
227 minwiipath
= sep
.join(minwiipath
)
228 sys
.path
.insert(1, minwiipath
)