diff --git a/build/litegraph.js b/build/litegraph.js
index 5f3d9b4cd..6471d303e 100644
--- a/build/litegraph.js
+++ b/build/litegraph.js
@@ -2512,6 +2512,7 @@ function LGraphCanvas( canvas, graph, options )
this.show_info = true;
this.allow_dragcanvas = true;
this.allow_dragnodes = true;
+ this.allow_interaction = true; //allow to control widgets, buttons, collapse, etc
this.always_render_background = false;
this.render_connections_shadows = false; //too much cpu
@@ -2987,7 +2988,7 @@ LGraphCanvas.prototype.processMouseDown = function(e)
//when clicked on top of a node
//and it is not interactive
- if(n)
+ if(n && this.allow_interaction )
{
if(!this.live_mode && !n.flags.pinned)
this.bringToFront(n); //if it wasnt selected?
@@ -3150,7 +3151,7 @@ LGraphCanvas.prototype.processMouseMove = function(e)
this.dirty_canvas = true;
this.dirty_bgcanvas = true;
}
- else
+ else if(this.allow_interaction)
{
if(this.connecting_node)
this.dirty_canvas = true;
@@ -3514,7 +3515,7 @@ LGraphCanvas.prototype.processDrop = function(e)
return;
}
- if(node.onDropFile)
+ if( node.onDropFile || node.onDropData )
{
var files = e.dataTransfer.files;
if(files && files.length)
@@ -3526,22 +3527,28 @@ LGraphCanvas.prototype.processDrop = function(e)
var ext = LGraphCanvas.getFileExtension( filename );
//console.log(file);
- //prepare reader
- var reader = new FileReader();
- reader.onload = function (event) {
- //console.log(event.target);
- var data = event.target.result;
- node.onDropFile( data, filename, file );
- };
+ if(node.onDropFile)
+ node.onDropFile(file);
- //read data
- var type = file.type.split("/")[0];
- if(type == "text" || type == "")
- reader.readAsText(file);
- else if (type == "image")
- reader.readAsDataURL(file);
- else
- reader.readAsArrayBuffer(file);
+ if(node.onDropData)
+ {
+ //prepare reader
+ var reader = new FileReader();
+ reader.onload = function (event) {
+ //console.log(event.target);
+ var data = event.target.result;
+ node.onDropData( data, filename, file );
+ };
+
+ //read data
+ var type = file.type.split("/")[0];
+ if(type == "text" || type == "")
+ reader.readAsText(file);
+ else if (type == "image")
+ reader.readAsDataURL(file);
+ else
+ reader.readAsArrayBuffer(file);
+ }
}
}
}
@@ -3992,7 +3999,7 @@ LGraphCanvas.prototype.drawBackCanvas = function()
if(this.background_image && this.scale > 0.5)
{
ctx.globalAlpha = (1.0 - 0.5 / this.scale) * this.editor_alpha;
- ctx.webkitImageSmoothingEnabled = ctx.mozImageSmoothingEnabled = ctx.imageSmoothingEnabled = false;
+ ctx.imageSmoothingEnabled = ctx.mozImageSmoothingEnabled = ctx.imageSmoothingEnabled = false;
if(!this._bg_img || this._bg_img.name != this.background_image)
{
this._bg_img = new Image();
@@ -4021,7 +4028,7 @@ LGraphCanvas.prototype.drawBackCanvas = function()
}
ctx.globalAlpha = 1.0;
- ctx.webkitImageSmoothingEnabled = ctx.mozImageSmoothingEnabled = ctx.imageSmoothingEnabled = true;
+ ctx.imageSmoothingEnabled = ctx.mozImageSmoothingEnabled = ctx.imageSmoothingEnabled = true;
}
if(this.onBackgroundRender)
@@ -4862,12 +4869,14 @@ LGraphCanvas.onShowMenuNodeProperties = function(node,e, prev_menu)
for (var i in node.properties)
{
var value = node.properties[i] !== undefined ? node.properties[i] : " ";
+ //value could contain invalid html characters, clean that
+ value = LGraphCanvas.decodeHTML(value);
entries.push({content: "" + i + "" + "" + value + "", value: i});
}
if(!entries.length)
return;
- var menu = LiteGraph.createContextMenu(entries, {event: e, callback: inner_clicked, from: prev_menu},ref_window);
+ var menu = LiteGraph.createContextMenu(entries, {event: e, callback: inner_clicked, from: prev_menu, allow_html: true },ref_window);
function inner_clicked( v, e, prev )
{
@@ -4879,6 +4888,70 @@ LGraphCanvas.onShowMenuNodeProperties = function(node,e, prev_menu)
return false;
}
+LGraphCanvas.decodeHTML = function( str )
+{
+ var e = document.createElement("div");
+ e.innerText = str;
+ return e.innerHTML;
+}
+
+LGraphCanvas.onShowTitleEditor = function( node, event )
+{
+ var input_html = "";
+
+ var dialog = document.createElement("div");
+ dialog.className = "graphdialog";
+ dialog.innerHTML = "Title";
+ var input = dialog.querySelector("input");
+ if(input)
+ {
+ input.value = node.title;
+ input.addEventListener("keydown", function(e){
+ if(e.keyCode != 13)
+ return;
+ inner();
+ e.preventDefault();
+ e.stopPropagation();
+ });
+ }
+
+ var rect = this.canvas.getClientRects()[0];
+ var offsetx = -20;
+ var offsety = -20;
+ if(rect)
+ {
+ offsetx -= rect.left;
+ offsety -= rect.top;
+ }
+
+ if( event )
+ {
+ dialog.style.left = (event.pageX + offsetx) + "px";
+ dialog.style.top = (event.pageY + offsety)+ "px";
+ }
+ else
+ {
+ dialog.style.left = (this.canvas.width * 0.5 + offsetx) + "px";
+ dialog.style.top = (this.canvas.height * 0.5 + offsety) + "px";
+ }
+
+ var button = dialog.querySelector("button");
+ button.addEventListener("click", inner );
+ this.canvas.parentNode.appendChild( dialog );
+
+ function inner()
+ {
+ setValue( input.value );
+ }
+
+ function setValue(value)
+ {
+ node.title = value;
+ dialog.parentNode.removeChild( dialog );
+ node.setDirtyCanvas(true,true);
+ }
+}
+
LGraphCanvas.prototype.showEditPropertyValue = function( node, property, options )
{
if(!node || node.properties[ property ] === undefined )
@@ -5148,6 +5221,7 @@ LGraphCanvas.prototype.getNodeMenuOptions = function(node)
null,
{content:"Properties", is_menu: true, callback: LGraphCanvas.onShowMenuNodeProperties },
null,
+ {content:"Title", callback: LGraphCanvas.onShowTitleEditor },
{content:"Mode", is_menu: true, callback: LGraphCanvas.onMenuNodeMode },
{content:"Collapse", callback: LGraphCanvas.onMenuNodeCollapse },
{content:"Pin", callback: LGraphCanvas.onMenuNodePin },
@@ -5362,7 +5436,7 @@ function num2hex(triplet) {
/* LiteGraph GUI elements *************************************/
-LiteGraph.createContextMenu = function(values,options, ref_window)
+LiteGraph.createContextMenu = function(values, options, ref_window)
{
options = options || {};
this.options = options;
@@ -5435,10 +5509,16 @@ LiteGraph.createContextMenu = function(values,options, ref_window)
element.style.cursor = "pointer";
element.dataset["value"] = typeof(item) == "string" ? item : item.value;
element.data = item;
+
+ var content = "";
if(typeof(item) == "string")
- element.innerHTML = values.constructor == Array ? values[i] : i;
+ content = values.constructor == Array ? values[i] : i;
else
- element.innerHTML = item.content ? item.content : i;
+ content = item.content ? item.content : i;
+ if(options.allow_html)
+ element.innerHTML = content;
+ else
+ element.innerText = content;
element.addEventListener("click", on_click );
root.appendChild(element);
@@ -7205,6 +7285,47 @@ MathClamp.prototype.getCode = function(lang)
LiteGraph.registerNodeType("math/clamp", MathClamp );
+
+//Math ABS
+function MathLerp()
+{
+ this.properties = { f: 0.5 };
+ this.addInput("A","number");
+ this.addInput("B","number");
+
+ this.addOutput("out","number");
+}
+
+MathLerp.title = "Lerp";
+MathLerp.desc = "Linear Interpolation";
+
+MathLerp.prototype.onExecute = function()
+{
+ var v1 = this.getInputData(0);
+ if(v1 == null)
+ v1 = 0;
+ var v2 = this.getInputData(1);
+ if(v2 == null)
+ v2 = 0;
+
+ var f = this.properties.f;
+
+ var _f = this.getInputData(2);
+ if(_f !== undefined)
+ f = _f;
+
+ this.setOutputData(0, v1 * (1-f) + v2 * f );
+}
+
+MathLerp.prototype.onGetInputs = function()
+{
+ return [["f","number"]];
+}
+
+LiteGraph.registerNodeType("math/lerp", MathLerp);
+
+
+
//Math ABS
function MathAbs()
{
@@ -12153,13 +12274,74 @@ LGAudio.onConnectionsChange = function( connection, slot, connected, link_info )
LGAudio.disconnect( local_audionode, target_audionode );
}
+//this function helps creating wrappers to existing classes
+LGAudio.createAudioNodeWrapper = function( class_object )
+{
+ class_object.prototype.onPropertyChanged = function(name, value)
+ {
+ if(!this.audionode)
+ return;
+
+ if( this.audionode[ name ] === undefined )
+ return;
+
+ if( this.audionode[ name ].value !== undefined )
+ this.audionode[ name ].value = value;
+ else
+ this.audionode[ name ] = value;
+ }
+
+ class_object.prototype.onConnectionsChange = LGAudio.onConnectionsChange;
+}
+
+
+LGAudio.cached_audios = {};
+
+LGAudio.loadSound = function( url, on_complete, on_error )
+{
+ if( LGAudio.cached_audios[ url ] && url.indexOf("blob:") == -1 )
+ {
+ if(on_complete)
+ on_complete( LGAudio.cached_audios[ url ] );
+ return;
+ }
+
+ //load new sample
+ var request = new XMLHttpRequest();
+ request.open('GET', url, true);
+ request.responseType = 'arraybuffer';
+
+ var context = LGAudio.getAudioContext();
+
+ // Decode asynchronously
+ request.onload = function() {
+ console.log("AudioSource loaded");
+ context.decodeAudioData( request.response, function( buffer ) {
+ console.log("AudioSource decoded");
+ LGAudio.cached_audios[ url ] = buffer;
+ if(on_complete)
+ on_complete( buffer );
+ }, onError);
+ }
+ request.send();
+
+ function onError(err)
+ {
+ console.log("Audio loading sample error:",err);
+ if(on_error)
+ on_error(err);
+ }
+
+ return request;
+}
+
//****************************************************
function LGAudioSource()
{
this.properties = {
- src: "demodata/audio.wav",
+ src: "",
gain: 0.5,
loop: true,
autoplay: true,
@@ -12212,6 +12394,8 @@ LGAudioSource.prototype.onStop = function()
LGAudioSource.prototype.onRemoved = function()
{
this.stopAllSounds();
+ if(this._dropped_url)
+ URL.revokeObjectURL( this._url );
}
LGAudioSource.prototype.stopAllSounds = function()
@@ -12235,7 +12419,6 @@ LGAudioSource.prototype.onExecute = function()
var input = this.inputs[i];
if(!input.link)
continue;
-
var v = this.getInputData(i);
if( v === undefined )
continue;
@@ -12311,36 +12494,19 @@ LGAudioSource.prototype.loadSound = function( url )
if(!url)
return;
- //load new sample
- var request = new XMLHttpRequest();
- request.open('GET', url, true);
- request.responseType = 'arraybuffer';
+ this._request = LGAudio.loadSound( url, inner );
+
this._loading_audio = true;
- this._request = request;
+ this.boxcolor = "#AA4";
- var context = LGAudio.getAudioContext();
-
- // Decode asynchronously
- request.onload = function() {
- context.decodeAudioData( request.response, function(buffer) {
- that._audio_buffer = buffer;
- if(that._url)
- {
- URL.revokeObjectURL( that._url );
- that._url = null;
- }
-
- that._loading_audio = false;
- //if is playing, then play it
- if(that.graph && that.graph.status === LGraph.STATUS_RUNNING)
- that.onStart();
- }, onError);
- }
- request.send();
-
- function onError(err)
+ function inner( buffer )
{
- console.log("Audio loading sample error:",err);
+ this.boxcolor = LiteGraph.NODE_DEFAULT_BOXCOLOR;
+ that._audio_buffer = buffer;
+ that._loading_audio = false;
+ //if is playing, then play it
+ if(that.graph && that.graph.status === LGraph.STATUS_RUNNING)
+ that.onStart(); //this controls the autoplay already
}
}
@@ -12359,11 +12525,12 @@ LGAudioSource.prototype.onGetOutputs = function()
LGAudioSource.prototype.onDropFile = function(file)
{
- if(this._url)
- URL.revokeObjectURL( this._url );
- this._url = URL.createObjectURL( file );
- this.properties.src = this._url;
- this.loadSound( this._url );
+ if(this._dropped_url)
+ URL.revokeObjectURL( this._dropped_url );
+ var url = URL.createObjectURL( file );
+ this.properties.src = url;
+ this.loadSound( url );
+ this._dropped_url = url;
}
@@ -12417,15 +12584,24 @@ LGAudioAnalyser.prototype.onExecute = function()
this.setOutputData(0,this._freq_bin);
}
+ //send analyzer
+ if(this.isOutputConnected(1))
+ this.setOutputData(1,this.audionode);
+
+
//properties
for(var i = 1; i < this.inputs.length; ++i)
{
var input = this.inputs[i];
+ if(!input.link)
+ continue;
var v = this.getInputData(i);
if (v !== undefined)
this.audionode[ input.name ].value = v;
}
+
+
//time domain
//this.audionode.getFloatTimeDomainData( dataArray );
}
@@ -12435,33 +12611,17 @@ LGAudioAnalyser.prototype.onGetInputs = function()
return [["minDecibels","number"],["maxDecibels","number"],["smoothingTimeConstant","number"]];
}
+/*
+LGAudioAnalyser.prototype.onGetOutputs = function()
+{
+ return [["Analyzer","analyzer"]];
+}
+*/
LGAudioAnalyser.title = "Analyser";
LGAudioAnalyser.desc = "Audio Analyser";
LiteGraph.registerNodeType( "audio/analyser", LGAudioAnalyser );
-
-
-//*****************************************************
-
-//this function helps creating wrappers to existing classes
-function createAudioNodeWrapper( class_object )
-{
- class_object.prototype.onPropertyChanged = function(name, value)
- {
- if( this.audionode[ name ] === undefined )
- return;
-
- if( this.audionode[ name ].value !== undefined )
- this.audionode[ name ].value = value;
- else
- this.audionode[ name ] = value;
- }
-
- class_object.prototype.onConnectionsChange = LGAudio.onConnectionsChange;
-}
-
-
//*****************************************************
function LGAudioGain()
@@ -12491,14 +12651,128 @@ LGAudioGain.prototype.onExecute = function()
}
}
-createAudioNodeWrapper( LGAudioGain );
+LGAudio.createAudioNodeWrapper( LGAudioGain );
LGAudioGain.title = "Gain";
LGAudioGain.desc = "Audio gain";
LiteGraph.registerNodeType("audio/gain", LGAudioGain);
+function LGAudioConvolver()
+{
+ //default
+ this.properties = {
+ impulse_src:"",
+ normalize: true
+ };
+ this.audionode = LGAudio.getAudioContext().createConvolver();
+ this.addInput("in","audio");
+ this.addOutput("out","audio");
+}
+
+LGAudio.createAudioNodeWrapper( LGAudioConvolver );
+
+LGAudioConvolver.prototype.onRemove = function()
+{
+ if(this._dropped_url)
+ URL.revokeObjectURL( this._dropped_url );
+}
+
+LGAudioConvolver.prototype.onPropertyChanged = function( name, value )
+{
+ if( name == "impulse_src" )
+ this.loadImpulse( value );
+ else if( name == "normalize" )
+ this.audionode.normalize = value;
+}
+
+LGAudioConvolver.prototype.onDropFile = function(file)
+{
+ if(this._dropped_url)
+ URL.revokeObjectURL( this._dropped_url );
+ this._dropped_url = URL.createObjectURL( file );
+ this.properties.impulse_src = this._dropped_url;
+ this.loadImpulse( this._dropped_url );
+}
+
+LGAudioConvolver.prototype.loadImpulse = function( url )
+{
+ var that = this;
+
+ //kill previous load
+ if(this._request)
+ {
+ this._request.abort();
+ this._request = null;
+ }
+
+ this._impulse_buffer = null;
+ this._loading_impulse = false;
+
+ if(!url)
+ return;
+
+ //load new sample
+ this._request = LGAudio.loadSound( url, inner );
+ this._loading_impulse = true;
+
+ // Decode asynchronously
+ function inner( buffer ) {
+ that._impulse_buffer = buffer;
+ that.audionode.buffer = buffer;
+ console.log("Impulse signal set");
+ that._loading_impulse = false;
+ }
+}
+
+LGAudioConvolver.title = "Convolver";
+LGAudioConvolver.desc = "Convolves the signal (used for reverb)";
+LiteGraph.registerNodeType("audio/convolver", LGAudioConvolver);
+
+
+function LGAudioDynamicsCompressor()
+{
+ //default
+ this.properties = {
+ threshold: -50,
+ knee: 40,
+ ratio: 12,
+ reduction: -20,
+ attack: 0,
+ release: 0.25
+ };
+
+ this.audionode = LGAudio.getAudioContext().createDynamicsCompressor();
+ this.addInput("in","audio");
+ this.addOutput("out","audio");
+}
+
+LGAudio.createAudioNodeWrapper( LGAudioDynamicsCompressor );
+
+LGAudioDynamicsCompressor.prototype.onExecute = function()
+{
+ if(!this.inputs || !this.inputs.length)
+ return;
+ for(var i = 1; i < this.inputs.length; ++i)
+ {
+ var input = this.inputs[i];
+ if(!input.link)
+ continue;
+ var v = this.getInputData(i);
+ if(v !== undefined)
+ this.audionode[ input.name ].value = v;
+ }
+}
+
+LGAudioDynamicsCompressor.prototype.onGetInputs = function()
+{
+ return [["threshold","number"],["knee","number"],["ratio","number"],["reduction","number"],["attack","number"],["release","number"]];
+}
+
+LGAudioDynamicsCompressor.title = "DynamicsCompressor";
+LGAudioDynamicsCompressor.desc = "Dynamics Compressor";
+LiteGraph.registerNodeType("audio/dynamicsCompressor", LGAudioDynamicsCompressor);
function LGAudioWaveShaper()
@@ -12528,7 +12802,7 @@ LGAudioWaveShaper.prototype.setWaveShape = function(shape)
this.audionode.curve = shape;
}
-createAudioNodeWrapper( LGAudioWaveShaper );
+LGAudio.createAudioNodeWrapper( LGAudioWaveShaper );
/* disabled till I dont find a way to do a wave shape
LGAudioWaveShaper.title = "WaveShaper";
@@ -12578,7 +12852,8 @@ LGAudioMixer.prototype.onExecute = function()
for(var i = 1; i < this.inputs.length; ++i)
{
var input = this.inputs[i];
- if(input.type == "audio")
+
+ if(!input.link || input.type == "audio")
continue;
var v = this.getInputData(i);
@@ -12592,7 +12867,7 @@ LGAudioMixer.prototype.onExecute = function()
}
}
-createAudioNodeWrapper( LGAudioMixer );
+LGAudio.createAudioNodeWrapper( LGAudioMixer );
LGAudioMixer.title = "Mixer";
LGAudioMixer.desc = "Audio mixer";
@@ -12613,7 +12888,7 @@ function LGAudioDelay()
this.addOutput("out","audio");
}
-createAudioNodeWrapper( LGAudioDelay );
+LGAudio.createAudioNodeWrapper( LGAudioDelay );
LGAudioDelay.prototype.onExecute = function()
{
@@ -12653,6 +12928,8 @@ LGAudioBiquadFilter.prototype.onExecute = function()
for(var i = 1; i < this.inputs.length; ++i)
{
var input = this.inputs[i];
+ if(!input.link)
+ continue;
var v = this.getInputData(i);
if(v !== undefined)
this.audionode[ input.name ].value = v;
@@ -12664,7 +12941,7 @@ LGAudioBiquadFilter.prototype.onGetInputs = function()
return [["frequency","number"],["detune","number"],["Q","number"]];
}
-createAudioNodeWrapper( LGAudioBiquadFilter );
+LGAudio.createAudioNodeWrapper( LGAudioBiquadFilter );
LGAudioBiquadFilter.title = "BiquadFilter";
LGAudioBiquadFilter.desc = "Audio filter";
@@ -12679,10 +12956,12 @@ LiteGraph.registerNodeType("audio/biquadfilter", LGAudioBiquadFilter);
function LGAudioVisualization()
{
this.properties = {
- continuous: true
+ continuous: true,
+ mark: -1
};
this.addInput("freqs","array");
+ this.addInput("mark","number");
this.size = [300,200];
this._last_buffer = null;
}
@@ -12690,6 +12969,10 @@ function LGAudioVisualization()
LGAudioVisualization.prototype.onExecute = function()
{
this._last_buffer = this.getInputData(0);
+ var v = this.getInputData(1);
+ if(v !== undefined)
+ this.properties.mark = v;
+ this.setDirtyCanvas(true,false);
}
LGAudioVisualization.prototype.onDrawForeground = function(ctx)
@@ -12699,6 +12982,7 @@ LGAudioVisualization.prototype.onDrawForeground = function(ctx)
var buffer = this._last_buffer;
+ //delta represents how many samples we advance per pixel
var delta = buffer.length / this.size[0];
var h = this.size[1];
@@ -12721,12 +13005,26 @@ LGAudioVisualization.prototype.onDrawForeground = function(ctx)
{
for(var i = 0; i < buffer.length; i+= delta)
{
- ctx.moveTo(x,h);
- ctx.lineTo(x,h - (buffer[i|0]/255) * h);
+ ctx.moveTo(x+0.5,h);
+ ctx.lineTo(x+0.5,h - (buffer[i|0]/255) * h);
x++;
}
}
ctx.stroke();
+
+ if(this.properties.mark >= 0)
+ {
+ var samplerate = LGAudio.getAudioContext().sampleRate;
+ var binfreq = samplerate / buffer.length;
+ var x = 2 * (this.properties.mark / binfreq) / delta;
+ if(x >= this.size[0])
+ x = this.size[0]-1;
+ ctx.strokeStyle = "red";
+ ctx.beginPath();
+ ctx.moveTo(x,h);
+ ctx.lineTo(x,0);
+ ctx.stroke();
+ }
}
LGAudioVisualization.title = "Visualization";
@@ -12734,6 +13032,59 @@ LGAudioVisualization.desc = "Audio Visualization";
LiteGraph.registerNodeType("audio/visualization", LGAudioVisualization);
+function LGAudioBandSignal()
+{
+ //default
+ this.properties = {
+ band: 440,
+ amplitude: 1
+ };
+
+ this.addInput("freqs","array");
+ this.addOutput("signal","number");
+}
+
+LGAudioBandSignal.prototype.onExecute = function()
+{
+ this._freqs = this.getInputData(0);
+ if( !this._freqs )
+ return;
+
+ var band = this.properties.band;
+ var v = this.getInputData(1);
+ if(v !== undefined)
+ band = v;
+
+ var samplerate = LGAudio.getAudioContext().sampleRate;
+ var binfreq = samplerate / this._freqs.length;
+ var index = 2 * (band / binfreq);
+ var v = 0;
+ if( index < 0 )
+ v = this._freqs[ 0 ];
+ if( index >= this._freqs.length )
+ v = this._freqs[ this._freqs.length - 1];
+ else
+ {
+ var pos = index|0;
+ var v0 = this._freqs[ pos ];
+ var v1 = this._freqs[ pos+1 ];
+ var f = index - pos;
+ v = v0 * (1-f) + v1 * f;
+ }
+
+ this.setOutputData( 0, (v/255) * this.properties.amplitude );
+}
+
+LGAudioBandSignal.prototype.onGetInputs = function()
+{
+ return [["band","number"]];
+}
+
+LGAudioBandSignal.title = "Signal";
+LGAudioBandSignal.desc = "extract the signal of some frequency";
+LiteGraph.registerNodeType("audio/signal", LGAudioBandSignal);
+
+
function LGAudioDestination()
{
diff --git a/demo/code.js b/demo/code.js
index 2ef7b1ce2..97ecb45e9 100755
--- a/demo/code.js
+++ b/demo/code.js
@@ -33,5 +33,6 @@ function addDemo( name, url )
//some examples
addDemo("Audio", "examples/audio.json");
addDemo("Audio Delay", "examples/audio_delay.json");
+addDemo("Audio Reverb", "examples/audio_reverb.json");
diff --git a/demo/demodata/impulse.wav b/demo/demodata/impulse.wav
new file mode 100644
index 000000000..3ce6a1b04
Binary files /dev/null and b/demo/demodata/impulse.wav differ
diff --git a/demo/examples/audio.json b/demo/examples/audio.json
index 8a6d853ca..3f577d12a 100644
--- a/demo/examples/audio.json
+++ b/demo/examples/audio.json
@@ -1 +1,689 @@
-{"iteration":55277,"last_node_id":11,"last_link_id":10,"links":{"0":{"id":0,"origin_id":0,"origin_slot":0,"target_id":2,"target_slot":0,"data":null},"1":{"id":1,"origin_id":2,"origin_slot":0,"target_id":1,"target_slot":0,"data":null},"2":{"id":2,"origin_id":2,"origin_slot":0,"target_id":3,"target_slot":0,"data":null},"3":{"id":3,"origin_id":3,"origin_slot":0,"target_id":4,"target_slot":0,"data":null},"4":{"id":4,"origin_id":5,"origin_slot":0,"target_id":2,"target_slot":1,"data":null},"5":{"id":5,"origin_id":6,"origin_slot":0,"target_id":0,"target_slot":0,"data":null},"6":{"id":6,"origin_id":7,"origin_slot":0,"target_id":0,"target_slot":1,"data":null},"7":{"id":7,"origin_id":8,"origin_slot":0,"target_id":0,"target_slot":2,"data":null},"8":{"id":8,"origin_id":9,"origin_slot":0,"target_id":0,"target_slot":3,"data":null},"9":{"id":9,"origin_id":9,"origin_slot":0,"target_id":10,"target_slot":0,"data":null}},"config":{},"nodes":[{"id":1,"title":"Destination","type":"audio/destination","pos":[673,171],"size":{"0":140,"1":20},"flags":{},"inputs":[{"name":"in","type":"audio","link":1}],"mode":0,"properties":{}},{"id":2,"title":"BiquadFilter","type":"audio/biquadfilter","pos":[437,251],"size":{"0":140,"1":34},"flags":{},"inputs":[{"name":"in","type":"audio","link":0},{"name":"frequency","type":"number","link":4}],"outputs":[{"name":"out","type":"audio","links":[1,2]}],"mode":0,"properties":{"frequency":350,"detune":0,"Q":1,"type":"lowpass"}},{"id":3,"title":"Analyser","type":"audio/analyser","pos":[672,306],"size":{"0":140,"1":20},"flags":{},"inputs":[{"name":"in","type":"audio","link":2}],"outputs":[{"name":"freqs","type":"array","links":[3]}],"mode":0,"properties":{"fftSize":2048,"minDecibels":-100,"maxDecibels":-10,"smoothingTimeConstant":0.5}},{"id":6,"title":"Knob","type":"widget/knob","pos":[120,183],"size":[54,74],"flags":{},"outputs":[{"name":"","type":"number","links":[5]}],"mode":0,"properties":{"min":0,"max":1,"value":0.5099999999999996,"wcolor":"#7AF","size":50},"boxcolor":"rgba(128,128,128,1.0)"},{"id":0,"title":"Source","type":"audio/source","pos":[251,196],"size":{"0":140,"1":62},"flags":{},"inputs":[{"name":"gain","type":"number","link":5},{"name":"Play","type":-1,"link":6},{"name":"Stop","type":-1,"link":7},{"name":"playbackRate","type":"number","link":8}],"outputs":[{"name":"out","type":"audio","links":[0]}],"mode":0,"properties":{"src":"demodata/audio.wav","gain":0.5,"loop":false,"autoplay":true,"playbackRate":1.0000000000000002}},{"id":8,"title":"Button","type":"widget/button","pos":[274,106],"size":[128,43],"flags":{},"outputs":[{"name":"clicked","type":-1,"links":[7]}],"mode":0,"properties":{"text":"Stop","font":"40px Arial","message":""}},{"id":5,"title":"Knob","type":"widget/knob","pos":[125,293],"size":[54,74],"flags":{},"outputs":[{"name":"","type":"number","links":[4]}],"mode":0,"properties":{"min":0,"max":20000,"value":14800.00000000001,"wcolor":"#7AF","size":50},"boxcolor":"rgba(189,189,189,1.0)"},{"id":10,"title":"Watch","type":"basic/watch","pos":[516,123],"size":{"0":140,"1":20},"flags":{},"inputs":[{"name":"value","type":0,"link":9,"label":"1.000"}],"outputs":[{"name":"value","type":0,"links":null,"label":""}],"mode":0,"properties":{"value":1.0000000000000002}},{"id":9,"title":"Knob","type":"widget/knob","pos":[429,119],"size":[64,84],"flags":{},"outputs":[{"name":"","type":"number","links":[8,9]}],"mode":0,"properties":{"min":0,"max":4,"value":1.0000000000000002,"wcolor":"#7AF","size":50},"boxcolor":"rgba(64,64,64,1.0)"},{"id":7,"title":"Button","type":"widget/button","pos":[114,103],"size":[128,43],"flags":{},"outputs":[{"name":"clicked","type":-1,"links":[6]}],"mode":0,"properties":{"text":"Play","font":"40px Arial","message":""}},{"id":4,"title":"Visualization","type":"audio/visualization","pos":[132,394],"size":[662,180],"flags":{},"inputs":[{"name":"freqs","type":"array","link":3}],"mode":0,"properties":{"continuous":true}}]}
\ No newline at end of file
+{
+ "iteration": 139049,
+ "last_node_id": 16,
+ "last_link_id": 16,
+ "links": {
+ "0": {
+ "id": 0,
+ "origin_id": 0,
+ "origin_slot": 0,
+ "target_id": 2,
+ "target_slot": 0,
+ "data": null
+ },
+ "1": {
+ "id": 1,
+ "origin_id": 2,
+ "origin_slot": 0,
+ "target_id": 1,
+ "target_slot": 0,
+ "data": null
+ },
+ "2": {
+ "id": 2,
+ "origin_id": 2,
+ "origin_slot": 0,
+ "target_id": 3,
+ "target_slot": 0,
+ "data": null
+ },
+ "3": {
+ "id": 3,
+ "origin_id": 3,
+ "origin_slot": 0,
+ "target_id": 4,
+ "target_slot": 0,
+ "data": null
+ },
+ "4": {
+ "id": 4,
+ "origin_id": 5,
+ "origin_slot": 0,
+ "target_id": 2,
+ "target_slot": 1,
+ "data": null
+ },
+ "5": {
+ "id": 5,
+ "origin_id": 6,
+ "origin_slot": 0,
+ "target_id": 0,
+ "target_slot": 0,
+ "data": null
+ },
+ "6": {
+ "id": 6,
+ "origin_id": 7,
+ "origin_slot": 0,
+ "target_id": 0,
+ "target_slot": 1,
+ "data": null
+ },
+ "7": {
+ "id": 7,
+ "origin_id": 8,
+ "origin_slot": 0,
+ "target_id": 0,
+ "target_slot": 2,
+ "data": null
+ },
+ "8": {
+ "id": 8,
+ "origin_id": 9,
+ "origin_slot": 0,
+ "target_id": 0,
+ "target_slot": 3,
+ "data": null
+ },
+ "9": {
+ "id": 9,
+ "origin_id": 9,
+ "origin_slot": 0,
+ "target_id": 10,
+ "target_slot": 0,
+ "data": null
+ },
+ "10": {
+ "id": 10,
+ "origin_id": 3,
+ "origin_slot": 0,
+ "target_id": 11,
+ "target_slot": 0,
+ "data": null
+ },
+ "11": {
+ "id": 11,
+ "origin_id": 12,
+ "origin_slot": 0,
+ "target_id": 11,
+ "target_slot": 1,
+ "data": null
+ },
+ "12": {
+ "id": 12,
+ "origin_id": 11,
+ "origin_slot": 0,
+ "target_id": 13,
+ "target_slot": 0,
+ "data": null
+ },
+ "13": {
+ "id": 13,
+ "origin_id": 12,
+ "origin_slot": 0,
+ "target_id": 4,
+ "target_slot": 1,
+ "data": null
+ },
+ "14": {
+ "id": 14,
+ "origin_id": 13,
+ "origin_slot": 0,
+ "target_id": 14,
+ "target_slot": 0,
+ "data": null
+ },
+ "15": {
+ "id": 15,
+ "origin_id": 12,
+ "origin_slot": 0,
+ "target_id": 15,
+ "target_slot": 0,
+ "data": null
+ }
+ },
+ "config": {},
+ "nodes": [
+ {
+ "id": 1,
+ "title": "Destination",
+ "type": "audio/destination",
+ "pos": [
+ 673,
+ 171
+ ],
+ "size": {
+ "0": 140,
+ "1": 20
+ },
+ "flags": {},
+ "inputs": [
+ {
+ "name": "in",
+ "type": "audio",
+ "link": 1
+ }
+ ],
+ "mode": 0,
+ "properties": {}
+ },
+ {
+ "id": 2,
+ "title": "BiquadFilter",
+ "type": "audio/biquadfilter",
+ "pos": [
+ 437,
+ 251
+ ],
+ "size": {
+ "0": 140,
+ "1": 34
+ },
+ "flags": {},
+ "inputs": [
+ {
+ "name": "in",
+ "type": "audio",
+ "link": 0
+ },
+ {
+ "name": "frequency",
+ "type": "number",
+ "link": 4
+ }
+ ],
+ "outputs": [
+ {
+ "name": "out",
+ "type": "audio",
+ "links": [
+ 1,
+ 2
+ ]
+ }
+ ],
+ "mode": 0,
+ "properties": {
+ "frequency": 350,
+ "detune": 0,
+ "Q": 1,
+ "type": "lowpass"
+ }
+ },
+ {
+ "id": 6,
+ "title": "Knob",
+ "type": "widget/knob",
+ "pos": [
+ 120,
+ 183
+ ],
+ "size": [
+ 54,
+ 74
+ ],
+ "flags": {},
+ "outputs": [
+ {
+ "name": "",
+ "type": "number",
+ "links": [
+ 5
+ ]
+ }
+ ],
+ "mode": 0,
+ "properties": {
+ "min": 0,
+ "max": 1,
+ "value": 0.5099999999999996,
+ "wcolor": "#7AF",
+ "size": 50
+ },
+ "boxcolor": "rgba(128,128,128,1.0)"
+ },
+ {
+ "id": 0,
+ "title": "Source",
+ "type": "audio/source",
+ "pos": [
+ 251,
+ 196
+ ],
+ "size": {
+ "0": 140,
+ "1": 62
+ },
+ "flags": {},
+ "inputs": [
+ {
+ "name": "gain",
+ "type": "number",
+ "link": 5
+ },
+ {
+ "name": "Play",
+ "type": -1,
+ "link": 6
+ },
+ {
+ "name": "Stop",
+ "type": -1,
+ "link": 7
+ },
+ {
+ "name": "playbackRate",
+ "type": "number",
+ "link": 8
+ }
+ ],
+ "outputs": [
+ {
+ "name": "out",
+ "type": "audio",
+ "links": [
+ 0
+ ]
+ }
+ ],
+ "mode": 0,
+ "properties": {
+ "src": "demodata/audio.wav",
+ "gain": 0.5,
+ "loop": true,
+ "autoplay": true,
+ "playbackRate": 0.24000000000000002
+ }
+ },
+ {
+ "id": 5,
+ "title": "Knob",
+ "type": "widget/knob",
+ "pos": [
+ 125,
+ 293
+ ],
+ "size": [
+ 54,
+ 74
+ ],
+ "flags": {},
+ "outputs": [
+ {
+ "name": "",
+ "type": "number",
+ "links": [
+ 4
+ ]
+ }
+ ],
+ "mode": 0,
+ "properties": {
+ "min": 0,
+ "max": 20000,
+ "value": 14800.00000000001,
+ "wcolor": "#7AF",
+ "size": 50
+ },
+ "boxcolor": "rgba(128,128,128,1.0)"
+ },
+ {
+ "id": 10,
+ "title": "Watch",
+ "type": "basic/watch",
+ "pos": [
+ 516,
+ 123
+ ],
+ "size": {
+ "0": 140,
+ "1": 20
+ },
+ "flags": {},
+ "inputs": [
+ {
+ "name": "value",
+ "type": 0,
+ "link": 9,
+ "label": "0.240"
+ }
+ ],
+ "outputs": [
+ {
+ "name": "value",
+ "type": 0,
+ "links": null,
+ "label": ""
+ }
+ ],
+ "mode": 0,
+ "properties": {
+ "value": 0.24000000000000002
+ }
+ },
+ {
+ "id": 8,
+ "title": "Button",
+ "type": "widget/button",
+ "pos": [
+ 274,
+ 106
+ ],
+ "size": [
+ 128,
+ 43
+ ],
+ "flags": {},
+ "outputs": [
+ {
+ "name": "clicked",
+ "type": -1,
+ "links": [
+ 7
+ ]
+ }
+ ],
+ "mode": 0,
+ "properties": {
+ "text": "Stop",
+ "font": "40px Arial",
+ "message": ""
+ }
+ },
+ {
+ "id": 9,
+ "title": "Knob",
+ "type": "widget/knob",
+ "pos": [
+ 429,
+ 119
+ ],
+ "size": [
+ 54,
+ 74
+ ],
+ "flags": {},
+ "outputs": [
+ {
+ "name": "",
+ "type": "number",
+ "links": [
+ 8,
+ 9
+ ]
+ }
+ ],
+ "mode": 0,
+ "properties": {
+ "min": 0,
+ "max": 4,
+ "value": 0.24000000000000002,
+ "wcolor": "#7AF",
+ "size": 50
+ },
+ "boxcolor": "rgba(128,128,128,1.0)"
+ },
+ {
+ "id": 7,
+ "title": "Button",
+ "type": "widget/button",
+ "pos": [
+ 114,
+ 103
+ ],
+ "size": [
+ 128,
+ 43
+ ],
+ "flags": {},
+ "outputs": [
+ {
+ "name": "clicked",
+ "type": -1,
+ "links": [
+ 6
+ ]
+ }
+ ],
+ "mode": 0,
+ "properties": {
+ "text": "Play",
+ "font": "40px Arial",
+ "message": ""
+ }
+ },
+ {
+ "id": 3,
+ "title": "Analyser",
+ "type": "audio/analyser",
+ "pos": [
+ 672,
+ 306
+ ],
+ "size": {
+ "0": 140,
+ "1": 20
+ },
+ "flags": {},
+ "inputs": [
+ {
+ "name": "in",
+ "type": "audio",
+ "link": 2
+ }
+ ],
+ "outputs": [
+ {
+ "name": "freqs",
+ "type": "array",
+ "links": [
+ 3,
+ 10
+ ]
+ }
+ ],
+ "mode": 0,
+ "properties": {
+ "fftSize": 2048,
+ "minDecibels": -100,
+ "maxDecibels": -10,
+ "smoothingTimeConstant": 0.5
+ }
+ },
+ {
+ "id": 11,
+ "title": "Signal",
+ "type": "audio/signal",
+ "pos": [
+ 241,
+ 391
+ ],
+ "size": {
+ "0": 140,
+ "1": 34
+ },
+ "flags": {},
+ "inputs": [
+ {
+ "name": "freqs",
+ "type": "array",
+ "link": 10
+ },
+ {
+ "name": "band",
+ "type": "number",
+ "link": 11
+ }
+ ],
+ "outputs": [
+ {
+ "name": "signal",
+ "type": "number",
+ "links": [
+ 12
+ ]
+ }
+ ],
+ "mode": 0,
+ "properties": {
+ "band": 440,
+ "amplitude": 1,
+ "samplerate": 44100
+ }
+ },
+ {
+ "id": 14,
+ "title": "Progress",
+ "type": "widget/progress",
+ "pos": [
+ 91,
+ 568
+ ],
+ "size": {
+ "0": 140,
+ "1": 20
+ },
+ "flags": {},
+ "inputs": [
+ {
+ "name": "",
+ "type": "number",
+ "link": 14
+ }
+ ],
+ "mode": 0,
+ "properties": {
+ "min": 0,
+ "max": 1,
+ "value": 0.3843137254901945,
+ "wcolor": "#AAF"
+ }
+ },
+ {
+ "id": 13,
+ "title": "Max. Signal",
+ "type": "basic/watch",
+ "pos": [
+ 92,
+ 527
+ ],
+ "size": {
+ "0": 140,
+ "1": 20
+ },
+ "flags": {},
+ "inputs": [
+ {
+ "name": "value",
+ "type": 0,
+ "link": 12,
+ "label": "0.396"
+ }
+ ],
+ "outputs": [
+ {
+ "name": "value",
+ "type": 0,
+ "links": [
+ 14
+ ],
+ "label": ""
+ }
+ ],
+ "mode": 0,
+ "properties": {
+ "value": 0.3843137254901945
+ }
+ },
+ {
+ "id": 4,
+ "title": "Visualization",
+ "type": "audio/visualization",
+ "pos": [
+ 253,
+ 497
+ ],
+ "size": [
+ 662,
+ 180
+ ],
+ "flags": {},
+ "inputs": [
+ {
+ "name": "freqs",
+ "type": "array",
+ "link": 3
+ },
+ {
+ "name": "mark",
+ "type": "number",
+ "link": 13
+ }
+ ],
+ "mode": 0,
+ "properties": {
+ "continuous": true,
+ "mark": 12000.000000000005,
+ "samplerate": 44100
+ }
+ },
+ {
+ "id": 15,
+ "title": "Watch",
+ "type": "basic/watch",
+ "pos": [
+ 93,
+ 398
+ ],
+ "size": {
+ "0": 87,
+ "1": 20
+ },
+ "flags": {},
+ "inputs": [
+ {
+ "name": "value",
+ "type": 0,
+ "link": 15,
+ "label": "12000.000"
+ }
+ ],
+ "outputs": [
+ {
+ "name": "value",
+ "type": 0,
+ "links": null,
+ "label": ""
+ }
+ ],
+ "mode": 0,
+ "properties": {
+ "value": 12000.000000000005
+ }
+ },
+ {
+ "id": 12,
+ "title": "Knob",
+ "type": "widget/knob",
+ "pos": [
+ 92,
+ 435
+ ],
+ "size": [
+ 54,
+ 74
+ ],
+ "flags": {},
+ "outputs": [
+ {
+ "name": "",
+ "type": "number",
+ "links": [
+ 11,
+ 13,
+ 15
+ ]
+ }
+ ],
+ "mode": 0,
+ "properties": {
+ "min": 0,
+ "max": 24000,
+ "value": 12000.000000000005,
+ "wcolor": "#7AF",
+ "size": 50
+ },
+ "boxcolor": "rgba(128,128,128,1.0)"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/demo/examples/audio_reverb.json b/demo/examples/audio_reverb.json
new file mode 100644
index 000000000..511a4d1bb
--- /dev/null
+++ b/demo/examples/audio_reverb.json
@@ -0,0 +1 @@
+{"iteration":48488,"last_node_id":8,"last_link_id":9,"links":{"0":{"id":0,"origin_id":0,"origin_slot":0,"target_id":1,"target_slot":0,"data":null},"1":{"id":1,"origin_id":0,"origin_slot":0,"target_id":2,"target_slot":0,"data":null},"3":{"id":3,"origin_id":1,"origin_slot":0,"target_id":3,"target_slot":0,"data":null},"4":{"id":4,"origin_id":4,"origin_slot":0,"target_id":1,"target_slot":1,"data":null},"5":{"id":5,"origin_id":5,"origin_slot":0,"target_id":1,"target_slot":3,"data":null},"6":{"id":6,"origin_id":6,"origin_slot":0,"target_id":2,"target_slot":1,"data":null},"7":{"id":7,"origin_id":0,"origin_slot":0,"target_id":7,"target_slot":0,"data":null},"8":{"id":8,"origin_id":7,"origin_slot":0,"target_id":1,"target_slot":2,"data":null}},"config":{},"nodes":[{"id":1,"title":"Mixer","type":"audio/mixer","pos":[655,183],"size":{"0":140,"1":62},"flags":{},"inputs":[{"name":"in1","type":"audio","link":0},{"name":"in1 gain","type":"number","link":4},{"name":"in2","type":"audio","link":8},{"name":"in2 gain","type":"number","link":5}],"outputs":[{"name":"out","type":"audio","links":[3]}],"mode":0,"properties":{"gain1":0.5,"gain2":0.8}},{"id":3,"title":"Destination","type":"audio/destination","pos":[911,180],"size":{"0":140,"1":20},"flags":{},"inputs":[{"name":"in","type":"audio","link":3}],"mode":0,"properties":{}},{"id":0,"title":"Source","type":"audio/source","pos":[195,187],"size":{"0":140,"1":20},"flags":{},"inputs":[{"name":"gain","type":"number","link":null}],"outputs":[{"name":"out","type":"audio","links":[0,7]}],"mode":0,"properties":{"src":"demodata/audio.wav","gain":0.5,"loop":true,"autoplay":true,"playbackRate":1}},{"id":7,"title":"Convolver","type":"audio/convolver","pos":[421,253],"size":{"0":140,"1":20},"flags":{},"inputs":[{"name":"in","type":"audio","link":7}],"outputs":[{"name":"out","type":"audio","links":[8]}],"mode":0,"properties":{"impulse_src":"demodata/impulse.wav","normalize":true}},{"id":5,"title":"Reverb","type":"widget/knob","pos":[398,311],"size":[84,100],"flags":{},"outputs":[{"name":"","type":"number","links":[5]}],"mode":0,"properties":{"min":0,"max":1,"value":0.4099999999999999,"wcolor":"#7AF","size":50},"boxcolor":"rgba(128,128,128,1.0)"},{"id":4,"title":"Main","type":"widget/knob","pos":[408,59],"size":[81,93],"flags":{},"outputs":[{"name":"","type":"number","links":[4]}],"mode":0,"properties":{"min":0,"max":1,"value":0.21000000000000002,"wcolor":"#7AF","size":50},"boxcolor":"rgba(128,128,128,1.0)"}]}
\ No newline at end of file
diff --git a/imgs/elephant.gif b/imgs/elephant.gif
new file mode 100644
index 000000000..3bd28df3c
Binary files /dev/null and b/imgs/elephant.gif differ
diff --git a/imgs/webglstudio.gif b/imgs/webglstudio.gif
new file mode 100644
index 000000000..7ba42947a
Binary files /dev/null and b/imgs/webglstudio.gif differ
diff --git a/src/litegraph-editor.js b/src/litegraph-editor.js
index 9d121d1c3..8df40aab6 100755
--- a/src/litegraph-editor.js
+++ b/src/litegraph-editor.js
@@ -22,8 +22,8 @@ function Editor( container_id, options )
graph.onAfterExecute = function() { graphcanvas.draw(true) };
//add stuff
- this.addToolsButton("loadsession_button","Load","imgs/icon-load.png", this.onLoadButton.bind(this), ".tools-left" );
- this.addToolsButton("savesession_button","Save","imgs/icon-save.png", this.onSaveButton.bind(this), ".tools-left" );
+ //this.addToolsButton("loadsession_button","Load","imgs/icon-load.png", this.onLoadButton.bind(this), ".tools-left" );
+ //this.addToolsButton("savesession_button","Save","imgs/icon-save.png", this.onSaveButton.bind(this), ".tools-left" );
this.addLoadCounter();
this.addToolsButton("playnode_button","Play","imgs/icon-play.png", this.onPlayButton.bind(this), ".tools-right" );
this.addToolsButton("playstepnode_button","Step","imgs/icon-playstep.png", this.onPlayStepButton.bind(this), ".tools-right" );
@@ -186,8 +186,14 @@ Editor.prototype.addMiniWindow = function(w,h)
var graphcanvas = new LGraphCanvas(canvas, this.graph);
graphcanvas.background_image = "imgs/grid.png";
graphcanvas.scale = 0.25;
+ graphcanvas.allow_dragnodes = false;
+ graphcanvas.allow_interaction = false;
this.miniwindow_graphcanvas = graphcanvas;
- graphcanvas.onClear = function() { graphcanvas.scale = 0.25; };
+ graphcanvas.onClear = function() {
+ graphcanvas.scale = 0.25;
+ graphcanvas.allow_dragnodes = false;
+ graphcanvas.allow_interaction = false;
+ };
miniwindow.style.position = "absolute";
miniwindow.style.top = "4px";
diff --git a/src/litegraph.js b/src/litegraph.js
index 173a31222..98326af92 100755
--- a/src/litegraph.js
+++ b/src/litegraph.js
@@ -2511,6 +2511,7 @@ function LGraphCanvas( canvas, graph, options )
this.show_info = true;
this.allow_dragcanvas = true;
this.allow_dragnodes = true;
+ this.allow_interaction = true; //allow to control widgets, buttons, collapse, etc
this.always_render_background = false;
this.render_connections_shadows = false; //too much cpu
@@ -2986,7 +2987,7 @@ LGraphCanvas.prototype.processMouseDown = function(e)
//when clicked on top of a node
//and it is not interactive
- if(n)
+ if(n && this.allow_interaction )
{
if(!this.live_mode && !n.flags.pinned)
this.bringToFront(n); //if it wasnt selected?
@@ -3149,7 +3150,7 @@ LGraphCanvas.prototype.processMouseMove = function(e)
this.dirty_canvas = true;
this.dirty_bgcanvas = true;
}
- else
+ else if(this.allow_interaction)
{
if(this.connecting_node)
this.dirty_canvas = true;
@@ -3513,7 +3514,7 @@ LGraphCanvas.prototype.processDrop = function(e)
return;
}
- if(node.onDropFile)
+ if( node.onDropFile || node.onDropData )
{
var files = e.dataTransfer.files;
if(files && files.length)
@@ -3525,22 +3526,28 @@ LGraphCanvas.prototype.processDrop = function(e)
var ext = LGraphCanvas.getFileExtension( filename );
//console.log(file);
- //prepare reader
- var reader = new FileReader();
- reader.onload = function (event) {
- //console.log(event.target);
- var data = event.target.result;
- node.onDropFile( data, filename, file );
- };
+ if(node.onDropFile)
+ node.onDropFile(file);
- //read data
- var type = file.type.split("/")[0];
- if(type == "text" || type == "")
- reader.readAsText(file);
- else if (type == "image")
- reader.readAsDataURL(file);
- else
- reader.readAsArrayBuffer(file);
+ if(node.onDropData)
+ {
+ //prepare reader
+ var reader = new FileReader();
+ reader.onload = function (event) {
+ //console.log(event.target);
+ var data = event.target.result;
+ node.onDropData( data, filename, file );
+ };
+
+ //read data
+ var type = file.type.split("/")[0];
+ if(type == "text" || type == "")
+ reader.readAsText(file);
+ else if (type == "image")
+ reader.readAsDataURL(file);
+ else
+ reader.readAsArrayBuffer(file);
+ }
}
}
}
@@ -3991,7 +3998,7 @@ LGraphCanvas.prototype.drawBackCanvas = function()
if(this.background_image && this.scale > 0.5)
{
ctx.globalAlpha = (1.0 - 0.5 / this.scale) * this.editor_alpha;
- ctx.webkitImageSmoothingEnabled = ctx.mozImageSmoothingEnabled = ctx.imageSmoothingEnabled = false;
+ ctx.imageSmoothingEnabled = ctx.mozImageSmoothingEnabled = ctx.imageSmoothingEnabled = false;
if(!this._bg_img || this._bg_img.name != this.background_image)
{
this._bg_img = new Image();
@@ -4020,7 +4027,7 @@ LGraphCanvas.prototype.drawBackCanvas = function()
}
ctx.globalAlpha = 1.0;
- ctx.webkitImageSmoothingEnabled = ctx.mozImageSmoothingEnabled = ctx.imageSmoothingEnabled = true;
+ ctx.imageSmoothingEnabled = ctx.mozImageSmoothingEnabled = ctx.imageSmoothingEnabled = true;
}
if(this.onBackgroundRender)
@@ -4861,12 +4868,14 @@ LGraphCanvas.onShowMenuNodeProperties = function(node,e, prev_menu)
for (var i in node.properties)
{
var value = node.properties[i] !== undefined ? node.properties[i] : " ";
+ //value could contain invalid html characters, clean that
+ value = LGraphCanvas.decodeHTML(value);
entries.push({content: "" + i + "" + "" + value + "", value: i});
}
if(!entries.length)
return;
- var menu = LiteGraph.createContextMenu(entries, {event: e, callback: inner_clicked, from: prev_menu},ref_window);
+ var menu = LiteGraph.createContextMenu(entries, {event: e, callback: inner_clicked, from: prev_menu, allow_html: true },ref_window);
function inner_clicked( v, e, prev )
{
@@ -4878,6 +4887,70 @@ LGraphCanvas.onShowMenuNodeProperties = function(node,e, prev_menu)
return false;
}
+LGraphCanvas.decodeHTML = function( str )
+{
+ var e = document.createElement("div");
+ e.innerText = str;
+ return e.innerHTML;
+}
+
+LGraphCanvas.onShowTitleEditor = function( node, event )
+{
+ var input_html = "";
+
+ var dialog = document.createElement("div");
+ dialog.className = "graphdialog";
+ dialog.innerHTML = "Title";
+ var input = dialog.querySelector("input");
+ if(input)
+ {
+ input.value = node.title;
+ input.addEventListener("keydown", function(e){
+ if(e.keyCode != 13)
+ return;
+ inner();
+ e.preventDefault();
+ e.stopPropagation();
+ });
+ }
+
+ var rect = this.canvas.getClientRects()[0];
+ var offsetx = -20;
+ var offsety = -20;
+ if(rect)
+ {
+ offsetx -= rect.left;
+ offsety -= rect.top;
+ }
+
+ if( event )
+ {
+ dialog.style.left = (event.pageX + offsetx) + "px";
+ dialog.style.top = (event.pageY + offsety)+ "px";
+ }
+ else
+ {
+ dialog.style.left = (this.canvas.width * 0.5 + offsetx) + "px";
+ dialog.style.top = (this.canvas.height * 0.5 + offsety) + "px";
+ }
+
+ var button = dialog.querySelector("button");
+ button.addEventListener("click", inner );
+ this.canvas.parentNode.appendChild( dialog );
+
+ function inner()
+ {
+ setValue( input.value );
+ }
+
+ function setValue(value)
+ {
+ node.title = value;
+ dialog.parentNode.removeChild( dialog );
+ node.setDirtyCanvas(true,true);
+ }
+}
+
LGraphCanvas.prototype.showEditPropertyValue = function( node, property, options )
{
if(!node || node.properties[ property ] === undefined )
@@ -5147,6 +5220,7 @@ LGraphCanvas.prototype.getNodeMenuOptions = function(node)
null,
{content:"Properties", is_menu: true, callback: LGraphCanvas.onShowMenuNodeProperties },
null,
+ {content:"Title", callback: LGraphCanvas.onShowTitleEditor },
{content:"Mode", is_menu: true, callback: LGraphCanvas.onMenuNodeMode },
{content:"Collapse", callback: LGraphCanvas.onMenuNodeCollapse },
{content:"Pin", callback: LGraphCanvas.onMenuNodePin },
@@ -5361,7 +5435,7 @@ function num2hex(triplet) {
/* LiteGraph GUI elements *************************************/
-LiteGraph.createContextMenu = function(values,options, ref_window)
+LiteGraph.createContextMenu = function(values, options, ref_window)
{
options = options || {};
this.options = options;
@@ -5434,10 +5508,16 @@ LiteGraph.createContextMenu = function(values,options, ref_window)
element.style.cursor = "pointer";
element.dataset["value"] = typeof(item) == "string" ? item : item.value;
element.data = item;
+
+ var content = "";
if(typeof(item) == "string")
- element.innerHTML = values.constructor == Array ? values[i] : i;
+ content = values.constructor == Array ? values[i] : i;
else
- element.innerHTML = item.content ? item.content : i;
+ content = item.content ? item.content : i;
+ if(options.allow_html)
+ element.innerHTML = content;
+ else
+ element.innerText = content;
element.addEventListener("click", on_click );
root.appendChild(element);
diff --git a/src/nodes/audio.js b/src/nodes/audio.js
index c7440a0cc..b4fb99307 100644
--- a/src/nodes/audio.js
+++ b/src/nodes/audio.js
@@ -151,13 +151,74 @@ LGAudio.onConnectionsChange = function( connection, slot, connected, link_info )
LGAudio.disconnect( local_audionode, target_audionode );
}
+//this function helps creating wrappers to existing classes
+LGAudio.createAudioNodeWrapper = function( class_object )
+{
+ class_object.prototype.onPropertyChanged = function(name, value)
+ {
+ if(!this.audionode)
+ return;
+
+ if( this.audionode[ name ] === undefined )
+ return;
+
+ if( this.audionode[ name ].value !== undefined )
+ this.audionode[ name ].value = value;
+ else
+ this.audionode[ name ] = value;
+ }
+
+ class_object.prototype.onConnectionsChange = LGAudio.onConnectionsChange;
+}
+
+
+LGAudio.cached_audios = {};
+
+LGAudio.loadSound = function( url, on_complete, on_error )
+{
+ if( LGAudio.cached_audios[ url ] && url.indexOf("blob:") == -1 )
+ {
+ if(on_complete)
+ on_complete( LGAudio.cached_audios[ url ] );
+ return;
+ }
+
+ //load new sample
+ var request = new XMLHttpRequest();
+ request.open('GET', url, true);
+ request.responseType = 'arraybuffer';
+
+ var context = LGAudio.getAudioContext();
+
+ // Decode asynchronously
+ request.onload = function() {
+ console.log("AudioSource loaded");
+ context.decodeAudioData( request.response, function( buffer ) {
+ console.log("AudioSource decoded");
+ LGAudio.cached_audios[ url ] = buffer;
+ if(on_complete)
+ on_complete( buffer );
+ }, onError);
+ }
+ request.send();
+
+ function onError(err)
+ {
+ console.log("Audio loading sample error:",err);
+ if(on_error)
+ on_error(err);
+ }
+
+ return request;
+}
+
//****************************************************
function LGAudioSource()
{
this.properties = {
- src: "demodata/audio.wav",
+ src: "",
gain: 0.5,
loop: true,
autoplay: true,
@@ -210,6 +271,8 @@ LGAudioSource.prototype.onStop = function()
LGAudioSource.prototype.onRemoved = function()
{
this.stopAllSounds();
+ if(this._dropped_url)
+ URL.revokeObjectURL( this._url );
}
LGAudioSource.prototype.stopAllSounds = function()
@@ -233,7 +296,6 @@ LGAudioSource.prototype.onExecute = function()
var input = this.inputs[i];
if(!input.link)
continue;
-
var v = this.getInputData(i);
if( v === undefined )
continue;
@@ -309,36 +371,19 @@ LGAudioSource.prototype.loadSound = function( url )
if(!url)
return;
- //load new sample
- var request = new XMLHttpRequest();
- request.open('GET', url, true);
- request.responseType = 'arraybuffer';
+ this._request = LGAudio.loadSound( url, inner );
+
this._loading_audio = true;
- this._request = request;
+ this.boxcolor = "#AA4";
- var context = LGAudio.getAudioContext();
-
- // Decode asynchronously
- request.onload = function() {
- context.decodeAudioData( request.response, function(buffer) {
- that._audio_buffer = buffer;
- if(that._url)
- {
- URL.revokeObjectURL( that._url );
- that._url = null;
- }
-
- that._loading_audio = false;
- //if is playing, then play it
- if(that.graph && that.graph.status === LGraph.STATUS_RUNNING)
- that.onStart();
- }, onError);
- }
- request.send();
-
- function onError(err)
+ function inner( buffer )
{
- console.log("Audio loading sample error:",err);
+ this.boxcolor = LiteGraph.NODE_DEFAULT_BOXCOLOR;
+ that._audio_buffer = buffer;
+ that._loading_audio = false;
+ //if is playing, then play it
+ if(that.graph && that.graph.status === LGraph.STATUS_RUNNING)
+ that.onStart(); //this controls the autoplay already
}
}
@@ -357,11 +402,12 @@ LGAudioSource.prototype.onGetOutputs = function()
LGAudioSource.prototype.onDropFile = function(file)
{
- if(this._url)
- URL.revokeObjectURL( this._url );
- this._url = URL.createObjectURL( file );
- this.properties.src = this._url;
- this.loadSound( this._url );
+ if(this._dropped_url)
+ URL.revokeObjectURL( this._dropped_url );
+ var url = URL.createObjectURL( file );
+ this.properties.src = url;
+ this.loadSound( url );
+ this._dropped_url = url;
}
@@ -415,15 +461,24 @@ LGAudioAnalyser.prototype.onExecute = function()
this.setOutputData(0,this._freq_bin);
}
+ //send analyzer
+ if(this.isOutputConnected(1))
+ this.setOutputData(1,this.audionode);
+
+
//properties
for(var i = 1; i < this.inputs.length; ++i)
{
var input = this.inputs[i];
+ if(!input.link)
+ continue;
var v = this.getInputData(i);
if (v !== undefined)
this.audionode[ input.name ].value = v;
}
+
+
//time domain
//this.audionode.getFloatTimeDomainData( dataArray );
}
@@ -433,33 +488,17 @@ LGAudioAnalyser.prototype.onGetInputs = function()
return [["minDecibels","number"],["maxDecibels","number"],["smoothingTimeConstant","number"]];
}
+/*
+LGAudioAnalyser.prototype.onGetOutputs = function()
+{
+ return [["Analyzer","analyzer"]];
+}
+*/
LGAudioAnalyser.title = "Analyser";
LGAudioAnalyser.desc = "Audio Analyser";
LiteGraph.registerNodeType( "audio/analyser", LGAudioAnalyser );
-
-
-//*****************************************************
-
-//this function helps creating wrappers to existing classes
-function createAudioNodeWrapper( class_object )
-{
- class_object.prototype.onPropertyChanged = function(name, value)
- {
- if( this.audionode[ name ] === undefined )
- return;
-
- if( this.audionode[ name ].value !== undefined )
- this.audionode[ name ].value = value;
- else
- this.audionode[ name ] = value;
- }
-
- class_object.prototype.onConnectionsChange = LGAudio.onConnectionsChange;
-}
-
-
//*****************************************************
function LGAudioGain()
@@ -489,14 +528,128 @@ LGAudioGain.prototype.onExecute = function()
}
}
-createAudioNodeWrapper( LGAudioGain );
+LGAudio.createAudioNodeWrapper( LGAudioGain );
LGAudioGain.title = "Gain";
LGAudioGain.desc = "Audio gain";
LiteGraph.registerNodeType("audio/gain", LGAudioGain);
+function LGAudioConvolver()
+{
+ //default
+ this.properties = {
+ impulse_src:"",
+ normalize: true
+ };
+ this.audionode = LGAudio.getAudioContext().createConvolver();
+ this.addInput("in","audio");
+ this.addOutput("out","audio");
+}
+
+LGAudio.createAudioNodeWrapper( LGAudioConvolver );
+
+LGAudioConvolver.prototype.onRemove = function()
+{
+ if(this._dropped_url)
+ URL.revokeObjectURL( this._dropped_url );
+}
+
+LGAudioConvolver.prototype.onPropertyChanged = function( name, value )
+{
+ if( name == "impulse_src" )
+ this.loadImpulse( value );
+ else if( name == "normalize" )
+ this.audionode.normalize = value;
+}
+
+LGAudioConvolver.prototype.onDropFile = function(file)
+{
+ if(this._dropped_url)
+ URL.revokeObjectURL( this._dropped_url );
+ this._dropped_url = URL.createObjectURL( file );
+ this.properties.impulse_src = this._dropped_url;
+ this.loadImpulse( this._dropped_url );
+}
+
+LGAudioConvolver.prototype.loadImpulse = function( url )
+{
+ var that = this;
+
+ //kill previous load
+ if(this._request)
+ {
+ this._request.abort();
+ this._request = null;
+ }
+
+ this._impulse_buffer = null;
+ this._loading_impulse = false;
+
+ if(!url)
+ return;
+
+ //load new sample
+ this._request = LGAudio.loadSound( url, inner );
+ this._loading_impulse = true;
+
+ // Decode asynchronously
+ function inner( buffer ) {
+ that._impulse_buffer = buffer;
+ that.audionode.buffer = buffer;
+ console.log("Impulse signal set");
+ that._loading_impulse = false;
+ }
+}
+
+LGAudioConvolver.title = "Convolver";
+LGAudioConvolver.desc = "Convolves the signal (used for reverb)";
+LiteGraph.registerNodeType("audio/convolver", LGAudioConvolver);
+
+
+function LGAudioDynamicsCompressor()
+{
+ //default
+ this.properties = {
+ threshold: -50,
+ knee: 40,
+ ratio: 12,
+ reduction: -20,
+ attack: 0,
+ release: 0.25
+ };
+
+ this.audionode = LGAudio.getAudioContext().createDynamicsCompressor();
+ this.addInput("in","audio");
+ this.addOutput("out","audio");
+}
+
+LGAudio.createAudioNodeWrapper( LGAudioDynamicsCompressor );
+
+LGAudioDynamicsCompressor.prototype.onExecute = function()
+{
+ if(!this.inputs || !this.inputs.length)
+ return;
+ for(var i = 1; i < this.inputs.length; ++i)
+ {
+ var input = this.inputs[i];
+ if(!input.link)
+ continue;
+ var v = this.getInputData(i);
+ if(v !== undefined)
+ this.audionode[ input.name ].value = v;
+ }
+}
+
+LGAudioDynamicsCompressor.prototype.onGetInputs = function()
+{
+ return [["threshold","number"],["knee","number"],["ratio","number"],["reduction","number"],["attack","number"],["release","number"]];
+}
+
+LGAudioDynamicsCompressor.title = "DynamicsCompressor";
+LGAudioDynamicsCompressor.desc = "Dynamics Compressor";
+LiteGraph.registerNodeType("audio/dynamicsCompressor", LGAudioDynamicsCompressor);
function LGAudioWaveShaper()
@@ -526,7 +679,7 @@ LGAudioWaveShaper.prototype.setWaveShape = function(shape)
this.audionode.curve = shape;
}
-createAudioNodeWrapper( LGAudioWaveShaper );
+LGAudio.createAudioNodeWrapper( LGAudioWaveShaper );
/* disabled till I dont find a way to do a wave shape
LGAudioWaveShaper.title = "WaveShaper";
@@ -576,7 +729,8 @@ LGAudioMixer.prototype.onExecute = function()
for(var i = 1; i < this.inputs.length; ++i)
{
var input = this.inputs[i];
- if(input.type == "audio")
+
+ if(!input.link || input.type == "audio")
continue;
var v = this.getInputData(i);
@@ -590,7 +744,7 @@ LGAudioMixer.prototype.onExecute = function()
}
}
-createAudioNodeWrapper( LGAudioMixer );
+LGAudio.createAudioNodeWrapper( LGAudioMixer );
LGAudioMixer.title = "Mixer";
LGAudioMixer.desc = "Audio mixer";
@@ -611,7 +765,7 @@ function LGAudioDelay()
this.addOutput("out","audio");
}
-createAudioNodeWrapper( LGAudioDelay );
+LGAudio.createAudioNodeWrapper( LGAudioDelay );
LGAudioDelay.prototype.onExecute = function()
{
@@ -651,6 +805,8 @@ LGAudioBiquadFilter.prototype.onExecute = function()
for(var i = 1; i < this.inputs.length; ++i)
{
var input = this.inputs[i];
+ if(!input.link)
+ continue;
var v = this.getInputData(i);
if(v !== undefined)
this.audionode[ input.name ].value = v;
@@ -662,7 +818,7 @@ LGAudioBiquadFilter.prototype.onGetInputs = function()
return [["frequency","number"],["detune","number"],["Q","number"]];
}
-createAudioNodeWrapper( LGAudioBiquadFilter );
+LGAudio.createAudioNodeWrapper( LGAudioBiquadFilter );
LGAudioBiquadFilter.title = "BiquadFilter";
LGAudioBiquadFilter.desc = "Audio filter";
@@ -677,10 +833,12 @@ LiteGraph.registerNodeType("audio/biquadfilter", LGAudioBiquadFilter);
function LGAudioVisualization()
{
this.properties = {
- continuous: true
+ continuous: true,
+ mark: -1
};
this.addInput("freqs","array");
+ this.addInput("mark","number");
this.size = [300,200];
this._last_buffer = null;
}
@@ -688,6 +846,10 @@ function LGAudioVisualization()
LGAudioVisualization.prototype.onExecute = function()
{
this._last_buffer = this.getInputData(0);
+ var v = this.getInputData(1);
+ if(v !== undefined)
+ this.properties.mark = v;
+ this.setDirtyCanvas(true,false);
}
LGAudioVisualization.prototype.onDrawForeground = function(ctx)
@@ -697,6 +859,7 @@ LGAudioVisualization.prototype.onDrawForeground = function(ctx)
var buffer = this._last_buffer;
+ //delta represents how many samples we advance per pixel
var delta = buffer.length / this.size[0];
var h = this.size[1];
@@ -719,12 +882,26 @@ LGAudioVisualization.prototype.onDrawForeground = function(ctx)
{
for(var i = 0; i < buffer.length; i+= delta)
{
- ctx.moveTo(x,h);
- ctx.lineTo(x,h - (buffer[i|0]/255) * h);
+ ctx.moveTo(x+0.5,h);
+ ctx.lineTo(x+0.5,h - (buffer[i|0]/255) * h);
x++;
}
}
ctx.stroke();
+
+ if(this.properties.mark >= 0)
+ {
+ var samplerate = LGAudio.getAudioContext().sampleRate;
+ var binfreq = samplerate / buffer.length;
+ var x = 2 * (this.properties.mark / binfreq) / delta;
+ if(x >= this.size[0])
+ x = this.size[0]-1;
+ ctx.strokeStyle = "red";
+ ctx.beginPath();
+ ctx.moveTo(x,h);
+ ctx.lineTo(x,0);
+ ctx.stroke();
+ }
}
LGAudioVisualization.title = "Visualization";
@@ -732,6 +909,59 @@ LGAudioVisualization.desc = "Audio Visualization";
LiteGraph.registerNodeType("audio/visualization", LGAudioVisualization);
+function LGAudioBandSignal()
+{
+ //default
+ this.properties = {
+ band: 440,
+ amplitude: 1
+ };
+
+ this.addInput("freqs","array");
+ this.addOutput("signal","number");
+}
+
+LGAudioBandSignal.prototype.onExecute = function()
+{
+ this._freqs = this.getInputData(0);
+ if( !this._freqs )
+ return;
+
+ var band = this.properties.band;
+ var v = this.getInputData(1);
+ if(v !== undefined)
+ band = v;
+
+ var samplerate = LGAudio.getAudioContext().sampleRate;
+ var binfreq = samplerate / this._freqs.length;
+ var index = 2 * (band / binfreq);
+ var v = 0;
+ if( index < 0 )
+ v = this._freqs[ 0 ];
+ if( index >= this._freqs.length )
+ v = this._freqs[ this._freqs.length - 1];
+ else
+ {
+ var pos = index|0;
+ var v0 = this._freqs[ pos ];
+ var v1 = this._freqs[ pos+1 ];
+ var f = index - pos;
+ v = v0 * (1-f) + v1 * f;
+ }
+
+ this.setOutputData( 0, (v/255) * this.properties.amplitude );
+}
+
+LGAudioBandSignal.prototype.onGetInputs = function()
+{
+ return [["band","number"]];
+}
+
+LGAudioBandSignal.title = "Signal";
+LGAudioBandSignal.desc = "extract the signal of some frequency";
+LiteGraph.registerNodeType("audio/signal", LGAudioBandSignal);
+
+
function LGAudioDestination()
{
diff --git a/src/nodes/math.js b/src/nodes/math.js
index 93b768d69..0ac8cdbda 100755
--- a/src/nodes/math.js
+++ b/src/nodes/math.js
@@ -216,6 +216,47 @@ MathClamp.prototype.getCode = function(lang)
LiteGraph.registerNodeType("math/clamp", MathClamp );
+
+//Math ABS
+function MathLerp()
+{
+ this.properties = { f: 0.5 };
+ this.addInput("A","number");
+ this.addInput("B","number");
+
+ this.addOutput("out","number");
+}
+
+MathLerp.title = "Lerp";
+MathLerp.desc = "Linear Interpolation";
+
+MathLerp.prototype.onExecute = function()
+{
+ var v1 = this.getInputData(0);
+ if(v1 == null)
+ v1 = 0;
+ var v2 = this.getInputData(1);
+ if(v2 == null)
+ v2 = 0;
+
+ var f = this.properties.f;
+
+ var _f = this.getInputData(2);
+ if(_f !== undefined)
+ f = _f;
+
+ this.setOutputData(0, v1 * (1-f) + v2 * f );
+}
+
+MathLerp.prototype.onGetInputs = function()
+{
+ return [["f","number"]];
+}
+
+LiteGraph.registerNodeType("math/lerp", MathLerp);
+
+
+
//Math ABS
function MathAbs()
{