jamidi/client.py

381 lines
10 KiB
Python
Executable File

#!/usr/bin/python3
# -*- coding: utf-8 -*-
# -*- mode: Python -*-
'''
Jamidi Client v0.1b
Input : local midi (hardware/software) instruments
Output : Jamidi server
ws = websocket.WebSocketApp("ws://"+str(serverIP)+":"+str(wsPORT),
on_message = on_message,
on_error = on_error,
on_close = on_close)
ws.on_open = on_open
ws.run_forever()
ws.send(message)
ws.send_message_to_all(msg = message)
ws.close()
'''
print("")
print("")
print("Jamidi Client")
print("v0.1b")
#from multiprocessing import Process, Queue, TimeoutError
#import subprocess
import sys
import traceback
import os
import time
import json
from rtmidi.midiconstants import (CHANNEL_PRESSURE, CONTROLLER_CHANGE, NOTE_ON, NOTE_OFF,
PITCH_BEND, POLY_PRESSURE, PROGRAM_CHANGE)
sys.path.append('libs/')
import midi3
import types
import websocket
import gstt
#import WScom
try:
import _thread
except ImportError:
import _thread as thread
import argparse
print ("")
print ("Arguments parsing if needed...")
argsparser = argparse.ArgumentParser(description="Jamidi Client v0.1b commands help mode")
argsparser.add_argument("-s","--servername",help="servername: 'local', 'xrkia' ('local' by default)", type=str)
# argsparser.add_argument('--default',help="All incoming midi <-> default midi device. Default option." , dest='default', action='store_true')
argsparser.add_argument('-nodefault',help="Do not send reset values to local device a startup.", dest='default', action='store_false')
argsparser.set_defaults(default=True)
args = argsparser.parse_args()
# Server
if args.servername:
servername = args.servername
else:
servername = "local"
# Default midi device like controller
if args.default == False:
print("Default device disabled")
defaultdevice = False
else:
print("Default device enabled")
defaultdevice = True
#
# Midi part
#
# default local MIDI device
nozmidi = "BCR2000 Port 1"
# nozmidi = "UM-ONE:UM-ONE MIDI 1 20:0"
midichanOCS2 = 2
midichanMMO3 = 1
# resetMMO3 = [64,64,0,32,96] # un truc comme ca pour les valeurs de reset ?
resetMMO3 = [0] * 32
resetOCS2 = [0] * 32
songs = ["song1", "song2"]
song = 0
def GetTime():
return time.strftime("%a, %d %b %Y %H:%M:%S", time.localtime())
#
# Settings from rules.json
#
# Load midi routing definitions in clientruler.json
def LoadMidiRules():
global MidiRules, nbmidirule
if os.path.exists('rules.json'):
f=open("rules.json","r")
s = f.read()
MidiRules = json.loads(s)
# return midi rulename number for given type 'Specials', 'cc2cc'
def findMidiRules(rulename,ruletype):
#print("searching", midirulename,'...')
position = -1
for counter in range(len(MidiRules[ruletype])):
if rulename == MidiRules[ruletype][counter]['name']:
#print(rulename, "is ", counter)
position = counter
return position
#
# 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()
gstt.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(gstt.Confs[conftype])):
if confname == gstt.Confs[conftype][counter]['name']:
#print(confname, "is ", counter)
position = counter
return position
def curved(value):
return round(np.sqrt(value)*11.27)
# /cc cc number value
def cc(midichannel, ccnumber, value, mididest):
#print "cc()"
print("CC for local Midi channel", midichannel, "cc", ccnumber, "value", value, "to", mididest)
# Apply CC routing ?
if len(MidiRules["cc2cc"]) > 0 :
#print "cc2cc test for channel", midichannel, "CC", ccnumber, "val", value, "song", songs[song]
for counter in range(len(MidiRules["cc2cc"])):
if (MidiRules["cc2cc"][counter]["songname"] == songs[song] or MidiRules["cc2cc"][counter]["songname"] == "all") and (MidiRules["cc2cc"][counter]["chanIN"] == midichannel or MidiRules["cc2cc"][counter]["chanIN"] == "all") and (MidiRules["cc2cc"][counter]["ccs"] == ccnumber or MidiRules["cc2cc"][counter]["ccs"] == "all"):
print("cc2cc routing for song",MidiRules["cc2cc"][counter]["songname"], ":", MidiRules["cc2cc"][counter]["name"])
#print("cc2cc got song :", MidiRules["cc2cc"][counter]["songname"]," IN Channel :", MidiRules["cc2cc"][counter]["chanIN"]," Code :", MidiRules["cc2cc"][counter]["code"], " value :",MidiRules["ZnotesLcc"][counter]["valuetype"], )
if MidiRules["cc2cc"][counter]["valuetype"] == "linear":
print("Linear", MidiVal)
midi3.MidiMsg((176 + MidiRules["cc2cc"][counter]["chanOUT"], MidiRules["cc2cc"][counter]["ccOUT"], MidiVal), MidiRules["cc2cc"][counter]["mididest"])
if MidiRules["cc2cc"][counter]["valuetype"] == "curved":
print(MidiVal,"got curved", curved(MidiVal))
midi3.MidiMsg((176 + MidiRules["cc2cc"][counter]["chanOUT"], MidiRules["cc2cc"][counter]["ccOUT"], curved(MidiVal)), MidiRules["cc2cc"][counter]["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+gstt.Confs["mmo3"][0]["midichan"]-1, ccnumber, resetMMO3[ccnumber]], gstt.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+gstt.Confs["ocs2"][0]["midichan"]-1, ccnumber, resetOCS2[ccnumber]], gstt.Confs["ocs2"][0]["mididevice"])
sendWSall("/ocs2/cc/"+str(ccnumber)+" "+str(resetOCS2[ccnumber]))
crtvalueOCS2[ccnumber]=resetOCS2[ccnumber]
print("End of reset for", nozoid)
print("")
#
# Websocket part
#
def on_error(ws, error):
print(error)
def on_close(ws):
print("### closed ###")
def on_open(ws):
def run(*args):
try:
while True:
time.sleep(1)
except Exception:
traceback.print_exc()
finally:
ws.close()
print("thread terminating...")
_thread.start_new_thread(run, ())
def on_message(ws, message):
print("")
print("Incoming ws message",message)
if len(message) > 200:
message = message[:200]+'..'
oscpath = message.split(" ") # ['/ocs2/cc/31', '0'] or ['/players', '(player:1)']
# print("oscpath", oscpath)
wscommand = oscpath[0].split("/") # ['', 'ocs2', 'cc', '31'] or ['', 'players']
# print("wscommand", wscommand)
device = wscommand[1]
if len(wscommand) == 2:
command = wscommand[1]
else:
command = wscommand[2]
# debug
if gstt.debug > 0 and command != "status" and command != "players":
print("for device", device, "command :", command)
# noarg
if len(oscpath) == 1:
args[0] = "noargs"
#print "noargs command"
# CC : /device/cc/2 127
elif command == "cc":
ccvr=int(wscommand[3]) #cc variable
ccvl=int(oscpath[1]) #cc value
if gstt.debug > 0:
print("device", device,"ccvr=%d/ccvl=%d"%(ccvr,ccvl))
for mididevice in midi3.findJamDevices(device):
cc(gstt.Confs[device][0]["midichan"], ccvr, ccvl, mididevice)
'''
# CC : /device/cc/2 127
elif wscommand[1] == "ocs2":
if wscommand[2] == "cc":
print("Incoming OCS-2 WS CC", wscommand[3], ":", int(oscpath[1]))
cc(gstt.Confs["ocs2"][0]["midichan"], int(wscommand[3]), int(oscpath[1]), gstt.Confs["default"][0]["mididevice"])
if wscommand[2] == "OSC1":
print("Incoming OCS-2 WS OSC1", wscommand[3], ":", int(oscpath[1]))
elif wscommand[1] == "mmo3":
if wscommand[2] == "cc":
print("Incoming MMO-3 WS CC", wscommand[3], ":", int(oscpath[1]))
cc(gstt.Confs["mmo3"][0]["midichan"], int(wscommand[3]), int(oscpath[1]), gstt.Confs["default"][0]["mididevice"])
if wscommand[2] == "OSC1":
print("Incoming MMO-3 WS OSC1", wscommand[3], ":", int(oscpath[1]))
'''
# RESET : /device/reset 1
elif command == "reset":
if device == "ocs2":
reset("ocs2")
else:
reset("mmo3")
# NOTEON : /device/noteon note velocity
elif command == "noteon":
for mididevice in midi3.findJamDevices(device):
midi3.NoteOn(int(oscpath[1]), int(oscpath[2]), mididevice)
# NOTEOFF /device/noteoff note
elif command == "noteoff":
for mididevice in midi3.findJamDevices(device):
midi3.NoteOff(int(oscpath[1]), mididevice)
# if needed a loop back : WS Client -> server -> WS Client
# sendWSall(message)
#
# Running...
#
LoadMidiRules()
LoadConfs()
serverIP = gstt.Confs[servername][0]["IP"]
#serverIP = "10.8.0.46"
wsPORT = gstt.Confs[servername][0]["port"]
print("Running....")
# Main loop do nothing. Maybe do the webui server ?
try:
print("")
print("Connecting to Jamidi server...")
print("at", serverIP, "port",wsPORT)
#websocket.enableTrace(True)
ws = websocket.WebSocketApp("ws://"+str(serverIP)+":"+str(wsPORT),
on_message = on_message,
on_error = on_error,
on_close = on_close)
midi3.ws = ws
gstt.clientmode = True
#gstt.Confs = Confs
print("Midi Configuration...")
midi3.check()
ws.on_open = on_open
ws.run_forever()
except Exception:
traceback.print_exc()
# Gently stop on CTRL C
print("Fin de Jamidi.")