Files
ComfyUI_frontend/src/nodes/audio.js
2020-04-03 17:45:05 +02:00

1457 lines
45 KiB
JavaScript

(function(global) {
var LiteGraph = global.LiteGraph;
var LGAudio = {};
global.LGAudio = LGAudio;
LGAudio.getAudioContext = function() {
if (!this._audio_context) {
window.AudioContext =
window.AudioContext || window.webkitAudioContext;
if (!window.AudioContext) {
console.error("AudioContext not supported by browser");
return null;
}
this._audio_context = new AudioContext();
this._audio_context.onmessage = function(msg) {
console.log("msg", msg);
};
this._audio_context.onended = function(msg) {
console.log("ended", msg);
};
this._audio_context.oncomplete = function(msg) {
console.log("complete", msg);
};
}
//in case it crashes
//if(this._audio_context.state == "suspended")
// this._audio_context.resume();
return this._audio_context;
};
LGAudio.connect = function(audionodeA, audionodeB) {
try {
audionodeA.connect(audionodeB);
} catch (err) {
console.warn("LGraphAudio:", err);
}
};
LGAudio.disconnect = function(audionodeA, audionodeB) {
try {
audionodeA.disconnect(audionodeB);
} catch (err) {
console.warn("LGraphAudio:", err);
}
};
LGAudio.changeAllAudiosConnections = function(node, connect) {
if (node.inputs) {
for (var i = 0; i < node.inputs.length; ++i) {
var input = node.inputs[i];
var link_info = node.graph.links[input.link];
if (!link_info) {
continue;
}
var origin_node = node.graph.getNodeById(link_info.origin_id);
var origin_audionode = null;
if (origin_node.getAudioNodeInOutputSlot) {
origin_audionode = origin_node.getAudioNodeInOutputSlot(
link_info.origin_slot
);
} else {
origin_audionode = origin_node.audionode;
}
var target_audionode = null;
if (node.getAudioNodeInInputSlot) {
target_audionode = node.getAudioNodeInInputSlot(i);
} else {
target_audionode = node.audionode;
}
if (connect) {
LGAudio.connect(origin_audionode, target_audionode);
} else {
LGAudio.disconnect(origin_audionode, target_audionode);
}
}
}
if (node.outputs) {
for (var i = 0; i < node.outputs.length; ++i) {
var output = node.outputs[i];
for (var j = 0; j < output.links.length; ++j) {
var link_info = node.graph.links[output.links[j]];
if (!link_info) {
continue;
}
var origin_audionode = null;
if (node.getAudioNodeInOutputSlot) {
origin_audionode = node.getAudioNodeInOutputSlot(i);
} else {
origin_audionode = node.audionode;
}
var target_node = node.graph.getNodeById(
link_info.target_id
);
var target_audionode = null;
if (target_node.getAudioNodeInInputSlot) {
target_audionode = target_node.getAudioNodeInInputSlot(
link_info.target_slot
);
} else {
target_audionode = target_node.audionode;
}
if (connect) {
LGAudio.connect(origin_audionode, target_audionode);
} else {
LGAudio.disconnect(origin_audionode, target_audionode);
}
}
}
}
};
//used by many nodes
LGAudio.onConnectionsChange = function(
connection,
slot,
connected,
link_info
) {
//only process the outputs events
if (connection != LiteGraph.OUTPUT) {
return;
}
var target_node = null;
if (link_info) {
target_node = this.graph.getNodeById(link_info.target_id);
}
if (!target_node) {
return;
}
//get origin audionode
var local_audionode = null;
if (this.getAudioNodeInOutputSlot) {
local_audionode = this.getAudioNodeInOutputSlot(slot);
} else {
local_audionode = this.audionode;
}
//get target audionode
var target_audionode = null;
if (target_node.getAudioNodeInInputSlot) {
target_audionode = target_node.getAudioNodeInInputSlot(
link_info.target_slot
);
} else {
target_audionode = target_node.audionode;
}
//do the connection/disconnection
if (connected) {
LGAudio.connect(local_audionode, target_audionode);
} else {
LGAudio.disconnect(local_audionode, target_audionode);
}
};
//this function helps creating wrappers to existing classes
LGAudio.createAudioNodeWrapper = function(class_object) {
var old_func = class_object.prototype.onPropertyChanged;
class_object.prototype.onPropertyChanged = function(name, value) {
if (old_func) {
old_func.call(this, 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;
};
//contains the samples decoded of the loaded audios in AudioBuffer format
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;
}
if (LGAudio.onProcessAudioURL) {
url = LGAudio.onProcessAudioURL(url);
}
//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: "",
gain: 0.5,
loop: true,
autoplay: true,
playbackRate: 1
};
this._loading_audio = false;
this._audiobuffer = null; //points to AudioBuffer with the audio samples decoded
this._audionodes = [];
this._last_sourcenode = null; //the last AudioBufferSourceNode (there could be more if there are several sounds playing)
this.addOutput("out", "audio");
this.addInput("gain", "number");
//init context
var context = LGAudio.getAudioContext();
//create gain node to control volume
this.audionode = context.createGain();
this.audionode.graphnode = this;
this.audionode.gain.value = this.properties.gain;
//debug
if (this.properties.src) {
this.loadSound(this.properties.src);
}
}
LGAudioSource.desc = "Plays an audio file";
LGAudioSource["@src"] = { widget: "resource" };
LGAudioSource.supported_extensions = ["wav", "ogg", "mp3"];
LGAudioSource.prototype.onAdded = function(graph) {
if (graph.status === LGraph.STATUS_RUNNING) {
this.onStart();
}
};
LGAudioSource.prototype.onStart = function() {
if (!this._audiobuffer) {
return;
}
if (this.properties.autoplay) {
this.playBuffer(this._audiobuffer);
}
};
LGAudioSource.prototype.onStop = function() {
this.stopAllSounds();
};
LGAudioSource.prototype.onPause = function() {
this.pauseAllSounds();
};
LGAudioSource.prototype.onUnpause = function() {
this.unpauseAllSounds();
//this.onStart();
};
LGAudioSource.prototype.onRemoved = function() {
this.stopAllSounds();
if (this._dropped_url) {
URL.revokeObjectURL(this._url);
}
};
LGAudioSource.prototype.stopAllSounds = function() {
//iterate and stop
for (var i = 0; i < this._audionodes.length; ++i) {
if (this._audionodes[i].started) {
this._audionodes[i].started = false;
this._audionodes[i].stop();
}
//this._audionodes[i].disconnect( this.audionode );
}
this._audionodes.length = 0;
};
LGAudioSource.prototype.pauseAllSounds = function() {
LGAudio.getAudioContext().suspend();
};
LGAudioSource.prototype.unpauseAllSounds = function() {
LGAudio.getAudioContext().resume();
};
LGAudioSource.prototype.onExecute = function() {
if (this.inputs) {
for (var i = 0; i < this.inputs.length; ++i) {
var input = this.inputs[i];
if (input.link == null) {
continue;
}
var v = this.getInputData(i);
if (v === undefined) {
continue;
}
if (input.name == "gain")
this.audionode.gain.value = v;
else if (input.name == "src") {
this.setProperty("src",v);
} else if (input.name == "playbackRate") {
this.properties.playbackRate = v;
for (var j = 0; j < this._audionodes.length; ++j) {
this._audionodes[j].playbackRate.value = v;
}
}
}
}
if (this.outputs) {
for (var i = 0; i < this.outputs.length; ++i) {
var output = this.outputs[i];
if (output.name == "buffer" && this._audiobuffer) {
this.setOutputData(i, this._audiobuffer);
}
}
}
};
LGAudioSource.prototype.onAction = function(event) {
if (this._audiobuffer) {
if (event == "Play") {
this.playBuffer(this._audiobuffer);
} else if (event == "Stop") {
this.stopAllSounds();
}
}
};
LGAudioSource.prototype.onPropertyChanged = function(name, value) {
if (name == "src") {
this.loadSound(value);
} else if (name == "gain") {
this.audionode.gain.value = value;
} else if (name == "playbackRate") {
for (var j = 0; j < this._audionodes.length; ++j) {
this._audionodes[j].playbackRate.value = value;
}
}
};
LGAudioSource.prototype.playBuffer = function(buffer) {
var that = this;
var context = LGAudio.getAudioContext();
//create a new audionode (this is mandatory, AudioAPI doesnt like to reuse old ones)
var audionode = context.createBufferSource(); //create a AudioBufferSourceNode
this._last_sourcenode = audionode;
audionode.graphnode = this;
audionode.buffer = buffer;
audionode.loop = this.properties.loop;
audionode.playbackRate.value = this.properties.playbackRate;
this._audionodes.push(audionode);
audionode.connect(this.audionode); //connect to gain
this._audionodes.push(audionode);
this.trigger("start");
audionode.onended = function() {
//console.log("ended!");
that.trigger("ended");
//remove
var index = that._audionodes.indexOf(audionode);
if (index != -1) {
that._audionodes.splice(index, 1);
}
};
if (!audionode.started) {
audionode.started = true;
audionode.start();
}
return audionode;
};
LGAudioSource.prototype.loadSound = function(url) {
var that = this;
//kill previous load
if (this._request) {
this._request.abort();
this._request = null;
}
this._audiobuffer = null; //points to the audiobuffer once the audio is loaded
this._loading_audio = false;
if (!url) {
return;
}
this._request = LGAudio.loadSound(url, inner);
this._loading_audio = true;
this.boxcolor = "#AA4";
function inner(buffer) {
this.boxcolor = LiteGraph.NODE_DEFAULT_BOXCOLOR;
that._audiobuffer = 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
}
};
//Helps connect/disconnect AudioNodes when new connections are made in the node
LGAudioSource.prototype.onConnectionsChange = LGAudio.onConnectionsChange;
LGAudioSource.prototype.onGetInputs = function() {
return [
["playbackRate", "number"],
["src","string"],
["Play", LiteGraph.ACTION],
["Stop", LiteGraph.ACTION]
];
};
LGAudioSource.prototype.onGetOutputs = function() {
return [["buffer", "audiobuffer"], ["start", LiteGraph.EVENT], ["ended", LiteGraph.EVENT]];
};
LGAudioSource.prototype.onDropFile = function(file) {
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;
};
LGAudioSource.title = "Source";
LGAudioSource.desc = "Plays audio";
LiteGraph.registerNodeType("audio/source", LGAudioSource);
//****************************************************
function LGAudioMediaSource() {
this.properties = {
gain: 0.5
};
this._audionodes = [];
this._media_stream = null;
this.addOutput("out", "audio");
this.addInput("gain", "number");
//create gain node to control volume
var context = LGAudio.getAudioContext();
this.audionode = context.createGain();
this.audionode.graphnode = this;
this.audionode.gain.value = this.properties.gain;
}
LGAudioMediaSource.prototype.onAdded = function(graph) {
if (graph.status === LGraph.STATUS_RUNNING) {
this.onStart();
}
};
LGAudioMediaSource.prototype.onStart = function() {
if (this._media_stream == null && !this._waiting_confirmation) {
this.openStream();
}
};
LGAudioMediaSource.prototype.onStop = function() {
this.audionode.gain.value = 0;
};
LGAudioMediaSource.prototype.onPause = function() {
this.audionode.gain.value = 0;
};
LGAudioMediaSource.prototype.onUnpause = function() {
this.audionode.gain.value = this.properties.gain;
};
LGAudioMediaSource.prototype.onRemoved = function() {
this.audionode.gain.value = 0;
if (this.audiosource_node) {
this.audiosource_node.disconnect(this.audionode);
this.audiosource_node = null;
}
if (this._media_stream) {
var tracks = this._media_stream.getTracks();
if (tracks.length) {
tracks[0].stop();
}
}
};
LGAudioMediaSource.prototype.openStream = function() {
if (!navigator.mediaDevices) {
console.log(
"getUserMedia() is not supported in your browser, use chrome and enable WebRTC from about://flags"
);
return;
}
this._waiting_confirmation = true;
// Not showing vendor prefixes.
navigator.mediaDevices
.getUserMedia({ audio: true, video: false })
.then(this.streamReady.bind(this))
.catch(onFailSoHard);
var that = this;
function onFailSoHard(err) {
console.log("Media rejected", err);
that._media_stream = false;
that.boxcolor = "red";
}
};
LGAudioMediaSource.prototype.streamReady = function(localMediaStream) {
this._media_stream = localMediaStream;
//this._waiting_confirmation = false;
//init context
if (this.audiosource_node) {
this.audiosource_node.disconnect(this.audionode);
}
var context = LGAudio.getAudioContext();
this.audiosource_node = context.createMediaStreamSource(
localMediaStream
);
this.audiosource_node.graphnode = this;
this.audiosource_node.connect(this.audionode);
this.boxcolor = "white";
};
LGAudioMediaSource.prototype.onExecute = function() {
if (this._media_stream == null && !this._waiting_confirmation) {
this.openStream();
}
if (this.inputs) {
for (var i = 0; i < this.inputs.length; ++i) {
var input = this.inputs[i];
if (input.link == null) {
continue;
}
var v = this.getInputData(i);
if (v === undefined) {
continue;
}
if (input.name == "gain") {
this.audionode.gain.value = this.properties.gain = v;
}
}
}
};
LGAudioMediaSource.prototype.onAction = function(event) {
if (event == "Play") {
this.audionode.gain.value = this.properties.gain;
} else if (event == "Stop") {
this.audionode.gain.value = 0;
}
};
LGAudioMediaSource.prototype.onPropertyChanged = function(name, value) {
if (name == "gain") {
this.audionode.gain.value = value;
}
};
//Helps connect/disconnect AudioNodes when new connections are made in the node
LGAudioMediaSource.prototype.onConnectionsChange =
LGAudio.onConnectionsChange;
LGAudioMediaSource.prototype.onGetInputs = function() {
return [
["playbackRate", "number"],
["Play", LiteGraph.ACTION],
["Stop", LiteGraph.ACTION]
];
};
LGAudioMediaSource.title = "MediaSource";
LGAudioMediaSource.desc = "Plays microphone";
LiteGraph.registerNodeType("audio/media_source", LGAudioMediaSource);
//*****************************************************
function LGAudioAnalyser() {
this.properties = {
fftSize: 2048,
minDecibels: -100,
maxDecibels: -10,
smoothingTimeConstant: 0.5
};
var context = LGAudio.getAudioContext();
this.audionode = context.createAnalyser();
this.audionode.graphnode = this;
this.audionode.fftSize = this.properties.fftSize;
this.audionode.minDecibels = this.properties.minDecibels;
this.audionode.maxDecibels = this.properties.maxDecibels;
this.audionode.smoothingTimeConstant = this.properties.smoothingTimeConstant;
this.addInput("in", "audio");
this.addOutput("freqs", "array");
this.addOutput("samples", "array");
this._freq_bin = null;
this._time_bin = null;
}
LGAudioAnalyser.prototype.onPropertyChanged = function(name, value) {
this.audionode[name] = value;
};
LGAudioAnalyser.prototype.onExecute = function() {
if (this.isOutputConnected(0)) {
//send FFT
var bufferLength = this.audionode.frequencyBinCount;
if (!this._freq_bin || this._freq_bin.length != bufferLength) {
this._freq_bin = new Uint8Array(bufferLength);
}
this.audionode.getByteFrequencyData(this._freq_bin);
this.setOutputData(0, this._freq_bin);
}
//send analyzer
if (this.isOutputConnected(1)) {
//send Samples
var bufferLength = this.audionode.frequencyBinCount;
if (!this._time_bin || this._time_bin.length != bufferLength) {
this._time_bin = new Uint8Array(bufferLength);
}
this.audionode.getByteTimeDomainData(this._time_bin);
this.setOutputData(1, this._time_bin);
}
//properties
for (var i = 1; i < this.inputs.length; ++i) {
var input = this.inputs[i];
if (input.link == null) {
continue;
}
var v = this.getInputData(i);
if (v !== undefined) {
this.audionode[input.name].value = v;
}
}
//time domain
//this.audionode.getFloatTimeDomainData( dataArray );
};
LGAudioAnalyser.prototype.onGetInputs = function() {
return [
["minDecibels", "number"],
["maxDecibels", "number"],
["smoothingTimeConstant", "number"]
];
};
LGAudioAnalyser.prototype.onGetOutputs = function() {
return [["freqs", "array"], ["samples", "array"]];
};
LGAudioAnalyser.title = "Analyser";
LGAudioAnalyser.desc = "Audio Analyser";
LiteGraph.registerNodeType("audio/analyser", LGAudioAnalyser);
//*****************************************************
function LGAudioGain() {
//default
this.properties = {
gain: 1
};
this.audionode = LGAudio.getAudioContext().createGain();
this.addInput("in", "audio");
this.addInput("gain", "number");
this.addOutput("out", "audio");
}
LGAudioGain.prototype.onExecute = function() {
if (!this.inputs || !this.inputs.length) {
return;
}
for (var i = 1; i < this.inputs.length; ++i) {
var input = this.inputs[i];
var v = this.getInputData(i);
if (v !== undefined) {
this.audionode[input.name].value = v;
}
}
};
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 == null) {
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() {
//default
this.properties = {};
this.audionode = LGAudio.getAudioContext().createWaveShaper();
this.addInput("in", "audio");
this.addInput("shape", "waveshape");
this.addOutput("out", "audio");
}
LGAudioWaveShaper.prototype.onExecute = function() {
if (!this.inputs || !this.inputs.length) {
return;
}
var v = this.getInputData(1);
if (v === undefined) {
return;
}
this.audionode.curve = v;
};
LGAudioWaveShaper.prototype.setWaveShape = function(shape) {
this.audionode.curve = shape;
};
LGAudio.createAudioNodeWrapper(LGAudioWaveShaper);
/* disabled till I dont find a way to do a wave shape
LGAudioWaveShaper.title = "WaveShaper";
LGAudioWaveShaper.desc = "Distortion using wave shape";
LiteGraph.registerNodeType("audio/waveShaper", LGAudioWaveShaper);
*/
function LGAudioMixer() {
//default
this.properties = {
gain1: 0.5,
gain2: 0.5
};
this.audionode = LGAudio.getAudioContext().createGain();
this.audionode1 = LGAudio.getAudioContext().createGain();
this.audionode1.gain.value = this.properties.gain1;
this.audionode2 = LGAudio.getAudioContext().createGain();
this.audionode2.gain.value = this.properties.gain2;
this.audionode1.connect(this.audionode);
this.audionode2.connect(this.audionode);
this.addInput("in1", "audio");
this.addInput("in1 gain", "number");
this.addInput("in2", "audio");
this.addInput("in2 gain", "number");
this.addOutput("out", "audio");
}
LGAudioMixer.prototype.getAudioNodeInInputSlot = function(slot) {
if (slot == 0) {
return this.audionode1;
} else if (slot == 2) {
return this.audionode2;
}
};
LGAudioMixer.prototype.onPropertyChanged = function(name, value) {
if (name == "gain1") {
this.audionode1.gain.value = value;
} else if (name == "gain2") {
this.audionode2.gain.value = value;
}
};
LGAudioMixer.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 == null || input.type == "audio") {
continue;
}
var v = this.getInputData(i);
if (v === undefined) {
continue;
}
if (i == 1) {
this.audionode1.gain.value = v;
} else if (i == 3) {
this.audionode2.gain.value = v;
}
}
};
LGAudio.createAudioNodeWrapper(LGAudioMixer);
LGAudioMixer.title = "Mixer";
LGAudioMixer.desc = "Audio mixer";
LiteGraph.registerNodeType("audio/mixer", LGAudioMixer);
function LGAudioADSR() {
//default
this.properties = {
A: 0.1,
D: 0.1,
S: 0.1,
R: 0.1
};
this.audionode = LGAudio.getAudioContext().createGain();
this.audionode.gain.value = 0;
this.addInput("in", "audio");
this.addInput("gate", "bool");
this.addOutput("out", "audio");
this.gate = false;
}
LGAudioADSR.prototype.onExecute = function() {
var audioContext = LGAudio.getAudioContext();
var now = audioContext.currentTime;
var node = this.audionode;
var gain = node.gain;
var current_gate = this.getInputData(1);
var A = this.getInputOrProperty("A");
var D = this.getInputOrProperty("D");
var S = this.getInputOrProperty("S");
var R = this.getInputOrProperty("R");
if (!this.gate && current_gate) {
gain.cancelScheduledValues(0);
gain.setValueAtTime(0, now);
gain.linearRampToValueAtTime(1, now + A);
gain.linearRampToValueAtTime(S, now + A + D);
} else if (this.gate && !current_gate) {
gain.cancelScheduledValues(0);
gain.setValueAtTime(gain.value, now);
gain.linearRampToValueAtTime(0, now + R);
}
this.gate = current_gate;
};
LGAudioADSR.prototype.onGetInputs = function() {
return [
["A", "number"],
["D", "number"],
["S", "number"],
["R", "number"]
];
};
LGAudio.createAudioNodeWrapper(LGAudioADSR);
LGAudioADSR.title = "ADSR";
LGAudioADSR.desc = "Audio envelope";
LiteGraph.registerNodeType("audio/adsr", LGAudioADSR);
function LGAudioDelay() {
//default
this.properties = {
delayTime: 0.5
};
this.audionode = LGAudio.getAudioContext().createDelay(10);
this.audionode.delayTime.value = this.properties.delayTime;
this.addInput("in", "audio");
this.addInput("time", "number");
this.addOutput("out", "audio");
}
LGAudio.createAudioNodeWrapper(LGAudioDelay);
LGAudioDelay.prototype.onExecute = function() {
var v = this.getInputData(1);
if (v !== undefined) {
this.audionode.delayTime.value = v;
}
};
LGAudioDelay.title = "Delay";
LGAudioDelay.desc = "Audio delay";
LiteGraph.registerNodeType("audio/delay", LGAudioDelay);
function LGAudioBiquadFilter() {
//default
this.properties = {
frequency: 350,
detune: 0,
Q: 1
};
this.addProperty("type", "lowpass", "enum", {
values: [
"lowpass",
"highpass",
"bandpass",
"lowshelf",
"highshelf",
"peaking",
"notch",
"allpass"
]
});
//create node
this.audionode = LGAudio.getAudioContext().createBiquadFilter();
//slots
this.addInput("in", "audio");
this.addOutput("out", "audio");
}
LGAudioBiquadFilter.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 == null) {
continue;
}
var v = this.getInputData(i);
if (v !== undefined) {
this.audionode[input.name].value = v;
}
}
};
LGAudioBiquadFilter.prototype.onGetInputs = function() {
return [["frequency", "number"], ["detune", "number"], ["Q", "number"]];
};
LGAudio.createAudioNodeWrapper(LGAudioBiquadFilter);
LGAudioBiquadFilter.title = "BiquadFilter";
LGAudioBiquadFilter.desc = "Audio filter";
LiteGraph.registerNodeType("audio/biquadfilter", LGAudioBiquadFilter);
function LGAudioOscillatorNode() {
//default
this.properties = {
frequency: 440,
detune: 0,
type: "sine"
};
this.addProperty("type", "sine", "enum", {
values: ["sine", "square", "sawtooth", "triangle", "custom"]
});
//create node
this.audionode = LGAudio.getAudioContext().createOscillator();
//slots
this.addOutput("out", "audio");
}
LGAudioOscillatorNode.prototype.onStart = function() {
if (!this.audionode.started) {
this.audionode.started = true;
try {
this.audionode.start();
} catch (err) {}
}
};
LGAudioOscillatorNode.prototype.onStop = function() {
if (this.audionode.started) {
this.audionode.started = false;
this.audionode.stop();
}
};
LGAudioOscillatorNode.prototype.onPause = function() {
this.onStop();
};
LGAudioOscillatorNode.prototype.onUnpause = function() {
this.onStart();
};
LGAudioOscillatorNode.prototype.onExecute = function() {
if (!this.inputs || !this.inputs.length) {
return;
}
for (var i = 0; i < this.inputs.length; ++i) {
var input = this.inputs[i];
if (input.link == null) {
continue;
}
var v = this.getInputData(i);
if (v !== undefined) {
this.audionode[input.name].value = v;
}
}
};
LGAudioOscillatorNode.prototype.onGetInputs = function() {
return [
["frequency", "number"],
["detune", "number"],
["type", "string"]
];
};
LGAudio.createAudioNodeWrapper(LGAudioOscillatorNode);
LGAudioOscillatorNode.title = "Oscillator";
LGAudioOscillatorNode.desc = "Oscillator";
LiteGraph.registerNodeType("audio/oscillator", LGAudioOscillatorNode);
//*****************************************************
//EXTRA
function LGAudioVisualization() {
this.properties = {
continuous: true,
mark: -1
};
this.addInput("data", "array");
this.addInput("mark", "number");
this.size = [300, 200];
this._last_buffer = null;
}
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) {
if (!this._last_buffer) {
return;
}
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];
ctx.fillStyle = "black";
ctx.fillRect(0, 0, this.size[0], this.size[1]);
ctx.strokeStyle = "white";
ctx.beginPath();
var x = 0;
if (this.properties.continuous) {
ctx.moveTo(x, h);
for (var i = 0; i < buffer.length; i += delta) {
ctx.lineTo(x, h - (buffer[i | 0] / 255) * h);
x++;
}
} else {
for (var i = 0; i < buffer.length; i += delta) {
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";
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 LGAudioScript() {
if (!LGAudioScript.default_code) {
var code = LGAudioScript.default_function.toString();
var index = code.indexOf("{") + 1;
var index2 = code.lastIndexOf("}");
LGAudioScript.default_code = code.substr(index, index2 - index);
}
//default
this.properties = {
code: LGAudioScript.default_code
};
//create node
var ctx = LGAudio.getAudioContext();
if (ctx.createScriptProcessor) {
this.audionode = ctx.createScriptProcessor(4096, 1, 1);
}
//buffer size, input channels, output channels
else {
console.warn("ScriptProcessorNode deprecated");
this.audionode = ctx.createGain(); //bypass audio
}
this.processCode();
if (!LGAudioScript._bypass_function) {
LGAudioScript._bypass_function = this.audionode.onaudioprocess;
}
//slots
this.addInput("in", "audio");
this.addOutput("out", "audio");
}
LGAudioScript.prototype.onAdded = function(graph) {
if (graph.status == LGraph.STATUS_RUNNING) {
this.audionode.onaudioprocess = this._callback;
}
};
LGAudioScript["@code"] = { widget: "code", type: "code" };
LGAudioScript.prototype.onStart = function() {
this.audionode.onaudioprocess = this._callback;
};
LGAudioScript.prototype.onStop = function() {
this.audionode.onaudioprocess = LGAudioScript._bypass_function;
};
LGAudioScript.prototype.onPause = function() {
this.audionode.onaudioprocess = LGAudioScript._bypass_function;
};
LGAudioScript.prototype.onUnpause = function() {
this.audionode.onaudioprocess = this._callback;
};
LGAudioScript.prototype.onExecute = function() {
//nothing! because we need an onExecute to receive onStart... fix that
};
LGAudioScript.prototype.onRemoved = function() {
this.audionode.onaudioprocess = LGAudioScript._bypass_function;
};
LGAudioScript.prototype.processCode = function() {
try {
var func = new Function("properties", this.properties.code);
this._script = new func(this.properties);
this._old_code = this.properties.code;
this._callback = this._script.onaudioprocess;
} catch (err) {
console.error("Error in onaudioprocess code", err);
this._callback = LGAudioScript._bypass_function;
this.audionode.onaudioprocess = this._callback;
}
};
LGAudioScript.prototype.onPropertyChanged = function(name, value) {
if (name == "code") {
this.properties.code = value;
this.processCode();
if (this.graph && this.graph.status == LGraph.STATUS_RUNNING) {
this.audionode.onaudioprocess = this._callback;
}
}
};
LGAudioScript.default_function = function() {
this.onaudioprocess = function(audioProcessingEvent) {
// The input buffer is the song we loaded earlier
var inputBuffer = audioProcessingEvent.inputBuffer;
// The output buffer contains the samples that will be modified and played
var outputBuffer = audioProcessingEvent.outputBuffer;
// Loop through the output channels (in this case there is only one)
for (
var channel = 0;
channel < outputBuffer.numberOfChannels;
channel++
) {
var inputData = inputBuffer.getChannelData(channel);
var outputData = outputBuffer.getChannelData(channel);
// Loop through the 4096 samples
for (var sample = 0; sample < inputBuffer.length; sample++) {
// make output equal to the same as the input
outputData[sample] = inputData[sample];
}
}
};
};
LGAudio.createAudioNodeWrapper(LGAudioScript);
LGAudioScript.title = "Script";
LGAudioScript.desc = "apply script to signal";
LiteGraph.registerNodeType("audio/script", LGAudioScript);
function LGAudioDestination() {
this.audionode = LGAudio.getAudioContext().destination;
this.addInput("in", "audio");
}
LGAudioDestination.title = "Destination";
LGAudioDestination.desc = "Audio output";
LiteGraph.registerNodeType("audio/destination", LGAudioDestination);
})(this);