Adapt fullscreen beat response
This commit is contained in:
1 parent
27c289483b
commit
d07221f600
3 files changed
+41
-12
No files matched your search
@@ -5,6 +5,7 @@ import {
|
|||||||
toOkLab,
|
toOkLab,
|
||||||
type AlbumPalette,
|
type AlbumPalette,
|
||||||
} from "./palette"
|
} from "./palette"
|
||||||
|
import { shapeBeatPulse } from "~/utils/beat"
|
||||||
|
|
||||||
const props = defineProps<{ src: string; active: boolean }>()
|
const props = defineProps<{ src: string; active: boolean }>()
|
||||||
const ANIMATION_SPEED = 0.3
|
const ANIMATION_SPEED = 0.3
|
||||||
@@ -159,7 +160,7 @@ function draw(now: number) {
|
|||||||
(value, channel) =>
|
(value, channel) =>
|
||||||
value * (1 - blend) + accent[channel] * blend,
|
value * (1 - blend) + accent[channel] * blend,
|
||||||
) as [number, number, number]
|
) as [number, number, number]
|
||||||
const beat = beatEnergy.value
|
const beat = shapeBeatPulse(beatEnergy.value)
|
||||||
lab[0] = Math.min(1, lab[0] + beat * 0.01)
|
lab[0] = Math.min(1, lab[0] + beat * 0.01)
|
||||||
lab[1] *= 1 + beat * 0.02
|
lab[1] *= 1 + beat * 0.02
|
||||||
lab[2] *= 1 + beat * 0.02
|
lab[2] *= 1 + beat * 0.02
|
||||||
@@ -171,7 +172,7 @@ function draw(now: number) {
|
|||||||
image.data[offset + 3] = 255
|
image.data[offset + 3] = 255
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
element.style.transform = `scale(${1.08 + beatEnergy.value * 0.003})`
|
element.style.transform = `scale(${1.08 + shapeBeatPulse(beatEnergy.value) * 0.003})`
|
||||||
context.putImageData(image, 0, 0)
|
context.putImageData(image, 0, 0)
|
||||||
if (props.active) frame = requestAnimationFrame(draw)
|
if (props.active) frame = requestAnimationFrame(draw)
|
||||||
}
|
}
|
||||||
|
|||||||
+29
-10
@@ -1,4 +1,5 @@
|
|||||||
import { Howl, Howler } from "howler"
|
import { Howl, Howler } from "howler"
|
||||||
|
import { shapeBeatPulse } from "~/utils/beat"
|
||||||
|
|
||||||
export default defineNuxtPlugin((nuxtApp) => {
|
export default defineNuxtPlugin((nuxtApp) => {
|
||||||
const mediaBaseUrl = useRuntimeConfig().public.mediaBaseUrl
|
const mediaBaseUrl = useRuntimeConfig().public.mediaBaseUrl
|
||||||
@@ -145,12 +146,11 @@ export default defineNuxtPlugin((nuxtApp) => {
|
|||||||
|
|
||||||
const samples = new Uint8Array(analysisNode.frequencyBinCount)
|
const samples = new Uint8Array(analysisNode.frequencyBinCount)
|
||||||
let previousBass: number | null = null
|
let previousBass: number | null = null
|
||||||
|
let riseAverage = 0.01
|
||||||
|
let riseVariance = 0.0004
|
||||||
let pulse = 0
|
let pulse = 0
|
||||||
let lastBeatLogAt = 0
|
let lastBeatLogAt = 0
|
||||||
let lastLevelLogAt = 0
|
let lastLevelLogAt = 0
|
||||||
const attackNoiseFloor = 0.05
|
|
||||||
const strongThumpThreshold = 0.05
|
|
||||||
const thumpCooldown = 800
|
|
||||||
const sample = () => {
|
const sample = () => {
|
||||||
if (!analysisNode) return
|
if (!analysisNode) return
|
||||||
analysisNode.getByteFrequencyData(samples)
|
analysisNode.getByteFrequencyData(samples)
|
||||||
@@ -164,17 +164,37 @@ export default defineNuxtPlugin((nuxtApp) => {
|
|||||||
for (let bin = firstBin; bin <= lastBin; bin++) bass += samples[bin]
|
for (let bin = firstBin; bin <= lastBin; bin++) bass += samples[bin]
|
||||||
bass /= Math.max(1, lastBin - firstBin + 1) * 255
|
bass /= Math.max(1, lastBin - firstBin + 1) * 255
|
||||||
if (previousBass === null) previousBass = bass
|
if (previousBass === null) previousBass = bass
|
||||||
const attack = Math.max(0, bass - previousBass - attackNoiseFloor)
|
const rise = Math.max(0, bass - previousBass)
|
||||||
previousBass = previousBass * 0.985 + bass * 0.015
|
previousBass = previousBass * 0.985 + bass * 0.015
|
||||||
|
const riseDeviation = Math.sqrt(riseVariance)
|
||||||
|
const strongThumpThreshold = Math.min(
|
||||||
|
0.1,
|
||||||
|
Math.max(0.045, riseAverage + riseDeviation * 2),
|
||||||
|
)
|
||||||
|
const attack = Math.max(0, rise - strongThumpThreshold)
|
||||||
|
const thumpCooldown = Math.min(
|
||||||
|
500,
|
||||||
|
Math.max(300, 300 + riseDeviation * 4000),
|
||||||
|
)
|
||||||
const now = performance.now()
|
const now = performance.now()
|
||||||
const isStrongThump =
|
const isStrongThump =
|
||||||
attack >= strongThumpThreshold && now - lastBeatLogAt > thumpCooldown
|
rise >= strongThumpThreshold && now - lastBeatLogAt > thumpCooldown
|
||||||
|
const riseDelta = rise - riseAverage
|
||||||
|
riseAverage += riseDelta * 0.01
|
||||||
|
riseVariance += (riseDelta * riseDelta - riseVariance) * 0.01
|
||||||
if (isStrongThump) {
|
if (isStrongThump) {
|
||||||
const thumpStrength = Math.min(
|
const thumpStrength = Math.min(
|
||||||
0.38,
|
0.38,
|
||||||
0.1 + (attack - strongThumpThreshold) * 2.5,
|
0.1 + attack * 2.5,
|
||||||
)
|
)
|
||||||
pulse = Math.max(pulse, thumpStrength)
|
pulse = Math.max(pulse, thumpStrength)
|
||||||
|
if (DEBUG_BEAT_ANALYSIS) {
|
||||||
|
console.log(
|
||||||
|
"[fullscreen beat]",
|
||||||
|
`detected=${thumpStrength.toFixed(2)}`,
|
||||||
|
`visual=${shapeBeatPulse(thumpStrength).toFixed(2)}`,
|
||||||
|
)
|
||||||
|
}
|
||||||
} else pulse *= 0.94
|
} else pulse *= 0.94
|
||||||
beatEnergy.value = Math.min(1, pulse)
|
beatEnergy.value = Math.min(1, pulse)
|
||||||
if (DEBUG_BEAT_ANALYSIS && now - lastLevelLogAt > 1000) {
|
if (DEBUG_BEAT_ANALYSIS && now - lastLevelLogAt > 1000) {
|
||||||
@@ -183,13 +203,12 @@ export default defineNuxtPlugin((nuxtApp) => {
|
|||||||
"[beat level]",
|
"[beat level]",
|
||||||
`bass=${bass.toFixed(3)}`,
|
`bass=${bass.toFixed(3)}`,
|
||||||
`attack=${attack.toFixed(3)}`,
|
`attack=${attack.toFixed(3)}`,
|
||||||
|
`gate=${strongThumpThreshold.toFixed(3)}`,
|
||||||
|
`cooldown=${Math.round(thumpCooldown)}`,
|
||||||
`pulse=${beatEnergy.value.toFixed(2)}`,
|
`pulse=${beatEnergy.value.toFixed(2)}`,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
if (isStrongThump) {
|
if (isStrongThump) lastBeatLogAt = now
|
||||||
lastBeatLogAt = now
|
|
||||||
console.log("[fullscreen beat]", beatEnergy.value.toFixed(2))
|
|
||||||
}
|
|
||||||
analysisFrame = requestAnimationFrame(sample)
|
analysisFrame = requestAnimationFrame(sample)
|
||||||
}
|
}
|
||||||
analysisFrame = requestAnimationFrame(sample)
|
analysisFrame = requestAnimationFrame(sample)
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
export function shapeBeatPulse(value: number) {
|
||||||
|
const strength = Math.max(0, Math.min(0.4, value))
|
||||||
|
if (strength <= 0.2) return strength * 0.4
|
||||||
|
if (strength <= 0.35) {
|
||||||
|
const progress = (strength - 0.2) / 0.15
|
||||||
|
return 0.08 + (1 - (1 - progress) ** 3) * 0.42
|
||||||
|
}
|
||||||
|
return 0.5 + ((strength - 0.35) / 0.05) * 0.05
|
||||||
|
}
|
||||||
Reference in new issue
Block a user