This commit is contained in:
tamat
2016-09-09 20:24:39 +02:00
parent 5f84d0c56c
commit dcf4930466
14 changed files with 3328 additions and 545 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -150,11 +150,11 @@ function GlobalInput()
this.addOutput(input_name, null );
this.properties = {name: input_name, type: null };
this.properties = { name: input_name, type: null };
var that = this;
Object.defineProperty(this.properties, "name", {
Object.defineProperty( this.properties, "name", {
get: function() {
return input_name;
},
@@ -173,7 +173,7 @@ function GlobalInput()
enumerable: true
});
Object.defineProperty(this.properties, "type", {
Object.defineProperty( this.properties, "type", {
get: function() { return that.outputs[0].type; },
set: function(v) {
that.outputs[0].type = v;
@@ -272,7 +272,7 @@ LiteGraph.registerNodeType("graph/output", GlobalOutput);
function Constant()
{
this.addOutput("value","number");
this.properties = { value:1.0 };
this.addProperty( "value", 1.0 );
this.editable = { property:"value", type:"number" };
}
@@ -313,7 +313,7 @@ function Watch()
this.size = [60,20];
this.addInput("value",0,{label:""});
this.addOutput("value",0,{label:""});
this.properties = { value:"" };
this.addProperty( "value", "" );
}
Watch.title = "Watch";
@@ -348,16 +348,37 @@ LiteGraph.registerNodeType("basic/watch", Watch);
//Show value inside the debug console
function Console()
{
this.mode = LiteGraph.ON_EVENT;
this.size = [60,20];
this.addInput("data",0);
this.addProperty( "msg", "" );
this.addInput("log", LiteGraph.EVENT);
this.addInput("msg",0);
}
Console.title = "Console";
Console.desc = "Show value inside the console";
Console.prototype.onAction = function(action, param)
{
if(action == "log")
console.log( param );
else if(action == "warn")
console.warn( param );
else if(action == "error")
console.error( param );
}
Console.prototype.onExecute = function()
{
console.log( this.getInputData(0) );
var msg = this.getInputData(0);
if(msg !== null)
this.properties.msg = msg;
console.log(msg);
}
Console.prototype.onGetInputs = function()
{
return [["log",LiteGraph.ACTION],["warn",LiteGraph.ACTION],["error",LiteGraph.ACTION]];
}
LiteGraph.registerNodeType("basic/console", Console );

51
src/nodes/events.js Normal file
View File

@@ -0,0 +1,51 @@
//event related nodes
(function(){
//Show value inside the debug console
function DelayEvent()
{
this.size = [60,20];
this.addProperty( "time", 1000 );
this.addInput("event", LiteGraph.ACTION);
this.addOutput("on_time", LiteGraph.EVENT);
this._pending = [];
}
DelayEvent.title = "Delay";
DelayEvent.desc = "Delays one event";
DelayEvent.prototype.onAction = function(action, param)
{
this._pending.push([ this.properties.time, param ]);
}
DelayEvent.prototype.onExecute = function()
{
var dt = this.graph.elapsed_time * 1000; //in ms
for(var i = 0; i < this._pending.length; ++i)
{
var action = this._pending[i];
action[0] -= dt;
if( action[0] > 0 )
continue;
//remove
this._pending.splice(i,1);
--i;
//trigger
this.trigger(null, action[1]);
}
}
DelayEvent.prototype.onGetInputs = function()
{
return [["event",LiteGraph.ACTION]];
}
LiteGraph.registerNodeType("events/delay", DelayEvent );
})();

View File

@@ -309,6 +309,9 @@ if(typeof(LiteGraph) != "undefined")
if(this.flags.collapsed)
return;
if(!ctx.webgl)
return; //not working well
var tex = this.getInputData(0);
if(!tex) return;
@@ -833,7 +836,7 @@ if(typeof(LiteGraph) != "undefined")
function LGraphTextureToViewport()
{
this.addInput("Texture","Texture");
this.properties = { additive: false, antialiasing: false, disable_alpha: false, gamma: 1.0 };
this.properties = { additive: false, antialiasing: false, filter: true, disable_alpha: false, gamma: 1.0 };
this.size[0] = 130;
}
@@ -862,6 +865,7 @@ if(typeof(LiteGraph) != "undefined")
if( this.isInputConnected(1) )
gamma = this.getInputData(1);
tex.setParameter( gl.TEXTURE_MAG_FILTER, this.properties.filter ? gl.LINEAR : gl.NEAREST );
if(this.properties.antialiasing)
{
@@ -1774,38 +1778,23 @@ if(typeof(LiteGraph) != "undefined")
this.properties.intensity = intensity;
}
gl.disable( gl.BLEND );
gl.disable( gl.DEPTH_TEST );
var mesh = Mesh.getScreenQuad();
var shader = LGraphTextureBlur._shader;
var scale = this.properties.scale || [1,1];
//blur sometimes needs an aspect correction
var aspect = LiteGraph.camera_aspect;
if(!aspect && window.gl !== undefined)
aspect = gl.canvas.height / gl.canvas.width;
if(!aspect)
aspect = 1;
//iterate
var start_texture = tex;
aspect = this.properties.preserve_aspect ? aspect : 1;
var start_texture = tex;
var scale = this.properties.scale || [1,1];
var origin = start_texture;
for(var i = 0; i < iterations; ++i)
{
this._temp_texture.drawTo( function() {
start_texture.bind(0);
shader.uniforms({u_texture:0, u_intensity: 1, u_offset: [0, 1/start_texture.height * scale[1] ] })
.draw(mesh);
});
this._temp_texture.bind(0);
this._final_texture.drawTo( function() {
shader.uniforms({u_texture:0, u_intensity: intensity, u_offset: [aspect/start_texture.width * scale[0], 0] })
.draw(mesh);
});
start_texture = this._final_texture;
origin.applyBlur( aspect * scale[0] * i, scale[1] * i, intensity, this._temp_texture, this._final_texture );
origin = this._final_texture;
}
this.setOutputData(0, this._final_texture);
}

View File

@@ -1,6 +1,65 @@
//widgets
(function(){
/* Button ****************/
function WidgetButton()
{
this.addOutput( "clicked", LiteGraph.EVENT );
this.addProperty( "text","" );
this.addProperty( "font","40px Arial" );
this.addProperty( "message", "" );
this.size = [64,84];
}
WidgetButton.title = "Button";
WidgetButton.desc = "Triggers an event";
WidgetButton.prototype.onDrawForeground = function(ctx)
{
if(this.flags.collapsed)
return;
//ctx.font = "40px Arial";
//ctx.textAlign = "center";
ctx.fillStyle = "black";
ctx.fillRect(1,1,this.size[0] - 3, this.size[1] - 3);
ctx.fillStyle = "#AAF";
ctx.fillRect(0,0,this.size[0] - 3, this.size[1] - 3);
ctx.fillStyle = this.clicked ? "white" : (this.mouseOver ? "#668" : "#334");
ctx.fillRect(1,1,this.size[0] - 4, this.size[1] - 4);
if( this.properties.text || this.properties.text === 0 )
{
ctx.textAlign = "center";
ctx.fillStyle = this.clicked ? "black" : "white";
if( this.properties.font )
ctx.font = this.properties.font;
ctx.fillText(this.properties.text, this.size[0] * 0.5, this.size[1] * 0.85 );
ctx.textAlign = "left";
}
}
WidgetButton.prototype.onMouseDown = function(e, local_pos)
{
if(local_pos[0] > 1 && local_pos[1] > 1 && local_pos[0] < (this.size[0] - 2) && local_pos[1] < (this.size[1] - 2) )
{
this.clicked = true;
this.trigger( "clicked", this.properties.message );
return true;
}
}
WidgetButton.prototype.onMouseUp = function(e)
{
this.clicked = false;
}
LiteGraph.registerNodeType("widget/button", WidgetButton );
/* Knob ****************/
function WidgetKnob()
{
this.addOutput("",'number');

View File

@@ -85,7 +85,12 @@ function MathRange()
{
this.addInput("in","number",{locked:true});
this.addOutput("out","number",{locked:true});
this.properties = { "in": 0, in_min:0, in_max:1, out_min: 0, out_max: 1 };
this.addProperty( "in", 0 );
this.addProperty( "in_min", 0 );
this.addProperty( "in_max", 1 );
this.addProperty( "out_min", 0 );
this.addProperty( "out_max", 1 );
}
MathRange.title = "Range";
@@ -136,7 +141,8 @@ LiteGraph.registerNodeType("math/range", MathRange);
function MathRand()
{
this.addOutput("value","number");
this.properties = { min:0, max:1 };
this.addProperty( "min", 0 );
this.addProperty( "max", 1 );
this.size = [60,20];
}
@@ -182,7 +188,8 @@ function MathClamp()
this.addInput("in","number");
this.addOutput("out","number");
this.size = [60,20];
this.properties = {min:0, max:1};
this.addProperty( "min", 0 );
this.addProperty( "max", 1 );
}
MathClamp.title = "Clamp";
@@ -310,7 +317,7 @@ function MathScale()
this.addInput("in","number",{label:""});
this.addOutput("out","number",{label:""});
this.size = [60,20];
this.properties = {"factor":1};
this.addProperty( "factor", 1 );
}
MathScale.title = "Scale";
@@ -326,18 +333,73 @@ MathScale.prototype.onExecute = function()
LiteGraph.registerNodeType("math/scale", MathScale );
//Math clamp
function MathAverageFilter()
{
this.addInput("in","number");
this.addOutput("out","number");
this.size = [60,20];
this.addProperty( "samples", 10 );
this._values = new Float32Array(10);
this._current = 0;
}
MathAverageFilter.title = "Average";
MathAverageFilter.desc = "Average Filter";
MathAverageFilter.prototype.onExecute = function()
{
var v = this.getInputData(0);
if(v == null)
v = 0;
var num_samples = this._values.length;
this._values[ this._current % num_samples ] = v;
this._current += 1;
if(this._current > num_samples)
this._current = 0;
var avr = 0;
for(var i = 0; i < num_samples; ++i)
avr += this._values[i];
this.setOutputData( 0, avr / num_samples );
}
MathAverageFilter.prototype.onPropertyChanged = function( name, value )
{
if(value < 1)
value = 1;
this.properties.samples = Math.round(value);
var old = this._values;
this._values = new Float32Array( this.properties.samples );
if(old.length <= this._values.length )
this._values.set(old);
else
this._values.set( old.subarray( 0, this._values.length ) );
}
LiteGraph.registerNodeType("math/average", MathAverageFilter );
//Math operation
function MathOperation()
{
this.addInput("A","number");
this.addInput("B","number");
this.addOutput("=","number");
this.properties = {A:1.0, B:1.0, OP:"+"};
this.addProperty( "A", 1 );
this.addProperty( "B", 1 );
this.addProperty( "OP", "+", "string", { values: MathOperation.values } );
}
MathOperation.values = ["+","-","*","/","%","^"];
MathOperation.title = "Operation";
MathOperation.desc = "Easy math operators";
MathOperation["@OP"] = { type:"enum", title: "operation", values:["+","-","*","/","%","^"]};
MathOperation["@OP"] = { type:"enum", title: "operation", values: MathOperation.values };
MathOperation.prototype.setValue = function(v)
@@ -365,16 +427,28 @@ MathOperation.prototype.onExecute = function()
{
case '+': result = A+B; break;
case '-': result = A-B; break;
case 'x':
case 'X':
case '*': result = A*B; break;
case '/': result = A/B; break;
case '%': result = A%B; break;
case '^': result = Math.pow(A,B); break;
default:
console.warn("Unknown operation: " + this.properties.OP);
}
this.setOutputData(0, result );
}
MathOperation.prototype.onDrawBackground = function(ctx)
{
this.outputs[0].label = "A" + this.properties.OP + "B";
if(this.flags.collapsed)
return;
ctx.font = "40px Arial";
ctx.fillStyle = "black";
ctx.textAlign = "center";
ctx.fillText(this.properties.OP, this.size[0] * 0.5, this.size[1] * 0.5 + LiteGraph.NODE_TITLE_HEIGHT );
ctx.textAlign = "left";
}
LiteGraph.registerNodeType("math/operation", MathOperation );
@@ -387,7 +461,8 @@ function MathCompare()
this.addInput( "B","number" );
this.addOutput("A==B","boolean");
this.addOutput("A!=B","boolean");
this.properties = {A:0,B:0};
this.addProperty( "A", 0 );
this.addProperty( "B", 0 );
}
MathCompare.title = "Compare";
@@ -437,11 +512,15 @@ function MathCondition()
this.addInput("A","number");
this.addInput("B","number");
this.addOutput("out","boolean");
this.properties = { A:0, B:1, OP:">" };
this.addProperty( "A", 1 );
this.addProperty( "B", 1 );
this.addProperty( "OP", ">", "string", { values: MathCondition.values } );
this.size = [60,40];
}
MathCondition["@OP"] = { type:"enum", title: "operation", values:[">","<","==","!=","<=",">="]};
MathCondition.values = [">","<","==","!=","<=",">="];
MathCondition["@OP"] = { type:"enum", title: "operation", values: MathCondition.values };
MathCondition.title = "Condition";
MathCondition.desc = "evaluates condition between A and B";
@@ -481,7 +560,8 @@ function MathAccumulate()
{
this.addInput("inc","number");
this.addOutput("total","number");
this.properties = { increment: 0, value: 0 };
this.addProperty( "increment", 1 );
this.addProperty( "value", 0 );
}
MathAccumulate.title = "Accumulate";
@@ -489,6 +569,9 @@ MathAccumulate.desc = "Increments a value every time";
MathAccumulate.prototype.onExecute = function()
{
if(this.properties.value === null)
this.properties.value = 0;
var inc = this.getInputData(0);
if(inc !== null)
this.properties.value += inc;
@@ -504,7 +587,9 @@ function MathTrigonometry()
{
this.addInput("v","number");
this.addOutput("sin","number");
this.properties = {amplitude:1.0, offset: 0};
this.addProperty( "amplitude", 1 );
this.addProperty( "offset", 0 );
this.bgImageUrl = "nodes/imgs/icon-sin.png";
}
@@ -515,6 +600,8 @@ MathTrigonometry.filter = "shader";
MathTrigonometry.prototype.onExecute = function()
{
var v = this.getInputData(0);
if(v == null)
v = 0;
var amplitude = this.properties["amplitude"];
var slot = this.findInputSlot("amplitude");
if(slot != -1)
@@ -778,12 +865,35 @@ LiteGraph.registerNodeType("math3d/xyzw-to-vec4", Math3DXYZWToVec4 );
if(window.glMatrix)
{
function Math3DQuaternion()
{
this.addOutput("quat","quat");
this.properties = { x:0, y:0, z:0, w: 1 };
this._value = quat.create();
}
Math3DQuaternion.title = "Quaternion";
Math3DQuaternion.desc = "quaternion";
Math3DQuaternion.prototype.onExecute = function()
{
this._value[0] = this.properties.x;
this._value[1] = this.properties.y;
this._value[2] = this.properties.z;
this._value[3] = this.properties.w;
this.setOutputData( 0, this._value );
}
LiteGraph.registerNodeType("math3d/quaternion", Math3DQuaternion );
function Math3DRotation()
{
this.addInputs([["degrees","number"],["axis","vec3"]]);
this.addOutput("quat","quat");
this.properties = { angle:90.0, axis: vec3.fromValues(0,1,0) };
this._value = quat.create();
}
Math3DRotation.title = "Rotation";
@@ -796,7 +906,7 @@ if(window.glMatrix)
var axis = this.getInputData(1);
if(axis == null) axis = this.properties.axis;
var R = quat.setAxisAngle(quat.create(), axis, angle * 0.0174532925 );
var R = quat.setAxisAngle( this._value, axis, angle * 0.0174532925 );
this.setOutputData( 0, R );
}
@@ -834,6 +944,8 @@ if(window.glMatrix)
{
this.addInputs( [["A","quat"],["B","quat"]] );
this.addOutput( "A*B","quat" );
this._value = quat.create();
}
Math3DMultQuat.title = "Mult. Quat";
@@ -846,12 +958,43 @@ if(window.glMatrix)
var B = this.getInputData(1);
if(B == null) return;
var R = quat.multiply(quat.create(), A,B);
var R = quat.multiply( this._value, A, B );
this.setOutputData( 0, R );
}
LiteGraph.registerNodeType("math3d/mult-quat", Math3DMultQuat );
function Math3DQuatSlerp()
{
this.addInputs( [["A","quat"],["B","quat"],["factor","number"]] );
this.addOutput( "slerp","quat" );
this.addProperty( "factor", 0.5 );
this._value = quat.create();
}
Math3DQuatSlerp.title = "Quat Slerp";
Math3DQuatSlerp.desc = "quaternion spherical interpolation";
Math3DQuatSlerp.prototype.onExecute = function()
{
var A = this.getInputData(0);
if(A == null)
return;
var B = this.getInputData(1);
if(B == null)
return;
var factor = this.properties.factor;
if( this.getInputData(2) != null )
factor = this.getInputData(2);
var R = quat.slerp( this._value, A, B, factor );
this.setOutputData( 0, R );
}
LiteGraph.registerNodeType("math3d/quat-slerp", Math3DQuatSlerp );
} //glMatrix
})();

614
src/nodes/midi.js Normal file
View File

@@ -0,0 +1,614 @@
(function( global )
{
function MIDIEvent( data )
{
this.channel = 0;
this.cmd = 0;
if(data)
this.setup(data)
else
this.data = [0,0,0];
}
MIDIEvent.prototype.setup = function( raw_data )
{
this.data = raw_data;
var midiStatus = raw_data[0];
this.status = midiStatus;
var midiCommand = midiStatus & 0xF0;
if(midiStatus >= 0xF0)
this.cmd = midiStatus;
else
this.cmd = midiCommand;
if(this.cmd == MIDIEvent.NOTEON && this.velocity == 0)
this.cmd = MIDIEvent.NOTEOFF;
this.cmd_str = MIDIEvent.commands[ this.cmd ] || "";
if ( midiCommand >= MIDIEvent.NOTEON || midiCommand <= MIDIEvent.NOTEOFF ) {
this.channel = midiStatus & 0x0F;
}
}
Object.defineProperty( MIDIEvent.prototype, "velocity", {
get: function() {
if(this.cmd == MIDIEvent.NOTEON)
return this.data[2];
return -1;
},
set: function(v) {
this.data[2] = v; // v / 127;
},
enumerable: true
});
MIDIEvent.notes = ["A","A#","B","C","C#","D","D#","E","F","F#","G","G#"];
//returns HZs
MIDIEvent.prototype.getPitch = function()
{
return Math.pow(2, (this.data[1] - 69) / 12 ) * 440;
}
MIDIEvent.computePitch = function( note )
{
return Math.pow(2, (note - 69) / 12 ) * 440;
}
//not tested, there is a formula missing here
MIDIEvent.prototype.getPitchBend = function()
{
return this.data[1] + (this.data[2] << 7) - 8192;
}
MIDIEvent.computePitchBend = function(v1,v2)
{
return v1 + (v2 << 7) - 8192;
}
MIDIEvent.prototype.setCommandFromString = function( str )
{
this.cmd = MIDIEvent.computeCommandFromString(str);
}
MIDIEvent.computeCommandFromString = function( str )
{
if(!str)
return 0;
if(str && str.constructor === Number)
return str;
str = str.toUpperCase();
switch( str )
{
case "NOTE ON":
case "NOTEON": return MIDIEvent.NOTEON; break;
case "NOTE OFF":
case "NOTEOFF": return MIDIEvent.NOTEON; break;
case "KEY PRESSURE":
case "KEYPRESSURE": return MIDIEvent.KEYPRESSURE; break;
case "CONTROLLER CHANGE":
case "CONTROLLERCHANGE":
case "CC": return MIDIEvent.CONTROLLERCHANGE; break;
case "PROGRAM CHANGE":
case "PROGRAMCHANGE":
case "PC": return MIDIEvent.PROGRAMCHANGE; break;
case "CHANNEL PRESSURE":
case "CHANNELPRESSURE": return MIDIEvent.CHANNELPRESSURE; break;
case "PITCH BEND":
case "PITCHBEND": return MIDIEvent.PITCHBEND; break;
case "TIME TICK":
case "TIMETICK": return MIDIEvent.TIMETICK; break;
default: return Number(str); //asume its a hex code
}
}
MIDIEvent.toNoteString = function(d)
{
var note = d - 21;
var octave = d - 24;
note = note % 12;
if(note < 0)
note = 12 + note;
return MIDIEvent.notes[ note ] + Math.floor(octave / 12 + 1);
}
MIDIEvent.prototype.toString = function()
{
var str = "" + this.channel + ". " ;
switch( this.cmd )
{
case MIDIEvent.NOTEON: str += "NOTEON " + MIDIEvent.toNoteString( this.data[1] ); break;
case MIDIEvent.NOTEOFF: str += "NOTEOFF " + MIDIEvent.toNoteString( this.data[1] ); break;
case MIDIEvent.CONTROLLERCHANGE: str += "CC " + this.data[1] + " " + this.data[2]; break;
case MIDIEvent.PROGRAMCHANGE: str += "PC " + this.data[1]; break;
case MIDIEvent.PITCHBEND: str += "PITCHBEND " + this.getPitchBend(); break;
case MIDIEvent.KEYPRESSURE: str += "KEYPRESS " + this.data[1]; break;
}
return str;
}
MIDIEvent.prototype.toHexString = function()
{
var str = "";
for(var i = 0; i < this.data.length; i++)
str += this.data[i].toString(16) + " ";
}
MIDIEvent.NOTEOFF = 0x80;
MIDIEvent.NOTEON = 0x90;
MIDIEvent.KEYPRESSURE = 0xA0;
MIDIEvent.CONTROLLERCHANGE = 0xB0;
MIDIEvent.PROGRAMCHANGE = 0xC0;
MIDIEvent.CHANNELPRESSURE = 0xD0;
MIDIEvent.PITCHBEND = 0xE0;
MIDIEvent.TIMETICK = 0xF8;
MIDIEvent.commands = {
0x80: "note off",
0x90: "note on",
0xA0: "key pressure",
0xB0: "controller change",
0xC0: "program change",
0xD0: "channel pressure",
0xE0: "pitch bend",
0xF0: "system",
0xF2: "Song pos",
0xF3: "Song select",
0xF6: "Tune request",
0xF8: "time tick",
0xFA: "Start Song",
0xFB: "Continue Song",
0xFC: "Stop Song",
0xFE: "Sensing",
0xFF: "Reset"
}
//MIDI wrapper
function MIDIInterface( on_ready, on_error )
{
if(!navigator.requestMIDIAccess)
{
this.error = "not suppoorted";
if(on_error)
on_error("Not supported");
else
console.error("MIDI NOT SUPPORTED, enable by chrome://flags");
return;
}
this.on_ready = on_ready;
navigator.requestMIDIAccess().then( this.onMIDISuccess.bind(this), this.onMIDIFailure.bind(this) );
}
MIDIInterface.MIDIEvent = MIDIEvent;
MIDIInterface.prototype.onMIDISuccess = function(midiAccess)
{
console.log( "MIDI ready!" );
console.log( midiAccess );
this.midi = midiAccess; // store in the global (in real usage, would probably keep in an object instance)
this.updatePorts();
if (this.on_ready)
this.on_ready(this);
}
MIDIInterface.prototype.updatePorts = function()
{
var midi = this.midi;
this.input_ports = midi.inputs;
var num = 0;
for (var i = 0; i < this.input_ports.size; ++i) {
var input = this.input_ports.get(i);
console.log( "Input port [type:'" + input.type + "'] id:'" + input.id +
"' manufacturer:'" + input.manufacturer + "' name:'" + input.name +
"' version:'" + input.version + "'" );
num++;
}
this.num_input_ports = num;
num = 0;
this.output_ports = midi.outputs;
for (var i = 0; i < this.output_ports.size; ++i) {
var output = this.output_ports.get(i);
console.log( "Output port [type:'" + output.type + "'] id:'" + output.id +
"' manufacturer:'" + output.manufacturer + "' name:'" + output.name +
"' version:'" + output.version + "'" );
num++;
}
this.num_output_ports = num;
}
MIDIInterface.prototype.onMIDIFailure = function(msg)
{
console.error( "Failed to get MIDI access - " + msg );
}
MIDIInterface.prototype.openInputPort = function( port, callback)
{
var input_port = this.input_ports.get( port );
if(!input_port)
return false;
input_port.onmidimessage = function(a) {
var midi_event = new MIDIEvent(a.data);
if(callback)
callback(a.data, midi_event );
if(MIDIInterface.on_message)
MIDIInterface.on_message( a.data, midi_event );
}
console.log("port open: ", input_port);
return true;
}
MIDIInterface.parseMsg = function(data)
{
}
MIDIInterface.prototype.sendMIDI = function( port, midi_data )
{
if( !midi_data )
return;
var output_port = this.output_ports.get(port);
if(!output_port)
return;
if( midi_data.constructor === MIDIEvent)
output_port.send( midi_data.data );
else
output_port.send( midi_data );
}
function LGMIDIIn()
{
this.addOutput( "on_midi", LiteGraph.EVENT );
this.addOutput( "out", "midi" );
this.properties = {port: 0};
this._last_midi_event = null;
this._current_midi_event = null;
var that = this;
new MIDIInterface( function( midi ){
//open
that._midi = midi;
if(that._waiting)
that.onStart();
that._waiting = false;
});
}
LGMIDIIn.MIDIInterface = MIDIInterface;
LGMIDIIn.title = "MIDI Input";
LGMIDIIn.desc = "Reads MIDI from a input port";
LGMIDIIn.prototype.getPropertyInfo = function(name)
{
if(!this._midi)
return;
if(name == "port")
{
var values = {};
for (var i = 0; i < this._midi.input_ports.size; ++i)
{
var input = this._midi.input_ports.get(i);
values[i] = i + ".- " + input.name + " version:" + input.version;
}
return { type: "enum", values: values };
}
}
LGMIDIIn.prototype.onStart = function()
{
if(this._midi)
this._midi.openInputPort( this.properties.port, this.onMIDIEvent.bind(this) );
else
this._waiting = true;
}
LGMIDIIn.prototype.onMIDIEvent = function( data, midi_event )
{
this._last_midi_event = midi_event;
this.trigger( "on_midi", midi_event );
if(midi_event.cmd == MIDIEvent.NOTEON)
this.trigger( "on_noteon", midi_event );
else if(midi_event.cmd == MIDIEvent.NOTEOFF)
this.trigger( "on_noteoff", midi_event );
else if(midi_event.cmd == MIDIEvent.CONTROLLERCHANGE)
this.trigger( "on_cc", midi_event );
else if(midi_event.cmd == MIDIEvent.PROGRAMCHANGE)
this.trigger( "on_pc", midi_event );
else if(midi_event.cmd == MIDIEvent.PITCHBEND)
this.trigger( "on_pitchbend", midi_event );
}
LGMIDIIn.prototype.onExecute = function()
{
if(this.outputs)
{
var last = this._last_midi_event;
for(var i = 0; i < this.outputs.length; ++i)
{
var output = this.outputs[i];
var v = null;
switch (output.name)
{
case "last_midi": v = last; break;
default:
continue;
}
this.setOutputData( i, v );
}
}
}
LGMIDIIn.prototype.onGetOutputs = function() {
return [
["last_midi","midi"],
["on_midi",LiteGraph.EVENT],
["on_noteon",LiteGraph.EVENT],
["on_noteoff",LiteGraph.EVENT],
["on_cc",LiteGraph.EVENT],
["on_pc",LiteGraph.EVENT],
["on_pitchbend",LiteGraph.EVENT]
];
}
LiteGraph.registerNodeType("midi/input", LGMIDIIn);
function LGMIDIOut()
{
this.addInput( "send", LiteGraph.EVENT );
this.properties = {port: 0};
var that = this;
new MIDIInterface( function( midi ){
that._midi = midi;
});
}
LGMIDIOut.MIDIInterface = MIDIInterface;
LGMIDIOut.title = "MIDI Output";
LGMIDIOut.desc = "Sends MIDI to output channel";
LGMIDIOut.prototype.getPropertyInfo = function(name)
{
if(!this._midi)
return;
if(name == "port")
{
var values = {};
for (var i = 0; i < this._midi.output_ports.size; ++i)
{
var output = this._midi.output_ports.get(i);
values[i] = i + ".- " + output.name + " version:" + output.version;
}
return { type: "enum", values: values };
}
}
LGMIDIOut.prototype.onAction = function(event, midi_event )
{
console.log(midi_event);
if(!this._midi)
return;
if(event == "send")
this._midi.sendMIDI( this.port, midi_event );
this.trigger("midi",midi_event);
}
LGMIDIOut.prototype.onGetInputs = function() {
return [["send",LiteGraph.ACTION]];
}
LGMIDIOut.prototype.onGetOutputs = function() {
return [["on_midi",LiteGraph.EVENT]];
}
LiteGraph.registerNodeType("midi/output", LGMIDIOut);
function LGMIDIShow()
{
this.addInput( "on_midi", LiteGraph.EVENT );
this._str = "";
this.size = [200,40]
}
LGMIDIShow.title = "MIDI Show";
LGMIDIShow.desc = "Shows MIDI in the graph";
LGMIDIShow.prototype.onAction = function(event, midi_event )
{
if(!midi_event)
return;
if(midi_event.constructor === MIDIEvent)
this._str = midi_event.toString();
else
this._str = "???";
}
LGMIDIShow.prototype.onDrawForeground = function( ctx )
{
if( !this._str )
return;
ctx.font = "30px Arial";
ctx.fillText( this._str, 10, this.size[1] * 0.8 );
}
LGMIDIShow.prototype.onGetInputs = function() {
return [["in",LiteGraph.ACTION]];
}
LGMIDIShow.prototype.onGetOutputs = function() {
return [["on_midi",LiteGraph.EVENT]];
}
LiteGraph.registerNodeType("midi/show", LGMIDIShow);
function LGMIDIFilter()
{
this.properties = {
channel: -1,
cmd: -1,
min_value: -1,
max_value: -1
};
this.addInput( "in", LiteGraph.EVENT );
this.addOutput( "on_midi", LiteGraph.EVENT );
}
LGMIDIFilter.title = "MIDI Filter";
LGMIDIFilter.desc = "Filters MIDI messages";
LGMIDIFilter.prototype.onAction = function(event, midi_event )
{
if(!midi_event || midi_event.constructor !== MIDIEvent)
return;
if( this.properties.channel != -1 && midi_event.channel != this.properties.channel)
return;
if(this.properties.cmd != -1 && midi_event.cmd != this.properties.cmd)
return;
if(this.properties.min_value != -1 && midi_event.data[1] < this.properties.min_value)
return;
if(this.properties.max_value != -1 && midi_event.data[1] > this.properties.max_value)
return;
this.trigger("on_midi",midi_event);
}
LiteGraph.registerNodeType("midi/filter", LGMIDIFilter);
function LGMIDIEvent()
{
this.properties = {
channel: 0,
cmd: "CC",
value1: 1,
value2: 1
};
this.addInput( "send", LiteGraph.EVENT );
this.addInput( "assign", LiteGraph.EVENT );
this.addOutput( "on_midi", LiteGraph.EVENT );
}
LGMIDIEvent.title = "MIDIEvent";
LGMIDIEvent.desc = "Create a MIDI Event";
LGMIDIEvent.prototype.onAction = function( event, midi_event )
{
if(event == "assign")
{
this.properties.channel = midi_event.channel;
this.properties.cmd = midi_event.cmd;
this.properties.value1 = midi_event.data[1];
this.properties.value2 = midi_event.data[2];
return;
}
//send
var midi_event = new MIDIEvent();
midi_event.channel = this.properties.channel;
if(this.properties.cmd && this.properties.cmd.constructor === String)
midi_event.setCommandFromString( this.properties.cmd );
else
midi_event.cmd = this.properties.cmd;
midi_event.data[0] = midi_event.cmd | midi_event.channel;
midi_event.data[1] = Number(this.properties.value1);
midi_event.data[2] = Number(this.properties.value2);
this.trigger("on_midi",midi_event);
}
LGMIDIEvent.prototype.onExecute = function()
{
var props = this.properties;
if(this.outputs)
{
for(var i = 0; i < this.outputs.length; ++i)
{
var output = this.outputs[i];
var v = null;
switch (output.name)
{
case "midi":
v = new MIDIEvent();
v.setup([ props.cmd, props.value1, props.value2 ]);
v.channel = props.channel;
break;
case "command": v = props.cmd; break;
case "note": v = (props.cmd == MIDIEvent.NOTEON || props.cmd == MIDIEvent.NOTEOFF) ? props.value1 : NULL; break;
case "velocity": v = props.cmd == MIDIEvent.NOTEON ? props.value2 : NULL; break;
case "pitch": v = props.cmd == MIDIEvent.NOTEON ? MIDIEvent.computePitch( props.value1 ) : null; break;
case "pitchbend": v = props.cmd == MIDIEvent.PITCHBEND ? MIDIEvent.computePitchBend( props.value1, props.value2 ) : null; break;
default:
continue;
}
if(v !== null)
this.setOutputData( i, v );
}
}
}
LGMIDIEvent.prototype.onPropertyChanged = function(name,value)
{
if(name == "cmd")
this.properties.cmd = MIDIEvent.computeCommandFromString( value );
}
LGMIDIEvent.prototype.onGetOutputs = function() {
return [
["midi","midi"],
["on_midi",LiteGraph.EVENT],
["command","number"],
["note","number"],
["velocity","number"],
["pitch","number"],
["pitchbend","number"]
];
}
LiteGraph.registerNodeType("midi/event", LGMIDIEvent);
function now() { return window.performance.now() }
})( window );