extended MIDI support. Some new general purpose nodes like sequencer, string, cast to number.

This commit is contained in:
tamat
2019-01-05 23:54:44 +01:00
parent 8dce8acb91
commit ac11db37b4
16 changed files with 2274 additions and 837 deletions

File diff suppressed because it is too large Load Diff

1011
build/litegraph.min.js vendored

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@@ -15,15 +15,17 @@
<script type="text/javascript" src="../external/jquery-1.6.2.min.js"></script>
<script type="text/javascript" src="http://tamats.com/projects/sillyserver/src/sillyclient.js"></script>
<script type="text/javascript" src="js/libs/audiosynth.js"></script>
<script type="text/javascript" src="../src/litegraph.js"></script>
<script type="text/javascript" src="../src/litegraph-editor.js"></script>
<script type="text/javascript" src="../src/nodes/base.js"></script>
<script type="text/javascript" src="../src/nodes/logic.js"></script>
<script type="text/javascript" src="../src/nodes/events.js"></script>
<script type="text/javascript" src="../src/nodes/math.js"></script>
<script type="text/javascript" src="../src/nodes/interface.js"></script>
<script type="text/javascript" src="../src/nodes/image.js"></script>
<script type="text/javascript" src="../src/nodes/graphics.js"></script>
<script type="text/javascript" src="../src/nodes/input.js"></script>
<script type="text/javascript" src="../src/nodes/midi.js"></script>
<script type="text/javascript" src="../src/nodes/audio.js"></script>

View File

@@ -5,6 +5,10 @@ window.graphcanvas = editor.graphcanvas;
window.graph = editor.graph;
window.addEventListener("resize", function() { editor.graphcanvas.resize(); } );
window.addEventListener("keydown", editor.graphcanvas.processKey.bind(editor.graphcanvas) );
window.onbeforeunload = function(){
var data = JSON.stringify( graph.serialize() );
localStorage.setItem("litegraphg demo backup", data );
}
//create scene selector
var elem = document.createElement("span");
@@ -18,6 +22,8 @@ select.addEventListener("change", function(e){
if(url)
graph.load( url );
else if(option.callback)
option.callback();
else
graph.clear();
});
@@ -37,7 +43,10 @@ elem.querySelector("#load").addEventListener("click",function(){
function addDemo( name, url )
{
var option = document.createElement("option");
option.dataset["url"] = url;
if(url.constructor === String)
option.dataset["url"] = url;
else
option.callback = url;
option.innerHTML = name;
select.appendChild( option );
}
@@ -48,6 +57,14 @@ addDemo("Benchmark", "examples/benchmark.json");
addDemo("Audio", "examples/audio.json");
addDemo("Audio Delay", "examples/audio_delay.json");
addDemo("Audio Reverb", "examples/audio_reverb.json");
addDemo("MIDI Generation", "examples/midi_generation.json");
addDemo("autobackup", function(){
var data = localStorage.getItem("litegraphg demo backup");
if(!data)
return;
var graph_data = JSON.parse(data);
graph.configure( graph_data );
});

View File

@@ -191,7 +191,7 @@ function TestSlotsNode()
this.addInput("C","number");
this.addOutput("A","number");
this.addOutput("B","number");
this.flags = { horizontal: true };
this.horizontal = true;
this.size = [100,40];
}

356
demo/js/libs/audiosynth.js Normal file
View File

@@ -0,0 +1,356 @@
var Synth, AudioSynth, AudioSynthInstrument;
!function(){
var URL = window.URL || window.webkitURL;
var Blob = window.Blob;
if(!URL || !Blob) {
throw new Error('This browser does not support AudioSynth');
}
var _encapsulated = false;
var AudioSynthInstance = null;
var pack = function(c,arg){ return [new Uint8Array([arg, arg >> 8]), new Uint8Array([arg, arg >> 8, arg >> 16, arg >> 24])][c]; };
var setPrivateVar = function(n,v,w,e){Object.defineProperty(this,n,{value:v,writable:!!w,enumerable:!!e});};
var setPublicVar = function(n,v,w){setPrivateVar.call(this,n,v,w,true);};
AudioSynthInstrument = function AudioSynthInstrument(){this.__init__.apply(this,arguments);};
var setPriv = setPrivateVar.bind(AudioSynthInstrument.prototype);
var setPub = setPublicVar.bind(AudioSynthInstrument.prototype);
setPriv('__init__', function(a,b,c) {
if(!_encapsulated) { throw new Error('AudioSynthInstrument can only be instantiated from the createInstrument method of the AudioSynth object.'); }
setPrivateVar.call(this, '_parent', a);
setPublicVar.call(this, 'name', b);
setPrivateVar.call(this, '_soundID', c);
});
setPub('play', function(note, octave, duration,volume) {
return this._parent.play(this._soundID, note, octave, duration, volume);
});
setPub('generate', function(note, octave, duration) {
return this._parent.generate(this._soundID, note, octave, duration);
});
AudioSynth = function AudioSynth(){if(AudioSynthInstance instanceof AudioSynth){return AudioSynthInstance;}else{ this.__init__(); return this; }};
setPriv = setPrivateVar.bind(AudioSynth.prototype);
setPub = setPublicVar.bind(AudioSynth.prototype);
setPriv('_debug',false,true);
setPriv('_bitsPerSample',16);
setPriv('_channels',1);
setPriv('_sampleRate',44100,true);
setPub('setSampleRate', function(v) {
this._sampleRate = Math.max(Math.min(v|0,44100), 4000);
this._clearCache();
return this._sampleRate;
});
setPub('getSampleRate', function() { return this._sampleRate; });
setPriv('_volume',32768,true);
setPub('setVolume', function(v) {
v = parseFloat(v); if(isNaN(v)) { v = 0; }
v = Math.round(v*32768);
this._volume = Math.max(Math.min(v|0,32768), 0);
this._clearCache();
return this._volume;
});
setPub('getVolume', function() { return Math.round(this._volume/32768*10000)/10000; });
setPriv('_notes',{'C':261.63,'C#':277.18,'D':293.66,'D#':311.13,'E':329.63,'F':346.23,'F#':369.99,'G':392.00,'G#':415.30,'A':440.00,'A#':466.16,'B':493.88});
setPriv('_fileCache',[],true);
setPriv('_temp',{},true);
setPriv('_sounds',[],true);
setPriv('_mod',[function(i,s,f,x){return Math.sin((2 * Math.PI)*(i/s)*f+x);}]);
setPriv('_resizeCache', function() {
var f = this._fileCache;
var l = this._sounds.length;
while(f.length<l) {
var octaveList = [];
for(var i = 0; i < 8; i++) {
var noteList = {};
for(var k in this._notes) {
noteList[k] = {};
}
octaveList.push(noteList);
}
f.push(octaveList);
}
});
setPriv('_clearCache', function() {
this._fileCache = [];
this._resizeCache();
});
setPub('generate', function(sound, note, octave, duration) {
var thisSound = this._sounds[sound];
if(!thisSound) {
for(var i=0;i<this._sounds.length;i++) {
if(this._sounds[i].name==sound) {
thisSound = this._sounds[i];
sound = i;
break;
}
}
}
if(!thisSound) { throw new Error('Invalid sound or sound ID: ' + sound); }
var t = (new Date).valueOf();
this._temp = {};
octave |= 0;
octave = Math.min(8, Math.max(1, octave));
var time = !duration?2:parseFloat(duration);
if(typeof(this._notes[note])=='undefined') { throw new Error(note + ' is not a valid note.'); }
if(typeof(this._fileCache[sound][octave-1][note][time])!='undefined') {
if(this._debug) { console.log((new Date).valueOf() - t, 'ms to retrieve (cached)'); }
return this._fileCache[sound][octave-1][note][time];
} else {
var frequency = this._notes[note] * Math.pow(2,octave-4);
var sampleRate = this._sampleRate;
var volume = this._volume;
var channels = this._channels;
var bitsPerSample = this._bitsPerSample;
var attack = thisSound.attack(sampleRate, frequency, volume);
var dampen = thisSound.dampen(sampleRate, frequency, volume);
var waveFunc = thisSound.wave;
var waveBind = {modulate: this._mod, vars: this._temp};
var val = 0;
var curVol = 0;
var data = new Uint8Array(new ArrayBuffer(Math.ceil(sampleRate * time * 2)));
var attackLen = (sampleRate * attack) | 0;
var decayLen = (sampleRate * time) | 0;
for (var i = 0 | 0; i !== attackLen; i++) {
val = volume * (i/(sampleRate*attack)) * waveFunc.call(waveBind, i, sampleRate, frequency, volume);
data[i << 1] = val;
data[(i << 1) + 1] = val >> 8;
}
for (; i !== decayLen; i++) {
val = volume * Math.pow((1-((i-(sampleRate*attack))/(sampleRate*(time-attack)))),dampen) * waveFunc.call(waveBind, i, sampleRate, frequency, volume);
data[i << 1] = val;
data[(i << 1) + 1] = val >> 8;
}
var out = [
'RIFF',
pack(1, 4 + (8 + 24/* chunk 1 length */) + (8 + 8/* chunk 2 length */)), // Length
'WAVE',
// chunk 1
'fmt ', // Sub-chunk identifier
pack(1, 16), // Chunk length
pack(0, 1), // Audio format (1 is linear quantization)
pack(0, channels),
pack(1, sampleRate),
pack(1, sampleRate * channels * bitsPerSample / 8), // Byte rate
pack(0, channels * bitsPerSample / 8),
pack(0, bitsPerSample),
// chunk 2
'data', // Sub-chunk identifier
pack(1, data.length * channels * bitsPerSample / 8), // Chunk length
data
];
var blob = new Blob(out, {type: 'audio/wav'});
var dataURI = URL.createObjectURL(blob);
this._fileCache[sound][octave-1][note][time] = dataURI;
if(this._debug) { console.log((new Date).valueOf() - t, 'ms to generate'); }
return dataURI;
}
});
setPub('play', function(sound, note, octave, duration, volume) {
var src = this.generate( sound, note, octave, duration );
var audio = new Audio(src);
if(volume != null)
{
if(volume <= 0)
return true;
audio.volume = volume > 1 ? 1 : volume;
}
audio.play();
return true;
});
setPub('debug', function() { this._debug = true; });
setPub('createInstrument', function(sound) {
var n = 0;
var found = false;
if(typeof(sound)=='string') {
for(var i=0;i<this._sounds.length;i++) {
if(this._sounds[i].name==sound) {
found = true;
n = i;
break;
}
}
} else {
if(this._sounds[sound]) {
n = sound;
sound = this._sounds[n].name;
found = true;
}
}
if(!found) { throw new Error('Invalid sound or sound ID: ' + sound); }
_encapsulated = true;
var ins = new AudioSynthInstrument(this, sound, n);
_encapsulated = false;
return ins;
});
setPub('listSounds', function() {
var r = [];
for(var i=0;i<this._sounds.length;i++) {
r.push(this._sounds[i].name);
}
return r;
});
setPriv('__init__', function(){
this._resizeCache();
});
setPub('loadSoundProfile', function() {
for(var i=0,len=arguments.length;i<len;i++) {
o = arguments[i];
if(!(o instanceof Object)) { throw new Error('Invalid sound profile.'); }
this._sounds.push(o);
}
this._resizeCache();
return true;
});
setPub('loadModulationFunction', function() {
for(var i=0,len=arguments.length;i<len;i++) {
f = arguments[i];
if(typeof(f)!='function') { throw new Error('Invalid modulation function.'); }
this._mod.push(f);
}
return true;
});
AudioSynthInstance = new AudioSynth();
Synth = AudioSynthInstance;
}();
Synth.loadModulationFunction(
function(i, sampleRate, frequency, x) { return 1 * Math.sin(2 * Math.PI * ((i / sampleRate) * frequency) + x); },
function(i, sampleRate, frequency, x) { return 1 * Math.sin(4 * Math.PI * ((i / sampleRate) * frequency) + x); },
function(i, sampleRate, frequency, x) { return 1 * Math.sin(8 * Math.PI * ((i / sampleRate) * frequency) + x); },
function(i, sampleRate, frequency, x) { return 1 * Math.sin(0.5 * Math.PI * ((i / sampleRate) * frequency) + x); },
function(i, sampleRate, frequency, x) { return 1 * Math.sin(0.25 * Math.PI * ((i / sampleRate) * frequency) + x); },
function(i, sampleRate, frequency, x) { return 0.5 * Math.sin(2 * Math.PI * ((i / sampleRate) * frequency) + x); },
function(i, sampleRate, frequency, x) { return 0.5 * Math.sin(4 * Math.PI * ((i / sampleRate) * frequency) + x); },
function(i, sampleRate, frequency, x) { return 0.5 * Math.sin(8 * Math.PI * ((i / sampleRate) * frequency) + x); },
function(i, sampleRate, frequency, x) { return 0.5 * Math.sin(0.5 * Math.PI * ((i / sampleRate) * frequency) + x); },
function(i, sampleRate, frequency, x) { return 0.5 * Math.sin(0.25 * Math.PI * ((i / sampleRate) * frequency) + x); }
);
Synth.loadSoundProfile({
name: 'piano',
attack: function() { return 0.002; },
dampen: function(sampleRate, frequency, volume) {
return Math.pow(0.5*Math.log((frequency*volume)/sampleRate),2);
},
wave: function(i, sampleRate, frequency, volume) {
var base = this.modulate[0];
return this.modulate[1](
i,
sampleRate,
frequency,
Math.pow(base(i, sampleRate, frequency, 0), 2) +
(0.75 * base(i, sampleRate, frequency, 0.25)) +
(0.1 * base(i, sampleRate, frequency, 0.5))
);
}
},
{
name: 'organ',
attack: function() { return 0.3 },
dampen: function(sampleRate, frequency) { return 1+(frequency * 0.01); },
wave: function(i, sampleRate, frequency) {
var base = this.modulate[0];
return this.modulate[1](
i,
sampleRate,
frequency,
base(i, sampleRate, frequency, 0) +
0.5*base(i, sampleRate, frequency, 0.25) +
0.25*base(i, sampleRate, frequency, 0.5)
);
}
},
{
name: 'acoustic',
attack: function() { return 0.002; },
dampen: function() { return 1; },
wave: function(i, sampleRate, frequency) {
var vars = this.vars;
vars.valueTable = !vars.valueTable?[]:vars.valueTable;
if(typeof(vars.playVal)=='undefined') { vars.playVal = 0; }
if(typeof(vars.periodCount)=='undefined') { vars.periodCount = 0; }
var valueTable = vars.valueTable;
var playVal = vars.playVal;
var periodCount = vars.periodCount;
var period = sampleRate/frequency;
var p_hundredth = Math.floor((period-Math.floor(period))*100);
var resetPlay = false;
if(valueTable.length<=Math.ceil(period)) {
valueTable.push(Math.round(Math.random())*2-1);
return valueTable[valueTable.length-1];
} else {
valueTable[playVal] = (valueTable[playVal>=(valueTable.length-1)?0:playVal+1] + valueTable[playVal]) * 0.5;
if(playVal>=Math.floor(period)) {
if(playVal<Math.ceil(period)) {
if((periodCount%100)>=p_hundredth) {
// Reset
resetPlay = true;
valueTable[playVal+1] = (valueTable[0] + valueTable[playVal+1]) * 0.5;
vars.periodCount++;
}
} else {
resetPlay = true;
}
}
var _return = valueTable[playVal];
if(resetPlay) { vars.playVal = 0; } else { vars.playVal++; }
return _return;
}
}
},
{
name: 'edm',
attack: function() { return 0.002; },
dampen: function() { return 1; },
wave: function(i, sampleRate, frequency) {
var base = this.modulate[0];
var mod = this.modulate.slice(1);
return mod[0](
i,
sampleRate,
frequency,
mod[9](
i,
sampleRate,
frequency,
mod[2](
i,
sampleRate,
frequency,
Math.pow(base(i, sampleRate, frequency, 0), 3) +
Math.pow(base(i, sampleRate, frequency, 0.5), 5) +
Math.pow(base(i, sampleRate, frequency, 1), 7)
)
) +
mod[8](
i,
sampleRate,
frequency,
base(i, sampleRate, frequency, 1.75)
)
);
}
});

View File

@@ -1,6 +1,6 @@
{
"name": "litegraph.js",
"version": "0.6.0",
"version": "0.7.0",
"description": "A graph node editor similar to PD or UDK Blueprints, it works in a HTML5 Canvas and allow to exported graphs to be included in applications.",
"main": "build/litegraph.js",
"directories": {
@@ -28,13 +28,13 @@
},
"homepage": "https://github.com/kriffe/litegraph.js#readme",
"devDependencies": {
"express": "^4.16.2",
"express": "^4.16.4",
"google-closure-compiler": "^20171112.0.0",
"grunt": "^1.0.1",
"grunt": "^1.0.3",
"grunt-cli": "^1.2.0",
"grunt-closure-tools": "^1.0.0",
"grunt-contrib-concat": "^1.0.1",
"nodemon": "^1.14.7",
"nodemon": "^1.18.9",
"rimraf": "^2.6.2"
}
}

View File

@@ -1719,11 +1719,16 @@ LGraph.prototype.onNodeTrace = function(node, msg, color)
input|output: every connection
+ { name:string, type:string, pos: [x,y]=Optional, direction: "input"|"output", links: Array });
flags:
general properties:
+ clip_area: if you render outside the node, it will be cliped
+ unsafe_execution: not allowed for safe execution
+ skip_repeated_outputs: when adding new outputs, it wont show if there is one already connected
+ resizable: if set to false it wont be resizable with the mouse
+ horizontal: slots are distributed horizontally
+ widgets_up: widgets start from the top of the node
flags object:
+ collapsed: if it is collapsed
supported callbacks:
+ onAdded: when added to graph
@@ -2168,13 +2173,16 @@ LGraphNode.prototype.getInputOrProperty = function( name )
return this.properties ? this.properties[name] : null;
for(var i = 0, l = this.inputs.length; i < l; ++i)
if(name == this.inputs[i].name)
{
var input_info = this.inputs[i];
if(name == input_info.name && input_info.link != null)
{
var link_id = this.inputs[i].link;
var link = this.graph.links[ link_id ];
return link ? link.data : null;
var link = this.graph.links[ input_info.link ];
if(link)
return link.data;
}
return this.properties[name];
}
return this.properties[ name ];
}
@@ -3985,7 +3993,7 @@ LGraphCanvas.prototype.processMouseDown = function(e)
if(!this.connecting_node && !node.flags.collapsed && !this.live_mode)
{
//Search for corner for resize
if( !skip_action && node.flags.resizable !== false && isInsideRectangle( e.canvasX, e.canvasY, node.pos[0] + node.size[0] - 5, node.pos[1] + node.size[1] - 5 ,10,10 ))
if( !skip_action && node.resizable !== false && isInsideRectangle( e.canvasX, e.canvasY, node.pos[0] + node.size[0] - 5, node.pos[1] + node.size[1] - 5 ,10,10 ))
{
this.resizing_node = node;
this.canvas.style.cursor = "se-resize";
@@ -5535,6 +5543,8 @@ LGraphCanvas.prototype.drawNode = function(node, ctx )
var shape = node._shape || LiteGraph.BOX_SHAPE;
var size = temp_vec2;
temp_vec2.set( node.size );
var horizontal = node.horizontal || node.flags.horizontal;
if( node.flags.collapsed )
{
ctx.font = this.inner_text_font;
@@ -5544,7 +5554,7 @@ LGraphCanvas.prototype.drawNode = function(node, ctx )
size[1] = 0;
}
if( node.flags.clip_area ) //Start clipping
if( node.clip_area ) //Start clipping
{
ctx.save();
ctx.beginPath();
@@ -5562,7 +5572,7 @@ LGraphCanvas.prototype.drawNode = function(node, ctx )
ctx.shadowColor = "transparent";
//connection slots
ctx.textAlign = node.flags.horizontal ? "center" : "left";
ctx.textAlign = horizontal ? "center" : "left";
ctx.font = this.inner_text_font;
var render_text = this.scale > 0.6;
@@ -5597,8 +5607,12 @@ LGraphCanvas.prototype.drawNode = function(node, ctx )
ctx.beginPath();
if (slot.type === LiteGraph.EVENT || slot.shape === LiteGraph.BOX_SHAPE) {
ctx.rect((pos[0] - 6) + 0.5, (pos[1] - 5) + 0.5, 14, 10);
if (slot.type === LiteGraph.EVENT || slot.shape === LiteGraph.BOX_SHAPE)
{
if (horizontal)
ctx.rect((pos[0] - 5) + 0.5, (pos[1] - 8) + 0.5, 10, 14);
else
ctx.rect((pos[0] - 6) + 0.5, (pos[1] - 5) + 0.5, 14, 10);
} else if (slot.shape === LiteGraph.ARROW_SHAPE) {
ctx.moveTo(pos[0] + 8, pos[1] + 0.5);
ctx.lineTo(pos[0] - 4, (pos[1] + 6) + 0.5);
@@ -5617,7 +5631,7 @@ LGraphCanvas.prototype.drawNode = function(node, ctx )
if(text)
{
ctx.fillStyle = LiteGraph.NODE_TEXT_COLOR;
if( node.flags.horizontal || slot.dir == LiteGraph.UP )
if( horizontal || slot.dir == LiteGraph.UP )
ctx.fillText(text,pos[0],pos[1] - 10);
else
ctx.fillText(text,pos[0] + 10,pos[1] + 5);
@@ -5629,7 +5643,7 @@ LGraphCanvas.prototype.drawNode = function(node, ctx )
if(this.connecting_node)
ctx.globalAlpha = 0.4 * editor_alpha;
ctx.textAlign = node.flags.horizontal ? "center" : "right";
ctx.textAlign = horizontal ? "center" : "right";
ctx.strokeStyle = "black";
if(node.outputs)
for(var i = 0; i < node.outputs.length; i++)
@@ -5646,8 +5660,12 @@ LGraphCanvas.prototype.drawNode = function(node, ctx )
ctx.beginPath();
//ctx.rect( node.size[0] - 14,i*14,10,10);
if (slot.type === LiteGraph.EVENT || slot.shape === LiteGraph.BOX_SHAPE) {
ctx.rect((pos[0] - 6) + 0.5,(pos[1] - 5) + 0.5,14,10);
if (slot.type === LiteGraph.EVENT || slot.shape === LiteGraph.BOX_SHAPE)
{
if( horizontal )
ctx.rect((pos[0] - 5) + 0.5,(pos[1] - 8) + 0.5,10,14);
else
ctx.rect((pos[0] - 6) + 0.5,(pos[1] - 5) + 0.5,14,10);
} else if (slot.shape === LiteGraph.ARROW_SHAPE) {
ctx.moveTo(pos[0] + 8, pos[1] + 0.5);
ctx.lineTo(pos[0] - 4, (pos[1] + 6) + 0.5);
@@ -5672,7 +5690,7 @@ LGraphCanvas.prototype.drawNode = function(node, ctx )
if(text)
{
ctx.fillStyle = LiteGraph.NODE_TEXT_COLOR;
if( node.flags.horizontal || slot.dir == LiteGraph.DOWN )
if( horizontal || slot.dir == LiteGraph.DOWN )
ctx.fillText(text,pos[0],pos[1] - 8);
else
ctx.fillText(text, pos[0] - 10,pos[1] + 5);
@@ -5685,7 +5703,7 @@ LGraphCanvas.prototype.drawNode = function(node, ctx )
if(node.widgets)
{
if( node.flags.horizontal || node.flags.widgets_up )
if( horizontal || node.widgets_up )
max_y = 2;
this.drawNodeWidgets( node, max_y, ctx, (this.node_widget && this.node_widget[0] == node) ? this.node_widget[1] : null );
}
@@ -5726,7 +5744,7 @@ LGraphCanvas.prototype.drawNode = function(node, ctx )
{
var x = 0;
var y = LiteGraph.NODE_TITLE_HEIGHT * -0.5; //center
if( node.flags.horizontal )
if( horizontal )
{
x = node._collapsed_width * 0.5;
y = -LiteGraph.NODE_TITLE_HEIGHT;
@@ -5750,7 +5768,7 @@ LGraphCanvas.prototype.drawNode = function(node, ctx )
{
var x = node._collapsed_width;
var y = LiteGraph.NODE_TITLE_HEIGHT * -0.5; //center
if( node.flags.horizontal )
if( horizontal )
{
x = node._collapsed_width * 0.5;
y = 0;
@@ -5773,7 +5791,7 @@ LGraphCanvas.prototype.drawNode = function(node, ctx )
}
}
if(node.flags.clip_area)
if(node.clip_area)
ctx.restore();
ctx.globalAlpha = 1.0;
@@ -5869,30 +5887,33 @@ LGraphCanvas.prototype.drawNodeShape = function( node, ctx, size, fgcolor, bgcol
}
//title box
if (shape == LiteGraph.ROUND_SHAPE || shape == LiteGraph.CIRCLE_SHAPE || shape == LiteGraph.CARD_SHAPE)
if(node.flags.render_box !== false)
{
if( this.scale > 0.5 )
if (shape == LiteGraph.ROUND_SHAPE || shape == LiteGraph.CIRCLE_SHAPE || shape == LiteGraph.CARD_SHAPE)
{
ctx.fillStyle = "black";
if( this.scale > 0.5 )
{
ctx.fillStyle = "black";
ctx.beginPath();
ctx.arc(title_height *0.5, title_height * -0.5, (title_height - 8) *0.5,0,Math.PI*2);
ctx.fill();
}
ctx.fillStyle = node.boxcolor || LiteGraph.NODE_DEFAULT_BOXCOLOR;
ctx.beginPath();
ctx.arc(title_height *0.5, title_height * -0.5, (title_height - 8) *0.5,0,Math.PI*2);
ctx.arc(title_height *0.5, title_height * -0.5, (title_height - 8) *0.4,0,Math.PI*2);
ctx.fill();
}
ctx.fillStyle = node.boxcolor || LiteGraph.NODE_DEFAULT_BOXCOLOR;
ctx.beginPath();
ctx.arc(title_height *0.5, title_height * -0.5, (title_height - 8) *0.4,0,Math.PI*2);
ctx.fill();
}
else
{
if( this.scale > 0.5 )
else
{
ctx.fillStyle = "black";
ctx.fillRect(4,-title_height + 4,title_height - 8,title_height - 8);
if( this.scale > 0.5 )
{
ctx.fillStyle = "black";
ctx.fillRect(4,-title_height + 4,title_height - 8,title_height - 8);
}
ctx.fillStyle = node.boxcolor || LiteGraph.NODE_DEFAULT_BOXCOLOR;
ctx.fillRect(5,-title_height + 5,title_height - 10,title_height - 10);
}
ctx.fillStyle = node.boxcolor || LiteGraph.NODE_DEFAULT_BOXCOLOR;
ctx.fillRect(5,-title_height + 5,title_height - 10,title_height - 10);
}
ctx.globalAlpha = old_alpha;
@@ -6341,6 +6362,7 @@ LGraphCanvas.prototype.drawNodeWidgets = function( node, posY, ctx, active_widge
ctx.fillText( w.value, width - 40, y + H*0.7 );
}
break;
case "string":
case "text":
ctx.textAlign = "left";
ctx.strokeStyle = "#AAA";
@@ -6463,6 +6485,7 @@ LGraphCanvas.prototype.processNodeWidgets = function( node, pos, event, active_w
setTimeout( function(){ w.callback( w.value, that, node, pos ); }, 20 );
}
break;
case "string":
case "text":
if( event.type == "mousedown" )
this.prompt( "Value", w.value, (function(v){ this.value = v; if(w.callback) w.callback(v, that, node ); }).bind(w), event );

View File

@@ -334,36 +334,55 @@ LiteGraph.registerNodeType("graph/output", GlobalOutput);
//Constant
function Constant()
function ConstantNumber()
{
this.addOutput("value","number");
this.addProperty( "value", 1.0 );
this.editable = { property:"value", type:"number" };
}
Constant.title = "Const";
Constant.desc = "Constant value";
ConstantNumber.title = "Const Number";
ConstantNumber.desc = "Constant number";
Constant.prototype.setValue = function(v)
{
if( typeof(v) == "string") v = parseFloat(v);
this.properties["value"] = v;
this.setDirtyCanvas(true);
};
Constant.prototype.onExecute = function()
ConstantNumber.prototype.onExecute = function()
{
this.setOutputData(0, parseFloat( this.properties["value"] ) );
}
Constant.prototype.onDrawBackground = function(ctx)
ConstantNumber.prototype.onDrawBackground = function(ctx)
{
//show the current value
this.outputs[0].label = this.properties["value"].toFixed(3);
}
LiteGraph.registerNodeType("basic/const", Constant);
LiteGraph.registerNodeType("basic/const", ConstantNumber);
function ConstantString()
{
this.addOutput("","string");
this.addProperty( "value", "" );
this.widget = this.addWidget("text","value","", this.setValue.bind(this) );
this.widgets_up = true;
}
ConstantString.title = "Const String";
ConstantString.desc = "Constant string";
ConstantString.prototype.setValue = function(v)
{
this.properties.value = v;
}
ConstantString.prototype.onPropertyChanged = function(name,value)
{
this.widget.value = value;
}
ConstantString.prototype.onExecute = function()
{
this.setOutputData(0, this.properties["value"] );
}
LiteGraph.registerNodeType("basic/string", ConstantString );
//Watch a value in the editor

View File

@@ -20,6 +20,39 @@ LogEvent.prototype.onAction = function( action, param )
LiteGraph.registerNodeType("events/log", LogEvent );
//Sequencer for events
function Sequencer()
{
this.addInput("", LiteGraph.ACTION);
this.addInput("", LiteGraph.ACTION);
this.addInput("", LiteGraph.ACTION);
this.addInput("", LiteGraph.ACTION);
this.addInput("", LiteGraph.ACTION);
this.addInput("", LiteGraph.ACTION);
this.addOutput("", LiteGraph.EVENT);
this.addOutput("", LiteGraph.EVENT);
this.addOutput("", LiteGraph.EVENT);
this.addOutput("", LiteGraph.EVENT);
this.addOutput("", LiteGraph.EVENT);
this.addOutput("", LiteGraph.EVENT);
this.size = [120,30];
this.flags = { horizontal: true, render_box: false };
}
Sequencer.title = "Sequencer";
Sequencer.desc = "Trigger events when an event arrives";
Sequencer.prototype.getTitle = function() { return ""; }
Sequencer.prototype.onAction = function( action, param )
{
if(this.outputs)
for(var i = 0; i < this.outputs.length; ++i)
this.triggerSlot( i, param );
}
LiteGraph.registerNodeType("events/sequencer", Sequencer );
//Filter events
function FilterEvent()
{
@@ -59,6 +92,52 @@ FilterEvent.prototype.onAction = function( action, param )
LiteGraph.registerNodeType("events/filter", FilterEvent );
//Show value inside the debug console
function EventCounter()
{
this.addInput("inc", LiteGraph.ACTION);
this.addInput("dec", LiteGraph.ACTION);
this.addInput("reset", LiteGraph.ACTION);
this.addOutput("change", LiteGraph.EVENT);
this.addOutput("num", "number");
this.num = 0;
}
EventCounter.title = "Counter";
EventCounter.desc = "Counts events";
EventCounter.prototype.onAction = function(action, param)
{
var v = this.num;
if(action == "inc")
this.num += 1;
else if(action == "dec")
this.num -= 1;
else if(action == "reset")
this.num = 0;
if(this.num != v)
this.trigger("change",this.num);
}
EventCounter.prototype.onDrawBackground = function(ctx)
{
if(this.flags.collapsed)
return;
ctx.fillStyle = "#AAA";
ctx.font = "20px Arial";
ctx.textAlign = "center";
ctx.fillText( this.num, this.size[0] * 0.5, this.size[1] * 0.5 );
}
EventCounter.prototype.onExecute = function()
{
this.setOutputData(1,this.num);
}
LiteGraph.registerNodeType("events/counter", EventCounter );
//Show value inside the debug console
function DelayEvent()
{
@@ -142,15 +221,19 @@ TimerEvent.prototype.onDrawBackground = function()
TimerEvent.prototype.onExecute = function()
{
var dt = this.graph.elapsed_time * 1000; //in ms
var trigger = this.time == 0;
this.time += dt;
this.last_interval = Math.max(1, this.getInputOrProperty("interval") | 0);
if( this.time < this.last_interval || isNaN(this.last_interval) )
if( !trigger && ( this.time < this.last_interval || isNaN(this.last_interval)) )
{
if( this.inputs && this.inputs.length > 1 && this.inputs[1] )
this.setOutputData(1,false);
return;
}
this.triggered = true;
this.time = this.time % this.last_interval;
this.trigger( "on_tick", this.properties.event );

View File

@@ -98,6 +98,8 @@ GraphicsImage.prototype.onAdded = function()
GraphicsImage.prototype.onDrawBackground = function(ctx)
{
if(this.flags.collapsed)
return;
if(this.img && this.size[0] > 5 && this.size[1] > 5)
ctx.drawImage(this.img, 0,0,this.size[0],this.size[1]);
}
@@ -244,7 +246,7 @@ LiteGraph.registerNodeType("color/palette", ColorPalette );
function ImageFrame()
{
this.addInput("","image");
this.addInput("","image,canvas");
this.size = [200,200];
}
@@ -255,7 +257,7 @@ ImageFrame.widgets = [{name:"resize",text:"Resize box",type:"button"},{name:"vie
ImageFrame.prototype.onDrawBackground = function(ctx)
{
if(this.frame)
if(this.frame && !this.flags.collapsed)
ctx.drawImage(this.frame, 0,0,this.size[0],this.size[1]);
}
@@ -298,86 +300,6 @@ LiteGraph.registerNodeType("graphics/frame", ImageFrame );
/*
LiteGraph.registerNodeType("visualization/graph", {
desc: "Shows a graph of the inputs",
inputs: [["",0],["",0],["",0],["",0]],
size: [200,200],
properties: {min:-1,max:1,bgColor:"#000"},
onDrawBackground: function(ctx)
{
var colors = ["#FFF","#FAA","#AFA","#AAF"];
if(this.properties.bgColor != null && this.properties.bgColor != "")
{
ctx.fillStyle="#000";
ctx.fillRect(2,2,this.size[0] - 4, this.size[1]-4);
}
if(this.data)
{
var min = this.properties["min"];
var max = this.properties["max"];
for(var i in this.data)
{
var data = this.data[i];
if(!data) continue;
if(this.getInputInfo(i) == null) continue;
ctx.strokeStyle = colors[i];
ctx.beginPath();
var d = data.length / this.size[0];
for(var j = 0; j < data.length; j += d)
{
var value = data[ Math.floor(j) ];
value = (value - min) / (max - min);
if (value > 1.0) value = 1.0;
else if(value < 0) value = 0;
if(j == 0)
ctx.moveTo( j / d, (this.size[1] - 5) - (this.size[1] - 10) * value);
else
ctx.lineTo( j / d, (this.size[1] - 5) - (this.size[1] - 10) * value);
}
ctx.stroke();
}
}
//ctx.restore();
},
onExecute: function()
{
if(!this.data) this.data = [];
for(var i in this.inputs)
{
var value = this.getInputData(i);
if(typeof(value) == "number")
{
value = value ? value : 0;
if(!this.data[i])
this.data[i] = [];
this.data[i].push(value);
if(this.data[i].length > (this.size[1] - 4))
this.data[i] = this.data[i].slice(1,this.data[i].length);
}
else
this.data[i] = value;
}
if(this.data.length)
this.setDirtyCanvas(true);
}
});
*/
function ImageFade()
{
@@ -509,6 +431,105 @@ ImageCrop.prototype.onPropertyChanged = function(name,value)
LiteGraph.registerNodeType("graphics/cropImage", ImageCrop );
//CANVAS stuff
function CanvasNode()
{
this.addInput("clear", LiteGraph.ACTION );
this.addOutput("","canvas");
this.properties = {width:512,height:512,autoclear:true};
this.canvas = document.createElement("canvas");
this.ctx = this.canvas.getContext("2d");
}
CanvasNode.title = "Canvas";
CanvasNode.desc = "Canvas to render stuff";
CanvasNode.prototype.onExecute = function()
{
var canvas = this.canvas;
var w = (this.properties.width)|0;
var h = (this.properties.height)|0;
if( canvas.width != w )
canvas.width = w;
if( canvas.height != h )
canvas.height = h;
if(this.properties.autoclear)
this.ctx.clearRect(0,0,canvas.width,canvas.height);
this.setOutputData(0,canvas);
}
CanvasNode.prototype.onAction = function( action, param )
{
if(action == "clear")
this.ctx.clearRect(0,0,this.canvas.width,this.canvas.height);
}
LiteGraph.registerNodeType("graphics/canvas", CanvasNode );
function DrawImageNode()
{
this.addInput("canvas", "canvas" );
this.addInput("img", "image,canvas" );
this.addInput("x", "number" );
this.addInput("y", "number" );
this.properties = {x:0,y:0,opacity:1};
}
DrawImageNode.title = "DrawImage";
DrawImageNode.desc = "Draws image into a canvas";
DrawImageNode.prototype.onExecute = function()
{
var canvas = this.getInputData(0);
if(!canvas)
return;
var img = this.getInputOrProperty("img");
if(!img)
return;
var x = this.getInputOrProperty("x");
var y = this.getInputOrProperty("y");
var ctx = canvas.getContext("2d");
ctx.drawImage( img, x, y );
}
LiteGraph.registerNodeType("graphics/drawImage", DrawImageNode );
function DrawRectangleNode()
{
this.addInput("canvas", "canvas" );
this.addInput("x", "number" );
this.addInput("y", "number" );
this.addInput("w", "number" );
this.addInput("h", "number" );
this.properties = { x:0,y:0,w:10,h:10,color:"white",opacity:1 };
}
DrawRectangleNode.title = "DrawRectangle";
DrawRectangleNode.desc = "Draws rectangle in canvas";
DrawRectangleNode.prototype.onExecute = function()
{
var canvas = this.getInputData(0);
if(!canvas)
return;
var x = this.getInputOrProperty("x");
var y = this.getInputOrProperty("y");
var w = this.getInputOrProperty("w");
var h = this.getInputOrProperty("h");
var ctx = canvas.getContext("2d");
ctx.fillRect( x, y, w, h );
}
LiteGraph.registerNodeType("graphics/drawRectangle", DrawRectangleNode );
function ImageVideo()
{

View File

@@ -3,39 +3,92 @@ var LiteGraph = global.LiteGraph;
function Selector()
{
this.addInput("sel","boolean");
this.addOutput("value","number");
this.properties = { A:0, B:1 };
this.size = [60,20];
this.addInput("sel","number");
this.addInput("A");
this.addInput("B");
this.addInput("C");
this.addInput("D");
this.addOutput("out");
this.selected = 0;
}
Selector.title = "Selector";
Selector.desc = "outputs A if selector is true, B if selector is false";
Selector.desc = "selects an output";
Selector.prototype.onDrawBackground = function(ctx)
{
if(this.flags.collapsed)
return;
ctx.fillStyle = "#AFB";
var y = (this.selected + 1) * LiteGraph.NODE_SLOT_HEIGHT + 2;
ctx.beginPath();
ctx.moveTo(30, y);
ctx.lineTo(30, y+LiteGraph.NODE_SLOT_HEIGHT);
ctx.lineTo(24, y+LiteGraph.NODE_SLOT_HEIGHT*0.5);
ctx.fill();
}
Selector.prototype.onExecute = function()
{
var cond = this.getInputData(0);
if(cond === undefined)
return;
for(var i = 1; i < this.inputs.length; i++)
{
var input = this.inputs[i];
var v = this.getInputData(i);
if(v === undefined)
continue;
this.properties[input.name] = v;
}
var A = this.properties.A;
var B = this.properties.B;
this.setOutputData(0, cond ? A : B );
var sel = this.getInputData(0);
if(sel == null)
sel = 0;
this.selected = sel = Math.round(sel) % (this.inputs.length - 1);
var v = this.getInputData(sel + 1);
if(v !== undefined)
this.setOutputData( 0, v );
}
Selector.prototype.onGetInputs = function() {
return [["A",0],["B",0]];
return [["E",0],["F",0],["G",0],["H",0]];
}
LiteGraph.registerNodeType("logic/selector", Selector);
function Sequence()
{
this.properties = {
sequence: "A,B,C"
};
this.addInput("index","number");
this.addInput("seq");
this.addOutput("out");
this.index = 0;
this.values = this.properties.sequence.split(",");
}
Sequence.title = "Sequence";
Sequence.desc = "select one element from a sequence from a string";
Sequence.prototype.onPropertyChanged = function(name,value)
{
if(name == "sequence")
{
this.values = value.split(",");
}
}
Sequence.prototype.onExecute = function()
{
var seq = this.getInputData(1);
if(seq && seq != this.current_sequence)
{
this.values = seq.split(",");
this.current_sequence = seq;
}
var index = this.getInputData(0);
if(index == null)
index = 0;
this.index = index = Math.round(index) % this.values.length;
this.setOutputData( 0, this.values[ index ] );
}
LiteGraph.registerNodeType("logic/sequence", Sequence );
})(this);

View File

@@ -81,6 +81,23 @@ Bypass.prototype.onExecute = function()
LiteGraph.registerNodeType("math/bypass", Bypass );
function ToNumber()
{
this.addInput("in");
this.addOutput("out");
}
ToNumber.title = "to Number";
ToNumber.desc = "Cast to number";
ToNumber.prototype.onExecute = function()
{
var v = this.getInputData(0);
this.setOutputData(0, Number(v) );
}
LiteGraph.registerNodeType("math/to_number", ToNumber );
function MathRange()
{

View File

@@ -6,16 +6,17 @@ function MIDIEvent( data )
{
this.channel = 0;
this.cmd = 0;
this.data = new Uint32Array(3);
if(data)
this.setup(data)
else
this.data = [0,0,0];
}
LiteGraph.MIDIEvent = MIDIEvent;
MIDIEvent.prototype.setup = function( raw_data )
{
this.data = raw_data;
this.data.set(raw_data);
var midiStatus = raw_data[0];
this.status = midiStatus;
@@ -50,6 +51,32 @@ Object.defineProperty( MIDIEvent.prototype, "velocity", {
});
MIDIEvent.notes = ["A","A#","B","C","C#","D","D#","E","F","F#","G","G#"];
MIDIEvent.note_to_index = {"A":0,"A#":1,"B":2,"C":3,"C#":4,"D":5,"D#":6,"E":7,"F":8,"F#":9,"G":10,"G#":11};
Object.defineProperty( MIDIEvent.prototype, "note", {
get: function() {
if(this.cmd != MIDIEvent.NOTEON)
return -1;
return MIDIEvent.toNoteString( this.data[1], true );
},
set: function(v) {
throw("notes cannot be assigned this way, must modify the data[1]");
},
enumerable: true
});
Object.defineProperty( MIDIEvent.prototype, "octave", {
get: function() {
if(this.cmd != MIDIEvent.NOTEON)
return -1;
var octave = this.data[1] - 24;
return Math.floor(octave / 12 + 1);
},
set: function(v) {
throw("octave cannot be assigned this way, must modify the data[1]");
},
enumerable: true
});
//returns HZs
MIDIEvent.prototype.getPitch = function()
@@ -121,14 +148,39 @@ MIDIEvent.computeCommandFromString = function( str )
}
}
MIDIEvent.toNoteString = function(d)
//transform from a pitch number to string like "C4"
MIDIEvent.toNoteString = function( d, skip_octave )
{
d = Math.round(d); //in case it has decimals
var note = d - 21;
var octave = d - 24;
var octave = Math.floor((d - 24) / 12 + 1);
note = note % 12;
if(note < 0)
note = 12 + note;
return MIDIEvent.notes[ note ] + Math.floor(octave / 12 + 1);
return MIDIEvent.notes[ note ] + (skip_octave ? "" : octave);
}
MIDIEvent.NoteStringToPitch = function(str)
{
str = str.toUpperCase();
var note = str[0];
var octave = 4;
if(str[1] == "#")
{
note += "#";
if( str.length > 2 )
octave = Number( str[2] );
}
else
{
if( str.length > 1 )
octave = Number( str[1] );
}
var pitch = MIDIEvent.note_to_index[note];
if(pitch == null)
return null;
return ((octave - 1) * 12) + pitch + 21;
}
MIDIEvent.prototype.toString = function()
@@ -482,7 +534,7 @@ LGMIDIOut.prototype.getPropertyInfo = function(name)
LGMIDIOut.prototype.onAction = function(event, midi_event )
{
console.log(midi_event);
//console.log(midi_event);
if(!this._midi)
return;
if(event == "send")
@@ -581,7 +633,7 @@ function LGMIDIEvent()
{
this.properties = {
channel: 0,
cmd: "CC",
cmd: 144, //0x90
value1: 1,
value2: 1
};
@@ -589,6 +641,8 @@ function LGMIDIEvent()
this.addInput( "send", LiteGraph.EVENT );
this.addInput( "assign", LiteGraph.EVENT );
this.addOutput( "on_midi", LiteGraph.EVENT );
this.midi_event = new MIDIEvent();
}
LGMIDIEvent.title = "MIDIEvent";
@@ -606,7 +660,7 @@ LGMIDIEvent.prototype.onAction = function( event, midi_event )
}
//send
var midi_event = new MIDIEvent();
var midi_event = this.midi_event;
midi_event.channel = this.properties.channel;
if(this.properties.cmd && this.properties.cmd.constructor === String)
midi_event.setCommandFromString( this.properties.cmd );
@@ -615,6 +669,7 @@ LGMIDIEvent.prototype.onAction = function( event, midi_event )
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);
}
@@ -622,6 +677,28 @@ LGMIDIEvent.prototype.onExecute = function()
{
var props = this.properties;
if(this.inputs)
{
for(var i = 0; i < this.inputs.length; ++i)
{
var input = this.inputs[i];
if(input.link == -1)
continue;
switch (input.name)
{
case "note":
var v = this.getInputData(i);
if(v != null)
{
if(v.constructor === String)
v = MIDIEvent.NoteStringToPitch(v);
this.properties.value1 = (v|0)%255;
}
break;
}
}
}
if(this.outputs)
{
for(var i = 0; i < this.outputs.length; ++i)
@@ -657,6 +734,9 @@ LGMIDIEvent.prototype.onPropertyChanged = function(name,value)
this.properties.cmd = MIDIEvent.computeCommandFromString( value );
}
LGMIDIEvent.prototype.onGetInputs = function() {
return [ ["note","number"] ];
}
LGMIDIEvent.prototype.onGetOutputs = function() {
return [
@@ -701,6 +781,232 @@ LGMIDICC.prototype.onExecute = function()
LiteGraph.registerNodeType("midi/cc", LGMIDICC);
function LGMIDIGenerator()
{
this.addInput( "generate", LiteGraph.ACTION );
this.addInput( "scale", "string" );
this.addInput( "octave", "number" );
this.addOutput( "note", LiteGraph.EVENT );
this.properties = {
notes: "A,A#,B,C,C#,D,D#,E,F,F#,G,G#",
octave: 2,
mode: "sequence"
};
this.notes_pitches = LGMIDIGenerator.processScale( this.properties.notes );
this.sequence_index = 0;
}
LGMIDIGenerator.title = "MIDI Generator";
LGMIDIGenerator.desc = "Generates a random MIDI note";
LGMIDIGenerator.processScale = function(scale)
{
var notes = scale.split(",");
for(var i = 0; i < notes.length; ++i)
notes[i] = MIDIEvent.note_to_index[ notes[i] ] || 0;
return notes;
}
LGMIDIGenerator.prototype.onPropertyChanged = function(name,value)
{
if(name == "notes")
this.notes_pitches = LGMIDIGenerator.processScale( value );
}
LGMIDIGenerator.prototype.onExecute = function()
{
var octave = this.getInputData(2);
if(octave != null)
this.properties.octave = octave;
var scale = this.getInputData(1);
if(scale)
this.notes_pitches = LGMIDIGenerator.processScale( scale );
}
LGMIDIGenerator.prototype.onAction = function( event, midi_event )
{
//var range = this.properties.max - this.properties.min;
//var pitch = this.properties.min + ((Math.random() * range)|0);
var pitch = 0;
var range = this.notes_pitches.length;
if( this.properties.mode == "sequence" )
{
var index = this.sequence_index = (this.sequence_index + 1) % range;
pitch = this.notes_pitches[ index ] + ( (this.properties.octave-1) * 12) + 33;
}
else if( this.properties.mode == "random" )
{
var index = Math.floor(Math.random()*range);
pitch = this.notes_pitches[ index ] + ( (this.properties.octave-1) * 12) + 33;
}
var note = new MIDIEvent();
note.setup([ MIDIEvent.NOTEON, pitch, 10 ]);
this.trigger("note", note);
}
LiteGraph.registerNodeType("midi/generator", LGMIDIGenerator);
function LGMIDITranspose()
{
this.properties = {
amount: 0
};
this.addInput( "in", LiteGraph.ACTION );
this.addInput( "amount", "number" );
this.addOutput( "out", LiteGraph.EVENT );
this.midi_event = new MIDIEvent();
}
LGMIDITranspose.title = "MIDI Transpose";
LGMIDITranspose.desc = "Transpose a MIDI note";
LGMIDITranspose.prototype.onAction = function( event, midi_event )
{
this.midi_event.setup( midi_event.data );
this.midi_event.data[1] = Math.round( this.midi_event.data[1] + this.properties.amount );
this.trigger("out", this.midi_event );
}
LGMIDITranspose.prototype.onExecute = function()
{
var amount = this.getInputData(1);
if(amount!= null)
this.properties.amount = amount;
}
LiteGraph.registerNodeType("midi/transpose", LGMIDITranspose);
function LGMIDIQuantize()
{
this.properties = {
scale: "A,A#,B,C,C#,D,D#,E,F,F#,G,G#"
};
this.addInput( "note", LiteGraph.ACTION );
this.addInput( "scale", "string" );
this.addOutput( "out", LiteGraph.EVENT );
this.valid_notes = new Array(12);
this.offset_notes = new Array(12);
this.processScale( this.properties.scale );
this.midi_event = new MIDIEvent();
}
LGMIDIQuantize.title = "MIDI Quantize Pitch";
LGMIDIQuantize.desc = "Transpose a MIDI note tp fit an scale";
LGMIDIQuantize.prototype.onPropertyChanged = function(name,value)
{
if(name == "scale")
this.processScale( value );
}
LGMIDIQuantize.prototype.processScale = function( scale )
{
this._current_scale = scale;
this.notes_pitches = LGMIDIGenerator.processScale( scale );
for(var i = 0; i < 12; ++i)
this.valid_notes[i] = this.notes_pitches.indexOf(i) != -1;
for(var i = 0; i < 12; ++i)
{
if (this.valid_notes[ i ])
{
this.offset_notes[i] = 0;
continue;
}
for(var j = 1; j < 12; ++j)
{
if( this.valid_notes[ (i - j)%12 ] )
{
this.offset_notes[i] = -j;
break;
}
if( this.valid_notes[ (i + j)%12 ] )
{
this.offset_notes[i] = j;
break;
}
}
}
}
LGMIDIQuantize.prototype.onAction = function( event, midi_event )
{
this.midi_event.setup( midi_event.data );
var note = midi_event.note;
var index = MIDIEvent.note_to_index[ note ];
var offset = this.offset_notes[index];
this.midi_event.data[1] += offset;
this.trigger("out", this.midi_event );
}
LGMIDIQuantize.prototype.onExecute = function()
{
var scale = this.getInputData(1);
if(scale != null && scale != this._current_scale )
this.processScale( scale );
}
LiteGraph.registerNodeType("midi/quantize", LGMIDIQuantize);
function LGMIDIPlay()
{
this.properties = {
volume: 0.5,
duration: 1
};
this.addInput( "note", LiteGraph.ACTION );
this.addInput( "volume", "number" );
this.addInput( "duration", "number" );
this.addOutput( "note", LiteGraph.EVENT );
if(typeof(AudioSynth) == "undefined")
{
console.error("Audiosynth.js not included, LGMidiPlay requires that library");
this.boxcolor = "red";
}
else
{
var Synth = this.synth = new AudioSynth();
this.instrument = Synth.createInstrument('piano');
}
}
LGMIDIPlay.title = "MIDI Play";
LGMIDIPlay.desc = "Plays a MIDI note";
LGMIDIPlay.prototype.onAction = function( event, midi_event )
{
if(!this.instrument)
return;
var note = midi_event.note; //C#
if( !note || note == "undefined" || note.constructor !== String )
return;
this.instrument.play( note, midi_event.octave, this.properties.duration, this.properties.volume );
this.trigger("note", midi_event );
}
LGMIDIPlay.prototype.onExecute = function()
{
var volume = this.getInputData(1);
if(volume != null)
this.properties.volume = volume;
var duration = this.getInputData(2);
if(duration != null)
this.properties.duration = duration;
}
LiteGraph.registerNodeType("midi/play", LGMIDIPlay);
function now() { return window.performance.now() }

View File

@@ -5,7 +5,7 @@
../src/nodes/input.js
../src/nodes/math.js
../src/nodes/logic.js
../src/nodes/image.js
../src/nodes/graphics.js
../src/nodes/gltextures.js
../src/nodes/glfx.js
../src/nodes/midi.js