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
16 PLAYING_MODES
= dict(PLAYING_MODES
)
18 DEFAULT_STATS
= (#'geometricmean',
19 ('harmonicmean', 'Moyenne harmonique'),
21 ('median', 'Médiane'),
25 ('variation', 'Variation'),
27 ('kurtosis', 'Kurtosis'),
35 def computeList(self
):
38 for name
, label
in DEFAULT_STATS
:
39 results
.append('%s : %s' % (label
, getattr(stats
, name
)(l
)))
40 return '\n'.join(results
)
41 computeList
.__name
__ = m
.__name
__
42 computeList
.__doc
__ = m
.__doc
__
45 class LogFileAnalyser(LogFileReader
) :
47 POSSIBLE_ANALYSES
= {'BEGINNER' : ('songDuration',
49 'noteEndNoteOnLatency',
51 ,'EASY' : ('songDuration',
53 'noteEndNoteOnLatency',
56 ,'NORMAL' : ('songDuration',
60 ,'ADVANCED' : ('songDuration',
64 ,'EXPERT' : ('songDuration',
74 self
.mode
= mode
= self
.getMode()
75 results
.append(('Mode de jeu', PLAYING_MODES
.get(mode
, mode
)))
76 for name
in self
.POSSIBLE_ANALYSES
[mode
] :
77 meth
= getattr(self
, name
)
78 results
.append((meth
.__doc
__, meth()))
84 def _toTimeDelta(self
, milliseconds
) :
85 duration
= milliseconds
/ 1000.
86 duration
= int(round(duration
, 0))
87 return str(timedelta(seconds
=duration
))
89 def playingDuration(self
) :
91 #retourne la durée écoulée entre le premier et de dernier message
92 #de type événement : correspond à la durée d'interprétation.
94 last
= self
.getLastEventTicks()
95 first
= self
.getFirstEventTicks()
96 return self
._toTimeDelta
(last
- first
)
99 def songDuration(self
) :
100 'Durée de référence de la chanson'
101 #retourne la durée de référence de la chanson
102 #en prenant en compte le tempo présent dans la transcription
103 #et en effectuant toutes les répétitions des couplets / refrains.
105 songFile
= self
.getSongFile()
106 song
= musicXml2Song(songFile
)
108 for note
, verseIndex
in song
.iterNotes() :
109 duration
= duration
+ note
.duration
110 duration
= duration
* song
.quarterNoteDuration
# en milisecondes
111 return self
._toTimeDelta
(duration
)
114 def noteEndNoteOnLatency(self
) :
116 eIter
= self
.getEventsIterator()
120 for ticks
, eventName
, message
in eIter
:
121 if eventName
== 'NOTEEND':
123 if eventName
== 'NOTEON' and lastnoteEndT
:
124 latencies
.append(ticks
- lastnoteEndT
)
128 def noteOnCount(self
) :
129 "retourne le nombre d'événements NOTEON"
131 eIter
= self
.getEventsIterator()
134 for ticks
, eventName
, message
in eIter
:
135 if eventName
== 'NOTEON' :
140 def realisationRate(self
) :
141 'Taux de réalisation'
142 #taux de réalisation en nombre de note
143 #peut être supérieur à 100 % car la chanson
146 songFile
= self
.getSongFile()
147 song
= musicXml2Song(songFile
)
149 for note
, verseIndex
in song
.iterNotes() :
150 songNoteCpt
= songNoteCpt
+ 1
152 return round(self
.noteOnCount() / float(songNoteCpt
) * 100, 1)
154 def missCount(self
) :
156 eIter
= self
.getEventsIterator()
158 if self
.mode
in ('EASY', 'NORMAL') :
160 for ticks
, eventName
, message
in eIter
:
161 if eventName
== 'COLDOWN' :
162 colState
= message
.split(None, 2)[1]
163 colState
= colState
== 'True'
169 elif eventName
== 'NOTEON' :
171 elif eventName
== 'COLUP' and catchColUp
:
174 for ticks
, eventName
, message
in eIter
:
175 if eventName
== 'COLDOWN' :
176 colState
= message
.split(None, 2)[1]
177 colState
= colState
== 'True'
188 from optparse
import OptionParser
189 usage
= "%prog logfile"
190 op
= OptionParser(usage
)
191 options
, args
= op
.parse_args()
193 op
.error("incorrect number of arguments")
196 lfa
= LogFileAnalyser(args
[0])
197 pprint(lfa
.analyse())
199 if __name__
== "__main__" :
200 from os
.path
import realpath
, sep
202 minwiipath
= realpath(__file__
).split(sep
)
203 minwiipath
= minwiipath
[:-2]
204 minwiipath
= sep
.join(minwiipath
)
205 sys
.path
.insert(1, minwiipath
)