From d850b04f929456c7c7a06949d334b09106a4b0bb Mon Sep 17 00:00:00 2001 From: SrGooglo Date: Fri, 24 Feb 2023 14:24:25 +0000 Subject: [PATCH] update assets --- .../app/public/assets/default_bg/dots.svg | 9 + .../app/public/assets/default_bg/hideout.svg | 7 + .../app/public/assets/default_bg/meteors.svg | 139 ++++++++ .../public/assets/default_bg/topography.svg | 1 + packages/app/public/realtime-bpm-processor.js | 324 ++++++++++++++++++ 5 files changed, 480 insertions(+) create mode 100644 packages/app/public/assets/default_bg/dots.svg create mode 100644 packages/app/public/assets/default_bg/hideout.svg create mode 100644 packages/app/public/assets/default_bg/meteors.svg create mode 100644 packages/app/public/assets/default_bg/topography.svg create mode 100644 packages/app/public/realtime-bpm-processor.js diff --git a/packages/app/public/assets/default_bg/dots.svg b/packages/app/public/assets/default_bg/dots.svg new file mode 100644 index 00000000..0becad4e --- /dev/null +++ b/packages/app/public/assets/default_bg/dots.svg @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/packages/app/public/assets/default_bg/hideout.svg b/packages/app/public/assets/default_bg/hideout.svg new file mode 100644 index 00000000..7b5eae54 --- /dev/null +++ b/packages/app/public/assets/default_bg/hideout.svg @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/packages/app/public/assets/default_bg/meteors.svg b/packages/app/public/assets/default_bg/meteors.svg new file mode 100644 index 00000000..527a5c39 --- /dev/null +++ b/packages/app/public/assets/default_bg/meteors.svg @@ -0,0 +1,139 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/packages/app/public/assets/default_bg/topography.svg b/packages/app/public/assets/default_bg/topography.svg new file mode 100644 index 00000000..c76c13d7 --- /dev/null +++ b/packages/app/public/assets/default_bg/topography.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/app/public/realtime-bpm-processor.js b/packages/app/public/realtime-bpm-processor.js new file mode 100644 index 00000000..f4c246a8 --- /dev/null +++ b/packages/app/public/realtime-bpm-processor.js @@ -0,0 +1,324 @@ +"use strict"; +(() => { + var __async = (__this, __arguments, generator) => { + return new Promise((resolve, reject) => { + var fulfilled = (value) => { + try { + step(generator.next(value)); + } catch (e) { + reject(e); + } + }; + var rejected = (value) => { + try { + step(generator.throw(value)); + } catch (e) { + reject(e); + } + }; + var step = (x) => x.done ? resolve(x.value) : Promise.resolve(x.value).then(fulfilled, rejected); + step((generator = generator.apply(__this, __arguments)).next()); + }); + }; + + // src/consts.ts + var realtimeBpmProcessorName = "realtime-bpm-processor"; + var startThreshold = 0.95; + var minValidThreshold = 0.3; + var minPeaks = 15; + var thresholdStep = 0.05; + var skipForwardIndexes = 1e4; + + // src/utils.ts + function descendingOverThresholds(_0) { + return __async(this, arguments, function* (onThreshold, minValidThreshold2 = minValidThreshold, startThreshold2 = startThreshold, thresholdStep2 = thresholdStep) { + let threshold = startThreshold2; + do { + threshold -= thresholdStep2; + const shouldExit = yield onThreshold(threshold); + if (shouldExit) { + break; + } + } while (threshold > minValidThreshold2); + }); + } + function generateValidPeaksModel(minValidThreshold2 = minValidThreshold, startThreshold2 = startThreshold, thresholdStep2 = thresholdStep) { + const object = {}; + let threshold = startThreshold2; + do { + threshold -= thresholdStep2; + object[threshold.toString()] = []; + } while (threshold > minValidThreshold2); + return object; + } + function generateNextIndexPeaksModel(minValidThreshold2 = minValidThreshold, startThreshold2 = startThreshold, thresholdStep2 = thresholdStep) { + const object = {}; + let threshold = startThreshold2; + do { + threshold -= thresholdStep2; + object[threshold.toString()] = 0; + } while (threshold > minValidThreshold2); + return object; + } + function chunckAggregator() { + const bufferSize = 4096; + let _bytesWritten = 0; + let buffer = new Float32Array(0); + function initBuffer() { + _bytesWritten = 0; + buffer = new Float32Array(0); + } + function isBufferFull() { + return _bytesWritten === bufferSize; + } + function flush() { + initBuffer(); + } + return function (pcmData) { + if (isBufferFull()) { + flush(); + } + const newBuffer = new Float32Array(buffer.length + pcmData.length); + newBuffer.set(buffer, 0); + newBuffer.set(pcmData, buffer.length); + buffer = newBuffer; + _bytesWritten += pcmData.length; + return { + isBufferFull: isBufferFull(), + buffer, + bufferSize + }; + }; + } + + // src/analyzer.ts + function findPeaksAtThreshold(data, threshold, offset = 0, skipForwardIndexes2 = skipForwardIndexes) { + const peaks = []; + const { length } = data; + for (let i = offset; i < length; i += 1) { + if (data[i] > threshold) { + peaks.push(i); + i += skipForwardIndexes2; + } + } + return { + peaks, + threshold + }; + } + function computeBpm(_0, _1) { + return __async(this, arguments, function* (data, audioSampleRate, minPeaks2 = minPeaks) { + let hasPeaks = false; + let foundThreshold = minValidThreshold; + yield descendingOverThresholds((threshold) => __async(this, null, function* () { + if (hasPeaks) { + return true; + } + if (data[threshold].length > minPeaks2) { + hasPeaks = true; + foundThreshold = threshold; + } + return false; + })); + if (hasPeaks && foundThreshold) { + const intervals = identifyIntervals(data[foundThreshold]); + const tempos = groupByTempo(audioSampleRate, intervals); + const candidates = getTopCandidates(tempos); + const bpmCandidates = { + bpm: candidates, + threshold: foundThreshold + }; + return bpmCandidates; + } + return { + bpm: [], + threshold: foundThreshold + }; + }); + } + function getTopCandidates(candidates, length = 5) { + return candidates.sort((a, b) => b.count - a.count).splice(0, length); + } + function identifyIntervals(peaks) { + const intervals = []; + for (let n = 0; n < peaks.length; n++) { + for (let i = 0; i < 10; i++) { + const peak = peaks[n]; + const peakIndex = n + i; + const interval = peaks[peakIndex] - peak; + const foundInterval = intervals.some((intervalCount) => { + if (intervalCount.interval === interval) { + intervalCount.count += 1; + return intervalCount.count; + } + return false; + }); + if (!foundInterval) { + const item = { + interval, + count: 1 + }; + intervals.push(item); + } + } + } + return intervals; + } + function groupByTempo(audioSampleRate, intervalCounts) { + const tempoCounts = []; + for (const intervalCount of intervalCounts) { + if (intervalCount.interval === 0) { + continue; + } + intervalCount.interval = Math.abs(intervalCount.interval); + let theoreticalTempo = 60 / (intervalCount.interval / audioSampleRate); + while (theoreticalTempo < 90) { + theoreticalTempo *= 2; + } + while (theoreticalTempo > 180) { + theoreticalTempo /= 2; + } + theoreticalTempo = Math.round(theoreticalTempo); + const foundTempo = tempoCounts.some((tempoCount) => { + if (tempoCount.tempo === theoreticalTempo) { + tempoCount.count += intervalCount.count; + return tempoCount.count; + } + return false; + }); + if (!foundTempo) { + const tempo = { + tempo: theoreticalTempo, + count: intervalCount.count, + confidence: 0 + }; + tempoCounts.push(tempo); + } + } + return tempoCounts; + } + + // src/realtime-bpm-analyzer.ts + var initialValue = { + minValidThreshold: () => minValidThreshold, + timeoutStabilization: () => 0, + validPeaks: () => generateValidPeaksModel(), + nextIndexPeaks: () => generateNextIndexPeaksModel(), + skipIndexes: () => 1 + }; + var RealTimeBpmAnalyzer = class { + constructor(config = {}) { + this.options = { + continuousAnalysis: false, + computeBpmDelay: 1e4, + stabilizationTime: 2e4, + muteTimeInIndexes: 1e4 + }; + this.minValidThreshold = initialValue.minValidThreshold(); + this.timeoutStabilization = initialValue.timeoutStabilization(); + this.validPeaks = initialValue.validPeaks(); + this.nextIndexPeaks = initialValue.nextIndexPeaks(); + this.skipIndexes = initialValue.skipIndexes(); + Object.assign(this.options, config); + } + setAsyncConfiguration(parameters) { + Object.assign(this.options, parameters); + } + reset() { + this.minValidThreshold = initialValue.minValidThreshold(); + this.timeoutStabilization = initialValue.timeoutStabilization(); + this.validPeaks = initialValue.validPeaks(); + this.nextIndexPeaks = initialValue.nextIndexPeaks(); + this.skipIndexes = initialValue.skipIndexes(); + } + clearValidPeaks(minThreshold) { + return __async(this, null, function* () { + console.log(`[clearValidPeaks] function: under ${minThreshold}, this.minValidThreshold has been setted to that threshold.`); + this.minValidThreshold = Number.parseFloat(minThreshold.toFixed(2)); + yield descendingOverThresholds((threshold) => __async(this, null, function* () { + if (threshold < minThreshold) { + delete this.validPeaks[threshold]; + delete this.nextIndexPeaks[threshold]; + } + return false; + })); + }); + } + analyzeChunck(channelData, audioSampleRate, bufferSize, postMessage) { + return __async(this, null, function* () { + const currentMaxIndex = bufferSize * this.skipIndexes; + const currentMinIndex = currentMaxIndex - bufferSize; + yield this.findPeaks(channelData, bufferSize, currentMinIndex, currentMaxIndex); + this.skipIndexes++; + const result = yield computeBpm(this.validPeaks, audioSampleRate); + const { threshold } = result; + postMessage({ message: "BPM", result }); + if (this.minValidThreshold < threshold) { + postMessage({ message: "BPM_STABLE", result }); + yield this.clearValidPeaks(threshold); + } + if (this.options.continuousAnalysis) { + clearTimeout(this.timeoutStabilization); + this.timeoutStabilization = window.setTimeout(() => { + console.log("[timeoutStabilization] setTimeout: Fired !"); + this.options.computeBpmDelay = 0; + this.reset(); + }, this.options.stabilizationTime); + } + }); + } + findPeaks(channelData, bufferSize, currentMinIndex, currentMaxIndex) { + return __async(this, null, function* () { + yield descendingOverThresholds((threshold) => __async(this, null, function* () { + if (this.nextIndexPeaks[threshold] >= currentMaxIndex) { + return false; + } + const offsetForNextPeak = this.nextIndexPeaks[threshold] % bufferSize; + const { peaks, threshold: atThreshold } = findPeaksAtThreshold(channelData, threshold, offsetForNextPeak); + if (peaks.length === 0) { + return false; + } + for (const relativeChunkPeak of peaks) { + this.nextIndexPeaks[atThreshold] = currentMinIndex + relativeChunkPeak + this.options.muteTimeInIndexes; + this.validPeaks[atThreshold].push(currentMinIndex + relativeChunkPeak); + } + return false; + }), this.minValidThreshold); + }); + } + }; + + // processor/realtime-bpm-processor.ts + var RealTimeBpmProcessor = class extends AudioWorkletProcessor { + constructor() { + super(); + this.realTimeBpmAnalyzer = new RealTimeBpmAnalyzer(); + this.aggregate = chunckAggregator(); + this.port.addEventListener("message", this.onMessage.bind(this)); + this.port.start(); + } + onMessage(event) { + if (event.data.message === "ASYNC_CONFIGURATION") { + this.realTimeBpmAnalyzer.setAsyncConfiguration(event.data.parameters); + } + } + process(inputs, _outputs, _parameters) { + const currentChunk = inputs[0][0]; + if (!currentChunk) { + return true; + } + const { isBufferFull, buffer, bufferSize } = this.aggregate(currentChunk); + if (isBufferFull) { + this.realTimeBpmAnalyzer.analyzeChunck(buffer, sampleRate, bufferSize, (event) => { + this.port.postMessage(event); + }).catch((error) => { + console.error(error); + }); + } + return true; + } + }; + registerProcessor(realtimeBpmProcessorName, RealTimeBpmProcessor); + var realtime_bpm_processor_default = {}; +})(); +//# sourceMappingURL=realtime-bpm-processor.js.map