123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577 |
- #!/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
-
|