mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-04-29 02:32:18 +00:00
extended MIDI support. Some new general purpose nodes like sequencer, string, cast to number.
This commit is contained in:
@@ -5,6 +5,10 @@ window.graphcanvas = editor.graphcanvas;
|
||||
window.graph = editor.graph;
|
||||
window.addEventListener("resize", function() { editor.graphcanvas.resize(); } );
|
||||
window.addEventListener("keydown", editor.graphcanvas.processKey.bind(editor.graphcanvas) );
|
||||
window.onbeforeunload = function(){
|
||||
var data = JSON.stringify( graph.serialize() );
|
||||
localStorage.setItem("litegraphg demo backup", data );
|
||||
}
|
||||
|
||||
//create scene selector
|
||||
var elem = document.createElement("span");
|
||||
@@ -18,6 +22,8 @@ select.addEventListener("change", function(e){
|
||||
|
||||
if(url)
|
||||
graph.load( url );
|
||||
else if(option.callback)
|
||||
option.callback();
|
||||
else
|
||||
graph.clear();
|
||||
});
|
||||
@@ -37,7 +43,10 @@ elem.querySelector("#load").addEventListener("click",function(){
|
||||
function addDemo( name, url )
|
||||
{
|
||||
var option = document.createElement("option");
|
||||
option.dataset["url"] = url;
|
||||
if(url.constructor === String)
|
||||
option.dataset["url"] = url;
|
||||
else
|
||||
option.callback = url;
|
||||
option.innerHTML = name;
|
||||
select.appendChild( option );
|
||||
}
|
||||
@@ -48,6 +57,14 @@ addDemo("Benchmark", "examples/benchmark.json");
|
||||
addDemo("Audio", "examples/audio.json");
|
||||
addDemo("Audio Delay", "examples/audio_delay.json");
|
||||
addDemo("Audio Reverb", "examples/audio_reverb.json");
|
||||
addDemo("MIDI Generation", "examples/midi_generation.json");
|
||||
addDemo("autobackup", function(){
|
||||
var data = localStorage.getItem("litegraphg demo backup");
|
||||
if(!data)
|
||||
return;
|
||||
var graph_data = JSON.parse(data);
|
||||
graph.configure( graph_data );
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -191,7 +191,7 @@ function TestSlotsNode()
|
||||
this.addInput("C","number");
|
||||
this.addOutput("A","number");
|
||||
this.addOutput("B","number");
|
||||
this.flags = { horizontal: true };
|
||||
this.horizontal = true;
|
||||
this.size = [100,40];
|
||||
}
|
||||
|
||||
|
||||
356
demo/js/libs/audiosynth.js
Normal file
356
demo/js/libs/audiosynth.js
Normal file
@@ -0,0 +1,356 @@
|
||||
var Synth, AudioSynth, AudioSynthInstrument;
|
||||
!function(){
|
||||
|
||||
var URL = window.URL || window.webkitURL;
|
||||
var Blob = window.Blob;
|
||||
|
||||
if(!URL || !Blob) {
|
||||
throw new Error('This browser does not support AudioSynth');
|
||||
}
|
||||
|
||||
var _encapsulated = false;
|
||||
var AudioSynthInstance = null;
|
||||
var pack = function(c,arg){ return [new Uint8Array([arg, arg >> 8]), new Uint8Array([arg, arg >> 8, arg >> 16, arg >> 24])][c]; };
|
||||
var setPrivateVar = function(n,v,w,e){Object.defineProperty(this,n,{value:v,writable:!!w,enumerable:!!e});};
|
||||
var setPublicVar = function(n,v,w){setPrivateVar.call(this,n,v,w,true);};
|
||||
AudioSynthInstrument = function AudioSynthInstrument(){this.__init__.apply(this,arguments);};
|
||||
var setPriv = setPrivateVar.bind(AudioSynthInstrument.prototype);
|
||||
var setPub = setPublicVar.bind(AudioSynthInstrument.prototype);
|
||||
setPriv('__init__', function(a,b,c) {
|
||||
if(!_encapsulated) { throw new Error('AudioSynthInstrument can only be instantiated from the createInstrument method of the AudioSynth object.'); }
|
||||
setPrivateVar.call(this, '_parent', a);
|
||||
setPublicVar.call(this, 'name', b);
|
||||
setPrivateVar.call(this, '_soundID', c);
|
||||
});
|
||||
setPub('play', function(note, octave, duration,volume) {
|
||||
return this._parent.play(this._soundID, note, octave, duration, volume);
|
||||
});
|
||||
setPub('generate', function(note, octave, duration) {
|
||||
return this._parent.generate(this._soundID, note, octave, duration);
|
||||
});
|
||||
AudioSynth = function AudioSynth(){if(AudioSynthInstance instanceof AudioSynth){return AudioSynthInstance;}else{ this.__init__(); return this; }};
|
||||
setPriv = setPrivateVar.bind(AudioSynth.prototype);
|
||||
setPub = setPublicVar.bind(AudioSynth.prototype);
|
||||
setPriv('_debug',false,true);
|
||||
setPriv('_bitsPerSample',16);
|
||||
setPriv('_channels',1);
|
||||
setPriv('_sampleRate',44100,true);
|
||||
setPub('setSampleRate', function(v) {
|
||||
this._sampleRate = Math.max(Math.min(v|0,44100), 4000);
|
||||
this._clearCache();
|
||||
return this._sampleRate;
|
||||
});
|
||||
setPub('getSampleRate', function() { return this._sampleRate; });
|
||||
setPriv('_volume',32768,true);
|
||||
setPub('setVolume', function(v) {
|
||||
v = parseFloat(v); if(isNaN(v)) { v = 0; }
|
||||
v = Math.round(v*32768);
|
||||
this._volume = Math.max(Math.min(v|0,32768), 0);
|
||||
this._clearCache();
|
||||
return this._volume;
|
||||
});
|
||||
setPub('getVolume', function() { return Math.round(this._volume/32768*10000)/10000; });
|
||||
setPriv('_notes',{'C':261.63,'C#':277.18,'D':293.66,'D#':311.13,'E':329.63,'F':346.23,'F#':369.99,'G':392.00,'G#':415.30,'A':440.00,'A#':466.16,'B':493.88});
|
||||
setPriv('_fileCache',[],true);
|
||||
setPriv('_temp',{},true);
|
||||
setPriv('_sounds',[],true);
|
||||
setPriv('_mod',[function(i,s,f,x){return Math.sin((2 * Math.PI)*(i/s)*f+x);}]);
|
||||
setPriv('_resizeCache', function() {
|
||||
var f = this._fileCache;
|
||||
var l = this._sounds.length;
|
||||
while(f.length<l) {
|
||||
var octaveList = [];
|
||||
for(var i = 0; i < 8; i++) {
|
||||
var noteList = {};
|
||||
for(var k in this._notes) {
|
||||
noteList[k] = {};
|
||||
}
|
||||
octaveList.push(noteList);
|
||||
}
|
||||
f.push(octaveList);
|
||||
}
|
||||
});
|
||||
setPriv('_clearCache', function() {
|
||||
this._fileCache = [];
|
||||
this._resizeCache();
|
||||
});
|
||||
setPub('generate', function(sound, note, octave, duration) {
|
||||
var thisSound = this._sounds[sound];
|
||||
if(!thisSound) {
|
||||
for(var i=0;i<this._sounds.length;i++) {
|
||||
if(this._sounds[i].name==sound) {
|
||||
thisSound = this._sounds[i];
|
||||
sound = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(!thisSound) { throw new Error('Invalid sound or sound ID: ' + sound); }
|
||||
var t = (new Date).valueOf();
|
||||
this._temp = {};
|
||||
octave |= 0;
|
||||
octave = Math.min(8, Math.max(1, octave));
|
||||
var time = !duration?2:parseFloat(duration);
|
||||
if(typeof(this._notes[note])=='undefined') { throw new Error(note + ' is not a valid note.'); }
|
||||
if(typeof(this._fileCache[sound][octave-1][note][time])!='undefined') {
|
||||
if(this._debug) { console.log((new Date).valueOf() - t, 'ms to retrieve (cached)'); }
|
||||
return this._fileCache[sound][octave-1][note][time];
|
||||
} else {
|
||||
var frequency = this._notes[note] * Math.pow(2,octave-4);
|
||||
var sampleRate = this._sampleRate;
|
||||
var volume = this._volume;
|
||||
var channels = this._channels;
|
||||
var bitsPerSample = this._bitsPerSample;
|
||||
var attack = thisSound.attack(sampleRate, frequency, volume);
|
||||
var dampen = thisSound.dampen(sampleRate, frequency, volume);
|
||||
var waveFunc = thisSound.wave;
|
||||
var waveBind = {modulate: this._mod, vars: this._temp};
|
||||
var val = 0;
|
||||
var curVol = 0;
|
||||
|
||||
var data = new Uint8Array(new ArrayBuffer(Math.ceil(sampleRate * time * 2)));
|
||||
var attackLen = (sampleRate * attack) | 0;
|
||||
var decayLen = (sampleRate * time) | 0;
|
||||
|
||||
for (var i = 0 | 0; i !== attackLen; i++) {
|
||||
|
||||
val = volume * (i/(sampleRate*attack)) * waveFunc.call(waveBind, i, sampleRate, frequency, volume);
|
||||
|
||||
data[i << 1] = val;
|
||||
data[(i << 1) + 1] = val >> 8;
|
||||
|
||||
}
|
||||
|
||||
for (; i !== decayLen; i++) {
|
||||
|
||||
val = volume * Math.pow((1-((i-(sampleRate*attack))/(sampleRate*(time-attack)))),dampen) * waveFunc.call(waveBind, i, sampleRate, frequency, volume);
|
||||
|
||||
data[i << 1] = val;
|
||||
data[(i << 1) + 1] = val >> 8;
|
||||
|
||||
}
|
||||
|
||||
var out = [
|
||||
'RIFF',
|
||||
pack(1, 4 + (8 + 24/* chunk 1 length */) + (8 + 8/* chunk 2 length */)), // Length
|
||||
'WAVE',
|
||||
// chunk 1
|
||||
'fmt ', // Sub-chunk identifier
|
||||
pack(1, 16), // Chunk length
|
||||
pack(0, 1), // Audio format (1 is linear quantization)
|
||||
pack(0, channels),
|
||||
pack(1, sampleRate),
|
||||
pack(1, sampleRate * channels * bitsPerSample / 8), // Byte rate
|
||||
pack(0, channels * bitsPerSample / 8),
|
||||
pack(0, bitsPerSample),
|
||||
// chunk 2
|
||||
'data', // Sub-chunk identifier
|
||||
pack(1, data.length * channels * bitsPerSample / 8), // Chunk length
|
||||
data
|
||||
];
|
||||
var blob = new Blob(out, {type: 'audio/wav'});
|
||||
var dataURI = URL.createObjectURL(blob);
|
||||
this._fileCache[sound][octave-1][note][time] = dataURI;
|
||||
if(this._debug) { console.log((new Date).valueOf() - t, 'ms to generate'); }
|
||||
return dataURI;
|
||||
}
|
||||
});
|
||||
setPub('play', function(sound, note, octave, duration, volume) {
|
||||
var src = this.generate( sound, note, octave, duration );
|
||||
var audio = new Audio(src);
|
||||
if(volume != null)
|
||||
{
|
||||
if(volume <= 0)
|
||||
return true;
|
||||
audio.volume = volume > 1 ? 1 : volume;
|
||||
}
|
||||
audio.play();
|
||||
return true;
|
||||
});
|
||||
setPub('debug', function() { this._debug = true; });
|
||||
setPub('createInstrument', function(sound) {
|
||||
var n = 0;
|
||||
var found = false;
|
||||
if(typeof(sound)=='string') {
|
||||
for(var i=0;i<this._sounds.length;i++) {
|
||||
if(this._sounds[i].name==sound) {
|
||||
found = true;
|
||||
n = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if(this._sounds[sound]) {
|
||||
n = sound;
|
||||
sound = this._sounds[n].name;
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
if(!found) { throw new Error('Invalid sound or sound ID: ' + sound); }
|
||||
_encapsulated = true;
|
||||
var ins = new AudioSynthInstrument(this, sound, n);
|
||||
_encapsulated = false;
|
||||
return ins;
|
||||
});
|
||||
setPub('listSounds', function() {
|
||||
var r = [];
|
||||
for(var i=0;i<this._sounds.length;i++) {
|
||||
r.push(this._sounds[i].name);
|
||||
}
|
||||
return r;
|
||||
});
|
||||
setPriv('__init__', function(){
|
||||
this._resizeCache();
|
||||
});
|
||||
setPub('loadSoundProfile', function() {
|
||||
for(var i=0,len=arguments.length;i<len;i++) {
|
||||
o = arguments[i];
|
||||
if(!(o instanceof Object)) { throw new Error('Invalid sound profile.'); }
|
||||
this._sounds.push(o);
|
||||
}
|
||||
this._resizeCache();
|
||||
return true;
|
||||
});
|
||||
setPub('loadModulationFunction', function() {
|
||||
for(var i=0,len=arguments.length;i<len;i++) {
|
||||
f = arguments[i];
|
||||
if(typeof(f)!='function') { throw new Error('Invalid modulation function.'); }
|
||||
this._mod.push(f);
|
||||
}
|
||||
return true;
|
||||
});
|
||||
AudioSynthInstance = new AudioSynth();
|
||||
Synth = AudioSynthInstance;
|
||||
}();
|
||||
|
||||
Synth.loadModulationFunction(
|
||||
function(i, sampleRate, frequency, x) { return 1 * Math.sin(2 * Math.PI * ((i / sampleRate) * frequency) + x); },
|
||||
function(i, sampleRate, frequency, x) { return 1 * Math.sin(4 * Math.PI * ((i / sampleRate) * frequency) + x); },
|
||||
function(i, sampleRate, frequency, x) { return 1 * Math.sin(8 * Math.PI * ((i / sampleRate) * frequency) + x); },
|
||||
function(i, sampleRate, frequency, x) { return 1 * Math.sin(0.5 * Math.PI * ((i / sampleRate) * frequency) + x); },
|
||||
function(i, sampleRate, frequency, x) { return 1 * Math.sin(0.25 * Math.PI * ((i / sampleRate) * frequency) + x); },
|
||||
function(i, sampleRate, frequency, x) { return 0.5 * Math.sin(2 * Math.PI * ((i / sampleRate) * frequency) + x); },
|
||||
function(i, sampleRate, frequency, x) { return 0.5 * Math.sin(4 * Math.PI * ((i / sampleRate) * frequency) + x); },
|
||||
function(i, sampleRate, frequency, x) { return 0.5 * Math.sin(8 * Math.PI * ((i / sampleRate) * frequency) + x); },
|
||||
function(i, sampleRate, frequency, x) { return 0.5 * Math.sin(0.5 * Math.PI * ((i / sampleRate) * frequency) + x); },
|
||||
function(i, sampleRate, frequency, x) { return 0.5 * Math.sin(0.25 * Math.PI * ((i / sampleRate) * frequency) + x); }
|
||||
);
|
||||
|
||||
Synth.loadSoundProfile({
|
||||
name: 'piano',
|
||||
attack: function() { return 0.002; },
|
||||
dampen: function(sampleRate, frequency, volume) {
|
||||
return Math.pow(0.5*Math.log((frequency*volume)/sampleRate),2);
|
||||
},
|
||||
wave: function(i, sampleRate, frequency, volume) {
|
||||
var base = this.modulate[0];
|
||||
return this.modulate[1](
|
||||
i,
|
||||
sampleRate,
|
||||
frequency,
|
||||
Math.pow(base(i, sampleRate, frequency, 0), 2) +
|
||||
(0.75 * base(i, sampleRate, frequency, 0.25)) +
|
||||
(0.1 * base(i, sampleRate, frequency, 0.5))
|
||||
);
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'organ',
|
||||
attack: function() { return 0.3 },
|
||||
dampen: function(sampleRate, frequency) { return 1+(frequency * 0.01); },
|
||||
wave: function(i, sampleRate, frequency) {
|
||||
var base = this.modulate[0];
|
||||
return this.modulate[1](
|
||||
i,
|
||||
sampleRate,
|
||||
frequency,
|
||||
base(i, sampleRate, frequency, 0) +
|
||||
0.5*base(i, sampleRate, frequency, 0.25) +
|
||||
0.25*base(i, sampleRate, frequency, 0.5)
|
||||
);
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'acoustic',
|
||||
attack: function() { return 0.002; },
|
||||
dampen: function() { return 1; },
|
||||
wave: function(i, sampleRate, frequency) {
|
||||
|
||||
var vars = this.vars;
|
||||
vars.valueTable = !vars.valueTable?[]:vars.valueTable;
|
||||
if(typeof(vars.playVal)=='undefined') { vars.playVal = 0; }
|
||||
if(typeof(vars.periodCount)=='undefined') { vars.periodCount = 0; }
|
||||
|
||||
var valueTable = vars.valueTable;
|
||||
var playVal = vars.playVal;
|
||||
var periodCount = vars.periodCount;
|
||||
|
||||
var period = sampleRate/frequency;
|
||||
var p_hundredth = Math.floor((period-Math.floor(period))*100);
|
||||
|
||||
var resetPlay = false;
|
||||
|
||||
if(valueTable.length<=Math.ceil(period)) {
|
||||
|
||||
valueTable.push(Math.round(Math.random())*2-1);
|
||||
|
||||
return valueTable[valueTable.length-1];
|
||||
|
||||
} else {
|
||||
|
||||
valueTable[playVal] = (valueTable[playVal>=(valueTable.length-1)?0:playVal+1] + valueTable[playVal]) * 0.5;
|
||||
|
||||
if(playVal>=Math.floor(period)) {
|
||||
if(playVal<Math.ceil(period)) {
|
||||
if((periodCount%100)>=p_hundredth) {
|
||||
// Reset
|
||||
resetPlay = true;
|
||||
valueTable[playVal+1] = (valueTable[0] + valueTable[playVal+1]) * 0.5;
|
||||
vars.periodCount++;
|
||||
}
|
||||
} else {
|
||||
resetPlay = true;
|
||||
}
|
||||
}
|
||||
|
||||
var _return = valueTable[playVal];
|
||||
if(resetPlay) { vars.playVal = 0; } else { vars.playVal++; }
|
||||
|
||||
return _return;
|
||||
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'edm',
|
||||
attack: function() { return 0.002; },
|
||||
dampen: function() { return 1; },
|
||||
wave: function(i, sampleRate, frequency) {
|
||||
var base = this.modulate[0];
|
||||
var mod = this.modulate.slice(1);
|
||||
return mod[0](
|
||||
i,
|
||||
sampleRate,
|
||||
frequency,
|
||||
mod[9](
|
||||
i,
|
||||
sampleRate,
|
||||
frequency,
|
||||
mod[2](
|
||||
i,
|
||||
sampleRate,
|
||||
frequency,
|
||||
Math.pow(base(i, sampleRate, frequency, 0), 3) +
|
||||
Math.pow(base(i, sampleRate, frequency, 0.5), 5) +
|
||||
Math.pow(base(i, sampleRate, frequency, 1), 7)
|
||||
)
|
||||
) +
|
||||
mod[8](
|
||||
i,
|
||||
sampleRate,
|
||||
frequency,
|
||||
base(i, sampleRate, frequency, 1.75)
|
||||
)
|
||||
);
|
||||
}
|
||||
});
|
||||
Reference in New Issue
Block a user