347 lines
8.9 KiB
Python
Executable File
347 lines
8.9 KiB
Python
Executable File
#!/usr/bin/python3
|
|
# -*- coding: utf-8 -*-
|
|
# -*- mode: Python -*-
|
|
'''
|
|
Jamidi Server v0.1b
|
|
|
|
wserver = WebsocketServer(wsPORT,host=serverIP)
|
|
|
|
wserver.set_fn_new_client(new_client)
|
|
wserver.set_fn_client_left(client_left)
|
|
wserver.set_fn_message_received(message_received)
|
|
|
|
wserver.run_forever()
|
|
|
|
wserver.send_message_to_all(message)
|
|
|
|
|
|
'''
|
|
|
|
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)
|
|
|
|
sys.path.append('libs/')
|
|
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 at startup ?
|
|
if args.reset == False:
|
|
print("Reset at startup disabled")
|
|
startreset = False
|
|
else:
|
|
print("Reset at startup enabled")
|
|
startreset = 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 definitions in jamidi.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
|
|
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 = 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"])
|
|
|
|
|
|
# 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
|
|
midi3.Confs = Confs
|
|
midi3.findJamDevice("UM-ONE:UM-ONE MIDI 1 20:0", 1)
|
|
|
|
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 startreset == True:
|
|
print("resetting nozoids...")
|
|
reset("mmo3")
|
|
reset("ocs2")
|
|
|
|
|
|
#print ""
|
|
print(GetTime(),"WS server running forever...")
|
|
|
|
wserver.run_forever()
|
|
|
|
except Exception:
|
|
traceback.print_exc()
|
|
|
|
except KeyboardInterrupt:
|
|
pass
|
|
|
|
# Gently stop on CTRL C
|
|
|
|
|
|
print("Fin de Jamidi.")
|
|
|
|
|
|
|
|
|