CircuitPython_equalizer/equalizer/equalizer.py
2023-01-30 19:09:14 -05:00

259 lines
8.5 KiB
Python

# SPDX-FileCopyrightText: 2021 Jose David M.
#
# SPDX-License-Identifier: MIT
"""
`equalizer`
================================================================================
A equalizer widget for displaying sound information.
* Author(s): Jose David M.
Implementation Notes
--------------------
**Hardware:**
**Software and Dependencies:**
* Adafruit CircuitPython firmware for the supported boards:
https://github.com/adafruit/circuitpython/releases
"""
# pylint: disable=too-many-lines, too-many-instance-attributes, too-many-arguments
# pylint: disable=too-many-locals, too-many-statements
import displayio
try:
import adafruit_fancyled.adafruit_fancyled as fancy
except ImportError:
pass
from adafruit_displayio_layout.widgets.widget import Widget
from equalizer import rectangle_helper
from equalizer import rgb
__version__ = "0.0.0-auto.0"
__repo__ = "https://github.com/jposada202020/CircuitPython_equalizer.git"
class Equalizer(Widget):
"""An equalizer widget. The origin is set using ``x`` and ``y``.
:param int x: x position of the plane origin
:param int y: y position of the plane origin
:param int width: requested width, in pixels.
:param int height: requested height, in pixels.
:param int background_color: background color to use defaults to black (0x000000)
:para int number_bars: number of bar in the equalizer. Defaults to 1.
:para bool bar_best_fit: Allows selecting the best fit for the bar in the given width
:param int bar_width: width in pixels of the equalizers bar. Defaults to 10.
:param int pad_x: pixels number to move the bars to the right
:param int number_segments: number of segments in each bar
:param int segments_height: height in pixel of each bar equalizer segment
:param bool seg_best_fit: When True it will calculate segment height automatically
Defaults to True.
**Quickstart: Importing and using Equalizer**
Here is one way of importing the `Equalizer` class so you can use it as
the name ``Equal``:
.. code-block:: python
from adafruit_displayio_layout.widgets.cartesian import Equalizer as Equal
Now you can create an equalizer at pixel position x=20, y=30 using:
.. code-block:: python
my_equalizer=Equal(x=20, y=30) # instance the equalizer at x=20, y=30
Once you setup your display, you can now add ``my_equalizer`` to your display using:
.. code-block:: python
display.show(my_equalizer) # add the group to the display
If you want to have multiple display elements, you can create a group and then
append the plane and the other elements to the group. Then, you can add the full
group to the display as in this example:
.. code-block:: python
my_equalizer= Equal(20, 30) # instance the equalizer at x=20, y=30
my_group = displayio.Group() # make a group
my_group.append(my_equalizer) # Add my_equalizer to the group
#
# Append other display elements to the group
#
display.show(my_group) # add the group to the display
**Summary: Cartesian Features and input variables**
The `cartesian` widget has some options for controlling its position, visible appearance,
and scale through a collection of input variables:
- **position**: ``x``, ``y``
- **size**: ``width`` and ``height``
- **color**: color is controlled internally, to ease the use of the widget
- **background color**: ``background_color``
- **range**: range is controlled internally to ease the use of the widget and is set
to 100. To have other ranges, normalize your values first and the pass them to the
updater.
.. figure:: equalizer.gif
:scale: 100 %
:figwidth: 50%
:align: center
:alt: Picture of the equalizer widget in motion.
This shows the equalizer capabilities.
"""
def __init__(
self,
background_color: int = 0x000000,
number_bars: int = 1,
bar_best_fit: bool = True,
bar_width: int = 10,
pad_x: int = 0,
number_segments: int = 2,
segments_height: int = 10,
seg_best_fit: bool = True,
**kwargs,
) -> None:
super().__init__(**kwargs)
self._background_color = background_color
if self.width < 42:
print("Equalizer minimum width is 40. Defaulting to 40")
self._width = 40
self._number_bars = number_bars
self._bar_width = bar_width
self._pad_x = pad_x
self._bar_best_fit = bar_best_fit
self._number_segments = number_segments
self._segments_height = segments_height
self._seg_best_fit = seg_best_fit
self._screen_bitmap = displayio.Bitmap(self.width, self.height, 5)
self._screen_bitmap.fill(10)
self._screen_palette = displayio.Palette(11)
self._screen_palette[10] = self._background_color
self._bar_inventory = []
self._segment_inventory = []
self._hor_bar_setup()
self._screen_tilegrid = displayio.TileGrid(
self._screen_bitmap,
pixel_shader=self._screen_palette,
x=0,
y=0,
)
self.append(self._screen_tilegrid)
def _hor_bar_setup(self):
if self._bar_best_fit:
self._bar_width = (
self.width - 2 * (self._number_bars + 1)
) // self._number_bars
else:
total_width = self._number_bars * self._bar_width + (
(self._number_bars + 1) * 2
)
if total_width > self.width:
print("Equalizer setup could not be displayed. Adjusting bar widths")
self._bar_width = (
self.width - ((self._number_bars + 1) * 2)
) // self._number_bars
widths_bars = self._number_bars * self._bar_width
width_free = self.width - widths_bars
separationx = width_free // (self._number_bars + 1)
x_local = separationx + self._pad_x
if self._seg_best_fit:
self._segments_height = (self.height - 2) // self._number_segments
else:
total_height = self._number_segments * self._segments_height + 6
if total_height > self.height:
print(
"Equalizer setup could not be displayed. Adjusting segments heights"
)
self._segments_height = (
self.height - ((self._number_segments + 1) * 2)
) // self._number_segments
heights_segs = self._number_segments * self._segments_height
height_free = self.height - heights_segs
self._separationy = height_free // (self._number_segments + 1)
for col in range(self._number_bars):
self._bar_inventory.append((col, x_local))
x_local = x_local + separationx + self._bar_width
y_local = self.height - self._separationy - self._segments_height
delta = 100 // self._number_segments
trigger_value = 0
for row in range(self._number_segments):
self._segment_inventory.append((row, y_local, trigger_value, 0))
y_local = y_local - self._separationy - self._segments_height
trigger_value = trigger_value + delta
for i, item in enumerate(self._segment_inventory):
prgb_color = rgb(item[1], 0, 100)
color_buffer = fancy.CRGB(prgb_color[0], prgb_color[1], prgb_color[2])
self._screen_palette[i] = color_buffer.pack()
def show_bars(self, values) -> None:
"""
:parm values: Tuple of values to update the equlizer bars
"""
for j, element in enumerate(self._segment_inventory):
for i, _ in enumerate(self._bar_inventory):
if element[2] < values[i]:
rectangle_helper(
self._bar_inventory[i][1],
self._segment_inventory[j][1],
self._segments_height,
self._bar_width,
self._screen_bitmap,
j,
self._screen_palette,
)
else:
rectangle_helper(
self._bar_inventory[i][1],
self._segment_inventory[j][1],
self._segments_height,
self._bar_width,
self._screen_bitmap,
10,
self._screen_palette,
)