From 012bef55b1e53045d151d5853aa76c4c78838e26 Mon Sep 17 00:00:00 2001 From: sam Date: Sat, 2 May 2020 02:38:06 +0200 Subject: [PATCH] BUgfixs --- .gitignore | 1 + client.py | 4 +- jamidi.json | 48 +++++++++-- libs/midi3.py | 151 +++++++++++++++++++++++++-------- main.py | 9 +- web/.DS_Store | Bin 10244 -> 0 bytes web/knobs/.DS_Store | Bin 6148 -> 0 bytes web/knobs/load.png | Bin 0 -> 16956 bytes web/knobs/save.png | Bin 0 -> 12651 bytes web/mmo3.html | 192 +++++++++++++++++++++++++++++------------- web/ocs2.html | 201 +++++++++++++++++++++++++++++--------------- 11 files changed, 435 insertions(+), 171 deletions(-) mode change 100644 => 100755 client.py delete mode 100644 web/.DS_Store delete mode 100644 web/knobs/.DS_Store create mode 100644 web/knobs/load.png create mode 100644 web/knobs/save.png 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 9b9704aeb476bf04d73a6129242821a418625741..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10244 zcmeHMeQX>@6`!9SCp-1JUMH!2d$yBnjN>G|5TEe}u2bi1Uz0dViRn49^Ff^Z*v6}z zZ{6EFJBgd(N+1w46(IVN6d{EO1WoB*p`}2|M=2_W7DQc@6ctefs>DA8D2RfDcr&|a z-}xf~fkK3Kq}{jk-n^Tcx4(J6ojJxBdUMGTV*$pPLYI)5O1DLdu#0O!DRDI#NsvFY z$Kxq0PPHG##}~Br6kY_p2zU|jBH%^9i@=Q#0pe^?lyXOUcP|271iT2$MnL3;09`_c zT{-PYnL6kuw*VxUky;jd7TW>g6L4kNmD7$S3l%u7Bpp|{TMRhvqz}05!mgZlq~p%u zZa(1l40neD**pDyKsILxJJP#*5%40gFalz^l(Gb~nH`@j+`p$xJKNm+I!a2Hl`UVP zC`!3fp?oMhYE4G7v24!njb#qWnwd1>?(?8!rDmUF@%^STIijuZv(nj^nKm*7%?}Tm z>7+H;Yvs~OTmH^KEIUfRoO6YbXuheb*4E}=Ak^7$IvAMhXln@u+L}8~pXN$UW9W|l zqhlveP2G3;{lv%?TqC%Nq8GR%uf_`V&@qAY)9>Y=;t7DmRuUOI0G4O5~`q z(UVF=%~OUwoHb5l#nVCC%4GXw!(o$rg>FP!Dg5`2XH6?@?>90wNzN~-x74p&e_M0g z?b{+V6*pIERVz6^ND)tG&3jBEvv1N&W=9Xi?1Yg{n(1Rh6Q*s(Q^sIAHfB_nlyN>& zUE{0J{d!&AG~Y$h8kFH&+`KnuW>3mK)~wa};UtaxE-NW|NorqHxU@8^)%8Qd(w8yl zo>Ua6ZrY&pL7Qfw#oUI$&72P&r&p}mGz2m5Pz%@hrDAa-r6sxJ*51MOp$TWI+HP0b zwUz6`^qR(Kigp!(t~%VK>qkV-dt!EW@3@hsQN2jr%h|2fcPPpMVeLsKX;cm24Gs)s z7#hOOPE8XWqq89EX4~08!hD)dvIp52_7FSAF0iNACH5Tq0s9eqh5dy6lwDzOvA5YD z*dN(D?9c2USb^23Mh$$p6`S#1Y{C0+2eu)M9_+$id=Pgdio=K_2?O`Q!X!@O6sGVI zoWZB?X?zBc;|V;8ui_~@gRkRRJdf|=W&8}ka7I{?hw)ubJ%7iW^yIG6ls(~|vXvZJ z46Od`J9b9?Ct$szItX30`j#~f8#cFeZ0(tSlk?tPA&4Y$9B4?$79LUvo?4k> zZRPEp0IV%@tew0|A)IQ<9cwoabFsuU=F}Vg5w2)h3Vlmr?>u{gJyib(g@s_vE#HNEf&Awm?+fJPE7Q^*8iA^3Z_`ZMiu6VivvK5 zJkLm%@{9b9Sw_04T&+-d_V23RtuD$N{~j-i)`^UjN)1@HDd^|GEK@Bo4KL1J3z$&@ zzj$*^-K~wAwzPJ3_st06Qdg_hOD)_2O5;`d3zutOdiiu;Z81fy)T%1$*Z9<;)F3HJ zSk|ra`3kcOafDE>Z}O>g6eTpb1yyy9qJ*Y7r1}adN~qe}RbM_INs1E6+q+cXY+`dL zN~pJmeZF~VPEwSxMQBt71f}o>df&3la#h2&t9gyfSDaEtJ!@eRwuc>M88$^!^%y%( zH1#xlhCR=I%x2gd>^GDyuChN7Nxh2_l%k9hMC0jjsTWLqLE(eRBXc*E8?s@K-W>aL=o+#9VuPtR`f>z xRm5?;JcS{f`#mfD!lW1e(~gwxRG01~uhX9a-u~a)|3{sJ>wn=S`u}qO|4*AZJ?H=c diff --git a/web/knobs/.DS_Store b/web/knobs/.DS_Store deleted file mode 100644 index 16180405450bc153625ed156b678443e19471df5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6148 zcmeHK%}T>S5dOCIP$@{gdCUWd#Wx709tz?`dREhx7+RZ>A4E@i3?Iq|@NM*)*`-+9 zdK8rznE5u7nc3ZMLv|N{8`RTtpa-Bs6>J^S>=1b`x+SgPX%n5+$gsc^XINpm6>W~+ z$bhU}7X@ZWP~-FZrK>8rtjnrk#3{xo8B@^qxyCcDa*YYwL$*uIQ6cB5_vy>|PJB=N z1|(uh&oecne~b}5bM+-!>$~2sLhpOV$8jC8MurFWEMrPJzB~5ozrS>}$S27nPZ%ek z_bc_~U4<>+n7NP4t~knj+~^Ea){sg&E4a5W6|{^QW55_N2L2!e+_ObGU5{ps0b{@z zSTi91hlnZ|2W&j*uY*Rf07N-Ps<17GEy;-j#sM3Td_!?QmFQDlxMDb;PJa~Q;((1u zpAHu;A1?Il!VSe~@2nqbI9%+}tTA8=Y%*}*AA7R@U+lmCZzkE3F<=b*D+XM9cs(3& zN?~u^nw;#l5%rcTB5{q!bqX806*E@0;uES0{gE_?alpnSJrp|#C=F(efgfey0~=0L AU;qFB diff --git a/web/knobs/load.png b/web/knobs/load.png new file mode 100644 index 0000000000000000000000000000000000000000..207453c649d8fdab2e258c0f447a0f7752b63577 GIT binary patch literal 16956 zcmaL9cUTkAw>F9bigb_;(h0pu=m^pU2?)|cQ>2C7ds7f;QbRLz5J9ShUZhD2y-2TO z00C*ChI+@}ch33lANSmO@-UN`z1QAr?X}kXuAO;^exa>SLdZafgM&k&@k~VzIH%#@ z+>s!_1+HpqGh=`gy_c%7*GpGhFJDU!8yrPz*Ecq-8qSt>HhMOe*8c7&8#x>tTuys^ zV=rTfmW-9FvykP#HbQ>RZa{Aw961F)H%lu=8!y&3Hg@(dPuTZc+Sys{t)H+Pi9R|dHLcUzd9 z^*>F#9G|fNpGg@*Ua%^=df2dv3yBF@iHM4_N=OKag2g2yL@Y7cHyL4JUteD#Uojz94?AH|X=&+yG9Et` z1X>7s`nz~p`U$#ta{RXh6&p`04|_K+dsi3Me-bUKt zJKMN;{dZsM|Jzsbf9?Cv8JyjKkyUIw?0syk)jV9CS^uN7jQ#&U7s>xs-hcJA{@>>! z^}qHN1|}o?Z)yKuOa0%s0RQ=S`9F>eeEA>Ow{Zdd-2-s!9`3j$AYO1@Xz8owZE-tMUWaWH_ zNdeU9=owIxQy3omX=q?dgGYFLd=ly(m|s{6gz>wNshKHh)3b}nmQFW2hnku$J+Xp@BWmQd|^YSAj z<8pHIQxKmw|7^W|`>yC~ML|(bX2vIozNrn&5zH_6xu_vAF%^4r?rHCooRNpcUY(s| zQ!{ftynM|p-c)}ny}YA+Q3k>uRc&DKW!D27BwzgzdvPEL%BO(E*f(Cb^PtLrz{7q%v5UN-jE zx7R1w(8rRn;Y>-zqA;CnY6){hU2JJC~M?$jwaQ;pYC{);>NyE-$Ze zd3kktarp=RiWhmD02XdAoe@49$h h!Ke!m$ZyS{!9c(>#lYH;?Je&T5zvTzv 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 G1OE@b@O0Mz literal 0 HcmV?d00001 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 - -