xxxxxxxxxx
// song from here
// https://www.youtube.com/watch?v=lswQFsqZj24&t=323s
// 未平滑化
// 顏色對應頻率lerpColor未處理
// 細部調整
// 粒子不要超過3000,容易悲劇
var colors = "a9e5bb-fcf6b1-f7b32b-f72c25-1e3888-47a8bd-f5e663-ffad69-9c3848-7f7f7f".split("-").map(a=>"#"+a)
var bgColor
var ww,wh
var particles = []
// var soundCacheL = []
// var soundCacheH = []
// GUI控制值
var controls = {
// movingAverage : 20,
spectrumLMin : 0,
spectrumLMax : 400,
spectrumHMin : 400,
spectrumHMax : 800,
ttt : 0.50,
changeBgOnPeaks: true,
showInfo: false,
showSpectrum: false,
}
// GUI設定
var gui = new dat.GUI()
// gui
// .add(controls, "movingAverage", 1, 60)
// .step(1)
gui
.add(controls, "spectrumLMin", 0, 1024)
.step(1)
gui
.add(controls, "spectrumLMax", 0, 1024)
.step(1)
gui
.add(controls, "spectrumHMin", 0, 1024)
.step(1)
gui
.add(controls, "spectrumHMax", 0, 1024)
.step(1)
gui
.add(controls, "ttt", 0, 1)
.step(0.01)
gui
.add(controls, "changeBgOnPeaks")
gui
.add(controls, "showInfo")
gui
.add(controls, "showSpectrum")
gui.close()
function setup() {
// 螢幕長寬縮寫
ww = 1024
wh = windowHeight
// 生成畫布並加上播放的滑鼠事件
let cnv = createCanvas(ww, wh);
cnv.mouseClicked(togglePlay)
background(0);
frameRate(60)
// 設定音量
sound.amp(0.8)
// 生成聲音分析物件
amplitude = new p5.Amplitude()
amplitude.smooth(1)
fft = new p5.FFT()
peakDetect = new p5.PeakDetect()
// 背景色
bgColor = color("7f7f7f")
}
function draw() {
// 背景
blendMode(BLEND)
background(0)
// FFT.analyze() Spectrum
let spectrum = fft.analyze()
let spectrumL = spectrum.slice(controls.spectrumLMin, controls.spectrumLMax)
let avgL = 0
let spectrumH = spectrum.slice(controls.spectrumHMin, controls.spectrumHMax)
let avgH = 0
// Level 改變初始粒子大小
level = amplitude.getLevel()
sizeMax = map(level, 0, 1, 0, 300)
//Peak detect
peakDetect.update(fft)
drag = lerp(drag, level + controls.ttt, 0.05)
// 定義高低頻
spectrumL.forEach(v => avgL += v)
avgL /= spectrumL.length
spectrumH.forEach(v => avgH += v)
avgH /= spectrumH.length
//--------------------
// let ratio = map(delta, 0, 30, 1, 0 , true)
// var soundScale = (1-easeOutCirc(1-ratio)) * 200
// 粒子(加算)
blendMode(ADD)
// 生成粒子陣列
if (frameCount % 2 == 0){
for(let i = 0; i < num; i ++){
let particle = new Particle()
particles.push(particle)
}
}
// 繪製粒子
particles.forEach((p, i)=>{
p.update(avgL, avgH)
p.draw()
if (p.type == 0 && peakDetect.isDetected && (i % 5 == 0) ){
let particle = new Particle(p.x, p.y)
particles.push(particle)
}
})
//刪除超出畫面的粒子
particles = particles.filter(p => p.y > 0 && p.y < height && p.x > 0 && p.x < width && p.dur > 0)
// 偵測到peak時改變覆蓋色
if (peakDetect.isDetected){
let bgNum = int(random(0, 9))
color(colors[bgNum]).setAlpha(0.3)
bgColor = color(colors[bgNum])
}
if (controls.changeBgOnPeaks){
blendMode(OVERLAY)
fill(bgColor)
rect(0, 0, ww, wh)
}
// 顯示Debug資訊
if (controls.showInfo){
blendMode(BLEND)
fill("white")
text("avgL: " + round(avgL), 50, 50)
text("avgH: " + round(avgH), 50, 70)
text("Particle Count: " + particles.length, 50, 90)
text("Level: " + round(level * 100), 50, 110)
}
// 檢查spectrum分布
if (controls.showSpectrum){
for (let o = 0; o < spectrum.length; o++){
let x = map(o, 0, spectrum.length, 0, width);
let h = - 250 + map(spectrum[o], 0, 255, 250, 0);
rectMode(CORNER)
fill(255, 237, 102)
noStroke()
rect(x, height, width / spectrum.length, h )
}
}
}
// Particle controls
p = {
durMax : 800,
durMin : 200
}
var num = 1
var locationRadius = 30
var vx = 2, vy = 2
var wander1 = 10;
var wander2 = 40;
var theta1 = -0.5;
var theta2 = 0.5;
var drag = 1
var alphaFade = 1
var sizeMin = 0
var sizeMax = 0
var sizeScalar = 0.99
// 粒子class
class Particle{
constructor(x, y){
// 類別0,1為低高頻影響,2則與頻率無關
this.type = round(random(0, 2))
this.x = x || (ww >> 1) + sin(random(2 * PI)) * locationRadius
this.y = y || (wh >> 1) + cos(random(2 * PI)) * locationRadius
this.theta = random( 2 * PI)
this.drag = drag
this.vx = 0
this.vy = 0
this.wander = 0.15
this.tempSize = random(sizeMin, sizeMax)
this.r = 0
this.alpha = 0
this.targetAlpha = random(200, 255)
let clr1 = color(random(0,255), random(0,255), 178, 255)
this.clr = clr1
this.dur = random(p.durMax, p.durMin)
this.spectrum = 0
}
update(spectrumL, spectrumH, changeColor){
this.x += this.vx
// this.y = sin(this.x / 100) * 50 + wh / 2
this.y += this.vy
this.vx *= this.drag
this.vy *= this.drag
this.theta += random( theta1, theta2 ) * this.wander;
this.vx += sin( this.theta ) * 0.2
this.vy += cos( this.theta ) * 0.2
this.alpha = lerp(this.alpha, this.targetAlpha, 0.02)
if (this.dur < 200){
this.targetAlpha = 0
}
// 根據類別影響不同特性
if(this.type == 0){
this.tempSize *= sizeScalar
this.r = this.tempSize + spectrumL * 0.1
this.dur -= 1
// if (spectrumL > 90){
// let distance = dist(this.x, this.y, ww >>1, wh >> 1)
// this.x += (this.x - ww >>1) / distance * spectrumL * 0.05
// this.y += (this.y - wh >>1) / distance * spectrumL * 0.05
// }
//test
// this.clr = color(232, 30, 70, this.alpha)
} else if(this.type == 1){
this.tempSize *= sizeScalar
this.r = this.tempSize + spectrumH * 0.2
this.dur -= 1
if (spectrumH > 20){
// let distance = dist(this.x, this.y, ww >>1, wh >> 1)
// this.x += distance * cos(frameCount / 1000)
// this.y += distance * sin(frameCount / 1000)
this.wander = 1
this.theta *= 3
this.x += this.vx * spectrumH * 0.1
this.y += this.vy * spectrumH * 0.1
}else{
this.wander = 0.15
}
// this.clr = color(84, 152, 255, this.alpha)
} else{
this.tempSize *= 0.995
this.dur -= 0.2
this.r = this.tempSize + this.dur * 0.006
this.x += map(noise(this.x / 200, this.y / 200, frameCount / 300), 0, 1, - 0.5, 0.5)
this.y += map(noise(this.y / 200, this.x / 200, frameCount / 300), 0, 1, - 0.5, 0.5)
}
}
draw(){
noStroke()
this.clr.setAlpha(this.alpha)
fill(this.clr)
circle(this.x, this.y, this.r)
this.r = this.tempSize
}
}
// 初始化聲音
preload = () => sound = loadSound("drain.mp3")
// 播放toggle
// 播放中則暫停,暫停中則繼續撥放
togglePlay = () => sound.isPlaying() ? sound.pause() : sound.play()