This commit is contained in:
Mal 2020-06-16 12:27:53 +02:00
commit cba3dd96d6
11 changed files with 993 additions and 0 deletions

20
Makefile Normal file
View File

@ -0,0 +1,20 @@
DESTDIR :=
ROOTDIR=/usr/share/pumpkin/
SERVICE_PATH=/etc/systemd/system/
install:
pip install RPi.GPIO
mkdir -p $(DESTDIR)$(ROOTDIR)
mkdir -p $(DESTDIR)$(SERVICE_PATH)
cp __main__.py $(DESTDIR)$(ROOTDIR)
cp rgb.py $(DESTDIR)$(ROOTDIR)
cp waveloader.py $(DESTDIR)$(ROOTDIR)
cp config.py $(DESTDIR)$(ROOTDIR)
cp pumpkin.py $(DESTDIR)$(ROOTDIR)
cp pumpkin.service $(DESTDIR)$(SERVICE_PATH)
systemctl daemon-reload
uninstall:
rm $(SERVICE_PATH)pumpkin.service
rm -R $(ROOTDIR)

34
README Normal file
View File

@ -0,0 +1,34 @@
RUN THE INSTALLER BY TYPING IN:
./installer
IF SOME DEPENDENCIES ARE MISSING PLEASE INSTALL THEM WITH YOUR PACKAGE MANAGER.
IF THE INSTALLER WAS SUCCESSFULL YOU HAVE TO START IT ONCE TO GET A DEFAULT CONFIG FILE:
sudo systemctl start pumpkin
IT WILL CRASH BUT NOW YOU WILL HAVE A CUSTOMIZABLE CONFIG FILE. OPEN IT:
sudo nano /etc/pumpkin.conf
TO MAKE IT START SUCCESSFULLY YOU HAVE TO GIVE THE ABSOLUTE PATH TO YOUR WAVE FOLDER:
AUDIO_PATH=/your/wave/folder
THEN SPECIFY YOUR GPIOS FOR EVERY COLOR. IF THERE ARE MULTIPLE LEDS FOR ONE COLOR SEPARATE THEM WITH , LIKE THAT:
GPIOS_RED=11,12,13
NOW YOU HAVE A BASIC CONFIGURATION AND IT SHOULD RUN. START THE PUMPKIN AGAIN BY TYPING IN
sudo systemctl start pumpkin
TO STOP IT TYPE
sudo systemctl stop pumpkin
IF YOU WANT TO MAKE IT START AUTOMATICALLY AFTER BOOT WITHOUT LOGIN TYPE
sudo systemctl enable pumpkin
TO DELETE THE AUTOSTART TYPE
sudo systemctl disable pumpkin
IF YOU HAVE A FOG MACHINE YOU CAN ALSO SPECIFY ONE GPIO FOR THE EVAPORATOR AND ONE FOR THE PUMP:
GPIO_EVAPORATOR=
GPIO_PUMP=
AND NOW HAVE FUN!

83
__main__.py Executable file
View File

@ -0,0 +1,83 @@
#!/usr/bin/env python3
from pumpkin import Pumpkin
from config import Config
from time import time
from time import sleep
from os import listdir
from os.path import isfile
from shutil import copy
from sys import argv
import atexit
def cleanup():
pumpkin.cleanup()
CONFIG_PATH = '/etc/pumpkin.conf'
# Make sure that a config file exists
if not isfile(CONFIG_PATH):
parts = argv[0].split('/')
pathDefaultConf = '/'.join(parts[:len(parts) - 1]) + '/pumpkin.conf'
print(pathDefaultConf)
copy(pathDefaultConf, CONFIG_PATH)
config = Config(CONFIG_PATH)
AUDIO_PATH = config.getString('AUDIO_PATH')
COLOR_RED = config.getInt('COLOR_RED')
COLOR_GREEN = config.getInt('COLOR_GREEN')
COLOR_BLUE = config.getInt('COLOR_BLUE')
COLOR_BACKGROUND_RED = config.getInt('COLOR_BACKGROUND_RED')
COLOR_BACKGROUND_GREEN = config.getInt('COLOR_BACKGROUND_GREEN')
COLOR_BACKGROUND_BLUE = config.getInt('COLOR_BACKGROUND_BLUE')
COLOR_FIRE_RED = config.getInt('COLOR_FIRE_RED')
COLOR_FIRE_GREEN = config.getInt('COLOR_FIRE_GREEN')
COLOR_FIRE_BLUE = config.getInt('COLOR_FIRE_BLUE')
GPIOS_RED = config.getIntList('GPIOS_RED')
GPIOS_GREEN = config.getIntList('GPIOS_GREEN')
GPIOS_BLUE = config.getIntList('GPIOS_BLUE')
GPIO_TRIGGER = config.getInt('GPIO_TRIGGER')
GPIO_EVAPORATOR = config.getInt('GPIO_EVAPORATOR')
GPIO_PUMP = config.getInt('GPIO_PUMP')
FOG_RUNTIME_MAX = config.getFloat('FOG_RUNTIME_MAX')
GPIO_POWEROFF = config.getInt('GPIO_POWEROFF')
RETRIGGER_TIMEOUT = config.getFloat('RETRIGGER_TIMEOUT')
PWM_FREQUENCY = config.getInt('PWM_FREQUENCY')
pumpkin = Pumpkin(AUDIO_PATH, GPIOS_RED, GPIOS_GREEN, GPIOS_BLUE, GPIO_TRIGGER, PWM_FREQUENCY)
pumpkin.setAudioColor(COLOR_RED, COLOR_GREEN, COLOR_BLUE)
pumpkin.setBackgroundColor(COLOR_BACKGROUND_RED, COLOR_BACKGROUND_GREEN, COLOR_BACKGROUND_BLUE)
pumpkin.setFireColor(COLOR_FIRE_RED, COLOR_FIRE_GREEN, COLOR_FIRE_BLUE)
pumpkin.enableFog(GPIO_EVAPORATOR, GPIO_PUMP, FOG_RUNTIME_MAX)
pumpkin.enablePowerOff(GPIO_POWEROFF)
atexit.register(cleanup)
retriggerTimeout = RETRIGGER_TIMEOUT
t_after_trigger = time() - retriggerTimeout
while True:
try:
if pumpkin.isTriggered() and time() - t_after_trigger > retriggerTimeout:
pumpkin.speak()
t_after_trigger = time()
else:
pumpkin.lurk()
sleep(0.5)
except Exception as e:
print(e)
exit()

77
config.py Normal file
View File

@ -0,0 +1,77 @@
#!/usr/bin/env python3
class Config:
def __init__(self, file):
self.COMMENT_MARK = '#'
self.path = file
try:
with open(file, 'r') as conf:
config = conf.read().split('\n')
except:
raise IOError('Couldn\'t read ' + file + '!')
if not self.isValidFile(config):
raise Exception('Error: ' + file + ' is no valid config file!')
self.values = {}
for c in config:
if self.lineHasValue(c):
parts = c.split('=')
self.values[parts[0].strip()] = parts[1].strip()
def getValue(self, key):
try:
return self.values[key]
except:
return None
def getInt(self, key):
value = self.getValue(key)
if value == None:
return None
return int(value)
def getFloat(self, key):
return float(self.getValue(key))
def getBool(self, key):
return bool(self.getValue(key))
def getString(self, key):
return self.getValue(key)
def getList(self, key):
values = []
for v in self.getValue(key).split(','):
values.append(v.strip())
return values
def getIntList(self, key):
values = []
for i in self.getList(key):
values.append(int(i))
return values
def lineHasValue(self, line):
return not line.startswith(self.COMMENT_MARK) and len(line.replace(' ', '').replace('\n', '')) > 0 and line.count('=') == 1
def lineHasSyntaxError(self, line):
return not line.startswith(self.COMMENT_MARK) and len(line.replace(' ', '').replace('\n', '')) > 0 and line.count('=') != 1
def isValidFile(self, config):
count = 1
for line in config:
if self.lineHasSyntaxError(line):
print(self.path + ' Line ' + str(count) + ': Invalid syntax!')
count += 1
return True

29
configure vendored Executable file
View File

@ -0,0 +1,29 @@
#!/bin/bash
function check_package()
{
printf "\tChecking for ${1}... "
${1} --version &> /dev/null
if [ ! $? == 0 ]
then
echo "Failed!"
echo ""
echo "Please install ${1}!"
exit 1
fi
echo "Found."
}
echo "Checking for dependencies:"
check_package python3
check_package pip3
check_package gcc
check_package make
check_package aplay
echo ""
echo "Ready to install."

9
installer Executable file
View File

@ -0,0 +1,9 @@
#!/bin/bash
./configure
if [ $? == 0 ]
then
echo "Installing..."
sudo make install
fi

58
pumpkin.conf Normal file
View File

@ -0,0 +1,58 @@
# The GPIO numbers are in board mode so you can count down on your
# Raspberry Pi to find the number:
# _____________
# (1) (2) \
# (3) (4) |
# (5) (6) |
#
# and so on...
#
# To get a view for the numbering install the python module gpiozero the following way:
#
# sudo pip install gpiozero
#
# Now if you type pinout you get a nice graphical overview for your pins.
# WARNING: Only use GPIOs that are NOT reserved for I²C, SPI or UART!
PWM_FREQUENCY=60
# You can specify multiple GPIOS for one color separated by ,
GPIOS_RED=11
GPIOS_GREEN=12
GPIOS_BLUE=13
# The path to your audio folder (must contain wave files!).
AUDIO_PATH=/path/to/audio/files
# The color for sound visualization
COLOR_RED=0
COLOR_GREEN=128
COLOR_BLUE=255
# The color for silence parts of your audio visualization.
COLOR_BACKGROUND_RED=0
COLOR_BACKGROUND_GREEN=0
COLOR_BACKGROUND_BLUE=0
# The color for the flickering candle on standby.
COLOR_FIRE_RED=255
COLOR_FIRE_GREEN=128
COLOR_FIRE_BLUE=0
# The input pin for your motion sensor.
GPIO_TRIGGER=7
# The seconds the pumpkin waits to be triggered again after he has been triggered.
RETRIGGER_TIMEOUT=10
# The input pin for your evaporator activation.
GPIO_EVAPORATOR=0
# The input pin for your pump activation.
GPIO_PUMP=0
# The maximal runtime for pump and evaporator in seconds during speaking.
FOG_RUNTIME_MAX=3
# The input pin for a poweroff switch to power down the whole system.
GPIO_POWEROFF=0

184
pumpkin.py Normal file
View File

@ -0,0 +1,184 @@
#!/usr/bin/env python3
import _thread
import atexit
import RPi.GPIO as GPIO
import os
from waveloader import WaveLoader
from rgb import RGBMatrix
from rgb import RGB
from subprocess import call
from time import time
from random import randint
class Pumpkin:
def __init__(self, soundpath, GPIOS_RED = [11], GPIOS_GREEN = [12], GPIOS_BLUE = [13], GPIO_TRIGGER = 15, PWM_FREQUENCY = 60):
self.soundfiles = []
self.loadWaveFolder(soundpath)
if len(self.soundfiles) == 0:
raise IOError('No audio files found in ' + soundpath + '!')
GPIO.setmode(GPIO.BOARD)
self.GPIOS_RED = GPIOS_RED
self.GPIOS_GREEN = GPIOS_GREEN
self.GPIOS_BLUE = GPIOS_BLUE
self.GPIO_TRIGGER = GPIO_TRIGGER
self.GPIO_EVAPORATOR = 0
self.GPIO_PUMP = 0
self.GPIO_POWEROFF = 0
self.fogRuntimeMax = 3
GPIO.setup(self.GPIO_TRIGGER, GPIO.IN)
self.rgbControl = RGBMatrix(PWM_FREQUENCY)
for r in self.GPIOS_RED:
self.rgbControl.addRed(r)
for g in self.GPIOS_GREEN:
self.rgbControl.addGreen(g)
for b in self.GPIOS_BLUE:
self.rgbControl.addBlue(b)
self.color = RGB(255, 255, 255)
self.colorBackground = RGB(0, 0, 0)
self.colorFire = RGB(255, 55, 0)
atexit.register(self.cleanup)
def __del__(self):
self.cleanup()
def cleanup(self):
GPIO.cleanup()
def enableFog(self, GPIO_EVAPORATOR, GPIO_PUMP, runtimeSecondsMax = 3):
self.GPIO_EVAPORATOR = GPIO_EVAPORATOR
self.GPIO_PUMP = GPIO_PUMP
self.fogRuntimeMax = runtimeSecondsMax
if self.GPIO_EVAPORATOR != 0:
GPIO.setup(self.GPIO_EVAPORATOR, GPIO.OUT)
if self.GPIO_PUMP != 0:
GPIO.setup(self.GPIO_PUMP, GPIO.OUT)
self.stopFog()
def enablePowerOff(self, GPIO_POWEROFF):
self.GPIO_POWEROFF = GPIO_POWEROFF
if self.GPIO_POWEROFF != 0:
self.powerSwitch = GPIO.setup(self.GPIO_POWEROFF, GPIO.IN)
def loadWaveFolder(self, path):
if not path.endswith('/'):
path += '/'
files = os.listdir(path)
for f in files:
if f.endswith('.wav'):
self.soundfiles.append(path + f)
def lurk(self):
if self.isTurnedOff():
self.poweroff()
self.rgbControl.candle(self.colorFire.r, self.colorFire.g, self.colorFire.b, 0.5)
def speak(self):
self.startFog()
soundfile = self.soundfiles[randint(0, len(self.soundfiles) - 1)]
sound = WaveLoader(soundfile)
duration = sound.frameCount / sound.frameRate
t_now = 0
player = _thread.start_new_thread(call, (['aplay', soundfile],))
t_start = time()
while t_now < duration:
t_now = time() - t_start
amplitude = sound.getCurrentLevelRealtime(t_now)
if amplitude < 0.1:
red = self.colorBackground.r
green = self.colorBackground.g
blue = self.colorBackground.b
else:
amplitude = 1
red = self.color.r * amplitude
green = self.color.g * amplitude
blue = self.color.b * amplitude
self.rgbControl.setColor(red, green, blue)
if t_now > self.fogRuntimeMax:
self.stopFog()
self.stopFog()
def startFog(self):
if self.GPIO_EVAPORATOR != 0:
GPIO.output(self.GPIO_EVAPORATOR, True)
if self.GPIO_PUMP != 0:
GPIO.output(self.GPIO_PUMP, True)
def stopFog(self):
if self.GPIO_EVAPORATOR != 0:
GPIO.output(self.GPIO_EVAPORATOR, False)
if self.GPIO_PUMP != 0:
GPIO.output(self.GPIO_PUMP, False)
def isTriggered(self):
return bool(GPIO.input(self.GPIO_TRIGGER))
def isTurnedOff(self):
if self.GPIO_POWEROFF == 0:
return False
return bool(GPIO.input(self.GPIO_POWEROFF))
def poweroff(self):
call(['poweroff'])
def setAudioColor(self, red, green, blue):
self.color = RGB(red, green, blue)
def setBackgroundColor(self, red, green, blue):
self.colorBackground = RGB(red, green, blue)
def setFireColor(self, red, green, blue):
self.colorFire = RGB(red, green, blue)
def loadAudioFolder(self, path):
if os.path.isdir(path):
if not path.endswith('/'):
path += '/'
for file in os.listdir(path):
if file.endswith('.wav'):
audioFiles.append(audioPath + file)
@staticmethod
def findAudioFiles():
audioFiles = []
for home in os.listdir('/home'):
audioPath = '/home/' + home + '/.config/pumpkin/audio/'
if os.path.isdir(audioPath):
for file in os.listdir(audioPath):
audioFiles.append(audioPath + file)
return audioFiles

5
pumpkin.service Normal file
View File

@ -0,0 +1,5 @@
[Service]
ExecStart=/usr/share/pumpkin/__main__.py
[Install]
WantedBy=multi-user.target

387
rgb.py Normal file
View File

@ -0,0 +1,387 @@
#!/usr/bin/env python3
import RPi.GPIO as GPIO
from time import sleep
from time import time
from random import randint
class RGB:
def __init__(self, red, green, blue):
self.r = red
self.g = green
self.b = blue
def getDutyCycles(self):
percent = RGB.rgbToPerc(self.r, self.g, self.b)
return RGB(percent[0], percent[1], percent[2])
@staticmethod
def sanitizeColor(value):
if value < 0 or value > 255:
print('Value must be between 0 and 255! Fixing color value.')
return max(0, min(value, 255))
return value
@staticmethod
def rgbToPerc(red, green, blue):
percent = 100 / 255
return (red * percent, green * percent, blue * percent)
@staticmethod
def percToRGB(red, green, blue):
percent = 255 / 100
return (red * percent, green * percent, blue * percent)
class RGB_Control:
def __init__(self, r, g, b, freq=60):
self.pins_allowed = (7, 11, 12, 13, 15, 16, 18, 19, 21, 22)
self.frequency = freq
if r not in self.pins_allowed:
print("Fehler: r wurde ungültigem Pin zugewiesen!")
exit()
if g not in self.pins_allowed:
print("Fehler: g wurde ungültigem Pin zugewiesen!")
exit()
if b not in self.pins_allowed:
print("Fehler: b wurde ungültigem Pin zugewiesen!")
exit()
try:
GPIO.setup(r, GPIO.OUT)
GPIO.setup(g, GPIO.OUT)
GPIO.setup(b, GPIO.OUT)
except:
GPIO.setmode(GPIO.BOARD)
GPIO.setup(r, GPIO.OUT)
GPIO.setup(g, GPIO.OUT)
GPIO.setup(b, GPIO.OUT)
self.r = GPIO.PWM(r, freq)
self.g = GPIO.PWM(g, freq)
self.b = GPIO.PWM(b, freq)
self.r.start(0)
self.g.start(0)
self.b.start(0)
self.r_alt = None
self.g_alt = None
self.b_alt = None
self.intensity = 1
self.current_r = 0
self.current_g = 0
self.current_b = 0
def __del__(self):
try:
GPIO.cleanup()
except:
pass
def enableSecondLED(self, gpioRed, gpioGreen, gpioBlue):
GPIO.setup(gpioRed, GPIO.OUT)
GPIO.setup(gpioGreen, GPIO.OUT)
GPIO.setup(gpioBlue, GPIO.OUT)
self.r_alt = GPIO.PWM(gpioRed, self.frequency)
self.g_alt = GPIO.PWM(gpioGreen, self.frequency)
self.b_alt = GPIO.PWM(gpioBlue, self.frequency)
self.r_alt.start(0)
self.g_alt.start(0)
self.b_alt.start(0)
def toggleSpectrum(self,fps=30,speed=1):
self.setColor(255,0,0)
self.transition((255, 255, 0), duration = speed)
self.transition((0, 255, 0), duration = speed)
self.transition((0, 255, 255), duration = speed)
self.transition((0, 0, 255), duration = speed)
self.transition((255, 0, 255), duration = speed)
self.transition((255, 0, 0), duration = speed)
def flash(self, color = (255, 255, 255)):
color_start = (self.current_r, self.current_g, self.current_b)
self.setColor(color[0], color[1], color[2])
sleep(1/30)
self.setColor(color_start[0], color_start[1], color_start[2])
def pulse(self, from_color, to_color, freq=0.5):
duration = freq * 0.5
self.transition(from_color, duration=duration)
self.transition(to_color, duration=duration)
def fire(self, wind = 0.5, r = 255, g = 55, b = 0):
wind_power = 100 - (100 * wind)
flicker = randint(int(wind_power), 100) * 0.01
color_start = (r * flicker, g * flicker, b * flicker)
duration = randint(10, int(40 - wind * 30)) * 0.01
self.transition(color_start, duration = duration)
def panic(self, times = 10, color = (255, 255, 255)):
for t in range(times):
self.flash(color = color)
sleep(1/30)
def setColor(self, r, g, b):
perc = RGB.rgbToPerc(r, g, b)
self.current_r = r
self.current_g = g
self.current_b = b
self.r.ChangeDutyCycle(perc[0] * self.intensity)
self.g.ChangeDutyCycle(perc[1] * self.intensity)
self.b.ChangeDutyCycle(perc[2] * self.intensity)
if self.r_alt != None:
self.r_alt.ChangeDutyCycle(perc[0] * self.intensity)
if self.g_alt != None:
self.g_alt.ChangeDutyCycle(perc[1] * self.intensity)
if self.b_alt != None:
self.b_alt.ChangeDutyCycle(perc[2] * self.intensity)
def transition(self, color_finish, duration = 3, fps = 30):
start_r = self.current_r
start_g = self.current_g
start_b = self.current_b
finish_r = color_finish[0]
finish_g = color_finish[1]
finish_b = color_finish[2]
delta_r = finish_r - start_r
delta_g = finish_g - start_g
delta_b = finish_b - start_b
frames = duration * fps
frame_duration = 1 / fps
# Step per frame for every color
stf_r = delta_r / frames
stf_g = delta_g / frames
stf_b = delta_b / frames
self.setColor(self.current_r, self.current_g, self.current_b)
sleep(frame_duration)
start_time = time()
while time() - start_time < duration:
self.current_r += stf_r
self.current_g += stf_g
self.current_b += stf_b
if self.current_r > 255:
self.current_r = 255
elif self.current_r < 0:
self.current_r = 0
if self.current_g > 255:
self.current_g = 255
elif self.current_g < 0:
self.current_g = 0
if self.current_b > 255:
self.current_b = 255
elif self.current_b < 0:
self.current_b = 0
self.setColor(self.current_r, self.current_g, self.current_b)
sleep(frame_duration)
class RGBMatrix(RGB):
def __init__(self, PWM_FREQUENCY = 60):
self.PWM_FREQUENCY = PWM_FREQUENCY
self.gpiosRed = []
self.gpiosGreen = []
self.gpiosBlue = []
self.currentColor = RGB(0, 0, 0)
GPIO.setmode(GPIO.BOARD)
def __del__(self):
GPIO.cleanup()
def setupLED(self, gpio):
GPIO.setup(gpio, GPIO.OUT)
led = GPIO.PWM(gpio, self.PWM_FREQUENCY)
led.start(0)
return led
def addRed(self, gpio):
led = self.setupLED(gpio)
led.ChangeDutyCycle(self.currentColor.getDutyCycles().r)
self.gpiosRed.append(led)
def addGreen(self, gpio):
led = self.setupLED(gpio)
led.ChangeDutyCycle(self.currentColor.getDutyCycles().g)
self.gpiosGreen.append(led)
def addBlue(self, gpio):
led = self.setupLED(gpio)
led.ChangeDutyCycle(self.currentColor.getDutyCycles().b)
self.gpiosBlue.append(led)
def setColor(self, red, green, blue):
rgb = RGB(red, green, blue)
colorsDutyCycle = rgb.getDutyCycles()
for red in self.gpiosRed:
red.ChangeDutyCycle(colorsDutyCycle.r)
for green in self.gpiosGreen:
green.ChangeDutyCycle(colorsDutyCycle.g)
for blue in self.gpiosBlue:
blue.ChangeDutyCycle(colorsDutyCycle.b)
self.currentColor = RGB(rgb.r, rgb.g, rgb.b)
def fadeToColor(self, red, green, blue, seconds, fps = 30):
timeStart = time()
timeEnd = timeStart + seconds
frameDuration = 1 / fps
colorStart = self.currentColor
colorTarget = RGB(red, green, blue)
deltaRed = colorTarget.r - colorStart.r
deltaGreen = colorTarget.g - colorStart.g
deltaBlue = colorTarget.b - colorStart.b
while time() < timeEnd:
frameStart = time()
progress = (time() - timeStart) / seconds
self.setColor(
colorStart.r + deltaRed * progress,
colorStart.g + deltaGreen * progress,
colorStart.b + deltaBlue * progress
)
duration = time() - frameStart
if duration < frameDuration:
sleep(frameDuration - duration)
self.setColor(red, green, blue)
def candle(self, red, green, blue, wind = 0.5):
darkenMax = 100 - wind * 100
color = RGB(red, green, blue)
intensity = randint(darkenMax, 100) / 100
seconds = randint(darkenMax, 50) / 100
targetColor = RGB(color.r * intensity, color.g * intensity, color.b * intensity)
self.fadeToColor(targetColor.r, targetColor.g, targetColor.b, seconds)
# Chance of 5% for flickering
flickering = randint(0, 100) < 5
if flickering:
intensityLight = 1
intensityDark = 0.9
times = randint(4, 10)
duration = randint(5, 30) / 100
colorLight = RGB(color.r * intensityLight, color.g * intensityLight, color.b * intensityLight)
colorDark = RGB(color.r * intensityDark, color.g * intensityDark, color.b * intensityDark)
intensity = 1
for t in range(times):
progress = (1 / times) * t
intensity = abs(0.5 - progress) * 2
colorDark = RGB(
color.r * intensityDark * intensity,
color.g * intensityDark * intensity,
color.b * intensityDark * intensity
)
self.fadeToColor(colorLight.r, colorLight.g, colorLight.b, duration)
self.fadeToColor(colorDark.r, colorDark.g, colorDark.b, duration)
def toggleSpectrum(self, seconds):
spectrum = [
RGB(255, 0, 0),
RGB(255, 255, 0),
RGB(0, 255, 0),
RGB(0, 255, 255),
RGB(0, 0, 255),
RGB(255, 0, 255)
]
fadeDuration = seconds / len(spectrum)
for color in spectrum:
self.fadeToColor(color.r, color.g, color.b, fadeDuration)
def halloween(control):
while True:
flash = randint(1, 100)
if flash >= 1 and flash <= 5:
t = randint(1, 3)
control.panic(times = t, color = (200, 255, 255))
control.fire(wind = 0.8)
def spectrum(control):
while True:
control.toggleSpectrum()
def fire(control, wind = 0.5):
while True:
control.fire(wind = wind)
if __name__ == "__main__":
try:
control = RGB_Control(11, 12, 13)
fire(control, wind = 0.9)
GPIO.cleanup()
except:
del control

107
waveloader.py Normal file
View File

@ -0,0 +1,107 @@
#!/usr/bin/env python3
import wave
class WaveLoader:
def __init__(self, filename):
self.soundfile = wave.open(filename, 'rb')
self.channels = self.soundfile.getnchannels()
self.sampleWidth = self.soundfile.getsampwidth()
self.frameCount = self.soundfile.getnframes()
self.frameRate = self.soundfile.getframerate()
self.filename = filename
self.amplitudeMax = 2 ** (self.sampleWidth * 8)
self.levelData = {}
self.amplitudeData = {}
def __del__(self):
self.soundfile.close()
def getCurrentLevelRealtime(self, seconds):
frameIndex = int(round(seconds * self.frameRate))
if frameIndex >= self.frameCount:
return 0
self.soundfile.setpos(frameIndex)
level = int.from_bytes(self.soundfile.readframes(1)[0:self.sampleWidth], 'little')
if level > self.amplitudeMax * 0.5:
level -= self.amplitudeMax
return abs(level) / (self.amplitudeMax * 0.5)
def getLevel(self, channel = 0):
if channel < self.channels:
return self.levelData[channel].copy()
def getAmplitude(self, channel = 0):
if channel < self.channels:
return self.amplitudeData[channel].copy()
def getLevelSum(self):
if self.channels == 1:
return self.levelData[0].copy()
levelSum = []
for f in range(self.frameCount):
amplitudeSum = 0
for c in range(self.channels):
amplitudeSum += self.levelData[c][f]
levelSum.append(amplitudeSum / self.channels)
return levelSum
def getCurrentLevel(self, seconds):
frameIndex = int(round(seconds * self.frameRate))
if frameIndex >= self.frameCount:
return 0
level = self.levelData[0][frameIndex]
return level
level = 0
for c in range(self.channels):
level += self.levelData[c][frameIndex]
return level / self.channels
def loadData(self):
self.levelReset()
self.amplitudeReset()
soundfile = wave.open(self.filename, 'rb')
for f in range(self.frameCount):
frame = soundfile.readframes(1)
for c in range(self.channels):
byteOffset = c * self.sampleWidth
amplitude = int.from_bytes(frame[byteOffset:byteOffset + self.sampleWidth], 'little')
if amplitude > self.amplitudeMax * 0.5:
amplitude -= self.amplitudeMax
self.amplitudeData[c].append(amplitude / (self.amplitudeMax * 0.5))
self.levelData[c].append(abs(amplitude) / (self.amplitudeMax * 0.5))
soundfile.close()
def levelReset(self):
self.levelData = {}
for c in range(self.channels):
self.levelData[c] = []
def amplitudeReset(self):
self.amplitudeData = {}
for c in range(self.channels):
self.amplitudeData[c] = []