#!/usr/bin/python3 # -*- coding: utf-8 -*- # -*- mode: Python -*- ''' Jamidi Server v0.1b ''' print("") print("") print("Jamidi Server") print("v0.1b") import sys import traceback import os import time from rtmidi.midiconstants import (CHANNEL_PRESSURE, CONTROLLER_CHANGE, NOTE_ON, NOTE_OFF, PITCH_BEND, POLY_PRESSURE, PROGRAM_CHANGE) import midi3 from websocket_server import WebsocketServer #import socket import types, json import argparse debug = 1 print ("") print ("Arguments parsing if needed...") argsparser = argparse.ArgumentParser(description="Jamidi Server v0.1b commands help mode") argsparser.add_argument("-s","--servername",help="Servername: 'local', 'llstrvpn' (local by default)", type=str) argsparser.add_argument('--current',help="Send all current CC values to all new client. Default option" , dest='current', action='store_true') argsparser.add_argument('--no-current',help="Do not send all current CC values to all new client. ", dest='current', action='store_false') argsparser.add_argument('--broadcast',help="Broadcast all incomings commands to all client. Default option" , dest='broadcast', action='store_true') argsparser.add_argument('--no-broadcast',help="Do not broadcast all incomings commands to all client", dest='broadcast', action='store_false') argsparser.add_argument('--reset',help="Send reset values to local device a startup. Default option" , dest='reset', action='store_true') argsparser.add_argument('--no-reset',help="Do not send reset values to local device a startup.", dest='reset', action='store_false') argsparser.set_defaults(reset=True) argsparser.set_defaults(current=True) argsparser.set_defaults(broadcast=True) args = argsparser.parse_args() # Mode if args.servername: servername = args.servername else: servername = "local" # Broadcast commands to all clients ? if args.broadcast == False: print("Broadcast disabled") broadcast = False else: print("Broadcast enabled") broadcast = True # Send current values to all new client ? if args.current == False: print("Do not send current values at startup disabled") current = False else: print("Current values update at startup disabled") current = True # reset = [64,64,0,32,96] # un truc comme ca pour les valeurs de reset ? resetMMO3 = [0] * 32 resetOCS2 = [0] * 32 # record current values crtvalueMMO3 = [0] * 32 crtvalueOCS2 = [0] * 32 # record number of loaded pages (aka client id or players) Players=0 # # Midi part # print("Midi Configuration...") # print("Midi Destination", nozmidi) midi3.check() def GetTime(): return time.strftime("%a, %d %b %Y %H:%M:%S", time.localtime()) # /cc cc number value def cc(midichannel, ccnumber, value, mididest): if debug>0: print("Jamidi Sending Midi channel", midichannel, "cc", ccnumber, "value", value, "to", mididest) midi3.MidiMsg([CONTROLLER_CHANGE+midichannel-1, ccnumber, value], mididest) # /reset nozoids with "default" values def reset(nozoid): print("") print(GetTime(),"reseting", nozoid) if nozoid == "mmo3": for ccnumber in range(0,32): midi3.MidiMsg([CONTROLLER_CHANGE+Confs["mmo3"][0]["midichan"]-1, ccnumber, resetMMO3[ccnumber]], Confs["mmo3"][0]["mididevice"]) sendWSall("/mmo3/cc/"+str(ccnumber)+" "+str(resetMMO3[ccnumber])) crtvalueMMO3[ccnumber]=resetMMO3[ccnumber] else: for ccnumber in range(0,32): midi3.MidiMsg([CONTROLLER_CHANGE+Confs["ocs2"][0]["midichan"]-1, ccnumber, resetOCS2[ccnumber]], Confs["ocs2"][0]["mididevice"]) sendWSall("/ocs2/cc/"+str(ccnumber)+" "+str(resetOCS2[ccnumber])) crtvalueOCS2[ccnumber]=resetOCS2[ccnumber] print("End of reset for", nozoid) print("") # /send all current cc values def sendallcurrentccvalues(nozoid): if broadcast == True: #print "" print(GetTime(),"sending all current cc values of", nozoid) if nozoid == "mmo3": for ccnumber in range(0,32): sendWSall("/mmo3/cc/"+str(ccnumber)+" "+str(crtvalueMMO3[ccnumber])) else: for ccnumber in range(0,32): sendWSall("/ocs2/cc/"+str(ccnumber)+" "+str(crtvalueOCS2[ccnumber])) # # Settings from jamidi.json # # Load midi routing definitions in clientconfr.json def LoadConfs(): global Confs, nbmidiconf if os.path.exists('jamidi.json'): f=open("jamidi.json","r") s = f.read() Confs = json.loads(s) # return midi confname number for given type 'Specials', 'cc2cc' def findConfs(confname,conftype): #print("searching", midiconfname,'...') position = -1 for counter in range(len(Confs[conftype])): if confname == Confs[conftype][counter]['name']: #print(confname, "is ", counter) position = counter return position # # Websocket part # # Called for every WS client connecting (after handshake) def new_client(client, wserver): global Players print(GetTime(),"New WS client connected and was given id %d" % client['id']) #sendWSall("/status Hello %d" % client['id']) if current == True: sendallcurrentccvalues("mmo3") sendallcurrentccvalues("ocs2") Players+=1 sendWSall("/status Hello %d" %(client['id'])) sendWSall("/players %d" %(Players)) # Called for every WS client disconnecting def client_left(client, wserver): global Players try: print(GetTime(),"WS Client(%d) disconnected" % client['id']) Players-=1 sendWSall("/players %d" %(Players)) except: print("Something weird if coming from",client,"on the wire...") pass # Called for each WS received message. def message_received(client, wserver, message): print("") if len(message) > 200: message = message[:200]+'..' oscpath = message.split(" ") if debug > 0: print(GetTime(),"Main got from WS", client['id'], "said :", message, "splitted in an oscpath :", oscpath) else: print(GetTime(),"Main got WS Client", client['id'], "said :", message) # wscommand will be like ['', 'ocs2', 'cc', '9'] wscommand = oscpath[0].split("/") # debug if debug > 0: print("wscommand :",wscommand) # noarg if len(oscpath) == 1: args[0] = "noargs" #print "noargs command" # CC : /device/cc/2 127 elif wscommand[2] == "cc": ccvr=int(wscommand[3]) #cc variable ccvl=int(oscpath[1]) #cc value if debug > 0: print("ccvr=%d/ccvl=%d"%(ccvr,ccvl)) if wscommand[1] == "ocs2": #cc(Confs[wscommand[1]][0]["midichan"], ccvr, ccvl, Confs[wscommand[1]][0]["mididevice"]) crtvalueOCS2[ccvr]=ccvl else: #cc(Confs[wscommand[1]][0]["midichan"], ccvr, ccvl, Confs[wscommand[1]][0]["mididevice"]) crtvalueMMO3[ccvr]=ccvl cc(Confs[wscommand[1]][0]["midichan"], ccvr, ccvl, Confs[wscommand[1]][0]["mididevice"]) # RESET : /device/reset 1 elif wscommand[2] == "reset": if wscommand[1] == "ocs2": reset("ocs2") else: reset("mmo3") # NOTEON : /device/noteon note velocity elif wscommand[2] == "noteon": midi3.NoteOn(int(oscpath[1]), int(oscpath[2]), Confs[wscommand[1]][0]["mididevice"]) # NOTEOFF /device/noteoff note elif wscommand[2] == "noteoff": midi3.NoteOff(int(oscpath[1]), Confs[wscommand[1]][0]["mididevice"]) ws.send("/noteon "+str(msg[1])+" "+str(msg[2])) # Loop back : WS Client -> server -> WS Client sendWSall(message) def sendWSall(message): if broadcast == True: if debug >0: print(GetTime(),"sending to all %s" % (message)) wserver.send_message_to_all(message) # # Running... # LoadConfs() serverIP = Confs[servername][0]["IP"] wsPORT = Confs[servername][0]["port"] print("Running....") # Main loop do nothing. Maybe do the webui server ? try: #while True: # Websocket startup wserver = WebsocketServer(wsPORT,host=serverIP) midi3.ws = wserver #print wserver print("") print(GetTime(),"Launching Jamidi Websocket server...") print(GetTime(),"at", serverIP, "port",wsPORT) wserver.set_fn_new_client(new_client) wserver.set_fn_client_left(client_left) wserver.set_fn_message_received(message_received) if reset == True: reset("mmo3") reset("ocs2") #print "" print(GetTime(),"WS server running forever...") wserver.run_forever() except KeyboardInterrupt: pass # Gently stop on CTRL C print("Fin de Jamidi.")