diff --git a/nodes.py b/nodes.py index 5e4d7e4..45f20f6 100644 --- a/nodes.py +++ b/nodes.py @@ -2667,42 +2667,31 @@ class SoundReactive: @classmethod def INPUT_TYPES(s): return {"required": { - "average_level": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 99999, "step": 0.01}), - "low_level": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 99999, "step": 0.01}), - "mid_level": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 99999, "step": 0.01}), - "high_level": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 99999, "step": 0.01}), - "low_range_hz": ("INT", {"default": 150, "min": 0, "max": 9999, "step": 1}), - "mid_range_hz": ("INT", {"default": 2000, "min": 0, "max": 9999, "step": 1}), + "sound_level": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 99999, "step": 0.01}), + "start_range_hz": ("INT", {"default": 150, "min": 0, "max": 9999, "step": 1}), + "end_range_hz": ("INT", {"default": 2000, "min": 0, "max": 9999, "step": 1}), "multiplier": ("FLOAT", {"default": 1.0, "min": 0.01, "max": 99999, "step": 0.01}), + "smoothing_factor": ("FLOAT", {"default": 0.5, "min": 0.0, "max": 1.0, "step": 0.01}), "normalize": ("BOOLEAN", {"default": False}), }, } - RETURN_TYPES = ("FLOAT","FLOAT","FLOAT","FLOAT","INT","INT","INT","INT") - RETURN_NAMES =("average_level", "low_level", "mid_level", "high_level", "average_level_int", "low_level_int", "mid_level_int", "high_level_int") + RETURN_TYPES = ("FLOAT","INT",) + RETURN_NAMES =("sound_level", "sound_level_int",) FUNCTION = "react" CATEGORY = "KJNodes/experimental" - def react(self, low_level, mid_level, high_level, low_range_hz, mid_range_hz, average_level, multiplier, normalize): - low_level *= multiplier - mid_level *= multiplier - high_level *= multiplier - average_level = average_level * multiplier + def react(self, sound_level, start_range_hz, end_range_hz, smoothing_factor, multiplier, normalize): + + sound_level *= multiplier if normalize: - low_level = low_level / 255 - mid_level = mid_level / 255 - high_level = high_level / 255 - average_level = average_level / 255 + sound_level /= 255 - low_level_int = int(low_level) - mid_level_int = int(mid_level) - high_level_int = int(high_level) - average_level_int = int(average_level) + sound_level_int = int(sound_level) - - return (average_level, low_level, mid_level, high_level, average_level_int, low_level_int, mid_level_int, high_level_int) + return (sound_level, sound_level_int, ) NODE_CLASS_MAPPINGS = { "INTConstant": INTConstant, diff --git a/web/js/jsnodes.js b/web/js/jsnodes.js index e90f83e..d8dbabf 100644 --- a/web/js/jsnodes.js +++ b/web/js/jsnodes.js @@ -36,8 +36,10 @@ app.registerExtension({ let animationFrameId; let analyser; let dataArray; - let lowRangeHz; - let midRangeHz; + let startRangeHz; + let endRangeHz; + let smoothingFactor = 0.5; + let smoothedSoundLevel = 0; // Function to update the widget value in real-time const updateWidgetValueInRealTime = () => { @@ -45,47 +47,35 @@ app.registerExtension({ if (analyser && dataArray) { analyser.getByteFrequencyData(dataArray); + const startRangeHzWidget = this.widgets.find(w => w.name === "start_range_hz"); + if (startRangeHzWidget) startRangeHz = startRangeHzWidget.value; + const endRangeHzWidget = this.widgets.find(w => w.name === "end_range_hz"); + if (endRangeHzWidget) endRangeHz = endRangeHzWidget.value; + const smoothingFactorWidget = this.widgets.find(w => w.name === "smoothing_factor"); + if (smoothingFactorWidget) smoothingFactor = smoothingFactorWidget.value; + // Calculate frequency bin width (frequency resolution) const frequencyBinWidth = audioContext.sampleRate / analyser.fftSize; // Convert the widget values from Hz to indices - const lowRangeIndex = Math.floor(lowRangeHz / frequencyBinWidth); - const midRangeIndex = Math.floor(midRangeHz / frequencyBinWidth); - - // Define frequency ranges for low, mid, and high - const frequencyRanges = { - low: { start: 0, end: lowRangeIndex }, - mid: { start: lowRangeIndex, end: midRangeIndex }, - high: { start: midRangeIndex, end: dataArray.length } - }; - const lowRangeHzWidget = this.widgets.find(w => w.name === "low_range_hz"); - if (lowRangeHzWidget) lowRangeHz = lowRangeHzWidget.value; - - const midRangeHzWidget = this.widgets.find(w => w.name === "mid_range_hz"); - if (midRangeHzWidget) midRangeHz = midRangeHzWidget.value; + const startRangeIndex = Math.floor(startRangeHz / frequencyBinWidth); + const endRangeIndex = Math.floor(endRangeHz / frequencyBinWidth); // Function to calculate the average value for a frequency range const calculateAverage = (start, end) => { const sum = dataArray.slice(start, end).reduce((acc, val) => acc + val, 0); - return sum / (end - start); + const average = sum / (end - start); + + // Apply exponential moving average smoothing + smoothedSoundLevel = (average * (1 - smoothingFactor)) + (smoothedSoundLevel * smoothingFactor); + return smoothedSoundLevel; }; // Calculate the average levels for each frequency range - const lowLevel = calculateAverage(frequencyRanges.low.start, frequencyRanges.low.end); - const midLevel = calculateAverage(frequencyRanges.low.end, frequencyRanges.mid.end); // mid starts where low ends - const highLevel = calculateAverage(frequencyRanges.mid.end, frequencyRanges.high.end); // high starts where mid ends - const averageLevel = dataArray.reduce((sum, averageLevel) => sum + averageLevel, 0) / dataArray.length; + const soundLevel = calculateAverage(startRangeIndex, endRangeIndex); // Update the widget values - const averageLevelWidget = this.widgets.find(w => w.name === "average_level"); - if (averageLevelWidget) averageLevelWidget.value = averageLevel; - const lowLevelWidget = this.widgets.find(w => w.name === "low_level"); - if (lowLevelWidget) lowLevelWidget.value = lowLevel; - - const midLevelWidget = this.widgets.find(w => w.name === "mid_level"); - if (midLevelWidget) midLevelWidget.value = midLevel; - - const highLevelWidget = this.widgets.find(w => w.name === "high_level"); - if (highLevelWidget) highLevelWidget.value = highLevel; + const lowLevelWidget = this.widgets.find(w => w.name === "sound_level"); + if (lowLevelWidget) lowLevelWidget.value = soundLevel; animationFrameId = requestAnimationFrame(updateWidgetValueInRealTime); } @@ -103,10 +93,10 @@ app.registerExtension({ dataArray = new Uint8Array(analyser.frequencyBinCount); // Get the range values from widgets (assumed to be in Hz) const lowRangeWidget = this.widgets.find(w => w.name === "low_range_hz"); - if (lowRangeWidget) lowRangeHz = lowRangeWidget.value; + if (lowRangeWidget) startRangeHz = lowRangeWidget.value; const midRangeWidget = this.widgets.find(w => w.name === "mid_range_hz"); - if (midRangeWidget) midRangeHz = midRangeWidget.value; + if (midRangeWidget) endRangeHz = midRangeWidget.value; } navigator.mediaDevices.getUserMedia({ audio: true }).then(stream => {