|
@@ -0,0 +1,577 @@
|
|
|
|
+#!/usr/bin/python
|
|
|
|
+# Type of LED Functions.
|
|
|
|
+# 0=off,
|
|
|
|
+# 1=green,
|
|
|
|
+# 2=green blink,
|
|
|
|
+# 3=red,
|
|
|
|
+# 4=red blink,
|
|
|
|
+# 5=yellow,
|
|
|
|
+# 6=yellow blink,
|
|
|
|
+# 7-127=green,
|
|
|
|
+
|
|
|
|
+# Type of LED Ring Functions
|
|
|
|
+# 0=off,
|
|
|
|
+# 1=Single,
|
|
|
|
+# 2=Volume Style,
|
|
|
|
+# 3=Pan Style,
|
|
|
|
+# 4-127=Single
|
|
|
|
+
|
|
|
|
+import logging
|
|
|
|
+import sys
|
|
|
|
+import time
|
|
|
|
+import rtmidi
|
|
|
|
+import random
|
|
|
|
+
|
|
|
|
+from rtmidi.midiutil import open_midioutput
|
|
|
|
+from rtmidi.midiconstants import NOTE_OFF, NOTE_ON
|
|
|
|
+from simplemidi.midiio import MidiInputPort, MidiOutputPort
|
|
|
|
+from simplemidi.midimessage import MidiType
|
|
|
|
+from simplemidi.alsaconnector import AlsaConnector
|
|
|
|
+from simplemidi.options import *
|
|
|
|
+"""
|
|
|
|
+Classe qui represente un bouton
|
|
|
|
+"""
|
|
|
|
+class Button:
|
|
|
|
+ def __init__(self, port, index, channel=1):
|
|
|
|
+ self.channel=channel # canal midi : int
|
|
|
|
+ self.oport=port # port de sortie : MidiOut
|
|
|
|
+ self.index=index # index (=note) du bouton : int
|
|
|
|
+ Button.LED_UNKNOWN=-1 # etat du bouton (couleur de la led) incoonu
|
|
|
|
+ Button.OFF=0
|
|
|
|
+ self.state=Button.LED_UNKNOWN # etat de la led : int
|
|
|
|
+ self.mindata=0 #valeur minimal que le bouton peut predre : int
|
|
|
|
+ self.maxdata=0 #valeur max que le bouton peut predre : int
|
|
|
|
+ self.values=[] # liste des valeurs que le bouton peut predre : int
|
|
|
|
+ self.lastState=0 # etat précédent (pour la fonction toggle()) : int
|
|
|
|
+
|
|
|
|
+ def onNoteOn(self, channel, idx, val):
|
|
|
|
+ pass
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ def onNoteOff(self, channel, idx, val):
|
|
|
|
+ pass
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ """
|
|
|
|
+ Envoi un note on pour changer de couleur
|
|
|
|
+ dat: int: couleur a mettre
|
|
|
|
+ """
|
|
|
|
+ def send(self, data):
|
|
|
|
+ if data<=self.maxdata and data>=self.mindata:
|
|
|
|
+ self.state=data
|
|
|
|
+ if data!=Button.OFF and (data in self.values):
|
|
|
|
+ self.lastState=data
|
|
|
|
+ self.oport.noteOn(1, self.index, data)
|
|
|
|
+ time.sleep(0.00001)
|
|
|
|
+
|
|
|
|
+ """
|
|
|
|
+ Demande au bouton de passe à la couleur suivante
|
|
|
|
+ """
|
|
|
|
+ def next(self):
|
|
|
|
+ if self.state==Button.LED_UNKNOWN:
|
|
|
|
+ return
|
|
|
|
+ x=self.state+1
|
|
|
|
+ if x>self.maxdata:
|
|
|
|
+ x=self.mindata
|
|
|
|
+ self.send(x)
|
|
|
|
+
|
|
|
|
+ """
|
|
|
|
+ Demande au bouton de passe à la couleur précédente
|
|
|
|
+ """
|
|
|
|
+ def prev(self):
|
|
|
|
+ if self.state==Button.LED_UNKNOWN:
|
|
|
|
+ return
|
|
|
|
+ x=self.state-1
|
|
|
|
+ if x<self.mindata:
|
|
|
|
+ x=self.maxdata
|
|
|
|
+ self.send(x)
|
|
|
|
+
|
|
|
|
+ """
|
|
|
|
+ Prend (parmis les valeur valide) une couleur au hasard
|
|
|
|
+ """
|
|
|
|
+ def random(self):
|
|
|
|
+ self.send(random.sample(self.values, 1)[0])
|
|
|
|
+
|
|
|
|
+ """
|
|
|
|
+ Bascule d'un état allumé à un etat eteint (et vis versa)
|
|
|
|
+ """
|
|
|
|
+ def toggle(self):
|
|
|
|
+ if self.lastState==Button.OFF:
|
|
|
|
+ self.lastState=self.values[0]
|
|
|
|
+ if self.state!=Button.LED_UNKNOWN and self.state!=Button.OFF:
|
|
|
|
+ self.send(0)
|
|
|
|
+ else:
|
|
|
|
+ self.send(self.lastState)
|
|
|
|
+
|
|
|
|
+"""
|
|
|
|
+Gere les boutons classiques a 7 états (les boutons de 0 a 63 dans
|
|
|
|
+l' AKAI APC Mini
|
|
|
|
+"""
|
|
|
|
+class LedButton(Button):
|
|
|
|
+ def __init__(self, port, index, channel=1):
|
|
|
|
+ Button.__init__(self, port, index, channel)
|
|
|
|
+ LedButton.UNKNOWN=Button.LED_UNKNOWN
|
|
|
|
+ LedButton.OFF=0
|
|
|
|
+ LedButton.GREEN=1
|
|
|
|
+ LedButton.GREEN_BLINK=2
|
|
|
|
+ LedButton.RED=3
|
|
|
|
+ LedButton.RED_BLINK=4
|
|
|
|
+ LedButton.YELLOW=5
|
|
|
|
+ LedButton.YELLOW_BLINK=6
|
|
|
|
+ self.maxdata=6
|
|
|
|
+ self.values=[0,1,2,3,4,5,6]
|
|
|
|
+
|
|
|
|
+ """
|
|
|
|
+ Eteint la led du bouton
|
|
|
|
+ """
|
|
|
|
+ def off(self):
|
|
|
|
+ self.send(LedButton.OFF)
|
|
|
|
+
|
|
|
|
+ """
|
|
|
|
+ Passe le bouton en vert
|
|
|
|
+ blink: bool : Fait clignter la led
|
|
|
|
+ """
|
|
|
|
+ def green(self, blink=False):
|
|
|
|
+ self.send(LedButton.GREEN_BLINK if blink else LedButton.GREEN)
|
|
|
|
+
|
|
|
|
+ """
|
|
|
|
+ Passe le bouton en rouge
|
|
|
|
+ blink: bool : Fait clignter la led
|
|
|
|
+ """
|
|
|
|
+ def red(self, blink=False):
|
|
|
|
+ self.send(LedButton.RED_BLINK if blink else LedButton.RED)
|
|
|
|
+
|
|
|
|
+ """
|
|
|
|
+ Passe le bouton en jaune
|
|
|
|
+ blink: bool : Fait clignter la led
|
|
|
|
+ """
|
|
|
|
+ def yellow(self, blink=False):
|
|
|
|
+ self.send(LedButton.YELLOW_BLINK if blink else LedButton.YELLOW)
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+class LedCtrlButton(Button):
|
|
|
|
+ def __init__(self, port, index, channel=1):
|
|
|
|
+ Button.__init__(self, port, index, channel)
|
|
|
|
+ LedCtrlButton.UNKNOWN=Button.LED_UNKNOWN
|
|
|
|
+ LedCtrlButton.OFF=0
|
|
|
|
+ LedCtrlButton.ON=1
|
|
|
|
+ LedCtrlButton.BLINK=2
|
|
|
|
+ self.maxdata=2
|
|
|
|
+ self.values=[0,1,2]
|
|
|
|
+
|
|
|
|
+ def off(self):
|
|
|
|
+ self.send(LedCtrlButton.OFF)
|
|
|
|
+
|
|
|
|
+ def on(self):
|
|
|
|
+ self.send(LedCtrlButton.ON)
|
|
|
|
+
|
|
|
|
+ def blink(self):
|
|
|
|
+ self.send(LedCtrlButton.BLINK)
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+MIDI_ERROR_STRING={
|
|
|
|
+ rtmidi.ERRORTYPE_WARNING:"ERRORTYPE_WARNING",
|
|
|
|
+ rtmidi.ERRORTYPE_DEBUG_WARNING:"ERRORTYPE_DEBUG_WARNING",
|
|
|
|
+ rtmidi.ERRORTYPE_UNSPECIFIED:"ERRORTYPE_UNSPECIFIED",
|
|
|
|
+ rtmidi.ERRORTYPE_NO_DEVICES_FOUND:"ERRORTYPE_NO_DEVICES_FOUND",
|
|
|
|
+ rtmidi.ERRORTYPE_INVALID_DEVICE:"ERRORTYPE_INVALID_DEVICE",
|
|
|
|
+ rtmidi.ERRORTYPE_MEMORY_ERROR:"ERRORTYPE_MEMORY_ERROR",
|
|
|
|
+ rtmidi.ERRORTYPE_INVALID_PARAMETER:"ERRORTYPE_INVALID_PARAMETER",
|
|
|
|
+ rtmidi.ERRORTYPE_INVALID_USE:"ERRORTYPE_INVALID_USE",
|
|
|
|
+ rtmidi.ERRORTYPE_DRIVER_ERROR:"ERRORTYPE_DRIVER_ERROR",
|
|
|
|
+ rtmidi.ERRORTYPE_SYSTEM_ERROR:"ERRORTYPE_SYSTEM_ERROR",
|
|
|
|
+ rtmidi.ERRORTYPE_THREAD_ERROR:"ERRORTYPE_THREAD_ERROR"
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+"""
|
|
|
|
+Classe qui gère un Pad
|
|
|
|
+"""
|
|
|
|
+
|
|
|
|
+class Pad():
|
|
|
|
+ _DEFAULT_PARAMS={
|
|
|
|
+ 'port_in': MidiInputPort._DEFAULT_PARAMS,
|
|
|
|
+ 'port_out': MidiOutputPort._DEFAULT_PARAMS
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ def __init__(self, adapter, params={}):
|
|
|
|
+ param=initParams(Pad._DEFAULT_PARAMS, params)
|
|
|
|
+
|
|
|
|
+ self.oport=MidiOutputPort.fromParams(param['port_out'])
|
|
|
|
+ self.iport=MidiInputPort.fromParams(param['port_in'])
|
|
|
|
+
|
|
|
|
+ self.iport.setInputCallback(self.inputMessage, self)
|
|
|
|
+ self.iport.setErrorCallback(self.inputError, self)
|
|
|
|
+
|
|
|
|
+ self.ledbuttons={} # liste des boutons
|
|
|
|
+ self.input={} # listes des états des boutons
|
|
|
|
+
|
|
|
|
+ self.ledbuttonsWidth=0 # nombre de bouton de note par ligne
|
|
|
|
+ self.ledbuttonsHeight=0 # nombre de bouton de note par collone
|
|
|
|
+ self.adapt(adapter)
|
|
|
|
+ self.clear()
|
|
|
|
+
|
|
|
|
+ """
|
|
|
|
+ Permet d'appeler un adapteur qui permettra de configurer les boutons
|
|
|
|
+ """
|
|
|
|
+ def adapt(self, adapter):
|
|
|
|
+ adapter.adapt(self)
|
|
|
|
+ self.onAdapt()
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ """
|
|
|
|
+ Appelé quand le pad a ete adapté
|
|
|
|
+ """
|
|
|
|
+ def onAdapt(self):
|
|
|
|
+ pass
|
|
|
|
+
|
|
|
|
+ """
|
|
|
|
+ Attend tant qu'un boutton est presse ou relache
|
|
|
|
+ idx: int : L'id de la touche, un liste d'id ou None pour n'importe que touche
|
|
|
|
+ state: bool: True pour une attente de NOTE_ON False pour NOTE_OFF
|
|
|
|
+ t: float : temps a attendre entre 2 poll
|
|
|
|
+ func: callback : permet d appeler une callback a chaque sondage
|
|
|
|
+ data: * : permet de passer un parametre a la fonction
|
|
|
|
+ Retourne l'id qui donne le signal
|
|
|
|
+ """
|
|
|
|
+ def waitForInput(self, idx=None, state=True, t=0.01, func=None, data=None):
|
|
|
|
+ while True:
|
|
|
|
+ i=self.pollInput(idx, state)
|
|
|
|
+
|
|
|
|
+ if i!=None:
|
|
|
|
+ return i
|
|
|
|
+
|
|
|
|
+ if func!=None:
|
|
|
|
+ func(data)
|
|
|
|
+ time.sleep(t)
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ """
|
|
|
|
+ Sonde si un boutton est presse ou relache
|
|
|
|
+ idx: int : L'id de la touche, un liste d'id ou None pour n'importe que touche
|
|
|
|
+ state: bool: True pour une attente de NOTE_ON False pour NOTE_OFFnction
|
|
|
|
+ Retourne l'id qui du bouton ou None
|
|
|
|
+ """
|
|
|
|
+ def pollInput(self, idx=None, state=True):
|
|
|
|
+ if isinstance(idx, int):
|
|
|
|
+ idx=[idx]
|
|
|
|
+ if idx==None:
|
|
|
|
+ idx=[]
|
|
|
|
+ for i in self.keys():
|
|
|
|
+ idx.append(i)
|
|
|
|
+
|
|
|
|
+ for i in idx:
|
|
|
|
+ if self.input[i]==state:
|
|
|
|
+ return i
|
|
|
|
+ return None
|
|
|
|
+
|
|
|
|
+ """
|
|
|
|
+ Eteint tous les boutons
|
|
|
|
+ """
|
|
|
|
+ def clear(self):
|
|
|
|
+ for k in self.keys():
|
|
|
|
+ self.ledbuttons[k].off()
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ """
|
|
|
|
+ Fonction qui va recevoir toutes les erreurs MIDI du pad
|
|
|
|
+ """
|
|
|
|
+ def inputError(self, code, msg):
|
|
|
|
+ ERR("Midi error:"+ str(MIDI_ERROR_STRING[code])+" : "+str(msg))
|
|
|
|
+
|
|
|
|
+ """
|
|
|
|
+ Fonction qui va recevoir tous les messages MIDI du pad
|
|
|
|
+ """
|
|
|
|
+ def inputMessage(self, delta, msg):
|
|
|
|
+ channel=msg.channel
|
|
|
|
+ if msg.getType()==NOTE_ON:
|
|
|
|
+ index=msg.key
|
|
|
|
+ self._onNoteOn(channel, index, msg.velocity)
|
|
|
|
+ if self.ledbuttons[index]:
|
|
|
|
+ self.ledbuttons[index].onNoteOn(channel, index, msg.velocity)
|
|
|
|
+ elif msg.getType()==NOTE_OFF:
|
|
|
|
+ index=msg.key
|
|
|
|
+ self._onNoteOff(channel, index, msg.velocity)
|
|
|
|
+ if self.ledbuttons[index]:
|
|
|
|
+ self.ledbuttons[index].onNoteOff(channel, index, msg.velocity)
|
|
|
|
+ elif msg.getType()==MidiType.CONTROL_CHANGE:
|
|
|
|
+ index=msg.key
|
|
|
|
+ self.onControlChange(channel,index, msg.value)
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ """
|
|
|
|
+ Fonction appellé en cas d'appui sur une touche
|
|
|
|
+ """
|
|
|
|
+ def onNoteOn(self, channel, idx, val):
|
|
|
|
+ pass
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ """
|
|
|
|
+ Fonction appellé quand une touche est relachée
|
|
|
|
+ """
|
|
|
|
+ def onNoteOff(self, channel, idx, val):
|
|
|
|
+ pass
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ def onControlChange(self, channel, key, value):
|
|
|
|
+ pass
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ def _onNoteOn(self, channel, idx, val):
|
|
|
|
+ self.input[idx]=True
|
|
|
|
+ self.onNoteOn(channel, idx, val)
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ def _onNoteOff(self, channel, idx, val):
|
|
|
|
+ self.input[idx]=False
|
|
|
|
+ self.onNoteOff(channel, idx, val)
|
|
|
|
+
|
|
|
|
+ def getInputState(self, idx):
|
|
|
|
+ if idx in self.input.keys():
|
|
|
|
+ return self.input[idx]
|
|
|
|
+ return None
|
|
|
|
+
|
|
|
|
+ @staticmethod
|
|
|
|
+ def matrixToArray(mat):
|
|
|
|
+ out=[]
|
|
|
|
+ for y in mat:
|
|
|
|
+ for x in y:
|
|
|
|
+ out.append(x)
|
|
|
|
+ return out
|
|
|
|
+
|
|
|
|
+ """
|
|
|
|
+ Mappe un vecteur ou une matrice sur une partie du pad
|
|
|
|
+ """
|
|
|
|
+ def mapSubRect(self, data, start, end=None):
|
|
|
|
+ if (isinstance(data, list) or isinstance(data, tuple)) and len(data)>0:
|
|
|
|
+ if (isinstance(data[0], list) or isinstance(data[0], tuple)):
|
|
|
|
+ end=(start[0]+len(data[0]), start[1]+len(data[1]))
|
|
|
|
+ data=Pad.matrixToArray(data)
|
|
|
|
+ if end==None:
|
|
|
|
+ end=(self.ledbuttonsWidth, self.ledbuttonsHeight)
|
|
|
|
+ w=end[0]-start[0]
|
|
|
|
+ realw= w if start[0]+w<self.ledbuttonsWidth else self.ledbuttonsWidth
|
|
|
|
+ for j in range(start[1], end[1]):
|
|
|
|
+ for i in range(start[0], end[0]):
|
|
|
|
+ if i>= realw: continue
|
|
|
|
+ n=(j-start[1])*w+(i-start[0])
|
|
|
|
+ self[i,j]=data[n]
|
|
|
|
+
|
|
|
|
+ def fill(self, value, matrixOnly=True):
|
|
|
|
+ if matrixOnly:
|
|
|
|
+ x=[]
|
|
|
|
+ for i in range(self.ledbuttonsWidth*self.ledbuttonsHeight):
|
|
|
|
+ x.append(value)
|
|
|
|
+ self.map(x)
|
|
|
|
+ else:
|
|
|
|
+ for k in self.keys():
|
|
|
|
+ self[k]=value
|
|
|
|
+
|
|
|
|
+ def blink(self, value, n=2):
|
|
|
|
+ self.fill(value)
|
|
|
|
+ time.sleep(n/2.0)
|
|
|
|
+
|
|
|
|
+ def loadingScreen(self, tt):
|
|
|
|
+ t=tt/9.0
|
|
|
|
+ for i in range(64,64+8):
|
|
|
|
+ self[i].on()
|
|
|
|
+ time.sleep(t)
|
|
|
|
+ self[i].off()
|
|
|
|
+ for k in range(2):
|
|
|
|
+ for i in range(64,64+8):
|
|
|
|
+ self[i].on()
|
|
|
|
+ time.sleep(t/3)
|
|
|
|
+ for i in range(64,64+8):
|
|
|
|
+ self[i].off()
|
|
|
|
+ if k<2: time.sleep(t/3)
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ def showText(self, text):
|
|
|
|
+ mat=text.getDataToShow()
|
|
|
|
+ self.mapSubRect(mat, text.pos, (text.dim[0]+text.pos[0],text.dim[1]+text.pos[1]))
|
|
|
|
+ text.step()
|
|
|
|
+
|
|
|
|
+ """
|
|
|
|
+ Mappe un vecteur ou une matrice tous les boutons de notes
|
|
|
|
+ """
|
|
|
|
+ def map(self, data):
|
|
|
|
+ first=self.ledbuttons[0].index
|
|
|
|
+ self.mapSubRect(data, (int(first/self.ledbuttonsWidth),first%self.ledbuttonsWidth))
|
|
|
|
+
|
|
|
|
+ #liste des id des boutons
|
|
|
|
+ def keys(self):
|
|
|
|
+ return self.ledbuttons.keys()
|
|
|
|
+
|
|
|
|
+ # transforeme une coordonnée d'uune matrice ou d'un vecteur en une coordonée d'un vecteur
|
|
|
|
+ def getIndex(self, x, y=-1):
|
|
|
|
+ return x if y<0 else x+y*self.ledbuttonsWidth
|
|
|
|
+
|
|
|
|
+ # renvoie le bouton a l'adresse x, y
|
|
|
|
+ def getButton(self, x, y=-1):
|
|
|
|
+ return self.ledbuttons[self.getIndex(x,y)]
|
|
|
|
+
|
|
|
|
+ def __getitem__(self,key):
|
|
|
|
+ if isinstance(key, tuple):
|
|
|
|
+ return self.getButton(key[0], key[1])
|
|
|
|
+ else:
|
|
|
|
+ return self.getButton(key)
|
|
|
|
+
|
|
|
|
+ def __setitem__(self,key,value):
|
|
|
|
+ if isinstance(key, tuple):
|
|
|
|
+ x=self.getButton(key[0], key[1])
|
|
|
|
+ if x:
|
|
|
|
+ x.send(value)
|
|
|
|
+ else:
|
|
|
|
+ x=self.getButton(key).send(value)
|
|
|
|
+ if x:
|
|
|
|
+ x.send(value)
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ def connectOut(self, port):
|
|
|
|
+ port=AlsaConnector.portAuto(port)
|
|
|
|
+ self.oport.connect(port)
|
|
|
|
+ INFO("Connected to input port: "+str(port)+"\n")
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ def connectIn(self, port):
|
|
|
|
+ port=AlsaConnector.portAuto(port)
|
|
|
|
+ self.iport.connect(port)
|
|
|
|
+ INFO("Connected to output port: "+str(port)+"\n")
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ def close(self):
|
|
|
|
+ self.oport.close()
|
|
|
|
+ self.iport.close()
|
|
|
|
+
|
|
|
|
+TEXT_DATA={
|
|
|
|
+ 'A': [[1,1,1,0], [1,0,1,0], [1,1,1,0], [1,0,1,0]],
|
|
|
|
+ 'R': [[1,1,1,0], [1,1,0,0], [1,0,1,0], [1,0,1,0]],
|
|
|
|
+ 'T': [[1,1,1,0], [0,1,0,0], [0,1,0,0], [0,1,0,0]],
|
|
|
|
+ 'Y': [[1,0,1,0], [1,0,1,0], [0,1,0,0], [0,1,0,0]],
|
|
|
|
+ 'G': [[1,1,1,0], [1,0,0,0], [1,0,1,0], [1,1,1,0]],
|
|
|
|
+ 'I': [[0,1,0,0], [0,1,0,0], [0,1,0,0], [0,1,0,0]],
|
|
|
|
+ 'L': [[0,1,0,0], [0,1,0,0], [0,1,0,0], [0,1,1,0]],
|
|
|
|
+ 'N': [[1,0,1,0], [1,1,1,0], [1,1,1,0], [1,0,1,0]],
|
|
|
|
+ 'O': [[1,1,1,0], [1,0,1,0], [1,0,1,0], [1,1,1,0]],
|
|
|
|
+ 'C': [[1,1,1,0], [1,0,0,0], [1,0,0,0], [1,1,1,0]],
|
|
|
|
+ 'S': [[1,1,1,0], [1,1,1,0], [0,0,1,0], [1,1,1,0]],
|
|
|
|
+ ' ': [[0,0,0,0], [0,0,0,0], [0,0,0,0], [0,0,0,0]],
|
|
|
|
+ ':': [[0,0,0,0], [0,1,0,0], [0,0,0,0], [0,1,0,0]],
|
|
|
|
+ '0': [[1,1,1,0], [1,0,1,0], [1,0,1,0], [1,1,1,0]],
|
|
|
|
+ '1': [[0,1,0,0], [1,1,0,0], [0,1,0,0], [0,1,0,0]],
|
|
|
|
+ '2': [[1,1,1,0], [0,0,1,0], [0,1,0,0], [1,1,1,0]],
|
|
|
|
+ '3': [[0,1,1,0], [0,0,1,0], [0,1,1,0], [0,1,1,0]],
|
|
|
|
+ '4': [[0,0,1,0], [0,1,1,0], [1,1,1,1], [0,0,1,0]],
|
|
|
|
+ '5': [[1,1,1,0], [1,0,0,0], [1,1,1,0], [1,1,1,0]],
|
|
|
|
+ '6': [[0,1,1,0], [0,1,0,0], [0,1,1,0], [0,1,1,0]],
|
|
|
|
+ '7': [[1,1,0,0], [0,1,0,0], [1,1,1,0], [0,1,0,0]],
|
|
|
|
+ '8': [[1,1,1,0], [1,1,1,0], [1,0,1,0], [1,1,1,0]],
|
|
|
|
+ '9': [[0,1,1,0], [0,1,1,0], [0,0,1,0], [0,1,1,0]],
|
|
|
|
+ 'M': [[1,0,1,0], [1,1,1,0], [1,1,1,0], [1,1,1,0]],
|
|
|
|
+ 'E': [[0,1,1,0], [0,1,1,0], [0,1,0,0], [0,1,1,0]],
|
|
|
|
+ 'V': [[1,0,1,0], [1,0,1,0], [1,0,1,0], [0,1,0,0]],
|
|
|
|
+ '!': [[0,1,0,0], [0,1,0,0], [0,0,0,0], [0,1,0,0]],
|
|
|
|
+ '?': [[1,1,1,0], [0,1,1,0], [0,0,0,0], [0,1,0,0]]
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+"""
|
|
|
|
+Classe qui gère les donnes a afficher pour afficher du texte défilant
|
|
|
|
+"""
|
|
|
|
+class Text:
|
|
|
|
+ def __init__(self, txt, dim, pos=(0,0)):
|
|
|
|
+ self.pos=pos # position : (x, y)
|
|
|
|
+ self.dim=dim # dimension : (w,h)
|
|
|
|
+ self.txt=txt.upper() # texte a afficher : str
|
|
|
|
+ self.len=len(txt)*4 # longueur en cases (pas en caracteres) : int
|
|
|
|
+ self.matrix=[] # toutes les données : Matrix [ [..] .. [..] ]
|
|
|
|
+ for y in range(4):
|
|
|
|
+ for x in range(self.len):
|
|
|
|
+ self.matrix.append(0)
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ self.offset=-1 # décalage de la partie a afficher actuellement : in
|
|
|
|
+ self.color=1 # couleur du text : int
|
|
|
|
+ self.backgroundcolor=0 #couleur du fond : int
|
|
|
|
+ for i in range(len(self.txt)):
|
|
|
|
+ self.map(self.txt[i], i*4)
|
|
|
|
+
|
|
|
|
+ """
|
|
|
|
+ Récupère un vecteur de bit a afficher pour une lettre
|
|
|
|
+ color: int : Couleur du texte
|
|
|
|
+ backcolor: int : couleur du fond
|
|
|
|
+ """
|
|
|
|
+ @staticmethod
|
|
|
|
+ def letterVector(char, color=1, backcolor=0):
|
|
|
|
+ x=[]
|
|
|
|
+ c=TEXT_DATA[char]
|
|
|
|
+ for j in reversed(range(4)):
|
|
|
|
+ for i in range(4):
|
|
|
|
+ x.append(color if c[j][i]>0 else backcolor)
|
|
|
|
+ return x
|
|
|
|
+
|
|
|
|
+ """
|
|
|
|
+ Map un vecteur ou une matrix dans les données a afficher
|
|
|
|
+ """
|
|
|
|
+ def map(self, char, off):
|
|
|
|
+ c=TEXT_DATA[char]
|
|
|
|
+ for j in reversed(range(4)):
|
|
|
|
+ for i in range(off, off+4):
|
|
|
|
+ self.matrix[i+(3-j)*self.len]=c[j][i-off]
|
|
|
|
+
|
|
|
|
+ """
|
|
|
|
+ Change la couleur du text
|
|
|
|
+ c: int : Couleur du texte
|
|
|
|
+ """
|
|
|
|
+ def setColor(self, c):
|
|
|
|
+ self.color=c
|
|
|
|
+
|
|
|
|
+ """
|
|
|
|
+ Change la couleur du fond
|
|
|
|
+ c: int : Couleur du fond
|
|
|
|
+ """
|
|
|
|
+ def setBackgroundColor(self, c):
|
|
|
|
+ self.backgroundcolor=c
|
|
|
|
+
|
|
|
|
+ """
|
|
|
|
+ Recupere le vecteur a mapper sur le pad
|
|
|
|
+ """
|
|
|
|
+ def getDataToShow(self):
|
|
|
|
+ out=[]
|
|
|
|
+ for j in range(4):
|
|
|
|
+ for i in range(self.offset, self.offset+self.dim[0]):
|
|
|
|
+ i=i%self.len
|
|
|
|
+ xx=self.matrix[i+j*self.len]
|
|
|
|
+ x= self.color if xx>0 else self.backgroundcolor
|
|
|
|
+ out.append(x)
|
|
|
|
+ return out
|
|
|
|
+
|
|
|
|
+ """
|
|
|
|
+ Donne le nombre d'étape pour afficher completement le text n fois
|
|
|
|
+ n: (int = 1) : Nombre de fois a afficher
|
|
|
|
+ """
|
|
|
|
+ def stepCount(self, n=1):
|
|
|
|
+ return (self.len-self.dim[0]+1)*n
|
|
|
|
+
|
|
|
|
+ """
|
|
|
|
+ Décale d'une position (case) le texte
|
|
|
|
+ """
|
|
|
|
+ def step(self):
|
|
|
|
+ self.offset=(self.offset+1)%self.len
|
|
|
|
+
|
|
|
|
+import os
|
|
|
|
+def setRealtime(rt=os.SCHED_FIFO):
|
|
|
|
+ try:
|
|
|
|
+ os.sched_setscheduler(0, rt, os.sched_param(0))
|
|
|
|
+ return True
|
|
|
|
+ except Exception as err:
|
|
|
|
+ ERR("Impossible de passer en temps-réel: "+str(err))
|
|
|
|
+ return False
|
|
|
|
+
|