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 statlib
import stats
13 from datetime
import timedelta
15 DEFAULT_STATS
= (#'geometricmean',
18 ('median', 'Médiane'),
22 ('variation', 'Variation'),
24 ('kurtosis', 'Kurtosis'),
32 def computeList(self
):
35 for name
, label
in DEFAULT_STATS
:
36 results
.append('%s : %s' % (label
, getattr(stats
, name
)(l
)))
37 return '\n'.join(results
)
38 computeList
.__name
__ = m
.__name
__
39 computeList
.__doc
__ = m
.__doc
__
42 class LogFileAnalyser(LogFileReader
) :
44 POSSIBLE_ANALYSES
= {'BEGINNER' : ('songDuration',
46 'noteEndNoteOnLatency',
48 ,'EASY' : ('songDuration',
50 'noteEndNoteOnLatency',
53 ,'NORMAL' : ('songDuration',
57 ,'ADVANCED' : ('songDuration',
61 ,'EXPERT' : ('songDuration',
71 self
.mode
= mode
= self
.getMode()
72 results
.append(('Mode de jeu', mode
))
73 for name
in self
.POSSIBLE_ANALYSES
[mode
] :
74 meth
= getattr(self
, name
)
75 results
.append((meth
.__doc
__, meth()))
81 def _toTimeDelta(self
, milliseconds
) :
82 duration
= milliseconds
/ 1000.
83 duration
= int(round(duration
, 0))
84 return str(timedelta(seconds
=duration
))
86 def playingDuration(self
) :
88 #retourne la durée écoulée entre le premier et de dernier message
89 #de type événement : correspond à la durée d'interprétation.
91 last
= self
.getLastEventTicks()
92 first
= self
.getFirstEventTicks()
93 return self
._toTimeDelta
(last
- first
)
96 def songDuration(self
) :
97 'Durée de référence de la chanson'
98 #retourne la durée de référence de la chanson
99 #en prenant en compte le tempo présent dans la transcription
100 #et en effectuant toutes les répétitions des couplets / refrains.
102 songFile
= self
.getSongFile()
103 song
= musicXml2Song(songFile
)
105 for note
, verseIndex
in song
.iterNotes() :
106 duration
= duration
+ note
.duration
107 duration
= duration
* song
.quarterNoteDuration
# en milisecondes
108 return self
._toTimeDelta
(duration
)
111 def noteEndNoteOnLatency(self
) :
113 eIter
= self
.getEventsIterator()
117 for ticks
, eventName
, message
in eIter
:
118 if eventName
== 'NOTEEND':
120 if eventName
== 'NOTEON' and lastnoteEndT
:
121 latencies
.append(ticks
- lastnoteEndT
)
125 def noteOnCount(self
) :
126 "retourne le nombre d'événements NOTEON"
128 eIter
= self
.getEventsIterator()
131 for ticks
, eventName
, message
in eIter
:
132 if eventName
== 'NOTEON' :
137 def realisationRate(self
) :
138 'Taux de réalisation'
139 #taux de réalisation en nombre de note
140 #peut être supérieur à 100 % car la chanson
143 songFile
= self
.getSongFile()
144 song
= musicXml2Song(songFile
)
146 for note
, verseIndex
in song
.iterNotes() :
147 songNoteCpt
= songNoteCpt
+ 1
149 return round(self
.noteOnCount() / float(songNoteCpt
) * 100, 1)
151 def missCount(self
) :
153 eIter
= self
.getEventsIterator()
155 if self
.mode
in ('EASY', 'NORMAL') :
157 for ticks
, eventName
, message
in eIter
:
158 if eventName
== 'COLDOWN' :
159 colState
= message
.split(None, 2)[1]
160 colState
= colState
== 'True'
166 elif eventName
== 'NOTEON' :
168 elif eventName
== 'COLUP' and catchColUp
:
171 for ticks
, eventName
, message
in eIter
:
172 if eventName
== 'COLDOWN' :
173 colState
= message
.split(None, 2)[1]
174 colState
= colState
== 'True'
185 from optparse
import OptionParser
186 usage
= "%prog logfile"
187 op
= OptionParser(usage
)
188 options
, args
= op
.parse_args()
190 op
.error("incorrect number of arguments")
193 lfa
= LogFileAnalyser(args
[0])
194 pprint(lfa
.analyse())
196 if __name__
== "__main__" :
197 from os
.path
import realpath
, sep
199 minwiipath
= realpath(__file__
).split(sep
)
200 minwiipath
= minwiipath
[:-2]
201 minwiipath
= sep
.join(minwiipath
)
202 sys
.path
.insert(1, minwiipath
)