-# -*- coding: utf-8 -*-
-"""
-Module de lecture des fichiers de log minwii
-
-$Id$
-$URL$
-"""
-
-from widgets.playingscreen import PlayingScreenBase
-from eventutils import EventDispatcher
-from events import eventCodes
-from synth import Synth
-from musicxml import musicXml2Song
-import pygame
-
-SUPPORTED_FILE_HEADER = 'ENV winwii log format version : 1.0'
-
-class LogFileReader(object) :
- """
- classe utilitaire pour l'accès aux données d'un fichier de log MinWii.
- """
-
- def __init__(self, logfile) :
- """ logfile : chemin d'accès au fichier de log MinWii.
- le format supporté est actuellement la version 1.0 uniquement.
- """
- if isinstance(logfile, str) :
- self.logfile = open(logfile, 'r')
- else :
- self.logfile = logfile
-
- firstline = self.next()
- assert firstline == SUPPORTED_FILE_HEADER
-
-
- def getSongFile(self) :
- "retourne le chemin d'accès au fichier musicxml de la chanson"
- f = self.logfile
- pos = f.tell()
-
- f.seek(0)
- for l in self :
- if l.startswith('APP chanson :') :
- break
- songfile = l.split(':', 1)[1].strip()
- f.seek(pos)
- return songfile
-
- def getSoundFontFile(self) :
- "retourne le chemin d'accès au fichier de la soundfont (*.sf2)"
- f = self.logfile
- pos = f.tell()
- f.seek(0)
- for l in self :
- if l.startswith('ENV soundfont :') :
- break
- soundFontFile = l.split(':', 1)[1].strip()
- f.seek(pos)
- return soundFontFile
-
- def getBank(self) :
- "retourne le paramètre bank du synthétiseur (entier)"
- f = self.logfile
- pos = f.tell()
- f.seek(0)
- for l in self :
- if l.startswith('APP bank :') :
- break
- f.seek(pos)
- bank = l.split(':', 1)[1].strip()
- return int(bank)
-
- def getPreset(self) :
- "retourne le paramètre preset du synthétiseur (entier)"
- f = self.logfile
- pos = f.tell()
- f.seek(0)
- for l in self :
- if l.startswith('APP preset :') :
- break
- f.seek(pos)
- preset = l.split(':', 1)[1].strip()
- return int(preset)
-
- def getScreenResolution(self) :
- "retourne la résolution écran (tuple de deux entiers)"
- f = self.logfile
- pos = f.tell()
- f.seek(0)
- for l in self :
- if l.startswith('ENV résolution écran :') :
- break
- screenResolution = eval(l.split(':', 1)[1].strip())
- f.seek(pos)
- return screenResolution
-
- def getFirstEventTicks(self) :
- "retourne le timecode du premier événement (entier)"
- f = self.logfile
- pos = f.tell()
- f.seek(0)
- for l in self :
- if l.startswith('EVT ') :
- break
- firstTicks = int(l.split(None, 2)[1])
- f.seek(pos)
- return firstTicks
-
- def __del__(self) :
- self.logfile.close()
-
- def __iter__(self) :
- return self
-
- def next(self) :
- line = self.logfile.next().strip()
- return line
-
- def getEventsIterator(self) :
- """ Retourne un itérateur sur les événements.
- Chaque itération retourne un tuple de 3 éléments :
- (timecode, nom_événement, données) avec le typage :
- (entier, chaîne, chaîne)
- """
- self.logfile.seek(0)
- while True :
- try :
- l = self.next()
- except StopIteration :
- break
-
- if not l.startswith('EVT ') :
- continue
- try :
- ticks, eventName, message = l.split(None, 3)[1:]
- ticks = int(ticks)
- yield ticks, eventName, message
- except ValueError :
- ticks, eventName = l.split(None, 3)[1:]
- ticks = int(ticks)
- yield ticks, eventName, ''
-
-
-class LogFilePlayer(PlayingScreenBase) :
- """
- ré-exécution d'une chanson sur la base de son fichier de log.
- """
-
- def __init__(self, logfile) :
- lfr = self.lfr = LogFileReader(logfile)
- songFile = lfr.getSongFile()
- soundFontFile = lfr.getSoundFontFile()
- sfPath = lfr.getSoundFontFile()
- bank = lfr.getBank()
- preset = lfr.getPreset()
- synth = Synth(sfPath=sfPath)
- synth.program_select(0, bank, preset)
- self.song = musicXml2Song(songFile)
- screenResolution = lfr.getScreenResolution()
-
- pygame.display.set_mode(screenResolution)
-
- super(LogFilePlayer, self).__init__(synth, self.song.distinctNotes)
-
- def run(self):
- self._running = True
- clock = pygame.time.Clock()
- pygame.display.flip()
- pygame.mouse.set_visible(False)
-
- previousTicks = self.lfr.getFirstEventTicks()
- eIter = self.lfr.getEventsIterator()
-
- for ticks, eventName, message in eIter :
- t0 = pygame.time.get_ticks()
- if eventName == 'COLSTATECHANGE' :
- parts = message.split(None, 4)
- if len(parts) == 4 :
- parts.append('')
- index, state, midi, name, syllabus = parts
- index = int(index)
- midi = int(midi)
- state = state == 'True'
- col = self.columns[midi]
- col.update(state, syllabus=syllabus.decode('utf-8'))
-
- elif eventName == 'NOTEON':
- chan, key, vel = [int(v) for v in message.split(None, 2)]
- self.synth.noteon(chan, key, vel)
-
- elif eventName == 'NOTEOFF':
- chan, key = [int(v) for v in message.split(None, 1)]
- self.synth.noteoff(chan, key)
-
- elif eventName.startswith('COL') :
- pos = [int(n) for n in message.split(None, 4)[-1].strip('()').split(',')]
- self.cursor.setPosition(pos)
-
-
- pygame.event.clear()
-
- dirty = self.draw(pygame.display.get_surface())
- pygame.display.update(dirty)
- execTime = pygame.time.get_ticks() - t0
-
- delay = ticks - previousTicks - execTime
- if delay > 0 :
- pygame.time.wait(delay)
-
- previousTicks = ticks
-
- self.stop()
-
-
\ No newline at end of file