From aa15929b5243069837098d67758eab7eb59621f5 Mon Sep 17 00:00:00 2001 From: Azaaxin Date: Mon, 19 Oct 2020 02:19:06 +0200 Subject: [PATCH] coffee shop code --- index.html | 87 +++++++++++++++++++++++++++ scripts/midi.js | 34 +++++++++++ scripts/osc1.js | 88 +++++++++++++++++++++++++++ scripts/osc2.js | 84 ++++++++++++++++++++++++++ scripts/scripts.js | 47 +++++++++++++++ scripts/scripts_obs.js | 133 +++++++++++++++++++++++++++++++++++++++++ scripts/webmidi.min.js | 31 ++++++++++ 7 files changed, 504 insertions(+) create mode 100644 index.html create mode 100644 scripts/midi.js create mode 100644 scripts/osc1.js create mode 100644 scripts/osc2.js create mode 100644 scripts/scripts.js create mode 100644 scripts/scripts_obs.js create mode 100644 scripts/webmidi.min.js diff --git a/index.html b/index.html new file mode 100644 index 0000000..2397574 --- /dev/null +++ b/index.html @@ -0,0 +1,87 @@ + + + + + + + + Document + + + +
Oscillator 1
+
+
+
Decay
+
Attack
+
Sustain
+
Release
+
+
+
+
Distortion
+
LFO
+
Reverb
+ +
+
+
Oscillator 2
+ +
+
+
Decay
+
Attack
+
Sustain
+
Release
+
+
+
+
Distortion
+
LFO
+
Reverb
+
+ +
+ + +
+ + + + + + + + + + + + + diff --git a/scripts/midi.js b/scripts/midi.js new file mode 100644 index 0000000..2af406f --- /dev/null +++ b/scripts/midi.js @@ -0,0 +1,34 @@ +var midi, data; +// request MIDI access +if (navigator.requestMIDIAccess) { + navigator.requestMIDIAccess({ + sysex: false + }).then(onMIDISuccess, onMIDIFailure); +} else { + alert("No MIDI support in your browser."); +} + +// midi functions +function onMIDISuccess(midiAccess) { + // when we get a succesful response, run this code + midi = midiAccess; // this is our raw MIDI data, inputs, outputs, and sysex status + + var inputs = midi.inputs.values(); + // loop over all available inputs and listen for any MIDI input + for (var input = inputs.next(); input && !input.done; input = inputs.next()) { + // each time there is a midi message call the onMIDIMessage function + input.value.onmidimessage = onMIDIMessage; + } +} + +function onMIDIFailure(error) { + // when we get a failed response, run this code + console.log("No access to MIDI devices or your browser doesn't support WebMIDI API. Please use WebMIDIAPIShim " + error); +} + +function onMIDIMessage(message) { + data = message.data; // this gives us our [command/channel, note, velocity] data. + console.log('MIDI data', data); // MIDI data [144, 63, 73] + midiNote(data[1], data[2]); + midiNote2(data[1], data[2]); +} \ No newline at end of file diff --git a/scripts/osc1.js b/scripts/osc1.js new file mode 100644 index 0000000..5c46bef --- /dev/null +++ b/scripts/osc1.js @@ -0,0 +1,88 @@ +var dist1_1; +var filter1_1; +var reverb1_1 = 1; +synth1 = new Tone.FMSynth({ + oscillator: { + type: "sawtooth", + detune: 0, + }, + envelope: { + attack: 0, + decay: 0, + sustain: 0, + release: 0, + }, + + }).toMaster(); + +piano.on('change',function(keyValue) { + if (keyValue.state == true) { // If Key is down + synth1.triggerAttackRelease(Tone.Frequency(keyValue.note, "midi").toNote()); // Play note + } +}) +// Midi input +function midiNote(note, vel){ + if(vel !=0){ // Skip Key up, If velocity is 0 then play + synth1.triggerAttackRelease(Tone.Frequency(note, "midi").toNote()); // Play note + piano.toggleKey(note, true) + }else{ + piano.toggleKey(note, false) + } +} + +//filters +function updateFilter1(){ + var filter1 = new Tone.AutoFilter(filter1_1).start(); //LFO + var reverb1 = new Tone.Reverb(reverb1_1); + var distortion1 = new Tone.Distortion(dist1_1); //DIST + // connect the player to the filter, distortion and then to the master output + synth1.chain(filter1, distortion1, reverb1, Tone.Destination); +} + + +// Controls +var sus1_d = new Nexus.Dial('#sustain1', dial_settings1) +var dec1_d = new Nexus.Dial('#decay1', dial_settings1) +var atk1_d = new Nexus.Dial('#attack1', dial_settings1) +var rel1_d = new Nexus.Dial('#release1', dial_settings1) +// var tune1_d = new Nexus.Dial('#tune1', dial_settings1) +var select1 = new Nexus.Select('#osc1', dropdown_osc) + +var dist1 = new Nexus.Dial('#dist1', distortion_settings) +var lfo1 = new Nexus.Dial('#lfo1', dial_settings1) +var reverb1_d = new Nexus.Dial('#reverb1', reverb_settings) +// When controls change + +sus1_d.on('change',function(value) { + synth1.envelope.attack = value; +}); +dec1_d.on('change',function(value) { + synth1.envelope.decay = value; +}); +atk1_d.on('change',function(value) { + synth1.envelope.attack = value; +}); +rel1_d.on('change',function(value) { + synth1.envelope.release = value; +}); +// tune1_d.on('change',function(value) { +// synth1.oscillator.detune = value; +// }); +select1.on('change',function(value) { + synth1.oscillator.type = value.value; +}); + +//Effect knobs +dist1.on('change',function(value) { + dist1_1 = value.toFixed(1); + typewatch(function(){updateFilter1();}, 100 ); + +}); +lfo1.on('change',function(value) { + filter1_1 = value.toFixed(1); + typewatch(function(){updateFilter1();}, 100 ); +}); +reverb1_d.on('change',function(value) { + reverb1_1 = value.toFixed(0); + typewatch(function(){updateFilter1();}, 100 ); +}); diff --git a/scripts/osc2.js b/scripts/osc2.js new file mode 100644 index 0000000..1b34ee0 --- /dev/null +++ b/scripts/osc2.js @@ -0,0 +1,84 @@ +var dist2_2; +var filter2_2; +var reverb2_2 = 1; +synth2 = new Tone.FMSynth({ + oscillator: { + type: "sawtooth", + detune: 0, + }, + envelope: { + attack: 0, + decay: 0, + sustain: 0, + release: 0, + }, + + }).toMaster(); + +piano.on('change',function(keyValue) { + if (keyValue.state == true) { // If Key is down + synth2.triggerAttackRelease(Tone.Frequency(keyValue.note, "midi").toNote()); // Play note + } +}) +// Midi input +function midiNote2(note, vel){ + if(vel !=0){ // Skip Key up, If velocity is 0 then play + synth2.triggerAttackRelease(Tone.Frequency(note, "midi").toNote()); // Play note + } +} +//filters +function updateFilter2(){ + var filter2 = new Tone.AutoFilter(filter2_2).start(); //LFO + var reverb2 = new Tone.Reverb(reverb2_2); + var distortion2 = new Tone.Distortion(dist2_2); //DIST + // connect the player to the filter, distortion and then to the master output + synth2.chain(filter2, distortion2, reverb2, Tone.Destination); +} + + +// Controls +var sus2_d = new Nexus.Dial('#sustain2', dial_settings1) +var dec2_d = new Nexus.Dial('#decay2', dial_settings1) +var atk2_d = new Nexus.Dial('#attack2', dial_settings1) +var rel2_d = new Nexus.Dial('#release2', dial_settings1) +// var tune2_d = new Nexus.Dial('#tune2', dial_settings2) +var select2 = new Nexus.Select('#osc2', dropdown_osc) + +var dist2 = new Nexus.Dial('#dist2', distortion_settings) +var lfo2 = new Nexus.Dial('#lfo2', dial_settings1) +var reverb2_d = new Nexus.Dial('#reverb2', reverb_settings) +// When controls change + +sus2_d.on('change',function(value) { + synth2.envelope.attack = value; +}); +dec2_d.on('change',function(value) { + synth2.envelope.decay = value; +}); +atk2_d.on('change',function(value) { + synth2.envelope.attack = value; +}); +rel2_d.on('change',function(value) { + synth2.envelope.release = value; +}); +// tune2_d.on('change',function(value) { +// synth2.oscillator.detune = value; +// }); +select2.on('change',function(value) { + synth2.oscillator.type = value.value; +}); + +//Effect knobs +dist2.on('change',function(value) { + dist2_2 = value.toFixed(1); + typewatch(function(){updateFilter2();}, 100 ); + +}); +lfo2.on('change',function(value) { + filter2_2 = value.toFixed(1); + typewatch(function(){updateFilter2();}, 100 ); +}); +reverb2_d.on('change',function(value) { + reverb2_2 = value.toFixed(0); + typewatch(function(){updateFilter2();}, 100 ); +}); diff --git a/scripts/scripts.js b/scripts/scripts.js new file mode 100644 index 0000000..9aa1e87 --- /dev/null +++ b/scripts/scripts.js @@ -0,0 +1,47 @@ +//Piano roll +var piano = new Nexus.Piano('#target',{ + 'size': [750,125], + 'mode': 'button', // 'button', 'toggle', or 'impulse' + 'lowNote': 24, + 'highNote': 100 +}) +var dial_settings1 = { + 'size': [50,50], + 'interaction': 'radial', + 'mode': 'relative', + 'min': 0, + 'max': 10, + 'step': 0, + 'value': 0 +} +var dropdown_osc = { + 'size': [100,30], + 'options': ['sawtooth', 'sawtooth2', 'sawtooth3', 'sawtooth4','sine', 'sine2', 'sine3', 'sine4', 'square', 'square2', 'square3', 'square4', 'triangle', 'triangle2', 'triangle3', 'triangle4'] +} +var distortion_settings = { + 'size': [50,50], + 'interaction': 'radial', // "radial", "vertical", or "horizontal" + 'mode': 'relative', // "absolute" or "relative" + 'min': 0, + 'max': 1, + 'step': 0.1, + 'value': 0 +} +var reverb_settings = { + 'size': [50,50], + 'interaction': 'radial', // "radial", "vertical", or "horizontal" + 'mode': 'relative', // "absolute" or "relative" + 'min': 1, + 'max': 10, + 'step': 0.1, + 'value': 0 +} + + +var typewatch = function(){ + var timer = 0; + return function(callback, ms){ + clearTimeout (timer); + timer = setTimeout(callback, ms); + } +}(); \ No newline at end of file diff --git a/scripts/scripts_obs.js b/scripts/scripts_obs.js new file mode 100644 index 0000000..dcffc6d --- /dev/null +++ b/scripts/scripts_obs.js @@ -0,0 +1,133 @@ +const synth = new Tone.FMSynth().toMaster(); +var sus1 = 0; +var atk1 = 0; +var wav = "sawtooth"; +// var synth = new Tone.AMSynth({ +// oscillator: { +// type: "square", +// }, +// envelope: { +// attack: atk1, +// decay: 0.1, +// sustain: sus1, +// release: 0.1, +// }, +// modulation: { +// type: "square", +// }, +// }).toMaster(); +// const synth = new Tone.FMSynth().toDestination(); +// synth.type = "pwm"; + +var activeSynths = {}; +const createPiano = (lowNote, highNote) => { + const newPiano = new Nexus.Piano("#target", { + size: [500, 125], + mode: "button", + lowNote, + highNote, + }); + + return newPiano; +}; + +let lowNote = 72; +let highNote = 84; +let piano = createPiano(lowNote, highNote); + +synth.triggerRelease(); + + +const keyMapper = { + a: 0, + w: 1, + s: 2, + e: 3, + d: 4, + f: 5, + t: 6, + g: 7, + y: 8, + h: 9, + u: 10, + j: 11, + k: 12, +}; + + + +piano.on("change", (k) => { + if (k.state) { + if (!activeSynths[k.note]) { + activeSynths[k.note] = new Tone.AMSynth({ + oscillator: { + type: wav, + }, + envelope: { + attack: atk1, + decay: 0.1, + sustain: sus1, + release: 0.1, + }, + modulation: { + type: wav, + }, + }).toMaster(); + } + activeSynths[k.note].triggerAttack(k.note); + document.querySelector(".innernote").innerHTML = + k.note + " - " + activeSynths[k.note].triggerAttack(k.note); + } else { + activeSynths[k.note].triggerRelease(); + } +}); + +var sustain1 = new Nexus.Dial('#sus1',{ + 'size': [75,75], + 'interaction': 'radial', // "radial", "vertical", or "horizontal" + 'mode': 'relative', // "absolute" or "relative" + 'min': 0, + 'max': 1, + 'step': 0, + 'value': 0 +}) + +sustain1.on('change',function(v) { + sus1 = v; +}) +var attack1 = new Nexus.Dial('#attack1',{ + 'size': [75,75], + 'interaction': 'radial', // "radial", "vertical", or "horizontal" + 'mode': 'relative', // "absolute" or "relative" + 'min': 0, + 'max': 1, + 'step': 0, + 'value': 0 +}) + +attack1.on('change',function(va) { + atk1 = va; +}) + +var select = new Nexus.Select('#waves',{ + 'size': [100,30], + 'options': ['sine','sawtooth', 'square'] +}) + +select.on('change',function(waves) { + wav = waves; +}) + +document.addEventListener("keydown", (event) => { + const keyIndex = keyMapper[event.key]; + keyIndex !== undefined && !piano.keys[keyIndex]._state.state + ? piano.toggleIndex(keyIndex, true) + : null; +}); + +document.addEventListener("keyup", (event) => { + const keyIndex = keyMapper[event.key]; + keyIndex !== undefined && piano.keys[keyIndex]._state.state + ? piano.toggleIndex(keyIndex, false) + : null; +}); \ No newline at end of file diff --git a/scripts/webmidi.min.js b/scripts/webmidi.min.js new file mode 100644 index 0000000..f5654ca --- /dev/null +++ b/scripts/webmidi.min.js @@ -0,0 +1,31 @@ +/* + +WebMidi v2.5.1 + +WebMidi.js helps you tame the Web MIDI API. Send and receive MIDI messages with ease. Control instruments with user-friendly functions (playNote, sendPitchBend, etc.). React to MIDI input with simple event listeners (noteon, pitchbend, controlchange, etc.). +https://github.com/djipco/webmidi + + +The MIT License (MIT) + +Copyright (c) 2015-2019, Jean-Philippe Côté + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and +associated documentation files (the "Software"), to deal in the Software without restriction, +including without limitation the rights to use, copy, modify, merge, publish, distribute, +sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial +portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT +NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES +OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +*/ + + +!function(scope){"use strict";function WebMidi(){if(WebMidi.prototype._singleton)throw new Error("WebMidi is a singleton, it cannot be instantiated directly.");(WebMidi.prototype._singleton=this)._inputs=[],this._outputs=[],this._userHandlers={},this._stateChangeQueue=[],this._processingStateChange=!1,this._midiInterfaceEvents=["connected","disconnected"],this._nrpnBuffer=[[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[]],this._nrpnEventsEnabled=!0,this._nrpnTypes=["entry","increment","decrement"],this._notes=["C","C#","D","D#","E","F","F#","G","G#","A","A#","B"],this._semitones={C:0,D:2,E:4,F:5,G:7,A:9,B:11},Object.defineProperties(this,{MIDI_SYSTEM_MESSAGES:{value:{sysex:240,timecode:241,songposition:242,songselect:243,tuningrequest:246,sysexend:247,clock:248,start:250,continue:251,stop:252,activesensing:254,reset:255,midimessage:0,unknownsystemmessage:-1},writable:!1,enumerable:!0,configurable:!1},MIDI_CHANNEL_MESSAGES:{value:{noteoff:8,noteon:9,keyaftertouch:10,controlchange:11,channelmode:11,nrpn:11,programchange:12,channelaftertouch:13,pitchbend:14},writable:!1,enumerable:!0,configurable:!1},MIDI_REGISTERED_PARAMETER:{value:{pitchbendrange:[0,0],channelfinetuning:[0,1],channelcoarsetuning:[0,2],tuningprogram:[0,3],tuningbank:[0,4],modulationrange:[0,5],azimuthangle:[61,0],elevationangle:[61,1],gain:[61,2],distanceratio:[61,3],maximumdistance:[61,4],maximumdistancegain:[61,5],referencedistanceratio:[61,6],panspreadangle:[61,7],rollangle:[61,8]},writable:!1,enumerable:!0,configurable:!1},MIDI_CONTROL_CHANGE_MESSAGES:{value:{bankselectcoarse:0,modulationwheelcoarse:1,breathcontrollercoarse:2,footcontrollercoarse:4,portamentotimecoarse:5,dataentrycoarse:6,volumecoarse:7,balancecoarse:8,pancoarse:10,expressioncoarse:11,effectcontrol1coarse:12,effectcontrol2coarse:13,generalpurposeslider1:16,generalpurposeslider2:17,generalpurposeslider3:18,generalpurposeslider4:19,bankselectfine:32,modulationwheelfine:33,breathcontrollerfine:34,footcontrollerfine:36,portamentotimefine:37,dataentryfine:38,volumefine:39,balancefine:40,panfine:42,expressionfine:43,effectcontrol1fine:44,effectcontrol2fine:45,holdpedal:64,portamento:65,sustenutopedal:66,softpedal:67,legatopedal:68,hold2pedal:69,soundvariation:70,resonance:71,soundreleasetime:72,soundattacktime:73,brightness:74,soundcontrol6:75,soundcontrol7:76,soundcontrol8:77,soundcontrol9:78,soundcontrol10:79,generalpurposebutton1:80,generalpurposebutton2:81,generalpurposebutton3:82,generalpurposebutton4:83,reverblevel:91,tremololevel:92,choruslevel:93,celestelevel:94,phaserlevel:95,databuttonincrement:96,databuttondecrement:97,nonregisteredparametercoarse:98,nonregisteredparameterfine:99,registeredparametercoarse:100,registeredparameterfine:101},writable:!1,enumerable:!0,configurable:!1},MIDI_NRPN_MESSAGES:{value:{entrymsb:6,entrylsb:38,increment:96,decrement:97,paramlsb:98,parammsb:99,nullactiveparameter:127},writable:!1,enumerable:!0,configurable:!1},MIDI_CHANNEL_MODE_MESSAGES:{value:{allsoundoff:120,resetallcontrollers:121,localcontrol:122,allnotesoff:123,omnimodeoff:124,omnimodeon:125,monomodeon:126,polymodeon:127},writable:!1,enumerable:!0,configurable:!1},octaveOffset:{value:0,writable:!0,enumerable:!0,configurable:!1}}),Object.defineProperties(this,{supported:{enumerable:!0,get:function(){return"requestMIDIAccess"in navigator}},enabled:{enumerable:!0,get:function(){return void 0!==this.interface}.bind(this)},inputs:{enumerable:!0,get:function(){return this._inputs}.bind(this)},outputs:{enumerable:!0,get:function(){return this._outputs}.bind(this)},sysexEnabled:{enumerable:!0,get:function(){return!(!this.interface||!this.interface.sysexEnabled)}.bind(this)},nrpnEventsEnabled:{enumerable:!0,get:function(){return!!this._nrpnEventsEnabled}.bind(this),set:function(enabled){return this._nrpnEventsEnabled=enabled,this._nrpnEventsEnabled}},nrpnTypes:{enumerable:!0,get:function(){return this._nrpnTypes}.bind(this)},time:{enumerable:!0,get:function(){return performance.now()}}})}var wm=new WebMidi;function Input(midiInput){var that=this;this._userHandlers={channel:{},system:{}},this._midiInput=midiInput,Object.defineProperties(this,{connection:{enumerable:!0,get:function(){return that._midiInput.connection}},id:{enumerable:!0,get:function(){return that._midiInput.id}},manufacturer:{enumerable:!0,get:function(){return that._midiInput.manufacturer}},name:{enumerable:!0,get:function(){return that._midiInput.name}},state:{enumerable:!0,get:function(){return that._midiInput.state}},type:{enumerable:!0,get:function(){return that._midiInput.type}}}),this._initializeUserHandlers(),this._midiInput.onmidimessage=this._onMidiMessage.bind(this)}function Output(midiOutput){var that=this;this._midiOutput=midiOutput,Object.defineProperties(this,{connection:{enumerable:!0,get:function(){return that._midiOutput.connection}},id:{enumerable:!0,get:function(){return that._midiOutput.id}},manufacturer:{enumerable:!0,get:function(){return that._midiOutput.manufacturer}},name:{enumerable:!0,get:function(){return that._midiOutput.name}},state:{enumerable:!0,get:function(){return that._midiOutput.state}},type:{enumerable:!0,get:function(){return that._midiOutput.type}}})}WebMidi.prototype.enable=function(callback,sysex){this.enabled||(this.supported?navigator.requestMIDIAccess({sysex:sysex}).then(function(midiAccess){var promiseTimeout,events=[],promises=[];this.interface=midiAccess,this._resetInterfaceUserHandlers(),this.interface.onstatechange=function(e){events.push(e)};for(var inputs=midiAccess.inputs.values(),input=inputs.next();input&&!input.done;input=inputs.next())promises.push(input.value.open());for(var outputs=midiAccess.outputs.values(),output=outputs.next();output&&!output.done;output=outputs.next())promises.push(output.value.open());function onPortsOpen(){clearTimeout(promiseTimeout),this._updateInputsAndOutputs(),this.interface.onstatechange=this._onInterfaceStateChange.bind(this),"function"==typeof callback&&callback.call(this),events.forEach(function(event){this._onInterfaceStateChange(event)}.bind(this))}promiseTimeout=setTimeout(onPortsOpen.bind(this),200),Promise&&Promise.all(promises).catch(function(err){}).then(onPortsOpen.bind(this))}.bind(this),function(err){"function"==typeof callback&&callback.call(this,err)}.bind(this)):"function"==typeof callback&&callback(new Error("The Web MIDI API is not supported by your browser.")))},WebMidi.prototype.disable=function(){if(!this.supported)throw new Error("The Web MIDI API is not supported by your browser.");this.interface&&(this.interface.onstatechange=void 0),this.interface=void 0,this._inputs=[],this._outputs=[],this._nrpnEventsEnabled=!0,this._resetInterfaceUserHandlers()},WebMidi.prototype.addListener=function(type,listener){if(!this.enabled)throw new Error("WebMidi must be enabled before adding event listeners.");if("function"!=typeof listener)throw new TypeError("The 'listener' parameter must be a function.");if(!(0<=this._midiInterfaceEvents.indexOf(type)))throw new TypeError("The specified event type is not supported.");return this._userHandlers[type].push(listener),this},WebMidi.prototype.hasListener=function(type,listener){if(!this.enabled)throw new Error("WebMidi must be enabled before checking event listeners.");if("function"!=typeof listener)throw new TypeError("The 'listener' parameter must be a function.");if(!(0<=this._midiInterfaceEvents.indexOf(type)))throw new TypeError("The specified event type is not supported.");for(var o=0;o>4,channelBufferIndex=15&e.data[0],channel=1+channelBufferIndex;if(1=wm.MIDI_NRPN_MESSAGES.increment&&data1<=wm.MIDI_NRPN_MESSAGES.parammsb||data1===wm.MIDI_NRPN_MESSAGES.entrymsb||data1===wm.MIDI_NRPN_MESSAGES.entrylsb)){var ccEvent={target:this,type:"controlchange",data:e.data,timestamp:e.timeStamp,channel:channel,controller:{number:data1,name:this.getCcNameByNumber(data1)},value:data2};if(ccEvent.controller.number===wm.MIDI_NRPN_MESSAGES.parammsb&&ccEvent.value!=wm.MIDI_NRPN_MESSAGES.nullactiveparameter)wm._nrpnBuffer[channelBufferIndex]=[],wm._nrpnBuffer[channelBufferIndex][0]=ccEvent;else if(1===wm._nrpnBuffer[channelBufferIndex].length&&ccEvent.controller.number===wm.MIDI_NRPN_MESSAGES.paramlsb)wm._nrpnBuffer[channelBufferIndex].push(ccEvent);else if(2!==wm._nrpnBuffer[channelBufferIndex].length||ccEvent.controller.number!==wm.MIDI_NRPN_MESSAGES.increment&&ccEvent.controller.number!==wm.MIDI_NRPN_MESSAGES.decrement&&ccEvent.controller.number!==wm.MIDI_NRPN_MESSAGES.entrymsb)if(3===wm._nrpnBuffer[channelBufferIndex].length&&wm._nrpnBuffer[channelBufferIndex][2].number===wm.MIDI_NRPN_MESSAGES.entrymsb&&ccEvent.controller.number===wm.MIDI_NRPN_MESSAGES.entrylsb)wm._nrpnBuffer[channelBufferIndex].push(ccEvent);else if(3<=wm._nrpnBuffer[channelBufferIndex].length&&wm._nrpnBuffer[channelBufferIndex].length<=4&&ccEvent.controller.number===wm.MIDI_NRPN_MESSAGES.parammsb&&ccEvent.value===wm.MIDI_NRPN_MESSAGES.nullactiveparameter)wm._nrpnBuffer[channelBufferIndex].push(ccEvent);else if(4<=wm._nrpnBuffer[channelBufferIndex].length&&wm._nrpnBuffer[channelBufferIndex].length<=5&&ccEvent.controller.number===wm.MIDI_NRPN_MESSAGES.paramlsb&&ccEvent.value===wm.MIDI_NRPN_MESSAGES.nullactiveparameter){wm._nrpnBuffer[channelBufferIndex].push(ccEvent);var rawData=[];wm._nrpnBuffer[channelBufferIndex].forEach(function(ev){rawData.push(ev.data)});var nrpnNumber=wm._nrpnBuffer[channelBufferIndex][0].value<<7|wm._nrpnBuffer[channelBufferIndex][1].value,nrpnValue=wm._nrpnBuffer[channelBufferIndex][2].value;6===wm._nrpnBuffer[channelBufferIndex].length&&(nrpnValue=wm._nrpnBuffer[channelBufferIndex][2].value<<7|wm._nrpnBuffer[channelBufferIndex][3].value);var nrpnControllerType="";switch(wm._nrpnBuffer[channelBufferIndex][2].controller.number){case wm.MIDI_NRPN_MESSAGES.entrymsb:nrpnControllerType=wm._nrpnTypes[0];break;case wm.MIDI_NRPN_MESSAGES.increment:nrpnControllerType=wm._nrpnTypes[1];break;case wm.MIDI_NRPN_MESSAGES.decrement:nrpnControllerType=wm._nrpnTypes[2];break;default:throw new Error("The NPRN type was unidentifiable.")}var nrpnEvent={timestamp:ccEvent.timestamp,channel:ccEvent.channel,type:"nrpn",data:rawData,controller:{number:nrpnNumber,type:nrpnControllerType,name:"Non-Registered Parameter "+nrpnNumber},value:nrpnValue};wm._nrpnBuffer[channelBufferIndex]=[],this._userHandlers.channel[nrpnEvent.type]&&this._userHandlers.channel[nrpnEvent.type][nrpnEvent.channel]&&this._userHandlers.channel[nrpnEvent.type][nrpnEvent.channel].forEach(function(callback){callback(nrpnEvent)})}else wm._nrpnBuffer[channelBufferIndex]=[];else wm._nrpnBuffer[channelBufferIndex].push(ccEvent)}},Input.prototype._parseChannelEvent=function(e){var data1,data2,command=e.data[0]>>4,channel=1+(15&e.data[0]);1>7&127,lsb=127&value;return this.send(wm.MIDI_SYSTEM_MESSAGES.songposition,[msb,lsb],this._parseTimeParameter(options.time)),this},Output.prototype.sendSongSelect=function(value,options){if(options=options||{},!(0<=(value=Math.floor(value))&&value<=127))throw new RangeError("The song number must be between 0 and 127.");return this.send(wm.MIDI_SYSTEM_MESSAGES.songselect,[value],this._parseTimeParameter(options.time)),this},Output.prototype.sendTuningRequest=function(options){return options=options||{},this.send(wm.MIDI_SYSTEM_MESSAGES.tuningrequest,void 0,this._parseTimeParameter(options.time)),this},Output.prototype.sendClock=function(options){return options=options||{},this.send(wm.MIDI_SYSTEM_MESSAGES.clock,void 0,this._parseTimeParameter(options.time)),this},Output.prototype.sendStart=function(options){return options=options||{},this.send(wm.MIDI_SYSTEM_MESSAGES.start,void 0,this._parseTimeParameter(options.time)),this},Output.prototype.sendContinue=function(options){return options=options||{},this.send(wm.MIDI_SYSTEM_MESSAGES.continue,void 0,this._parseTimeParameter(options.time)),this},Output.prototype.sendStop=function(options){return options=options||{},this.send(wm.MIDI_SYSTEM_MESSAGES.stop,void 0,this._parseTimeParameter(options.time)),this},Output.prototype.sendActiveSensing=function(options){return options=options||{},this.send(wm.MIDI_SYSTEM_MESSAGES.activesensing,[],this._parseTimeParameter(options.time)),this},Output.prototype.sendReset=function(options){return options=options||{},this.send(wm.MIDI_SYSTEM_MESSAGES.reset,void 0,this._parseTimeParameter(options.time)),this},Output.prototype.stopNote=function(note,channel,options){if("all"===note)return this.sendChannelMode("allnotesoff",0,channel,options);var nVelocity=64;return(options=options||{}).rawVelocity?!isNaN(options.velocity)&&0<=options.velocity&&options.velocity<=127&&(nVelocity=options.velocity):!isNaN(options.velocity)&&0<=options.velocity&&options.velocity<=1&&(nVelocity=127*options.velocity),this._convertNoteToArray(note).forEach(function(item){wm.toMIDIChannels(channel).forEach(function(ch){this.send((wm.MIDI_CHANNEL_MESSAGES.noteoff<<4)+(ch-1),[item,Math.round(nVelocity)],this._parseTimeParameter(options.time))}.bind(this))}.bind(this)),this},Output.prototype.playNote=function(note,channel,options){var time,nVelocity=64;if((options=options||{}).rawVelocity?!isNaN(options.velocity)&&0<=options.velocity&&options.velocity<=127&&(nVelocity=options.velocity):!isNaN(options.velocity)&&0<=options.velocity&&options.velocity<=1&&(nVelocity=127*options.velocity),time=this._parseTimeParameter(options.time),this._convertNoteToArray(note).forEach(function(item){wm.toMIDIChannels(channel).forEach(function(ch){this.send((wm.MIDI_CHANNEL_MESSAGES.noteon<<4)+(ch-1),[item,Math.round(nVelocity)],time)}.bind(this))}.bind(this)),!isNaN(options.duration)){options.duration<=0&&(options.duration=0);var nRelease=64;options.rawVelocity?!isNaN(options.release)&&0<=options.release&&options.release<=127&&(nRelease=options.release):!isNaN(options.release)&&0<=options.release&&options.release<=1&&(nRelease=127*options.release),this._convertNoteToArray(note).forEach(function(item){wm.toMIDIChannels(channel).forEach(function(ch){this.send((wm.MIDI_CHANNEL_MESSAGES.noteoff<<4)+(ch-1),[item,Math.round(nRelease)],(time||wm.time)+options.duration)}.bind(this))}.bind(this))}return this},Output.prototype.sendKeyAftertouch=function(note,channel,pressure,options){var that=this;if(options=options||{},channel<1||16>7&127,lsb=127&fine;return wm.toMIDIChannels(channel).forEach(function(){that.setRegisteredParameter("channelcoarsetuning",coarse,channel,{time:options.time}),that.setRegisteredParameter("channelfinetuning",[msb,lsb],channel,{time:options.time})}),this},Output.prototype.setTuningProgram=function(value,channel,options){var that=this;if(options=options||{},!(0<=(value=Math.floor(value))&&value<=127))throw new RangeError("The program value must be between 0 and 127");return wm.toMIDIChannels(channel).forEach(function(){that.setRegisteredParameter("tuningprogram",value,channel,{time:options.time})}),this},Output.prototype.setTuningBank=function(value,channel,options){var that=this;if(options=options||{},!(0<=(value=Math.floor(value)||0)&&value<=127))throw new RangeError("The bank value must be between 0 and 127");return wm.toMIDIChannels(channel).forEach(function(){that.setRegisteredParameter("tuningbank",value,channel,{time:options.time})}),this},Output.prototype.sendChannelMode=function(command,value,channel,options){if(options=options||{},"string"==typeof command){if(!(command=wm.MIDI_CHANNEL_MODE_MESSAGES[command]))throw new TypeError("Invalid channel mode message name.")}else if(!(120<=(command=Math.floor(command))&&command<=127))throw new RangeError("Channel mode numerical identifiers must be between 120 and 127.");if((value=Math.floor(value)||0)<0||127>7&127,lsb=127&nLevel;return wm.toMIDIChannels(channel).forEach(function(ch){that.send((wm.MIDI_CHANNEL_MESSAGES.pitchbend<<4)+(ch-1),[lsb,msb],that._parseTimeParameter(options.time))}),this},Output.prototype._parseTimeParameter=function(time){var value,parsed=parseFloat(time);return"string"==typeof time&&"+"===time.substring(0,1)?parsed&&0wm.time&&(value=parsed),value},Output.prototype._convertNoteToArray=function(note){var notes=[];return Array.isArray(note)||(note=[note]),note.forEach(function(item){notes.push(wm.guessNoteNumber(item))}),notes},"function"==typeof define&&"object"==typeof define.amd?define([],function(){return wm}):"undefined"!=typeof module&&module.exports?module.exports=wm:scope.WebMidi||(scope.WebMidi=wm)}(this); \ No newline at end of file