Lots of little updates behind the scenes
This commit is contained in:
parent
35358e2d75
commit
cd06d65e60
@ -1,13 +1,223 @@
|
|||||||
"""Settings for audio reactive LED strip"""
|
"""Default settings and configuration for audio reactive LED strip"""
|
||||||
from __future__ import print_function
|
from __future__ import print_function
|
||||||
from __future__ import division
|
from __future__ import division
|
||||||
import os
|
import os
|
||||||
|
|
||||||
DEVICE = 'stripless'
|
use_defaults = {"configuration": True, # See notes below for detailed explanation
|
||||||
"""Device used to control LED strip. Must be 'pi', 'esp8266' or 'blinkstick'
|
"GUI_opts": False,
|
||||||
|
"devices": True,
|
||||||
|
"colors": True,
|
||||||
|
"gradients": True}
|
||||||
|
|
||||||
|
settings = { # All settings are stored in this dict
|
||||||
|
|
||||||
|
"configuration":{ # Program configuration
|
||||||
|
'USE_GUI': True, # Whether to display the GUI
|
||||||
|
'DISPLAY_FPS': False, # Whether to print the FPS when running (can reduce performance)
|
||||||
|
'MIC_RATE': 48000, # Sampling frequency of the microphone in Hz
|
||||||
|
'FPS': 60, # Desired refresh rate of the visualization (frames per second)
|
||||||
|
'MIN_FREQUENCY': 20, # Frequencies below this value will be removed during audio processing
|
||||||
|
'MAX_FREQUENCY': 18000, # Frequencies above this value will be removed during audio processing
|
||||||
|
'MAX_BRIGHTNESS': 250, # Frequencies above this value will be removed during audio processing
|
||||||
|
'N_ROLLING_HISTORY': 4, # Number of past audio frames to include in the rolling window
|
||||||
|
'MIN_VOLUME_THRESHOLD': 0.01 # No music visualization displayed if recorded audio volume below threshold
|
||||||
|
#'LOGARITHMIC_SCALING': True, # Scale frequencies logarithmically to match perceived pitch of human ear
|
||||||
|
},
|
||||||
|
|
||||||
|
"GUI_opts":{"Graphs":True, # Which parts of the gui to show
|
||||||
|
"Reactive Effect Buttons":True,
|
||||||
|
"Non Reactive Effect Buttons":True,
|
||||||
|
"Frequency Range":True,
|
||||||
|
"Effect Options":True},
|
||||||
|
|
||||||
|
# All devices and their respective settings. Indexed by name, call each one what you want.
|
||||||
|
"devices":{"Desk Strip":{
|
||||||
|
"configuration":{"TYPE": "esp8266", # Device type (see below for all supported boards)
|
||||||
|
# Required configuration for device. See below for all required keys per device
|
||||||
|
"AUTO_DETECT": True, # Set this true if you're using windows hotspot to connect (see below for more info)
|
||||||
|
"MAC_ADDR": "2c-3a-e8-2f-2c-9f", # MAC address of the ESP8266. Only used if AUTO_DETECT is True
|
||||||
|
"UDP_IP": "192.168.1.208", # IP address of the ESP8266. Must match IP in ws2812_controller.ino
|
||||||
|
"UDP_PORT": 7778, # Port number used for socket communication between Python and ESP8266
|
||||||
|
"MAX_BRIGHTNESS": 250, # Max brightness of output (0-255) (my strip sometimes bugs out with high brightness)
|
||||||
|
# Other configuration
|
||||||
|
"N_PIXELS": 58, # Number of pixels in the LED strip (must match ESP8266 firmware)
|
||||||
|
"N_FFT_BINS": 24, # Number of frequency bins to use when transforming audio to frequency domain
|
||||||
|
"current_effect": "Single" # Currently selected effect for this board, used as default when program launches
|
||||||
|
},
|
||||||
|
|
||||||
|
# Configurable options for this board's effects go in this dictionary.
|
||||||
|
# Usage: config.settings["devices"][name]["effect_opts"][effect][option]
|
||||||
|
"effect_opts":{"Energy": {"blur": 1, # Amount of blur to apply
|
||||||
|
"scale":0.9, # Width of effect on strip
|
||||||
|
"r_multiplier": 1.0, # How much red
|
||||||
|
"g_multiplier": 1.0, # How much green
|
||||||
|
"b_multiplier": 1.0}, # How much blue
|
||||||
|
"Wave": {"color_wave": "Red", # Colour of moving bit
|
||||||
|
"color_flash": "White", # Colour of flashy bit
|
||||||
|
"wipe_len":5, # Initial length of colour bit after beat
|
||||||
|
"decay": 0.7, # How quickly the flash fades away
|
||||||
|
"wipe_speed":2}, # Number of pixels added to colour bit every frame
|
||||||
|
"Spectrum": {"r_multiplier": 1.0, # How much red
|
||||||
|
"g_multiplier": 1.0, # How much green
|
||||||
|
"b_multiplier": 1.0}, # How much blue
|
||||||
|
"Wavelength":{"roll_speed": 0, # How fast (if at all) to cycle colour overlay across strip
|
||||||
|
"color_mode": "Spectral", # Colour gradient to display
|
||||||
|
"mirror": False, # Reflect output down centre of strip
|
||||||
|
"reverse_grad": False, # Flip (LR) gradient
|
||||||
|
"reverse_roll": False, # Reverse movement of gradient roll
|
||||||
|
"blur": 3.0, # Amount of blur to apply
|
||||||
|
"flip_lr":False}, # Flip output left-right
|
||||||
|
"Scroll": {"decay": 0.995, # How quickly the colour fades away as it moves
|
||||||
|
"speed": 1, # Speed of scroll
|
||||||
|
"r_multiplier": 1.0, # How much red
|
||||||
|
"g_multiplier": 1.0, # How much green
|
||||||
|
"b_multiplier": 1.0, # How much blue
|
||||||
|
"blur": 0.2}, # Amount of blur to apply
|
||||||
|
"Power": {"color_mode": "Spectral", # Colour gradient to display
|
||||||
|
"s_count": 20, # Initial number of sparks
|
||||||
|
"s_color": "White", # Color of sparks
|
||||||
|
"mirror": True, # Mirror output down central axis
|
||||||
|
"flip_lr":False}, # Flip output left-right
|
||||||
|
"Single": {"color": "Purple"}, # Static color to show
|
||||||
|
"Beat": {"color": "Red", # Colour of beat flash
|
||||||
|
"decay": 0.7}, # How quickly the flash fades away
|
||||||
|
"Bars": {"resolution":4, # Number of "bars"
|
||||||
|
"color_mode":"Spectral", # Multicolour mode to use
|
||||||
|
"roll_speed":0, # How fast (if at all) to cycle colour colours across strip
|
||||||
|
"mirror": False, # Mirror down centre of strip
|
||||||
|
#"reverse_grad": False, # Flip (LR) gradient
|
||||||
|
"reverse_roll": False, # Reverse movement of gradient roll
|
||||||
|
"flip_lr":False}, # Flip output left-right
|
||||||
|
"Gradient": {"color_mode":"Spectral", # Colour gradient to display
|
||||||
|
"roll_speed": 0, # How fast (if at all) to cycle colour colours across strip
|
||||||
|
"mirror": False, # Mirror gradient down central axis
|
||||||
|
"reverse": False}, # Reverse movement of gradient
|
||||||
|
"Fade": {"color_mode":"Spectral", # Colour gradient to fade through
|
||||||
|
"roll_speed": 1, # How fast (if at all) to fade through colours
|
||||||
|
"reverse": False}, # Reverse "direction" of fade (r->g->b or r<-g<-b)
|
||||||
|
"Calibration":{"r": 100,
|
||||||
|
"g": 100,
|
||||||
|
"b": 100}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Main Strip":{
|
||||||
|
"configuration":{"TYPE": "esp8266", # Device type (see below for all supported boards)
|
||||||
|
# Required configuration for device. See below for all required keys per device
|
||||||
|
"AUTO_DETECT": True, # Set this true if you're using windows hotspot to connect (see below for more info)
|
||||||
|
"MAC_ADDR": "5c-cf-7f-f0-8c-f3", # MAC address of the ESP8266. Only used if AUTO_DETECT is True
|
||||||
|
"UDP_IP": "192.168.1.208", # IP address of the ESP8266. Must match IP in ws2812_controller.ino
|
||||||
|
"UDP_PORT": 7778, # Port number used for socket communication between Python and ESP8266
|
||||||
|
"MAX_BRIGHTNESS": 180, # Max brightness of output (0-255) (my strip sometimes bugs out with high brightness)
|
||||||
|
# Other configuration
|
||||||
|
"N_PIXELS": 226, # Number of pixels in the LED strip (must match ESP8266 firmware)
|
||||||
|
"N_FFT_BINS": 24, # Number of frequency bins to use when transforming audio to frequency domain
|
||||||
|
"current_effect": "Single" # Currently selected effect for this board, used as default when program launches
|
||||||
|
},
|
||||||
|
|
||||||
|
# Configurable options for this board's effects go in this dictionary.
|
||||||
|
# Usage: config.settings["devices"][name]["effect_opts"][effect][option]
|
||||||
|
"effect_opts":{"Energy": {"blur": 1, # Amount of blur to apply
|
||||||
|
"scale":0.9, # Width of effect on strip
|
||||||
|
"r_multiplier": 1.0, # How much red
|
||||||
|
"g_multiplier": 1.0, # How much green
|
||||||
|
"b_multiplier": 1.0}, # How much blue
|
||||||
|
"Wave": {"color_wave": "Red", # Colour of moving bit
|
||||||
|
"color_flash": "White", # Colour of flashy bit
|
||||||
|
"wipe_len":5, # Initial length of colour bit after beat
|
||||||
|
"decay": 0.7, # How quickly the flash fades away
|
||||||
|
"wipe_speed":2}, # Number of pixels added to colour bit every frame
|
||||||
|
"Spectrum": {"r_multiplier": 1.0, # How much red
|
||||||
|
"g_multiplier": 1.0, # How much green
|
||||||
|
"b_multiplier": 1.0}, # How much blue
|
||||||
|
"Wavelength":{"roll_speed": 0, # How fast (if at all) to cycle colour overlay across strip
|
||||||
|
"color_mode": "Spectral", # Colour gradient to display
|
||||||
|
"mirror": False, # Reflect output down centre of strip
|
||||||
|
"reverse_grad": False, # Flip (LR) gradient
|
||||||
|
"reverse_roll": False, # Reverse movement of gradient roll
|
||||||
|
"blur": 3.0, # Amount of blur to apply
|
||||||
|
"flip_lr":False}, # Flip output left-right
|
||||||
|
"Scroll": {"decay": 0.995, # How quickly the colour fades away as it moves
|
||||||
|
"speed": 1, # Speed of scroll
|
||||||
|
"r_multiplier": 1.0, # How much red
|
||||||
|
"g_multiplier": 1.0, # How much green
|
||||||
|
"b_multiplier": 1.0, # How much blue
|
||||||
|
"blur": 0.2}, # Amount of blur to apply
|
||||||
|
"Power": {"color_mode": "Spectral", # Colour gradient to display
|
||||||
|
"s_count": 20, # Initial number of sparks
|
||||||
|
"s_color": "White", # Color of sparks
|
||||||
|
"mirror": True, # Mirror output down central axis
|
||||||
|
"flip_lr":False}, # Flip output left-right
|
||||||
|
"Single": {"color": "Purple"}, # Static color to show
|
||||||
|
"Beat": {"color": "Red", # Colour of beat flash
|
||||||
|
"decay": 0.7}, # How quickly the flash fades away
|
||||||
|
"Bars": {"resolution":4, # Number of "bars"
|
||||||
|
"color_mode":"Spectral", # Multicolour mode to use
|
||||||
|
"roll_speed":0, # How fast (if at all) to cycle colour colours across strip
|
||||||
|
"mirror": False, # Mirror down centre of strip
|
||||||
|
#"reverse_grad": False, # Flip (LR) gradient
|
||||||
|
"reverse_roll": False, # Reverse movement of gradient roll
|
||||||
|
"flip_lr":False}, # Flip output left-right
|
||||||
|
"Gradient": {"color_mode":"Spectral", # Colour gradient to display
|
||||||
|
"roll_speed": 0, # How fast (if at all) to cycle colour colours across strip
|
||||||
|
"mirror": False, # Mirror gradient down central axis
|
||||||
|
"reverse": False}, # Reverse movement of gradient
|
||||||
|
"Fade": {"color_mode":"Spectral", # Colour gradient to fade through
|
||||||
|
"roll_speed": 1, # How fast (if at all) to fade through colours
|
||||||
|
"reverse": False}, # Reverse "direction" of fade (r->g->b or r<-g<-b)
|
||||||
|
"Calibration":{"r": 100,
|
||||||
|
"g": 100,
|
||||||
|
"b": 100}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# Collection of different colours in RGB format
|
||||||
|
"colors":{"Red":(255,0,0),
|
||||||
|
"Orange":(255,40,0),
|
||||||
|
"Yellow":(255,255,0),
|
||||||
|
"Green":(0,255,0),
|
||||||
|
"Blue":(0,0,255),
|
||||||
|
"Light blue":(1,247,161),
|
||||||
|
"Purple":(80,5,252),
|
||||||
|
"Pink":(255,0,178),
|
||||||
|
"White":(255,255,255)},
|
||||||
|
|
||||||
|
# Multicolour gradients. Colours must be in list above
|
||||||
|
"gradients":{"Spectral" : ["Red", "Orange", "Yellow", "Green", "Light blue", "Blue", "Purple", "Pink"],
|
||||||
|
"Dancefloor": ["Red", "Pink", "Purple", "Blue"],
|
||||||
|
"Sunset" : ["Red", "Orange", "Yellow"],
|
||||||
|
"Ocean" : ["Green", "Light blue", "Blue"],
|
||||||
|
"Jungle" : ["Green", "Red", "Orange"],
|
||||||
|
"Sunny" : ["Yellow", "Light blue", "Orange", "Blue"]
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
"""
|
||||||
|
~~ NOTES ~~
|
||||||
|
|
||||||
|
[use_defaults]
|
||||||
|
|
||||||
|
For any dicts in this file (config.py), you can add them into the use_defaults
|
||||||
|
dict to force the program to use these values over any stored in settings.ini
|
||||||
|
that you would have set using the GUI. At runtime, settings.ini is used to update
|
||||||
|
the above dicts with custom set values.
|
||||||
|
|
||||||
|
If you're running a headless RPi, you may want to edit settings in this file, then
|
||||||
|
specify to use the dict you wrote, rather than have the program overwrite from
|
||||||
|
settings.ini at runtime. You could also run the program with the gui, set the
|
||||||
|
settings that you want, then disable the gui and the custom settings will still
|
||||||
|
be loaded. Basically it works as you would expect it to.
|
||||||
|
|
||||||
|
[DEVICE TYPE]
|
||||||
|
|
||||||
|
Device used to control LED strip.
|
||||||
|
|
||||||
'esp8266' means that you are using an ESP8266 module to control the LED strip
|
'esp8266' means that you are using an ESP8266 module to control the LED strip
|
||||||
and commands will be sent to the ESP8266 over WiFi.
|
and commands will be sent to the ESP8266 over WiFi. You can have as many of
|
||||||
|
these as your computer is able to handle.
|
||||||
|
|
||||||
'pi' means that you are using a Raspberry Pi as a standalone unit to process
|
'pi' means that you are using a Raspberry Pi as a standalone unit to process
|
||||||
audio input and control the LED strip directly.
|
audio input and control the LED strip directly.
|
||||||
@ -15,65 +225,45 @@ audio input and control the LED strip directly.
|
|||||||
'blinkstick' means that a BlinkstickPro is connected to this PC which will be used
|
'blinkstick' means that a BlinkstickPro is connected to this PC which will be used
|
||||||
to control the leds connected to it.
|
to control the leds connected to it.
|
||||||
|
|
||||||
|
'fadecandy' means that a fadecandy server is running on your computer and is connected
|
||||||
|
via usb to a fadecandy board connected to LEDs
|
||||||
|
|
||||||
|
'dotstar' creates an APA102-based output device. LMK if you have any success
|
||||||
|
getting this to work becuase i have no clue if it will.
|
||||||
|
|
||||||
'stripless' means that the program will run without sending data to a strip.
|
'stripless' means that the program will run without sending data to a strip.
|
||||||
Useful for development etc, but doesn't look half as good ;)
|
Useful for development etc, but doesn't look half as good ;)
|
||||||
"""
|
|
||||||
|
|
||||||
if DEVICE == 'esp8266':
|
[REQUIRED CONFIGURATION KEYS]
|
||||||
AUTO_DETECT = False
|
|
||||||
"""Set to true if the ip address of the device changes. This is the case if it's connecting
|
|
||||||
through windows hotspot for instance. If so, give the mac address of the device."""
|
|
||||||
MAC_ADDR = "5c-cf-7f-f0-8c-f3"
|
|
||||||
"""MAC address of the ESP8266."""
|
|
||||||
UDP_IP = "192.168.1.68"
|
|
||||||
"""IP address of the ESP8266.
|
|
||||||
Unless using auto detect, it must match IP in ws2812_controller.ino"""
|
|
||||||
UDP_PORT = 7778
|
|
||||||
"""Port number used for socket communication between Python and ESP8266"""
|
|
||||||
SOFTWARE_GAMMA_CORRECTION = False
|
|
||||||
"""Set to False because the firmware handles gamma correction + dither"""
|
|
||||||
|
|
||||||
elif DEVICE == 'pi':
|
===== 'esp8266'
|
||||||
LED_PIN = 18
|
"AUTO_DETECT" # Set this true if you're using windows hotspot to connect (see below for more info)
|
||||||
"""GPIO pin connected to the LED strip pixels (must support PWM)"""
|
"MAC_ADDR" # MAC address of the ESP8266. Only used if AUTO_DETECT is True
|
||||||
LED_FREQ_HZ = 800000
|
"UDP_IP" # IP address of the ESP8266. Must match IP in ws2812_controller.ino
|
||||||
"""LED signal frequency in Hz (usually 800kHz)"""
|
"UDP_PORT" # Port number used for socket communication between Python and ESP8266
|
||||||
LED_DMA = 5
|
===== 'pi'
|
||||||
"""DMA channel used for generating PWM signal (try 5)"""
|
"LED_PIN" # GPIO pin connected to the LED strip pixels (must support PWM)
|
||||||
BRIGHTNESS = 255
|
"LED_FREQ_HZ" # LED signal frequency in Hz (usually 800kHz)
|
||||||
"""Brightness of LED strip between 0 and 255"""
|
"LED_DMA" # DMA channel used for generating PWM signal (try 5)
|
||||||
LED_INVERT = True
|
"BRIGHTNESS" # Brightness of LED strip between 0 and 255
|
||||||
"""Set True if using an inverting logic level converter"""
|
"LED_INVERT" # Set True if using an inverting logic level converter
|
||||||
SOFTWARE_GAMMA_CORRECTION = True
|
===== 'blinkstick'
|
||||||
"""Set to True because Raspberry Pi doesn't use hardware dithering"""
|
No required configuration keys
|
||||||
|
===== 'fadecandy'
|
||||||
|
"SERVER" # Address of fadecandy server. (usually 'localhost:7890')
|
||||||
|
===== 'dotstar'
|
||||||
|
No required configuration keys
|
||||||
|
===== 'stripless'
|
||||||
|
No required configuration keys (heh)
|
||||||
|
|
||||||
elif DEVICE == 'blinkstick':
|
[AUTO_DETECT]
|
||||||
SOFTWARE_GAMMA_CORRECTION = True
|
|
||||||
"""Set to True because blinkstick doesn't use hardware dithering"""
|
|
||||||
|
|
||||||
elif DEVICE == 'stripless':
|
Set to true if the ip address of the device changes. This is the case if it's connecting
|
||||||
pass
|
through windows hotspot, for instance. If so, give the mac address of the device. This
|
||||||
|
allows windows to look for the device's IP using "arp -a" and finding the matching
|
||||||
|
mac address. I haven't tested this on Linux or macOS.
|
||||||
|
|
||||||
else:
|
[FPS]
|
||||||
raise ValueError("Invalid device selected. Device {} not known.".format(DEVICE))
|
|
||||||
|
|
||||||
USE_GUI = True
|
|
||||||
"""Whether or not to display a PyQtGraph GUI plot of visualization"""
|
|
||||||
|
|
||||||
DISPLAY_FPS = False
|
|
||||||
"""Whether to display the FPS when running (can reduce performance)"""
|
|
||||||
|
|
||||||
N_PIXELS = 242
|
|
||||||
"""Number of pixels in the LED strip (must match ESP8266 firmware)"""
|
|
||||||
|
|
||||||
GAMMA_TABLE_PATH = os.path.join(os.path.dirname(__file__), 'gamma_table.npy')
|
|
||||||
"""Location of the gamma correction table"""
|
|
||||||
|
|
||||||
MIC_RATE = 48000
|
|
||||||
"""Sampling frequency of the microphone in Hz"""
|
|
||||||
|
|
||||||
FPS = 40
|
|
||||||
"""Desired refresh rate of the visualization (frames per second)
|
|
||||||
|
|
||||||
FPS indicates the desired refresh rate, or frames-per-second, of the audio
|
FPS indicates the desired refresh rate, or frames-per-second, of the audio
|
||||||
visualization. The actual refresh rate may be lower if the computer cannot keep
|
visualization. The actual refresh rate may be lower if the computer cannot keep
|
||||||
@ -87,21 +277,8 @@ appear "sluggish" or out of sync with the audio being played if it is too low.
|
|||||||
|
|
||||||
The FPS should not exceed the maximum refresh rate of the LED strip, which
|
The FPS should not exceed the maximum refresh rate of the LED strip, which
|
||||||
depends on how long the LED strip is.
|
depends on how long the LED strip is.
|
||||||
"""
|
|
||||||
_max_led_FPS = int(((N_PIXELS * 30e-6) + 50e-6)**-1.0)
|
|
||||||
assert FPS <= _max_led_FPS, 'FPS must be <= {}'.format(_max_led_FPS)
|
|
||||||
|
|
||||||
MIN_FREQUENCY = 20
|
[N_FFT_BINS]
|
||||||
"""Frequencies below this value will be removed during audio processing"""
|
|
||||||
|
|
||||||
MAX_FREQUENCY = 18000
|
|
||||||
"""Frequencies above this value will be removed during audio processing"""
|
|
||||||
|
|
||||||
LOGARITHMIC_SCALING = True
|
|
||||||
"""Scale frequencies logarithmically to match perceived pitch of human ear"""
|
|
||||||
|
|
||||||
N_FFT_BINS = 24
|
|
||||||
"""Number of frequency bins to use when transforming audio to frequency domain
|
|
||||||
|
|
||||||
Fast Fourier transforms are used to transform time-domain audio data to the
|
Fast Fourier transforms are used to transform time-domain audio data to the
|
||||||
frequency domain. The frequencies present in the audio signal are assigned
|
frequency domain. The frequencies present in the audio signal are assigned
|
||||||
@ -115,8 +292,28 @@ number of bins. More bins is not always better!
|
|||||||
There is no point using more bins than there are pixels on the LED strip.
|
There is no point using more bins than there are pixels on the LED strip.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
N_ROLLING_HISTORY = 4
|
for board in settings["devices"]:
|
||||||
"""Number of past audio frames to include in the rolling window"""
|
if settings["devices"][board]["configuration"]["TYPE"] == 'esp8266':
|
||||||
|
settings["devices"][board]["configuration"]["SOFTWARE_GAMMA_CORRECTION"] = False
|
||||||
|
# Set to False because the firmware handles gamma correction + dither
|
||||||
|
elif settings["devices"][board]["configuration"]["TYPE"] == 'pi':
|
||||||
|
settings["devices"][board]["configuration"]["SOFTWARE_GAMMA_CORRECTION"] = True
|
||||||
|
# Set to True because Raspberry Pi doesn't use hardware dithering
|
||||||
|
elif settings["devices"][board]["configuration"]["TYPE"] == 'blinkstick':
|
||||||
|
settings["devices"][board]["configuration"]["SOFTWARE_GAMMA_CORRECTION"] = True
|
||||||
|
elif settings["devices"][board]["configuration"]["TYPE"] == 'dotstar':
|
||||||
|
settings["devices"][board]["configuration"]["SOFTWARE_GAMMA_CORRECTION"] = False
|
||||||
|
elif settings["devices"][board]["configuration"]["TYPE"] == 'fadecandy':
|
||||||
|
settings["devices"][board]["configuration"]["SOFTWARE_GAMMA_CORRECTION"] = False
|
||||||
|
elif settings["devices"][board]["configuration"]["TYPE"] == 'stripless':
|
||||||
|
settings["devices"][board]["configuration"]["SOFTWARE_GAMMA_CORRECTION"] = False
|
||||||
|
else:
|
||||||
|
raise ValueError("Invalid device selected. Device {} not known.".format(settings["devices"][board]["configuration"]["TYPE"]))
|
||||||
|
settings["devices"][board]["effect_opts"]["Power"]["s_count"] = settings["devices"][board]["configuration"]["N_PIXELS"]//6
|
||||||
|
# Cheeky lil fix in case the user sets an odd number of LEDs
|
||||||
|
if settings["devices"][board]["configuration"]["N_PIXELS"] % 2:
|
||||||
|
settings["devices"][board]["configuration"]["N_PIXELS"] -= 1
|
||||||
|
|
||||||
|
# Ignore these
|
||||||
|
# settings["configuration"]['_max_led_FPS'] = int(((settings["configuration"]["N_PIXELS"] * 30e-6) + 50e-6)**-1.0)
|
||||||
|
|
||||||
MIN_VOLUME_THRESHOLD = 1e-3
|
|
||||||
"""No music visualization displayed if recorded audio volume below threshold"""
|
|
||||||
|
289
python/lib/devices.py
Normal file
289
python/lib/devices.py
Normal file
@ -0,0 +1,289 @@
|
|||||||
|
import time
|
||||||
|
import numpy as np
|
||||||
|
import lib.config as config
|
||||||
|
|
||||||
|
_GAMMA_TABLE = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1,
|
||||||
|
1, 1, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5,
|
||||||
|
5, 5, 6, 6, 6, 7, 7, 7, 8, 8, 8, 9, 9, 9, 10, 10, 11,
|
||||||
|
11, 11, 12, 12, 13, 13, 14, 14, 15, 15, 16, 16, 17, 17,
|
||||||
|
18, 18, 19, 19, 20, 20, 21, 21, 22, 23, 23, 24, 24, 25,
|
||||||
|
26, 26, 27, 28, 28, 29, 30, 30, 31, 32, 32, 33, 34, 35,
|
||||||
|
35, 36, 37, 38, 38, 39, 40, 41, 42, 42, 43, 44, 45, 46,
|
||||||
|
47, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 56, 57, 58,
|
||||||
|
59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 73,
|
||||||
|
74, 75, 76, 77, 78, 79, 80, 81, 82, 84, 85, 86, 87, 88,
|
||||||
|
89, 91, 92, 93, 94, 95, 97, 98, 99, 100, 102, 103, 104,
|
||||||
|
105, 107, 108, 109, 111, 112, 113, 115, 116, 117, 119,
|
||||||
|
120, 121, 123, 124, 126, 127, 128, 130, 131, 133, 134,
|
||||||
|
136, 137, 139, 140, 142, 143, 145, 146, 148, 149, 151,
|
||||||
|
152, 154, 155, 157, 158, 160, 162, 163, 165, 166, 168,
|
||||||
|
170, 171, 173, 175, 176, 178, 180, 181, 183, 185, 186,
|
||||||
|
188, 190, 192, 193, 195, 197, 199, 200, 202, 204, 206,
|
||||||
|
207, 209, 211, 213, 215, 217, 218, 220, 222, 224, 226,
|
||||||
|
228, 230, 232, 233, 235, 237, 239, 241, 243, 245, 247,
|
||||||
|
249, 251, 253, 255]
|
||||||
|
_GAMMA_TABLE = np.array(_GAMMA_TABLE)
|
||||||
|
|
||||||
|
class LEDController:
|
||||||
|
"""Base class for interfacing with hardware LED strip controllers
|
||||||
|
To add support for another hardware device, simply inherit this class
|
||||||
|
and implement the show() method.
|
||||||
|
Example usage:
|
||||||
|
import numpy as np
|
||||||
|
N_pixels = 60
|
||||||
|
pixels = np.random.random(size=(3, N_pixels))
|
||||||
|
device = LEDController()
|
||||||
|
device.show(pixels)
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def show(self, pixels):
|
||||||
|
"""Set LED pixels to the values given in the array
|
||||||
|
This function accepts an array of RGB pixel values (pixels)
|
||||||
|
and displays them on the LEDs. To add support for another
|
||||||
|
hardware device, you should create a class that inherits from
|
||||||
|
this class, and then implement this method.
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
pixels: numpy.ndarray
|
||||||
|
2D array containing RGB pixel values for each of the LEDs.
|
||||||
|
The shape of the array is (3, n_pixels), where n_pixels is the
|
||||||
|
number of LEDs that the device has.
|
||||||
|
The array is formatted as shown below. There are three rows
|
||||||
|
(axis 0) which represent the red, green, and blue color channels.
|
||||||
|
Each column (axis 1) contains the red, green, and blue color values
|
||||||
|
for a single pixel:
|
||||||
|
np.array([ [r0, ..., rN], [g0, ..., gN], [b0, ..., bN]])
|
||||||
|
Each value brightness value is an integer between 0 and 255.
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
None
|
||||||
|
"""
|
||||||
|
raise NotImplementedError('Show() was not implemented')
|
||||||
|
|
||||||
|
def test(self, n_pixels):
|
||||||
|
pixels = np.zeros((3, n_pixels))
|
||||||
|
pixels[0][0] = 255
|
||||||
|
pixels[1][1] = 255
|
||||||
|
pixels[2][2] = 255
|
||||||
|
print('Starting LED strip test.')
|
||||||
|
print('Press CTRL+C to stop the test at any time.')
|
||||||
|
print('You should see a scrolling red, green, and blue pixel.')
|
||||||
|
while True:
|
||||||
|
self.show(pixels)
|
||||||
|
pixels = np.roll(pixels, 1, axis=1)
|
||||||
|
time.sleep(0.2)
|
||||||
|
|
||||||
|
|
||||||
|
class ESP8266(LEDController):
|
||||||
|
def __init__(self, auto_detect=False,
|
||||||
|
mac_addr="aa-bb-cc-dd-ee-ff",
|
||||||
|
ip='192.168.0.150',
|
||||||
|
port=7778):
|
||||||
|
"""Initialize object for communicating with as ESP8266
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
auto_detect: bool, optional
|
||||||
|
Automatically search for and find devices on windows hotspot
|
||||||
|
with given mac addresses. Windows hotspot resets the IP
|
||||||
|
addresses of any devices on reset, meaning the IP of the
|
||||||
|
ESP8266 changes every time you turn on the hotspot. This
|
||||||
|
will find the IP address of the devices for you.
|
||||||
|
mac_addr: str, optional
|
||||||
|
The MAC address of the ESP8266 on the network. Only used if
|
||||||
|
auto-detect is used
|
||||||
|
ip: str, optional
|
||||||
|
The IP address of the ESP8266 on the network. This must exactly
|
||||||
|
match the IP address of your ESP8266 device, unless using
|
||||||
|
the auto-detect feature.
|
||||||
|
port: int, optional
|
||||||
|
The port number to use when sending data to the ESP8266. This
|
||||||
|
must exactly match the port number in the ESP8266's firmware.
|
||||||
|
"""
|
||||||
|
import socket
|
||||||
|
self._mac_addr = mac_addr
|
||||||
|
self._ip = ip
|
||||||
|
self._port = port
|
||||||
|
self._sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||||
|
if auto_detect:
|
||||||
|
self.detect()
|
||||||
|
|
||||||
|
def detect(self):
|
||||||
|
from subprocess import check_output
|
||||||
|
from time import sleep
|
||||||
|
""" Uses "arp -a" to find esp8266 on windows hotspot"""
|
||||||
|
# Find the audio strip automagically
|
||||||
|
ip_addr = False
|
||||||
|
while not ip_addr:
|
||||||
|
arp_out = check_output(['arp', '-a']).splitlines()
|
||||||
|
for i in arp_out:
|
||||||
|
if self._mac_addr in str(i):
|
||||||
|
ip_addr = i.split()[0].decode("utf-8")
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
print("Device not found at physical address {}, retrying in 1s".format(self._mac_addr))
|
||||||
|
sleep(1)
|
||||||
|
print("Found device {}, with IP address {}".format(self._mac_addr, ip_addr))
|
||||||
|
self._ip = ip_addr
|
||||||
|
|
||||||
|
def show(self, pixels):
|
||||||
|
"""Sends UDP packets to ESP8266 to update LED strip values
|
||||||
|
The ESP8266 will receive and decode the packets to determine what values
|
||||||
|
to display on the LED strip. The communication protocol supports LED strips
|
||||||
|
with a maximum of 256 LEDs.
|
||||||
|
The packet encoding scheme is:
|
||||||
|
|i|r|g|b|
|
||||||
|
where
|
||||||
|
i (0 to 255): Index of LED to change (zero-based)
|
||||||
|
r (0 to 255): Red value of LED
|
||||||
|
g (0 to 255): Green value of LED
|
||||||
|
b (0 to 255): Blue value of LED
|
||||||
|
"""
|
||||||
|
message = pixels.T.clip(0, config.settings["configuration"]["MAX_BRIGHTNESS"]).astype(np.uint8).ravel().tostring()
|
||||||
|
self._sock.sendto(message, (self._ip, self._port))
|
||||||
|
|
||||||
|
|
||||||
|
class FadeCandy(LEDController):
|
||||||
|
def __init__(self, server='localhost:7890'):
|
||||||
|
"""Initializes object for communicating with a FadeCandy device
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
server: str, optional
|
||||||
|
FadeCandy server used to communicate with the FadeCandy device.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
import audioled.opc
|
||||||
|
except ImportError as e:
|
||||||
|
print('Unable to import audioled.opc library')
|
||||||
|
print('You can install this library with `pip install opc`')
|
||||||
|
raise e
|
||||||
|
self.client = audioled.opc.Client(server)
|
||||||
|
if self.client.can_connect():
|
||||||
|
print('Successfully connected to FadeCandy server.')
|
||||||
|
else:
|
||||||
|
print('Could not connect to FadeCandy server.')
|
||||||
|
print('Ensure that fcserver is running and try again.')
|
||||||
|
|
||||||
|
def show(self, pixels):
|
||||||
|
self.client.put_pixels(pixels.T.clip(0, 255).astype(int).tolist())
|
||||||
|
|
||||||
|
|
||||||
|
class BlinkStick(LEDController):
|
||||||
|
def __init__(self):
|
||||||
|
"""Initializes a BlinkStick controller"""
|
||||||
|
try:
|
||||||
|
from blinkstick import blinkstick
|
||||||
|
except ImportError as e:
|
||||||
|
print('Unable to import the blinkstick library')
|
||||||
|
print('You can install this library with `pip install blinkstick`')
|
||||||
|
raise e
|
||||||
|
self.stick = blinkstick.find_first()
|
||||||
|
|
||||||
|
def show(self, pixels):
|
||||||
|
"""Writes new LED values to the Blinkstick.
|
||||||
|
This function updates the LED strip with new values.
|
||||||
|
"""
|
||||||
|
# Truncate values and cast to integer
|
||||||
|
n_pixels = pixels.shape[1]
|
||||||
|
pixels = pixels.clip(0, 255).astype(int)
|
||||||
|
pixels = _GAMMA_TABLE[pixels]
|
||||||
|
# Read the rgb values
|
||||||
|
r = pixels[0][:].astype(int)
|
||||||
|
g = pixels[1][:].astype(int)
|
||||||
|
b = pixels[2][:].astype(int)
|
||||||
|
|
||||||
|
# Create array in which we will store the led states
|
||||||
|
newstrip = [None] * (n_pixels * 3)
|
||||||
|
|
||||||
|
for i in range(n_pixels):
|
||||||
|
# Blinkstick uses GRB format
|
||||||
|
newstrip[i * 3] = g[i]
|
||||||
|
newstrip[i * 3 + 1] = r[i]
|
||||||
|
newstrip[i * 3 + 2] = b[i]
|
||||||
|
# Send the data to the blinkstick
|
||||||
|
self.stick.set_led_data(0, newstrip)
|
||||||
|
|
||||||
|
|
||||||
|
class RaspberryPi(LEDController):
|
||||||
|
def __init__(self, n_pixels, pin=18, invert_logic=False,
|
||||||
|
freq=800000, dma=5):
|
||||||
|
"""Creates a Raspberry Pi output device
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
n_pixels: int
|
||||||
|
Number of LED strip pixels
|
||||||
|
pin: int, optional
|
||||||
|
GPIO pin used to drive the LED strip (must be a PWM pin).
|
||||||
|
Pin 18 can be used on the Raspberry Pi 2.
|
||||||
|
invert_logic: bool, optional
|
||||||
|
Whether or not to invert the driving logic.
|
||||||
|
Set this to True if you are using an inverting logic level
|
||||||
|
converter, otherwise set to False.
|
||||||
|
freq: int, optional
|
||||||
|
LED strip protocol frequency (Hz). For ws2812 this is 800000.
|
||||||
|
dma: int, optional
|
||||||
|
DMA (direct memory access) channel used to drive PWM signals.
|
||||||
|
If you aren't sure, try 5.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
import neopixel
|
||||||
|
except ImportError as e:
|
||||||
|
url = 'learn.adafruit.com/neopixels-on-raspberry-pi/software'
|
||||||
|
print('Could not import the neopixel library')
|
||||||
|
print('For installation instructions, see {}'.format(url))
|
||||||
|
raise e
|
||||||
|
self.strip = neopixel.Adafruit_NeoPixel(n_pixels, pin, freq, dma,
|
||||||
|
invert_logic, 255)
|
||||||
|
self.strip.begin()
|
||||||
|
|
||||||
|
def show(self, pixels):
|
||||||
|
"""Writes new LED values to the Raspberry Pi's LED strip
|
||||||
|
Raspberry Pi uses the rpi_ws281x to control the LED strip directly.
|
||||||
|
This function updates the LED strip with new values.
|
||||||
|
"""
|
||||||
|
# Truncate values and cast to integer
|
||||||
|
n_pixels = pixels.shape[1]
|
||||||
|
pixels = pixels.clip(0, 255).astype(int)
|
||||||
|
# Optional gamma correction
|
||||||
|
pixels = _GAMMA_TABLE[pixels]
|
||||||
|
# Encode 24-bit LED values in 32 bit integers
|
||||||
|
r = np.left_shift(pixels[0][:].astype(int), 8)
|
||||||
|
g = np.left_shift(pixels[1][:].astype(int), 16)
|
||||||
|
b = pixels[2][:].astype(int)
|
||||||
|
rgb = np.bitwise_or(np.bitwise_or(r, g), b)
|
||||||
|
# Update the pixels
|
||||||
|
for i in range(n_pixels):
|
||||||
|
self.strip._led_data[i] = rgb[i]
|
||||||
|
self.strip.show()
|
||||||
|
|
||||||
|
|
||||||
|
class DotStar(LEDController):
|
||||||
|
def __init__(self, pixels, brightness=31):
|
||||||
|
"""Creates an APA102-based output device
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
pixels: int
|
||||||
|
Number of LED strip pixels
|
||||||
|
brightness: int, optional
|
||||||
|
Global brightness
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
import apa102
|
||||||
|
except ImportError as e:
|
||||||
|
url = 'https://github.com/tinue/APA102_Pi'
|
||||||
|
print('Could not import the apa102 library')
|
||||||
|
print('For installation instructions, see {}'.format(url))
|
||||||
|
raise e
|
||||||
|
self.strip = apa102.APA102(numLEDs=pixels, globalBrightness=brightness) # Initialize the strip
|
||||||
|
led_data = np.array(self.strip.leds, dtype=np.uint8)
|
||||||
|
# memoryview preserving the first 8 bits of LED frames (w/ global brightness)
|
||||||
|
self.strip.leds = led_data.data
|
||||||
|
# 2D view of led_data
|
||||||
|
self.led_data = led_data.reshape((pixels, 4)) # or (-1, 4)
|
||||||
|
|
||||||
|
def show(self, pixels):
|
||||||
|
bgr = [2,1,0]
|
||||||
|
self.led_data[0:,1:4] = pixels[bgr].T.clip(0,255)
|
||||||
|
self.strip.show()
|
@ -1,7 +1,7 @@
|
|||||||
from __future__ import print_function
|
from __future__ import print_function
|
||||||
import numpy as np
|
import numpy as np
|
||||||
import config
|
import lib.config as config
|
||||||
import melbank
|
import lib.melbank as melbank
|
||||||
|
|
||||||
|
|
||||||
class ExpFilter:
|
class ExpFilter:
|
||||||
@ -24,30 +24,3 @@ class ExpFilter:
|
|||||||
self.value = alpha * value + (1.0 - alpha) * self.value
|
self.value = alpha * value + (1.0 - alpha) * self.value
|
||||||
return self.value
|
return self.value
|
||||||
|
|
||||||
|
|
||||||
def rfft(data, window=None):
|
|
||||||
window = 1.0 if window is None else window(len(data))
|
|
||||||
ys = np.abs(np.fft.rfft(data * window))
|
|
||||||
xs = np.fft.rfftfreq(len(data), 1.0 / config.MIC_RATE)
|
|
||||||
return xs, ys
|
|
||||||
|
|
||||||
|
|
||||||
def fft(data, window=None):
|
|
||||||
window = 1.0 if window is None else window(len(data))
|
|
||||||
ys = np.fft.fft(data * window)
|
|
||||||
xs = np.fft.fftfreq(len(data), 1.0 / config.MIC_RATE)
|
|
||||||
return xs, ys
|
|
||||||
|
|
||||||
|
|
||||||
def create_mel_bank():
|
|
||||||
global samples, mel_y, mel_x
|
|
||||||
samples = int(config.MIC_RATE * config.N_ROLLING_HISTORY / (2.0 * config.FPS))
|
|
||||||
mel_y, (_, mel_x) = melbank.compute_melmat(num_mel_bands=config.N_FFT_BINS,
|
|
||||||
freq_min=config.MIN_FREQUENCY,
|
|
||||||
freq_max=config.MAX_FREQUENCY,
|
|
||||||
num_fft_bands=samples,
|
|
||||||
sample_rate=config.MIC_RATE)
|
|
||||||
samples = None
|
|
||||||
mel_y = None
|
|
||||||
mel_x = None
|
|
||||||
create_mel_bank()
|
|
||||||
|
BIN
python/lib/gamma_table.npy
Normal file
BIN
python/lib/gamma_table.npy
Normal file
Binary file not shown.
@ -1,51 +1,276 @@
|
|||||||
from __future__ import print_function
|
#from __future__ import print_function
|
||||||
from __future__ import division
|
#from __future__ import division
|
||||||
import time
|
#from scipy.ndimage.filters import gaussian_filter1d
|
||||||
|
#from collections import deque
|
||||||
|
#import time
|
||||||
|
#import sys
|
||||||
import numpy as np
|
import numpy as np
|
||||||
from pyqtgraph.Qt import QtGui
|
import lib.config as config
|
||||||
|
#import microphone
|
||||||
|
#import dsp
|
||||||
|
#import led
|
||||||
|
#import random
|
||||||
|
|
||||||
|
from lib.qrangeslider import QRangeSlider
|
||||||
|
from lib.qfloatslider import QFloatSlider
|
||||||
import pyqtgraph as pg
|
import pyqtgraph as pg
|
||||||
from pyqtgraph.dockarea import *
|
from PyQt5.QtCore import *
|
||||||
|
from PyQt5.QtWidgets import *
|
||||||
|
|
||||||
|
class GUI(QMainWindow):
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__()
|
||||||
|
self.settings = QSettings('settings.ini', QSettings.IniFormat)
|
||||||
|
self.settings.setFallbacksEnabled(False) # File only, no fallback to registry or or.
|
||||||
|
self.initUI()
|
||||||
|
|
||||||
|
def hideGraphs(self):
|
||||||
|
print("Blah")
|
||||||
|
|
||||||
|
def hideOpts(self):
|
||||||
|
print("Bleh")
|
||||||
|
|
||||||
|
def config.settings["configuration"]["configDialogue"](self):
|
||||||
|
self.d = QDialog(None, Qt.WindowSystemMenuHint | Qt.WindowCloseButtonHint)
|
||||||
|
b1 = QPushButton("ok",self.d)
|
||||||
|
b1.move(50,50)
|
||||||
|
self.d.setWindowTitle("Dialog")
|
||||||
|
self.d.setWindowModality(Qt.ApplicationModal)
|
||||||
|
self.d.show()
|
||||||
|
|
||||||
|
def initUI(self):
|
||||||
|
# ==================================== Set up window and wrapping layout
|
||||||
|
self.setWindowTitle("Visualization")
|
||||||
|
wrapper = QVBoxLayout()
|
||||||
|
|
||||||
|
# ======================================================= Set up toolbar
|
||||||
|
#toolbar_hideGraphs.setShortcut('Ctrl+H')
|
||||||
|
toolbar_hideGraphs = QAction('GUI Properties', self)
|
||||||
|
toolbar_hideGraphs.triggered.connect(self.config.settings["configuration"]["configDialogue"])
|
||||||
|
toolbar_hideOpts = QAction('Hide Opts', self)
|
||||||
|
toolbar_hideOpts.triggered.connect(self.hideOpts)
|
||||||
|
|
||||||
|
self.toolbar = self.addToolBar('Toolbar')
|
||||||
|
self.toolbar.addAction(toolbar_hideGraphs)
|
||||||
|
self.toolbar.addAction(toolbar_hideOpts)
|
||||||
|
|
||||||
|
# ========================================== Set up FPS and error labels
|
||||||
|
labels_layout = QHBoxLayout()
|
||||||
|
self.label_error = QLabel("")
|
||||||
|
self.label_fps = QLabel("")
|
||||||
|
self.label_latency = QLabel("")
|
||||||
|
self.label_fps.setAlignment(Qt.AlignRight | Qt.AlignVCenter)
|
||||||
|
self.label_latency.setAlignment(Qt.AlignRight | Qt.AlignVCenter)
|
||||||
|
labels_layout.addWidget(self.label_error)
|
||||||
|
labels_layout.addStretch()
|
||||||
|
labels_layout.addWidget(self.label_latency)
|
||||||
|
labels_layout.addWidget(self.label_fps)
|
||||||
|
|
||||||
|
# ================================================== Set up graph layout
|
||||||
|
graph_view = pg.GraphicsView()
|
||||||
|
graph_layout = pg.GraphicsLayout(border=(100,100,100))
|
||||||
|
graph_view.setCentralItem(graph_layout)
|
||||||
|
# Mel filterbank plot
|
||||||
|
fft_plot = graph_layout.addPlot(title='Filterbank Output', colspan=3)
|
||||||
|
fft_plot.setRange(yRange=[-0.1, 1.2])
|
||||||
|
fft_plot.disableAutoRange(axis=pg.ViewBox.YAxis)
|
||||||
|
x_data = np.array(range(1, config.settings["configuration"]["N_FFT_BINS"] + 1))
|
||||||
|
self.mel_curve = pg.PlotCurveItem()
|
||||||
|
self.mel_curve.setData(x=x_data, y=x_data*0)
|
||||||
|
fft_plot.addItem(self.mel_curve)
|
||||||
|
# Visualization plot
|
||||||
|
graph_layout.nextRow()
|
||||||
|
led_plot = graph_layout.addPlot(title='Visualization Output', colspan=3)
|
||||||
|
led_plot.setRange(yRange=[-5, 260])
|
||||||
|
led_plot.disableAutoRange(axis=pg.ViewBox.YAxis)
|
||||||
|
# Pen for each of the color channel curves
|
||||||
|
r_pen = pg.mkPen((255, 30, 30, 200), width=4)
|
||||||
|
g_pen = pg.mkPen((30, 255, 30, 200), width=4)
|
||||||
|
b_pen = pg.mkPen((30, 30, 255, 200), width=4)
|
||||||
|
# Color channel curves
|
||||||
|
self.r_curve = pg.PlotCurveItem(pen=r_pen)
|
||||||
|
self.g_curve = pg.PlotCurveItem(pen=g_pen)
|
||||||
|
self.b_curve = pg.PlotCurveItem(pen=b_pen)
|
||||||
|
# Define x data
|
||||||
|
x_data = np.array(range(1, config.settings["configuration"]["N_PIXELS"] + 1))
|
||||||
|
self.r_curve.setData(x=x_data, y=x_data*0)
|
||||||
|
self.g_curve.setData(x=x_data, y=x_data*0)
|
||||||
|
self.b_curve.setData(x=x_data, y=x_data*0)
|
||||||
|
# Add curves to plot
|
||||||
|
led_plot.addItem(self.r_curve)
|
||||||
|
led_plot.addItem(self.g_curve)
|
||||||
|
led_plot.addItem(self.b_curve)
|
||||||
|
|
||||||
|
# ================================================= Set up button layout
|
||||||
|
label_reactive = QLabel("Audio Reactive Effects")
|
||||||
|
label_non_reactive = QLabel("Non Reactive Effects")
|
||||||
|
reactive_button_grid = QGridLayout()
|
||||||
|
non_reactive_button_grid = QGridLayout()
|
||||||
|
buttons = {}
|
||||||
|
connecting_funcs = {}
|
||||||
|
grid_width = 4
|
||||||
|
i = 0
|
||||||
|
j = 0
|
||||||
|
k = 0
|
||||||
|
l = 0
|
||||||
|
# Dynamically layout reactive_buttons and connect them to the visualisation effects
|
||||||
|
def connect_generator(effect):
|
||||||
|
def func():
|
||||||
|
visualizer.current_effect = effect
|
||||||
|
buttons[effect].setDown(True)
|
||||||
|
func.__name__ = effect
|
||||||
|
return func
|
||||||
|
# Where the magic happens
|
||||||
|
for effect in visualizer.effects:
|
||||||
|
if not effect in visualizer.non_reactive_effects:
|
||||||
|
connecting_funcs[effect] = connect_generator(effect)
|
||||||
|
buttons[effect] = QPushButton(effect)
|
||||||
|
buttons[effect].clicked.connect(connecting_funcs[effect])
|
||||||
|
reactive_button_grid.addWidget(buttons[effect], j, i)
|
||||||
|
i += 1
|
||||||
|
if i % grid_width == 0:
|
||||||
|
i = 0
|
||||||
|
j += 1
|
||||||
|
else:
|
||||||
|
connecting_funcs[effect] = connect_generator(effect)
|
||||||
|
buttons[effect] = QPushButton(effect)
|
||||||
|
buttons[effect].clicked.connect(connecting_funcs[effect])
|
||||||
|
non_reactive_button_grid.addWidget(buttons[effect], l, k)
|
||||||
|
k += 1
|
||||||
|
if k % grid_width == 0:
|
||||||
|
k = 0
|
||||||
|
l += 1
|
||||||
|
|
||||||
|
# ============================================== Set up frequency slider
|
||||||
|
# Frequency range label
|
||||||
|
label_slider = QLabel("Frequency Range")
|
||||||
|
# Frequency slider
|
||||||
|
def freq_slider_change(tick):
|
||||||
|
minf = freq_slider.tickValue(0)**2.0 * (config.settings["configuration"]["MIC_RATE"] / 2.0)
|
||||||
|
maxf = freq_slider.tickValue(1)**2.0 * (config.settings["configuration"]["MIC_RATE"] / 2.0)
|
||||||
|
t = 'Frequency range: {:.0f} - {:.0f} Hz'.format(minf, maxf)
|
||||||
|
freq_label.setText(t)
|
||||||
|
config.settings["configuration"]["MIN_FREQUENCY"] = minf
|
||||||
|
config.settings["configuration"]["MAX_FREQUENCY"] = maxf
|
||||||
|
dsp.create_mel_bank()
|
||||||
|
def set_freq_min():
|
||||||
|
config.settings["configuration"]["MIN_FREQUENCY"] = freq_slider.start()
|
||||||
|
dsp.create_mel_bank()
|
||||||
|
def set_freq_max():
|
||||||
|
config.settings["configuration"]["MAX_FREQUENCY"] = freq_slider.end()
|
||||||
|
dsp.create_mel_bank()
|
||||||
|
freq_slider = QRangeSlider()
|
||||||
|
freq_slider.show()
|
||||||
|
freq_slider.setMin(0)
|
||||||
|
freq_slider.setMax(20000)
|
||||||
|
freq_slider.setRange(config.settings["configuration"]["MIN_FREQUENCY"], config.settings["configuration"]["MAX_FREQUENCY"])
|
||||||
|
freq_slider.setBackgroundStyle('background: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 #222, stop:1 #333);')
|
||||||
|
freq_slider.setSpanStyle('background: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 #282, stop:1 #393);')
|
||||||
|
freq_slider.setDrawValues(True)
|
||||||
|
freq_slider.endValueChanged.connect(set_freq_max)
|
||||||
|
freq_slider.startValueChanged.connect(set_freq_min)
|
||||||
|
freq_slider.setStyleSheet("""
|
||||||
|
QRangeSlider * {
|
||||||
|
border: 0px;
|
||||||
|
padding: 0px;
|
||||||
|
}
|
||||||
|
QRangeSlider > QSplitter::handle {
|
||||||
|
background: #fff;
|
||||||
|
}
|
||||||
|
QRangeSlider > QSplitter::handle:vertical {
|
||||||
|
height: 3px;
|
||||||
|
}
|
||||||
|
QRangeSlider > QSplitter::handle:pressed {
|
||||||
|
background: #ca5;
|
||||||
|
}
|
||||||
|
""")
|
||||||
|
|
||||||
|
# ============================================ Set up option tabs layout
|
||||||
|
label_options = QLabel("Effect Options")
|
||||||
|
opts_tabs = QTabWidget()
|
||||||
|
# Dynamically set up tabs
|
||||||
|
tabs = {}
|
||||||
|
grid_layouts = {}
|
||||||
|
self.grid_layout_widgets = {}
|
||||||
|
options = visualizer.effect_opts.keys()
|
||||||
|
for effect in visualizer.effects:
|
||||||
|
# Make the tab
|
||||||
|
self.grid_layout_widgets[effect] = {}
|
||||||
|
tabs[effect] = QWidget()
|
||||||
|
grid_layouts[effect] = QGridLayout()
|
||||||
|
tabs[effect].setLayout(grid_layouts[effect])
|
||||||
|
opts_tabs.addTab(tabs[effect],effect)
|
||||||
|
# These functions make functions for the dynamic ui generation
|
||||||
|
# YOU WANT-A DYNAMIC I GIVE-A YOU DYNAMIC!
|
||||||
|
def gen_slider_valuechanger(effect, key):
|
||||||
|
def func():
|
||||||
|
visualizer.effect_opts[effect][key] = self.grid_layout_widgets[effect][key].value()
|
||||||
|
return func
|
||||||
|
def gen_float_slider_valuechanger(effect, key):
|
||||||
|
def func():
|
||||||
|
visualizer.effect_opts[effect][key] = self.grid_layout_widgets[effect][key].slider_value
|
||||||
|
return func
|
||||||
|
def gen_combobox_valuechanger(effect, key):
|
||||||
|
def func():
|
||||||
|
visualizer.effect_opts[effect][key] = self.grid_layout_widgets[effect][key].currentText()
|
||||||
|
return func
|
||||||
|
def gen_checkbox_valuechanger(effect, key):
|
||||||
|
def func():
|
||||||
|
visualizer.effect_opts[effect][key] = self.grid_layout_widgets[effect][key].isChecked()
|
||||||
|
return func
|
||||||
|
# Dynamically generate ui for settings
|
||||||
|
if effect in visualizer.dynamic_effects_config.settings["configuration"]["config:"]
|
||||||
|
i = 0
|
||||||
|
connecting_funcs[effect] = {}
|
||||||
|
for key, label, ui_element, *opts in visualizer.dynamic_effects_config.settings["configuration"]["config[effect"]]:
|
||||||
|
if opts: # neatest way ^^^^^ i could think of to unpack and handle an unknown number of opts (if any)
|
||||||
|
opts = opts[0]
|
||||||
|
if ui_element == "slider":
|
||||||
|
connecting_funcs[effect][key] = gen_slider_valuechanger(effect, key)
|
||||||
|
self.grid_layout_widgets[effect][key] = QSlider(Qt.Horizontal)
|
||||||
|
self.grid_layout_widgets[effect][key].setMinimum(opts[0])
|
||||||
|
self.grid_layout_widgets[effect][key].setMaximum(opts[1])
|
||||||
|
self.grid_layout_widgets[effect][key].setValue(visualizer.effect_opts[effect][key])
|
||||||
|
self.grid_layout_widgets[effect][key].valueChanged.connect(
|
||||||
|
connecting_funcs[effect][key])
|
||||||
|
elif ui_element == "float_slider":
|
||||||
|
connecting_funcs[effect][key] = gen_float_slider_valuechanger(effect, key)
|
||||||
|
self.grid_layout_widgets[effect][key] = QFloatSlider(*opts, visualizer.effect_opts[effect][key])
|
||||||
|
self.grid_layout_widgets[effect][key].setValue(visualizer.effect_opts[effect][key])
|
||||||
|
self.grid_layout_widgets[effect][key].valueChanged.connect(
|
||||||
|
connecting_funcs[effect][key])
|
||||||
|
elif ui_element == "dropdown":
|
||||||
|
connecting_funcs[effect][key] = gen_combobox_valuechanger(effect, key)
|
||||||
|
self.grid_layout_widgets[effect][key] = QComboBox()
|
||||||
|
self.grid_layout_widgets[effect][key].addItems(opts)
|
||||||
|
self.grid_layout_widgets[effect][key].currentIndexChanged.connect(
|
||||||
|
connecting_funcs[effect][key])
|
||||||
|
elif ui_element == "checkbox":
|
||||||
|
connecting_funcs[effect][key] = gen_checkbox_valuechanger(effect, key)
|
||||||
|
self.grid_layout_widgets[effect][key] = QCheckBox()
|
||||||
|
self.grid_layout_widgets[effect][key].setCheckState(visualizer.effect_opts[effect][key])
|
||||||
|
self.grid_layout_widgets[effect][key].stateChanged.connect(
|
||||||
|
connecting_funcs[effect][key])
|
||||||
|
grid_layouts[effect].addWidget(QLabel(label),i,0)
|
||||||
|
grid_layouts[effect].addWidget(self.grid_layout_widgets[effect][key],i,1)
|
||||||
|
i += 1
|
||||||
|
#visualizer.effect_settings[effect]
|
||||||
|
else:
|
||||||
|
grid_layouts[effect].addWidget(QLabel("No customisable options for this effect :("),0,0)
|
||||||
|
|
||||||
|
|
||||||
class GUI:
|
|
||||||
plot = []
|
|
||||||
curve = []
|
|
||||||
|
|
||||||
def __init__(self, width=800, height=450, title=''):
|
# ============================================= Add layouts into wrapper
|
||||||
# Create GUI window
|
self.setCentralWidget(QWidget(self))
|
||||||
self.app = QtGui.QApplication([])
|
self.centralWidget().setLayout(wrapper)
|
||||||
self.win = pg.GraphicsWindow(title)
|
wrapper.addLayout(labels_layout)
|
||||||
self.win.resize(width, height)
|
wrapper.addWidget(graph_view)
|
||||||
self.win.setWindowTitle(title)
|
wrapper.addWidget(label_reactive)
|
||||||
# Create GUI layout
|
wrapper.addLayout(reactive_button_grid)
|
||||||
self.layout = QtGui.QVBoxLayout()
|
wrapper.addWidget(label_non_reactive)
|
||||||
self.win.setLayout(self.layout)
|
wrapper.addLayout(non_reactive_button_grid)
|
||||||
|
wrapper.addWidget(label_slider)
|
||||||
def add_plot(self, title):
|
wrapper.addWidget(freq_slider)
|
||||||
new_plot = pg.PlotWidget()
|
wrapper.addWidget(label_options)
|
||||||
self.layout.addWidget(new_plot)
|
wrapper.addWidget(opts_tabs)
|
||||||
self.plot.append(new_plot)
|
#self.show()
|
||||||
self.curve.append([])
|
|
||||||
|
|
||||||
def add_curve(self, plot_index, pen=(255, 255, 255)):
|
|
||||||
self.curve[plot_index].append(self.plot[plot_index].plot(pen=pen))
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
# Example test gui
|
|
||||||
N = 48
|
|
||||||
gui = GUI(title='Test')
|
|
||||||
# Sin plot
|
|
||||||
gui.add_plot(title='Sin Plot')
|
|
||||||
gui.add_curve(plot_index=0)
|
|
||||||
gui.win.nextRow()
|
|
||||||
# Cos plot
|
|
||||||
gui.add_plot(title='Cos Plot')
|
|
||||||
gui.add_curve(plot_index=1)
|
|
||||||
while True:
|
|
||||||
t = time.time()
|
|
||||||
x = np.linspace(t, 2 * np.pi + t, N)
|
|
||||||
gui.curve[0][0].setData(x=x, y=np.sin(x))
|
|
||||||
gui.curve[1][0].setData(x=x, y=np.cos(x))
|
|
||||||
gui.app.processEvents()
|
|
||||||
time.sleep(1.0 / 30.0)
|
|
||||||
|
@ -4,45 +4,45 @@ from __future__ import division
|
|||||||
|
|
||||||
import platform
|
import platform
|
||||||
import numpy as np
|
import numpy as np
|
||||||
import config
|
import lib.config as config
|
||||||
|
|
||||||
# ESP8266 uses WiFi communication
|
|
||||||
if config.DEVICE == 'esp8266':
|
|
||||||
import socket
|
|
||||||
from subprocess import check_output
|
|
||||||
from time import sleep
|
|
||||||
|
|
||||||
|
def detect_esp8266():
|
||||||
|
""" Uses "arp -a" to find esp8266 on windows hotspot"""
|
||||||
# Find the audio strip automagically
|
# Find the audio strip automagically
|
||||||
if config.AUTO_DETECT:
|
|
||||||
ip_addr = False
|
ip_addr = False
|
||||||
while not ip_addr:
|
while not ip_addr:
|
||||||
arp_out = check_output(['arp', '-a']).splitlines()
|
arp_out = check_output(['arp', '-a']).splitlines()
|
||||||
for i in arp_out:
|
for i in arp_out:
|
||||||
if config.MAC_ADDR in str(i):
|
if config.settings["configuration"]["MAC_ADDR"] in str(i):
|
||||||
ip_addr = i.split()[0].decode("utf-8")
|
ip_addr = i.split()[0].decode("utf-8")
|
||||||
break
|
break
|
||||||
else:
|
else:
|
||||||
print("Device not found at physical address {}, retrying in 1s".format(config.MAC_ADDR))
|
print("Device not found at physical address {}, retrying in 1s".format(config.settings["configuration"]["MAC_ADDR"]))
|
||||||
sleep(1)
|
sleep(1)
|
||||||
print("Found device {}, with IP address {}".format(config.MAC_ADDR, ip_addr))
|
print("Found device {}, with IP address {}".format(config.settings["configuration"]["MAC_ADDR"], ip_addr))
|
||||||
config.UDP_IP = ip_addr
|
config.settings["configuration"]["UDP_IP"] = ip_addr
|
||||||
|
|
||||||
|
# ESP8266 uses WiFi communication
|
||||||
|
if config.settings["configuration"]["DEVICE"] == 'esp8266':
|
||||||
|
import socket
|
||||||
|
from subprocess import check_output
|
||||||
|
from time import sleep
|
||||||
_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||||
_sock.settimeout(0.005)
|
_sock.settimeout(0.005)
|
||||||
# Raspberry Pi controls the LED strip directly
|
# Raspberry Pi controls the LED strip directly
|
||||||
elif config.DEVICE == 'pi':
|
elif config.settings["configuration"]["DEVICE"] == 'pi':
|
||||||
import neopixel
|
import neopixel
|
||||||
strip = neopixel.Adafruit_NeoPixel(config.N_PIXELS, config.LED_PIN,
|
strip = neopixel.Adafruit_NeoPixel(config.settings["configuration"]["N_PIXELS"], config.settings["configuration"]["LED_PIN"],
|
||||||
config.LED_FREQ_HZ, config.LED_DMA,
|
config.settings["configuration"]["LED_FREQ_HZ"], config.settings["configuration"]["LED_DMA"],
|
||||||
config.LED_INVERT, config.BRIGHTNESS)
|
config.settings["configuration"]["LED_INVERT"], config.settings["configuration"]["BRIGHTNESS"])
|
||||||
strip.begin()
|
strip.begin()
|
||||||
elif config.DEVICE == 'blinkstick':
|
elif config.settings["configuration"]["DEVICE"] == 'blinkstick':
|
||||||
from blinkstick import blinkstick
|
from blinkstick import blinkstick
|
||||||
import signal
|
import signal
|
||||||
import sys
|
import sys
|
||||||
#Will turn all leds off when invoked.
|
#Will turn all leds off when invoked.
|
||||||
def signal_handler(signal, frame):
|
def signal_handler(signal, frame):
|
||||||
all_off = [0]*(config.N_PIXELS*3)
|
all_off = [0]*(config.settings["configuration"]["N_PIXELS"]*3)
|
||||||
stick.set_led_data(0, all_off)
|
stick.set_led_data(0, all_off)
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
|
|
||||||
@ -51,13 +51,13 @@ elif config.DEVICE == 'blinkstick':
|
|||||||
signal.signal(signal.SIGTERM, signal_handler)
|
signal.signal(signal.SIGTERM, signal_handler)
|
||||||
signal.signal(signal.SIGINT, signal_handler)
|
signal.signal(signal.SIGINT, signal_handler)
|
||||||
|
|
||||||
_gamma = np.load(config.GAMMA_TABLE_PATH)
|
_gamma = np.load(config.settings["configuration"]["GAMMA_TABLE_PATH"])
|
||||||
"""Gamma lookup table used for nonlinear brightness correction"""
|
"""Gamma lookup table used for nonlinear brightness correction"""
|
||||||
|
|
||||||
_prev_pixels = np.tile(253, (3, config.N_PIXELS))
|
_prev_pixels = np.tile(253, (3, config.settings["configuration"]["N_PIXELS"]))
|
||||||
"""Pixel values that were most recently displayed on the LED strip"""
|
"""Pixel values that were most recently displayed on the LED strip"""
|
||||||
|
|
||||||
pixels = np.tile(1, (3, config.N_PIXELS))
|
pixels = np.tile(1, (3, config.settings["configuration"]["N_PIXELS"]))
|
||||||
"""Pixel values for the LED strip"""
|
"""Pixel values for the LED strip"""
|
||||||
|
|
||||||
_is_python_2 = int(platform.python_version_tuple()[0]) == 2
|
_is_python_2 = int(platform.python_version_tuple()[0]) == 2
|
||||||
@ -79,27 +79,37 @@ def _update_esp8266():
|
|||||||
"""
|
"""
|
||||||
global pixels, _prev_pixels
|
global pixels, _prev_pixels
|
||||||
# Truncate values and cast to integer
|
# Truncate values and cast to integer
|
||||||
pixels = np.clip(pixels, 0, 200).astype(int)
|
pixels = np.clip(pixels, 0, config.settings["configuration"]["MAX_BRIGHTNESS"]).astype(int)
|
||||||
# Optionally apply gamma correc tio
|
# Optionally apply gamma correction
|
||||||
p = _gamma[pixels] if config.SOFTWARE_GAMMA_CORRECTION else np.copy(pixels)
|
p = _gamma[pixels] if config.settings["configuration"]["SOFTWARE_GAMMA_CORRECTION"] else np.copy(pixels)
|
||||||
MAX_PIXELS_PER_PACKET = 256
|
MAX_PIXELS_PER_PACKET = 255
|
||||||
# Pixel indices
|
# Pixel indices
|
||||||
idx = range(pixels.shape[1])
|
idx = range(pixels.shape[1])
|
||||||
#idx = [i for i in idx if not np.array_equal(p[:, i], _prev_pixels[:, i])]
|
#idx = [i for i in idx if not np.array_equal(p[:, i], _prev_pixels[:, i])]
|
||||||
n_packets = len(idx) // MAX_PIXELS_PER_PACKET + 1
|
n_packets = len(idx) // MAX_PIXELS_PER_PACKET + 1
|
||||||
idx = np.array_split(idx, n_packets)
|
idx = np.array_split(idx, n_packets)
|
||||||
for packet_indices in idx:
|
|
||||||
m = '' if _is_python_2 else []
|
m = []
|
||||||
for i in packet_indices:
|
for i in range(config.settings["configuration"]["N_PIXELS"]):
|
||||||
if _is_python_2:
|
#m.append(i) # Index of pixel to change
|
||||||
m += chr(i) + chr(pixels[0][i]) + chr(pixels[1][i]) + chr(pixels[2][i])
|
|
||||||
else:
|
|
||||||
m.append(i) # Index of pixel to change
|
|
||||||
m.append(pixels[0][i]) # Pixel red value
|
m.append(pixels[0][i]) # Pixel red value
|
||||||
m.append(pixels[1][i]) # Pixel green value
|
m.append(pixels[1][i]) # Pixel green value
|
||||||
m.append(pixels[2][i]) # Pixel blue value
|
m.append(pixels[2][i]) # Pixel blue value
|
||||||
m = m if _is_python_2 else bytes(m)
|
m = bytes(m)
|
||||||
_sock.sendto(m, (config.UDP_IP, config.UDP_PORT))
|
_sock.sendto(m, (config.settings["configuration"]["UDP_IP"], config.settings["configuration"]["UDP_PORT"]))
|
||||||
|
|
||||||
|
# for packet_indices in idx:
|
||||||
|
# m = '' if _is_python_2 else []
|
||||||
|
# for i in packet_indices:
|
||||||
|
# if _is_python_2:
|
||||||
|
# m += chr(i) + chr(pixels[0][i]) + chr(pixels[1][i]) + chr(pixels[2][i])
|
||||||
|
# else:
|
||||||
|
# m.append(i) # Index of pixel to change
|
||||||
|
# m.append(pixels[0][i]) # Pixel red value
|
||||||
|
# m.append(pixels[1][i]) # Pixel green value
|
||||||
|
# m.append(pixels[2][i]) # Pixel blue value
|
||||||
|
# m = m if _is_python_2 else bytes(m)
|
||||||
|
# _sock.sendto(m, (config.settings["configuration"]["UDP_IP"], config.settings["configuration"]["UDP_PORT"]))
|
||||||
_prev_pixels = np.copy(pixels)
|
_prev_pixels = np.copy(pixels)
|
||||||
|
|
||||||
|
|
||||||
@ -113,14 +123,14 @@ def _update_pi():
|
|||||||
# Truncate values and cast to integer
|
# Truncate values and cast to integer
|
||||||
pixels = np.clip(pixels, 0, 255).astype(int)
|
pixels = np.clip(pixels, 0, 255).astype(int)
|
||||||
# Optional gamma correction
|
# Optional gamma correction
|
||||||
p = _gamma[pixels] if config.SOFTWARE_GAMMA_CORRECTION else np.copy(pixels)
|
p = _gamma[pixels] if config.settings["configuration"]["SOFTWARE_GAMMA_CORRECTION"] else np.copy(pixels)
|
||||||
# Encode 24-bit LED values in 32 bit integers
|
# Encode 24-bit LED values in 32 bit integers
|
||||||
r = np.left_shift(p[0][:].astype(int), 8)
|
r = np.left_shift(p[0][:].astype(int), 8)
|
||||||
g = np.left_shift(p[1][:].astype(int), 16)
|
g = np.left_shift(p[1][:].astype(int), 16)
|
||||||
b = p[2][:].astype(int)
|
b = p[2][:].astype(int)
|
||||||
rgb = np.bitwise_or(np.bitwise_or(r, g), b)
|
rgb = np.bitwise_or(np.bitwise_or(r, g), b)
|
||||||
# Update the pixels
|
# Update the pixels
|
||||||
for i in range(config.N_PIXELS):
|
for i in range(config.settings["configuration"]["N_PIXELS"]):
|
||||||
# Ignore pixels if they haven't changed (saves bandwidth)
|
# Ignore pixels if they haven't changed (saves bandwidth)
|
||||||
if np.array_equal(p[:, i], _prev_pixels[:, i]):
|
if np.array_equal(p[:, i], _prev_pixels[:, i]):
|
||||||
continue
|
continue
|
||||||
@ -137,16 +147,16 @@ def _update_blinkstick():
|
|||||||
# Truncate values and cast to integer
|
# Truncate values and cast to integer
|
||||||
pixels = np.clip(pixels, 0, 250).astype(int)
|
pixels = np.clip(pixels, 0, 250).astype(int)
|
||||||
# Optional gamma correction
|
# Optional gamma correction
|
||||||
p = _gamma[pixels] if config.SOFTWARE_GAMMA_CORRECTION else np.copy(pixels)
|
p = _gamma[pixels] if config.settings["configuration"]["SOFTWARE_GAMMA_CORRECTION"] else np.copy(pixels)
|
||||||
# Read the rgb values
|
# Read the rgb values
|
||||||
r = p[0][:].astype(int)
|
r = p[0][:].astype(int)
|
||||||
g = p[1][:].astype(int)
|
g = p[1][:].astype(int)
|
||||||
b = p[2][:].astype(int)
|
b = p[2][:].astype(int)
|
||||||
|
|
||||||
#create array in which we will store the led states
|
#create array in which we will store the led states
|
||||||
newstrip = [None]*(config.N_PIXELS*3)
|
newstrip = [None]*(config.settings["configuration"]["N_PIXELS"]*3)
|
||||||
|
|
||||||
for i in range(config.N_PIXELS):
|
for i in range(config.settings["configuration"]["N_PIXELS"]):
|
||||||
# blinkstick uses GRB format
|
# blinkstick uses GRB format
|
||||||
newstrip[i*3] = g[i]
|
newstrip[i*3] = g[i]
|
||||||
newstrip[i*3+1] = r[i]
|
newstrip[i*3+1] = r[i]
|
||||||
@ -157,13 +167,13 @@ def _update_blinkstick():
|
|||||||
|
|
||||||
def update():
|
def update():
|
||||||
"""Updates the LED strip values"""
|
"""Updates the LED strip values"""
|
||||||
if config.DEVICE == 'esp8266':
|
if config.settings["configuration"]["DEVICE"] == 'esp8266':
|
||||||
_update_esp8266()
|
_update_esp8266()
|
||||||
elif config.DEVICE == 'pi':
|
elif config.settings["configuration"]["DEVICE"] == 'pi':
|
||||||
_update_pi()
|
_update_pi()
|
||||||
elif config.DEVICE == 'blinkstick':
|
elif config.settings["configuration"]["DEVICE"] == 'blinkstick':
|
||||||
_update_blinkstick()
|
_update_blinkstick()
|
||||||
elif config.DEVICE == 'stripless':
|
elif config.settings["configuration"]["DEVICE"] == 'stripless':
|
||||||
pass
|
pass
|
||||||
|
|
||||||
# Execute this file to run a LED strand test
|
# Execute this file to run a LED strand test
|
||||||
|
@ -1,30 +1,29 @@
|
|||||||
import time
|
import time
|
||||||
import numpy as np
|
import numpy as np
|
||||||
import pyaudio
|
import pyaudio
|
||||||
import config
|
import lib.config as config
|
||||||
|
|
||||||
|
|
||||||
def start_stream(callback):
|
def start_stream(callback):
|
||||||
p = pyaudio.PyAudio()
|
p = pyaudio.PyAudio()
|
||||||
frames_per_buffer = int(config.MIC_RATE / config.FPS)
|
frames_per_buffer = int(config.settings["configuration"]["MIC_RATE"] / config.settings["configuration"]["FPS"])
|
||||||
stream = p.open(format=pyaudio.paInt16,
|
stream = p.open(format=pyaudio.paInt16,
|
||||||
channels=1,
|
channels=1,
|
||||||
rate=config.MIC_RATE,
|
rate=config.settings["configuration"]["MIC_RATE"],
|
||||||
input=True,
|
input=True,
|
||||||
frames_per_buffer=frames_per_buffer)
|
frames_per_buffer=frames_per_buffer)
|
||||||
overflows = 0
|
overflows = 0
|
||||||
prev_ovf_time = time.time()
|
prev_ovf_time = time.time()
|
||||||
while True:
|
while True:
|
||||||
try:
|
try:
|
||||||
y = np.fromstring(stream.read(frames_per_buffer, exception_on_overflow=False), dtype=np.int16)
|
y = np.fromstring(stream.read(frames_per_buffer), dtype=np.int16)
|
||||||
y = y.astype(np.float32)
|
y = y.astype(np.float32)
|
||||||
#stream.read(get_read_available(), exception_on_overflow=False)
|
|
||||||
callback(y)
|
callback(y)
|
||||||
except IOError:
|
except IOError:
|
||||||
overflows += 1
|
overflows += 1
|
||||||
if time.time() > prev_ovf_time + 1:
|
if time.time() > prev_ovf_time + 1:
|
||||||
prev_ovf_time = time.time()
|
prev_ovf_time = time.time()
|
||||||
if config.USE_GUI:
|
if config.settings["configuration"]["USE_GUI"]:
|
||||||
gui.label_error.setText('Audio buffer has overflowed {} times'.format(overflows))
|
gui.label_error.setText('Audio buffer has overflowed {} times'.format(overflows))
|
||||||
else:
|
else:
|
||||||
print('Audio buffer has overflowed {} times'.format(overflows))
|
print('Audio buffer has overflowed {} times'.format(overflows))
|
||||||
|
@ -15,7 +15,7 @@ class QFloatSlider(QtWidgets.QSlider):
|
|||||||
"""
|
"""
|
||||||
def __init__(self, min_value, max_value, step, default):
|
def __init__(self, min_value, max_value, step, default):
|
||||||
super().__init__(QtCore.Qt.Horizontal)
|
super().__init__(QtCore.Qt.Horizontal)
|
||||||
self.precision = 0.001
|
self.precision = 0.0001
|
||||||
self.min_value = min_value
|
self.min_value = min_value
|
||||||
self.max_value = max_value
|
self.max_value = max_value
|
||||||
self.step = step
|
self.step = step
|
||||||
@ -30,7 +30,10 @@ class QFloatSlider(QtWidgets.QSlider):
|
|||||||
super().setSingleStep(1)
|
super().setSingleStep(1)
|
||||||
super().setValue(self._float_to_int(self.default))
|
super().setValue(self._float_to_int(self.default))
|
||||||
super().valueChanged.connect(self._value_handler)
|
super().valueChanged.connect(self._value_handler)
|
||||||
self.slider_value = 2.0
|
#self.slider_value = 2.0
|
||||||
|
|
||||||
|
def setValue(self, value):
|
||||||
|
super().setValue(self._float_to_int(value))
|
||||||
|
|
||||||
# This is mostly disgusting python i hate floating points >:(
|
# This is mostly disgusting python i hate floating points >:(
|
||||||
def _float_divmod(self,a,b):
|
def _float_divmod(self,a,b):
|
||||||
|
Loading…
Reference in New Issue
Block a user