diff --git a/.gitignore b/.gitignore index e61bca2..297fd49 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +*.DS_Store # ---> Python # Byte-compiled / optimized / DLL files __pycache__/ diff --git a/client.py b/client.py old mode 100644 new mode 100755 index c9136a9..1711a55 --- a/client.py +++ b/client.py @@ -60,8 +60,8 @@ 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('--no-default',help="Do not send reset values to local device a startup.", dest='default', action='store_false') +# 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() diff --git a/jamidi.json b/jamidi.json index 706ae2a..e16d282 100644 --- a/jamidi.json +++ b/jamidi.json @@ -6,7 +6,9 @@ "type": "serverconf", "name": "local", "IP": "127.0.0.1", - "port": 8081 + "port": 8081, + "oscport": 8082, + "udport": 8083 } ], @@ -17,7 +19,9 @@ "type": "serverconf", "name": "llstrvpn", "IP": "10.8.0.46", - "port": 8081 + "port": 8081, + "oscport": 8082, + "udport": 8083 } ], @@ -28,7 +32,9 @@ "type": "serverconf", "name": "xrkia", "IP": "xrkia.org", - "port": 8081 + "port": 8081, + "oscport": 8082, + "udport": 8083 } ], @@ -39,7 +45,29 @@ "type": "serverconf", "name": "tmlsr", "IP": "laser.teamlaser.fr", - "port": 8081 + "port": 8081, + "oscport": 8082, + "udport": 8083 + } +], + +"sq-1": [ + { + "_comment": "SQ-1 device parameters", + "type": "mididevice", + "mididevice": "UM-ONE:UM-ONE MIDI 1 20:0", + "midichan" : 3, + "xname" : "sq-1(3)" + } +], + +"sq-1": [ + { + "_comment": "SQ-1 device parameters", + "type": "mididevice", + "mididevice": "UM-ONE:UM-ONE MIDI 1 20:0", + "midichan" : 4, + "xname" : "sq-1(4)" } ], @@ -93,6 +121,16 @@ } ], +"maxwell": [ + { + "_comment": "Mawell device parameters", + "type": "mididevice", + "mididevice": "to Maxwell 1", + "midichan" : 0, + "xname" : "ocs2" + } + ], + "default": [ { @@ -106,4 +144,4 @@ ] -} \ No newline at end of file +} diff --git a/libs/midi3.py b/libs/midi3.py index 6a1a135..ebfdb24 100644 --- a/libs/midi3.py +++ b/libs/midi3.py @@ -1,3 +1,4 @@ + #!/usr/bin/python3 # -*- coding: utf-8 -*- @@ -13,6 +14,8 @@ Midi Handler : by Sam Neurohack from /team/laser +Midi conversions from https://github.com/craffel/pretty-midi + """ @@ -32,6 +35,7 @@ import weakref import sys from sys import platform import os +import re is_py2 = sys.version[0] == '2' @@ -103,13 +107,90 @@ STATUS_MAP = { } +def GetTime(): + return time.strftime("%a, %d %b %Y %H:%M:%S", time.localtime()) notes = ["C","C#","D","D#","E","F","F#","G","G#","A","A#","B"] def midi2note(midinote): - print("midinote",midinote, "note", notes[midinote%12]+str(round(midinote/12))) + print(GetTime(),"midinote",midinote, "note", notes[midinote%12]+str(round(midinote/12))) return notes[midinote%12]+str(round(midinote/12)) + +def note2midi(note_name): + """Converts a note name in the format + ``'(note)(accidental)(octave number)'`` (e.g. ``'C#4'``) to MIDI note + number. + ``'(note)'`` is required, and is case-insensitive. + ``'(accidental)'`` should be ``''`` for natural, ``'#'`` for sharp and + ``'!'`` or ``'b'`` for flat. + If ``'(octave)'`` is ``''``, octave 0 is assumed. + Parameters + ---------- + note_name : str + A note name, as described above. + Returns + ------- + note_number : int + MIDI note number corresponding to the provided note name. + Notes + ----- + Thanks to Brian McFee. + """ + + # Map note name to the semitone + pitch_map = {'C': 0, 'D': 2, 'E': 4, 'F': 5, 'G': 7, 'A': 9, 'B': 11} + # Relative change in semitone denoted by each accidental + acc_map = {'#': 1, '': 0, 'b': -1, '!': -1} + + # Reg exp will raise an error when the note name is not valid + try: + # Extract pitch, octave, and accidental from the supplied note name + match = re.match(r'^(?P[A-Ga-g])(?P[#b!]?)(?P[+-]?\d+)$', + note_name) + + pitch = match.group('n').upper() + offset = acc_map[match.group('off')] + octave = int(match.group('oct')) + except: + raise ValueError('Improper note format: {}'.format(note_name)) + # Convert from the extrated ints to a full note number + return 12*(octave + 1) + pitch_map[pitch] + offset + + + +def hz2midi(frequency): + """Convert a frequency in Hz to a (fractional) note number. + Parameters + ---------- + frequency : float + Frequency of the note in Hz. + Returns + ------- + note_number : float + MIDI note number, can be fractional. + """ + # MIDI note numbers are defined as the number of semitones relative to C0 + # in a 440 Hz tuning + return 12*(np.log2(frequency) - np.log2(440.0)) + 69 + +def midi2hz(note_number): + """Convert a (fractional) MIDI note number to its frequency in Hz. + Parameters + ---------- + note_number : float + MIDI note number, can be fractional. + Returns + ------- + note_frequency : float + Frequency of the note in Hz. + """ + # MIDI note numbers are defined as the number of semitones relative to C0 + # in a 440 Hz tuning + return 440.0*(2.0**((note_number - 69)/12.0)) + + + # Send through websocket. # Different websocket library for client (websocket) or server (websocket_server. # ws object is added here by main.py or client.py startup : midi3.ws = @@ -139,7 +220,7 @@ def MidinProcess(inqueue, portname): time.sleep(0.001) msg = inqueue_get() print("") - print("Generic from", portname,"msg : ", msg) + print(GetTime(),"Generic from", portname,"msg : ", msg) # Noteon message on all midi channels @@ -148,9 +229,9 @@ def MidinProcess(inqueue, portname): MidiChannel = msg[0]-144 MidiNote = msg[1] MidiVel = msg[2] - print("NOTE ON :", MidiNote, 'velocity :', MidiVel, "Channel", MidiChannel) + print(GetTime(),"NOTE ON :", MidiNote, 'velocity :', MidiVel, "Channel", MidiChannel) #NoteOn(msg[1],msg[2],mididest) - print("Midi in process send /"+findJamName(portname, MidiChannel)+"/noteon "+str(msg[1])+" "+str(msg[2])) + print(GetTime(),"Midi in process send /"+findJamName(portname, MidiChannel)+"/noteon "+str(msg[1])+" "+str(msg[2])) wssend("/"+findJamName(portname, MidiChannel)+"/noteon "+str(msg[1])+" "+str(msg[2])) ''' @@ -174,15 +255,15 @@ def MidinProcess(inqueue, portname): # Note Off or Note with 0 velocity on all midi channels if NOTE_OFF -1 < msg[0] < 145 or (NOTE_OFF -1 < msg[0] < 160 and msg[2] == 0): - print(NOTE_OFF) + print(GetTime(),"NOTE_OFF :",NOTE_OFF) if msg[0] > 143: MidiChannel = msg[0]-144 else: MidiChannel = msg[0]-128 - print("NOTE OFF :", MidiNote, 'velocity :', MidiVel, "Channel", MidiChannel) + print(GetTime(),"NOTE OFF :", MidiNote, 'velocity :', MidiVel, "Channel", MidiChannel) #NoteOff(msg[1],msg[2], mididest) - print("Midi in process send /"+findJamName(portname, MidiChannel)+"/noteoff "+str(msg[1])) + print(GetTime(),"Midi in process send /"+findJamName(portname, MidiChannel)+"/noteoff "+str(msg[1])) wssend("/"+findJamName(portname, MidiChannel)+"/noteoff "+str(msg[1])) @@ -191,8 +272,8 @@ def MidinProcess(inqueue, portname): MidiChannel = msg[0]-175 #findJamName(portname, MidiChannel) - print("channel", MidiChannel, " ",findJamName(portname, MidiChannel), " CC :", msg[1], msg[2]) - print("Midi in process send /"+findJamName(portname, MidiChannel)+"/cc/"+str(msg[1])+" "+str(msg[2])+" to WS") + print(GetTime(),"channel", MidiChannel, " ",findJamName(portname, MidiChannel), " CC :", msg[1], msg[2]) + print(GetTime(),"Midi in process send /"+findJamName(portname, MidiChannel)+"/cc/"+str(msg[1])+" "+str(msg[2])+" to WS") wssend("/"+findJamName(portname, MidiChannel)+"/cc/"+str(msg[1])+" "+str(msg[2])) @@ -290,7 +371,7 @@ class OutObject(): self._instances.add(weakref.ref(self)) OutObject.counter += 1 - print("Adding OutDevice name", self.name, "kind", self.kind, "port", self.port) + print(GetTime(),"Adding OutDevice name", self.name, "kind", self.kind, "port", self.port) @classmethod def getinstances(cls): @@ -314,8 +395,8 @@ def OutConfig(): # if len(OutDevice) == 0: print("") - print("MIDIout...") - print("List and attach to available devices on host with IN port :") + print(GetTime(),"MIDIout...") + print(GetTime(),"List and attach to available devices on host with IN port :") # Display list of available midi IN devices on the host, create and start an OUT instance to talk to each of these Midi IN devices midiout = rtmidi.MidiOut() @@ -331,7 +412,7 @@ def OutConfig(): OutDevice.append(OutObject(name, "generic", port)) #print "") - print(len(OutDevice), "Out devices") + print(GetTime(),len(OutDevice), "Out devices") #ListOutDevice() MidInsNumber = len(OutDevice)+1 @@ -339,7 +420,7 @@ def ListOutDevice(): for item in OutObject.getinstances(): - print(item.name) + print(GetTime(),item.name) def FindOutDevice(name): @@ -355,14 +436,14 @@ def FindOutDevice(name): def DelOutDevice(name): Outnumber = Findest(name) - print('deleting OutDevice', name) + print(GetTime(),'deleting OutDevice', name) if Outnumber != -1: - print('found OutDevice', Outnumber) + print(GetTime(),'found OutDevice', Outnumber) delattr(OutObject, str(name)) - print("OutDevice", Outnumber,"was removed") + print(GetTime(),"OutDevice", Outnumber,"was removed") else: - print("OutDevice was not found") + print(GetTime(),"OutDevice was not found") @@ -387,7 +468,7 @@ class InObject(): self._instances.add(weakref.ref(self)) InObject.counter += 1 - print("Adding InDevice name", self.name, "kind", self.kind, "port", self.port) + print(GetTime(),"Adding InDevice name", self.name, "kind", self.kind, "port", self.port) @classmethod def getinstances(cls): @@ -407,16 +488,16 @@ class InObject(): def InConfig(): print("") - print("MIDIin...") + print(GetTime(),"MIDIin...") # client mode if debug > 0: if clientmode == True: - print("midi3 in client mode") + print(GetTime(),"midi3 in client mode") else: - print("midi3 in server mode") + print(GetTime(),"midi3 in server mode") - print("List and attach to available devices on host with OUT port :") + print(GetTime(),"List and attach to available devices on host with OUT port :") if platform == 'darwin': mido.set_backend('mido.backends.rtmidi/MACOSX_CORE') @@ -437,7 +518,7 @@ def InConfig(): try: #print name, name.find("RtMidi output")) if name.find("RtMidi output") > -1: - print("No thread started for device", name) + print(GetTime(),"No thread started for device", name) else: portin = object port_name = "" @@ -460,7 +541,7 @@ def InConfig(): traceback.print_exc() #print "") - print(InObject.counter, "In devices") + print(GetTime(),InObject.counter, "In devices") #ListInDevice() @@ -469,7 +550,7 @@ def ListInDevice(): #print "known IN devices :" for item in InObject.getinstances(): - print(item.name) + print(GetTime(),item.name) print("") def FindInDevice(name): @@ -486,14 +567,14 @@ def FindInDevice(name): def DelInDevice(name): Innumber = Findest(name) - print('deleting InDevice', name) + print(GetTime(),'deleting InDevice', name) if Innumber != -1: - print('found InDevice', Innumber) + print(GetTime(),'found InDevice', Innumber) delattr(InObject, str(name)) - print("InDevice", Innumber,"was removed") + print(GetTime(),"InDevice", Innumber,"was removed") else: - print("InDevice was not found") + print(GetTime(),"InDevice was not found") @@ -517,18 +598,18 @@ def MidiMsg(midimsg, mididest): desterror = -1 - print("jamidi3 got midimsg", midimsg, "for", mididest) + print(GetTime(),"jamidi3 got midimsg", midimsg, "for", mididest) for port in range(len(OutDevice)): # To mididest if midiname[port].find(mididest) != -1: if debug>0: - print("jamidi 3 sending to name", midiname[port], "port", port, ":", midimsg) + print(GetTime(),"jamidi 3 sending to name", midiname[port], "port", port, ":", midimsg) midiport[port].send_message(midimsg) desterror = 0 if desterror == -1: - print("mididest",mididest, ": ** This midi destination doesn't exists **") + print(GetTime(),"mididest",mididest, ": ** This midi destination doesn't exists **") # send midi msg over ws. #if clientmode == True: @@ -566,7 +647,7 @@ def findJamName(mididevice, midichan): #print(v[0]["mididevice"],v[0]["midichan"], type(v[0]["midichan"])) if (v[0]["mididevice"] == mididevice) and (v[0]["midichan"] == midichan): - print("Incoming event from", k, "xname", v[0]["xname"]) + print(GetTime(),"Incoming event from", k, "xname", v[0]["xname"]) return v[0]["xname"] return "None" @@ -575,7 +656,7 @@ def findJamName(mididevice, midichan): def findJamDevices(name): devices = [] - print ("searching", name) + print (GetTime(),"searching", name) for (k, v) in Confs.items(): if v[0]["type"] == "mididevice": diff --git a/main.py b/main.py index e707087..16dd5eb 100755 --- a/main.py +++ b/main.py @@ -58,7 +58,7 @@ argsparser.set_defaults(broadcast=True) args = argsparser.parse_args() -# Mode +# Server name if args.servername: servername = args.servername else: @@ -201,7 +201,12 @@ def new_client(client, wserver): Players+=1 sendWSall("/status Hello %d" %(client['id'])) - sendWSall("/players %d" %(Players)) + if Players > 1: + #sendWSall("/players %d" %(Players)) + sendWSall("/players (players:%d)" %(Players)) + else: + sendWSall("/players (player:%d)" %(Players)) + # Called for every WS client disconnecting diff --git a/web/.DS_Store b/web/.DS_Store deleted file mode 100644 index 9b9704a..0000000 Binary files a/web/.DS_Store and /dev/null differ diff --git a/web/knobs/.DS_Store b/web/knobs/.DS_Store deleted file mode 100644 index 1618040..0000000 Binary files a/web/knobs/.DS_Store and /dev/null differ diff --git a/web/knobs/load.png b/web/knobs/load.png new file mode 100644 index 0000000..207453c Binary files /dev/null and b/web/knobs/load.png differ diff --git a/web/knobs/save.png b/web/knobs/save.png new file mode 100644 index 0000000..e822e6b Binary files /dev/null and b/web/knobs/save.png differ diff --git a/web/mmo3.html b/web/mmo3.html index bece34b..9c2fb87 100644 --- a/web/mmo3.html +++ b/web/mmo3.html @@ -28,6 +28,12 @@ color: #ccc; font-size: 2ex; } + #smalltext{ + font-family: "Lucida Grande", Verdana, Arial, sans-serif; + text-align: center; + color: #ccc; + font-size: 1.6ex; + } .encoders{ margin: 0 auto; } @@ -93,7 +99,7 @@ Buttons Line
-
+
@@ -119,22 +125,22 @@
-
FQ -0
+
FQ -0
-
Mod 1 -1
+
Mod 1 -1
-
Mod 2 -2
+
Mod 2 -2
-
Mod 3 -3
+
Mod 3 -3
@@ -147,22 +153,22 @@
-
FQ -5
+
FQ -5
-
Mod 1 -6
+
Mod 1 -6
-
Mod 2 -7
+
Mod 2 -7
-
Mod 3 -8
+
Mod 3 -8
@@ -175,22 +181,22 @@
-
FQ -9
+
FQ -9
-
Mod 1 -10
+
Mod 1 -10
-
Mod 2 -11
+
Mod 2 -11
-
Mod 3 -12
+
Mod 3 -12
@@ -201,22 +207,22 @@
-
FQ -13
+
FQ -13
-
WF -14
+
WF -14
-
SYM -15
+
SYM -15
-
ATTACK -16
+
ATTACK -16
@@ -228,22 +234,22 @@
-
FQ 1 -17
+
FQ 1 -17
-
FQ 2 -18
+
FQ 2 -18
-
MOD -19
+
MOD -19
-
DECAY -20
+
DECAY -20
@@ -255,22 +261,22 @@
-
FQ -21
+
FQ -21
-
Param 1 -22
+
Param 1 -22
-
Param 2 -23
+
Param 2 -23
-
SUSTAIN -24
+
SUSTAIN -24
@@ -282,22 +288,22 @@
-
OSC 1 -25
+
OSC 1 -25
-
OSC 2 -26
+
OSC 2 -26
-
OSC 3 -27
+
OSC 3 -27
-
RELEASE -28
+
RELEASE -28
@@ -309,7 +315,7 @@
-
VOL -29
+
VOL -29
@@ -330,14 +336,24 @@
+
+ + +
+ +
+ OCS-2 + +
-
- OCS-2 - -
+ diff --git a/web/ocs2.html b/web/ocs2.html index e2b82b0..3349838 100644 --- a/web/ocs2.html +++ b/web/ocs2.html @@ -28,6 +28,12 @@ color: #ccc; font-size: 2ex; } + #smalltext{ + font-family: "Lucida Grande", Verdana, Arial, sans-serif; + text-align: center; + color: #ccc; + font-size: 1.6ex; + } .encoders{ margin: 0 auto; } @@ -93,7 +99,7 @@ Buttons Line
-
+
@@ -119,22 +125,22 @@
-
FQ -0
+
FQ -0
-
WF -1
+
WF -1
-
Mod 1 -2
+
Mod 1 -2
-
Mod 2 -3
+
Mod 2 -3
@@ -147,22 +153,22 @@
-
FQ -5
+
FQ -5
-
WF -6
+
WF -6
-
Mod 1 -7
+
Mod 1 -7
-
Mod 2 -8
+
Mod 2 -8
@@ -175,22 +181,22 @@
-
FQ -9
+
FQ -9
-
Q -10
+
Q -10
-
Mod 1 -11
+
Mod 1 -11
-
Mod 2 -12
+
Mod 2 -12
@@ -201,22 +207,22 @@
-
FQ -13
+
FQ -13
-
WF -14
+
WF -14
-
SYM -15
+
SYM -15
-
ATTACK -16
+
ATTACK -16
@@ -228,22 +234,22 @@
-
FQ -17
+
FQ -17
-
WF -18
+
WF -18
-
SYM -19
+
SYM -19
-
DECAY -20
+
DECAY -20
@@ -255,22 +261,22 @@
-
FQ -21
+
FQ -21
-
MOD -22
+
MOD -22
-
WET EFFECT -23
+
WET EFFECT -23
-
SUSTAIN -24
+
SUSTAIN -24
@@ -282,22 +288,22 @@
-
PARAM 1 -25
+
PARAM 1 -25
-
PARAM 2 -26
+
PARAM 2 -26
-
MOD EFFECT -27
+
MOD EFFECT -27
-
RELEASE -28
+
RELEASE -28
@@ -309,17 +315,17 @@
-
MIX 1/2 -29
+
MIX 1/2 -29
-
MOD -30
+
MOD -30
-
VOL -31
+
VOL -31
@@ -334,14 +340,24 @@
+
+ + +
+ +
+ MMO-3 + +
@@ -451,6 +474,7 @@ var message=""; var log=[]; var knobs = document.getElementsByTagName('webaudio-knob'); + var knobState = [] for(var i = 0; i < knobs.length; i++){ knobs[i].addEventListener("input",Dump,false); @@ -471,7 +495,7 @@ function Dump(e) { var str=""; str=e.type + " : " + e.target.id + " : " + e.target.value + " "; - console.log(str); + //console.log(str); log.unshift(str); log.length=1; str=""; @@ -483,33 +507,72 @@ //var evview=document.getElementById("events"); //evview.innerHTML=str; //console.log( e.type + "/" + e.target.id + "/" + e.target.value); - //console.log('/' + e.target.id + ' ' + e.target.value); + //console.log('/' + e.target.id + ' ' + e.target.value + ' ' + e.type); //socket.emit('message', '/' + e.target.id + ' ' + e.target.value); - _WS.send("/" + e.target.id + " " + e.target.value); + + if (e.target.id === "load" || e.target.id === "store") { + + if (e.type === "change") { + + if (e.target.id === "store") { + //var knobState = [] + //var knobs = document.getElementsByTagName('webaudio-knob'); + for (var i = 0; i < knobs.length; i++) { + var knob = knobs[i] ; + //console.log(knob) ; + knobState[i] = knob.getAttribute('id')+" "+knob.getAttribute('value') ; + localStorage.setItem(knob.getAttribute('id'),knob.getAttribute('value')) ; + } + //console.log(knobState) ; + console.log('store clique') ; + } + if (e.target.id === "load") { + if (knobState.length > 0) { + for (var i = 0; i < knobState.length; i++) { + ccstate=knobState[i] ; + _WS.send("/" + ccstate) ; + } + } + else { + for (var i = 0; i < knobs.length; i++) { + var knob = knobs[i]; + var value = localStorage.getItem(knob.getAttribute('id')); + if ( value != null) { + ccstate = knob.getAttribute('id')+" "+value; + _WS.send("/" + ccstate) ; + console.log(ccstate) ; + } + else console.log("no localstorage"); + } + } + console.log('load clique') ; + } + } - if (e.target.id === "on" && e.type === "change") { - window.location.reload(); } - - if (e.target.id === "rate" && e.type === "change") { - e.target.value = 1 ; - } - - if (e.target.id === "range" && e.type === "change") { - e.target.value = 1 ; - } - if (e.target.id === "select" && e.type === "change") { - e.target.value = 1 ; - } - + else { + + _WS.send("/" + e.target.id + " " + e.target.value); + + if (e.target.id === "on" && e.type === "change") { + window.location.reload(); + } + + if (e.target.id === "rate" && e.type === "change") { + e.target.value = 1 ; + } + + if (e.target.id === "range" && e.type === "change") { + e.target.value = 1 ; + } + if (e.target.id === "select" && e.type === "change") { + e.target.value = 1 ; + } + + } } -
- MMO-3 - -