249 lines
8.6 KiB
Python
249 lines
8.6 KiB
Python
from Utils.BaseEffect import BaseEffect
|
|
from Utils.EffectParameter import slider, colorpicker
|
|
import time
|
|
|
|
import sys
|
|
import numpy
|
|
from time import perf_counter, sleep
|
|
from random import randint, shuffle
|
|
|
|
class MusikEffect(BaseEffect):
|
|
name = "musikEffect"
|
|
desc = "LED-Band *sollte* nach musik blinken"
|
|
|
|
|
|
|
|
# Something that will be used to show descriptions and value options
|
|
# of the parameters the effect will accept, in a way, that eg the webclient can decide,
|
|
# if the parameters can be toggeled by a button/checkbox/slider/whatever
|
|
effectParameters: list = [
|
|
# radio(\
|
|
# "Shuffle LED to Freq Order",\
|
|
# "Off -> Mapping ",\
|
|
# [\
|
|
# [0,255,255,"red"],\
|
|
# [0,255,255,"green"],\
|
|
# [0,255,255,"blue"]\
|
|
# ]\
|
|
# ),\
|
|
# slider(\
|
|
# "Effect Brightnes",\
|
|
# "Choose a brightness for your LED's",\
|
|
# [\
|
|
# [0,100,100,"brightness"],\
|
|
# ]\
|
|
# )\
|
|
]
|
|
|
|
def init(self):
|
|
|
|
self.fft_random_keys = [0,1,2,3]
|
|
self.fft_random = [0,0,0,0]
|
|
|
|
self.y_max_freq_avg_list = []
|
|
self.low_freq_avg_list = []
|
|
self.bpm_list = []
|
|
self.prev_beat = 0 # timestamp
|
|
|
|
# used by strobe()
|
|
self.lastmode = 0
|
|
|
|
return
|
|
|
|
#loop effect as long as not stopped
|
|
def effect(self):
|
|
self.plot_audio_and_detect_beats()
|
|
#self.freqtocolor()
|
|
#if(time.time() - self.lastTime >= 0.002):
|
|
#for RGBStrip in self.effectRGBStrips():
|
|
# r= RGBStrip.red-1
|
|
# if(r<0):
|
|
# r=0
|
|
# g= RGBStrip.green-1
|
|
# if(g<0):
|
|
# g=0
|
|
# b= RGBStrip.blue-1
|
|
# if(b<0):
|
|
# b=0
|
|
# RGBStrip.RGB(r,g,b)
|
|
#self.lastTime = time.time()
|
|
sleep(0.00833333333)
|
|
|
|
def end(self):
|
|
pass
|
|
|
|
def plot_audio_and_detect_beats(self):
|
|
if not self.hasNewFttValues:
|
|
return
|
|
|
|
# get x and y values from FFT
|
|
xs, ys = self.ftt
|
|
|
|
# calculate average for all frequency ranges
|
|
y_avg = numpy.mean(ys)
|
|
|
|
|
|
|
|
#low_freq = numpy.mean(ys[20:47])
|
|
#mid_freq = numpy.mean(ys[88:115])
|
|
#hig_freq = numpy.mean(ys[156:184])
|
|
|
|
low_freq = numpy.mean(ys[0:int(ys.__len__() / 3 * 1)])
|
|
mid_freq = numpy.mean(ys[int(ys.__len__() / 3 * 1):int(ys.__len__() / 3 * 2)])
|
|
hig_freq = numpy.mean(ys[int(ys.__len__() / 3 * 2):int(ys.__len__() / 3 * 3)])
|
|
|
|
#get the maximum of all freq
|
|
if len(self.y_max_freq_avg_list) < 250 or y_avg > 10 and numpy.amax([low_freq,mid_freq,hig_freq])/2 > numpy.amin(self.y_max_freq_avg_list):
|
|
self.y_max_freq_avg_list.append(numpy.amax([low_freq,mid_freq,hig_freq]))
|
|
|
|
y_max = numpy.amax(self.y_max_freq_avg_list)
|
|
|
|
#y_max = numpy.mean([numpy.amax(ys),y_max])
|
|
#print(low_freq,mid_freq,hig_freq,y_max)
|
|
|
|
for i,item in enumerate([low_freq,mid_freq,hig_freq]):
|
|
if item is None:
|
|
item = 0
|
|
|
|
low = round(low_freq/(y_max+10)*255)
|
|
mid = round(mid_freq/(y_max+10)*255)
|
|
hig = round(hig_freq/(y_max+10)*255)
|
|
#print(low,mid,hig,y_max, numpy.amax(ys), y_avg)
|
|
#print("------")
|
|
|
|
self.fft_random[self.fft_random_keys[0]] = low
|
|
self.fft_random[self.fft_random_keys[1]] = mid
|
|
self.fft_random[self.fft_random_keys[2]] = hig
|
|
self.fft_random[self.fft_random_keys[3]] = 0
|
|
|
|
# calculate low frequency average
|
|
#low_freq = [ys[i] for i in range(len(xs)) if xs[i] < 1000]
|
|
low_freq = ys[0:47]
|
|
low_freq_avg = numpy.mean(low_freq)
|
|
|
|
if len(self.low_freq_avg_list) < 250 or low_freq_avg > numpy.amin(self.low_freq_avg_list)/2:
|
|
self.low_freq_avg_list.append(low_freq_avg)
|
|
cumulative_avg = numpy.mean(self.low_freq_avg_list)
|
|
|
|
bass = low_freq[:int(len(low_freq)/2)]
|
|
bass_avg = numpy.mean(bass)
|
|
#print("bass: {:.2f} vs cumulative: {:.2f}".format(bass_avg, cumulative_avg))
|
|
|
|
# check if there is a beat
|
|
# song is pretty uniform across all frequencies
|
|
if (y_avg > y_avg/5 and (bass_avg > cumulative_avg * 1.8 or (low_freq_avg < y_avg * 1.2 and bass_avg > cumulative_avg))):
|
|
#self.prev_beat
|
|
curr_time = perf_counter()
|
|
|
|
# print(curr_time - self.prev_beat)
|
|
if curr_time - self.prev_beat > 60/360*2: # 180 BPM max
|
|
shuffle(self.fft_random_keys)
|
|
|
|
# change the button color
|
|
#self.beats_idx += 1
|
|
#self.strobe()
|
|
#print("beat {}".format(self.beats_idx))
|
|
#print("bass: {:.2f} vs cumulative: {:.2f}".format(bass_avg, cumulative_avg))
|
|
|
|
#print(self.fft_random)
|
|
# change the button text
|
|
bpm = int(60 / (curr_time - self.prev_beat))
|
|
if len(self.bpm_list) < 4:
|
|
if bpm > 60:
|
|
self.bpm_list.append(bpm)
|
|
else:
|
|
bpm_avg = int(numpy.mean(self.bpm_list))
|
|
if abs(bpm_avg - bpm) < 35:
|
|
self.bpm_list.append(bpm)
|
|
print("bpm: {:d}".format(bpm_avg))
|
|
|
|
# reset the timer
|
|
self.prev_beat = curr_time
|
|
if y_avg > 10:
|
|
for RGBStrip in self.effectRGBStrips():
|
|
RGBStrip.RGB(
|
|
self.fft_random[0],
|
|
self.fft_random[1],
|
|
self.fft_random[2]
|
|
)
|
|
"""
|
|
rgbStrip: RGBStrip
|
|
for rgbStrip in self.effectRGBStrips():
|
|
for i in range(rgbStrip.STRIP_LENGHT):
|
|
if rgbStrip.STRIP_LENGHT-1-10 is not i:
|
|
rgbStrip.WS2812b(rgbStrip.STRIP_LENGHT-i-1, rgbStrip.red[rgbStrip.STRIP_LENGHT-i-2], rgbStrip.green[rgbStrip.STRIP_LENGHT-i-2], rgbStrip.blue[rgbStrip.STRIP_LENGHT-i-2])
|
|
else:
|
|
rgbStrip.WS2812b(rgbStrip.STRIP_LENGHT-i-1, self.fft_random[0], self.fft_random[1], self.fft_random[2])
|
|
rgbStrip.show()
|
|
"""
|
|
|
|
# shorten the cumulative list to account for changes in dynamics
|
|
if len(self.low_freq_avg_list) > 500:
|
|
self.low_freq_avg_list = self.low_freq_avg_list[250:]
|
|
#print("REFRESH!!")
|
|
|
|
# shorten the cumulative list to account for changes in dynamics
|
|
if len(self.y_max_freq_avg_list ) > 500:
|
|
self.y_max_freq_avg_list = self.y_max_freq_avg_list[250:]
|
|
print("--REFRESH y_max_freq_avg_list")
|
|
|
|
# keep two 8-counts of BPMs so we can maybe catch tempo changes
|
|
if len(self.bpm_list) > 24:
|
|
self.bpm_list = self.bpm_list[8:]
|
|
|
|
# reset song data if the song has stopped
|
|
if y_avg < 10:
|
|
self.bpm_list = []
|
|
self.low_freq_avg_list = []
|
|
print("new song")
|
|
self.off()
|
|
self.hasNewFttValues = False
|
|
# print(self.bpm_list)250
|
|
|
|
def strobe(self):
|
|
x = randint(0,5)
|
|
while x is self.lastmode:
|
|
x = randint(0,5)
|
|
|
|
self.lastmode = x
|
|
r = 255#randint(0,255)
|
|
g = 255#randint(0,255)
|
|
b = 255#randint(0,255)
|
|
if x is 0:
|
|
for RGBStrip in self.effectRGBStrips():
|
|
RGBStrip.RGB(r,g,0)
|
|
if x is 1:
|
|
for RGBStrip in self.effectRGBStrips():
|
|
RGBStrip.RGB(0,g,b)
|
|
if x is 2:
|
|
for RGBStrip in self.effectRGBStrips():
|
|
RGBStrip.RGB(r,0,b)
|
|
if x is 3:
|
|
for RGBStrip in self.effectRGBStrips():
|
|
RGBStrip.RGB(r,0,0)
|
|
if x is 4:
|
|
for RGBStrip in self.effectRGBStrips():
|
|
RGBStrip.RGB(0,g,0)
|
|
if x is 5:
|
|
for RGBStrip in self.effectRGBStrips():
|
|
RGBStrip.RGB(0,0,b)
|
|
if x is 6:
|
|
for RGBStrip in self.effectRGBStrips():
|
|
RGBStrip.RGB(r,g,b)
|
|
|
|
def off(self):
|
|
for RGBStrip in self.effectRGBStrips():
|
|
RGBStrip.RGB(0,0,0)
|
|
|
|
def left_rotate(self,arr):
|
|
if not arr:
|
|
return arr
|
|
|
|
left_most_element = arr[0]
|
|
length = len(arr)
|
|
|
|
for i in range(length - 1):
|
|
arr[i], arr[i + 1] = arr[i + 1], arr[i]
|
|
|
|
arr[length - 1] = left_most_element
|
|
return arr |