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'
18 class LogFileReader(object) :
20 classe utilitaire pour l'accès aux données d'un fichier de log MinWii.
23 def __init__(self
, logfile
) :
24 """ logfile : chemin d'accès au fichier de log MinWii.
25 le format supporté est actuellement la version 1.0 uniquement.
27 if isinstance(logfile
, str) :
28 self
.logfile
= open(logfile
, 'r')
30 self
.logfile
= logfile
32 firstline
= self
.next()
33 assert firstline
== SUPPORTED_FILE_HEADER
36 def getSongFile(self
) :
37 "retourne le chemin d'accès au fichier musicxml de la chanson"
43 if l
.startswith('APP chanson :') :
45 songfile
= l
.split(':', 1)[1].strip()
49 def getSoundFontFile(self
) :
50 "retourne le chemin d'accès au fichier de la soundfont (*.sf2)"
55 if l
.startswith('ENV soundfont :') :
57 soundFontFile
= l
.split(':', 1)[1].strip()
62 "retourne le paramètre bank du synthétiseur (entier)"
67 if l
.startswith('APP bank :') :
70 bank
= l
.split(':', 1)[1].strip()
74 "retourne le paramètre preset du synthétiseur (entier)"
79 if l
.startswith('APP preset :') :
82 preset
= l
.split(':', 1)[1].strip()
85 def getScreenResolution(self
) :
86 "retourne la résolution écran (tuple de deux entiers)"
91 if l
.startswith('ENV résolution écran :') :
93 screenResolution
= eval(l
.split(':', 1)[1].strip())
95 return screenResolution
97 def getFirstEventTicks(self
) :
98 "retourne le timecode du premier événement (entier)"
103 if l
.startswith('EVT ') :
105 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