Gestion explicite de KeyboardInterrupt pour être sûr de quitter le jeu sur un ^C.
[minwii.git] / src / minwii / loganalyse.py
index 1a1f676..61b0382 100755 (executable)
@@ -12,12 +12,14 @@ from minwii.musicxml import musicXml2Song
 from minwii.globals import PLAYING_MODES
 from statlib import stats
 from datetime import timedelta
+from xml.etree import ElementTree
+import os.path
 
 PLAYING_MODES = dict(PLAYING_MODES)
 
 DEFAULT_STATS = (#'geometricmean',
-                 #'harmonicmean',
-                 #'mean',
+                 ('harmonicmean', 'Moyenne harmonique'),
+                 ('mean', 'Moyenne  '),
                  ('median', 'Médiane'),
                  #'medianscore',
                  #'mode',
@@ -42,29 +44,41 @@ def statsresults(m) :
     computeList.__doc__ = m.__doc__
     return computeList
 
+def timebased(m) :
+    m.timebased = True
+    return m
+
 class LogFileAnalyser(LogFileReader) :
 
     POSSIBLE_ANALYSES = {'BEGINNER' : ('songDuration',
                                        'playingDuration',
                                        'noteEndNoteOnLatency',
                                        'realisationRate')
+                                       
                         ,'EASY'     : ('songDuration',
                                        'playingDuration',
                                        'noteEndNoteOnLatency',
                                        'realisationRate',
-                                       'missCount')
+                                       'missCount',
+                                       'getMissPerTimeFrame')
+                                       
                         ,'NORMAL'   : ('songDuration',
                                        'playingDuration',
                                        'realisationRate',
-                                       'missCount')
+                                       'missCount',
+                                       'getMissPerTimeFrame')
+                                       
                         ,'ADVANCED' : ('songDuration',
                                        'playingDuration',
                                        'realisationRate',
-                                       'missCount')
+                                       'missCount',
+                                       'getMissPerTimeFrame')
+                                       
                         ,'EXPERT'   : ('songDuration',
                                        'playingDuration',
                                        'realisationRate',
-                                       'missCount')
+                                       'missCount',
+                                       'getMissPerTimeFrame')
                         }
     
     def analyse(self) :
@@ -72,15 +86,36 @@ class LogFileAnalyser(LogFileReader) :
         
         try :
             self.mode = mode = self.getMode()
-            results.append(('Mode de jeu', PLAYING_MODES.get(mode, mode)))
+            results.append(('Mode de jeu', PLAYING_MODES.get(mode, mode), False))
+
+            self.songTitle = LogFileAnalyser.getSongTitle(self.getSongFile())
+            results.append(('Chanson', self.songTitle, False))
+
             for name in self.POSSIBLE_ANALYSES[mode] :
                 meth = getattr(self, name)
-                results.append((meth.__doc__, meth()))
+                results.append( (meth.__doc__, meth(), getattr(meth, 'timebased', False)) )
         except :
             raise
         
         return results
     
+    @staticmethod
+    def getSongTitle(file) :
+        if os.path.exists(file) :
+            it = ElementTree.iterparse(file, ['start', 'end'])
+            creditFound = False
+
+            for evt, el in it :
+                if el.tag == 'credit' :
+                    creditFound = True
+                if el.tag == 'credit-words' and creditFound:
+                    return el.text
+                if el.tag == 'part-list' :
+                    # plus de chance de trouver un titre
+                    return os.path.basename(file)
+        else :
+            return os.path.basename(file)
+
     def _toTimeDelta(self, milliseconds) :
         duration = milliseconds / 1000.
         duration = int(round(duration, 0))
@@ -104,10 +139,7 @@ class LogFileAnalyser(LogFileReader) :
         
         songFile = self.getSongFile()
         song = musicXml2Song(songFile)
-        duration = 0
-        for note, verseIndex in song.iterNotes() :
-            duration = duration + note.duration
-        duration = duration * song.quarterNoteDuration # en milisecondes
+        duration = song.duration
         return self._toTimeDelta(duration)
     
     @statsresults
@@ -179,6 +211,46 @@ class LogFileAnalyser(LogFileReader) :
                         miss = miss + 1
         
         return miss
+    
+    @timebased
+    def getMissPerTimeFrame(self, timeFrame=10000) :
+        "Nombre d'erreurs en fonction du temps"
+        eIter = self.getEventsIterator()
+        firstTicks = self.getFirstEventTicks()
+        frames = [0]
+
+        if self.mode in ('EASY', 'NORMAL') :
+            catchColUp = False
+            for ticks, eventName, message in eIter :
+                if ticks - firstTicks > timeFrame :
+                    firstTicks = ticks
+                    frames.append(0)
+                    
+                if eventName == 'COLDOWN' :
+                    colState = message.split(None, 2)[1]
+                    colState = colState == 'True'
+                    if colState :
+                        catchColUp = False
+                        continue
+                    else :
+                        catchColUp = True
+                elif eventName == 'NOTEON' :
+                    catchColUp = False
+                elif eventName == 'COLUP' and catchColUp :
+                    frames[-1] = frames[-1] + 1
+        else :
+            for ticks, eventName, message in eIter :
+                if ticks - firstTicks > timeFrame :
+                    firstTicks = ticks
+                    frames.append(0)
+                
+                if eventName == 'COLDOWN' :
+                    colState = message.split(None, 2)[1]
+                    colState = colState == 'True'
+                    if not colState :
+                        frames[-1] = frames[-1] + 1
+        
+        return frames