equalizer.py
1 # SPDX-FileCopyrightText: 2021 Jose David M. 2 # 3 # SPDX-License-Identifier: MIT 4 """ 5 6 `equalizer` 7 ================================================================================ 8 A equalizer widget for displaying sound information. 9 10 * Author(s): Jose David M. 11 12 Implementation Notes 13 -------------------- 14 15 **Hardware:** 16 17 **Software and Dependencies:** 18 19 * Adafruit CircuitPython firmware for the supported boards: 20 https://github.com/adafruit/circuitpython/releases 21 22 """ 23 24 # pylint: disable=too-many-lines, too-many-instance-attributes, too-many-arguments 25 # pylint: disable=too-many-locals, too-many-statements 26 27 import displayio 28 29 try: 30 import adafruit_fancyled.adafruit_fancyled as fancy 31 except ImportError: 32 pass 33 from adafruit_displayio_layout.widgets.widget import Widget 34 from equalizer import rectangle_helper 35 from equalizer import rgb 36 37 __version__ = "0.0.0-auto.0" 38 __repo__ = "https://github.com/jposada202020/CircuitPython_equalizer.git" 39 40 41 class Equalizer(Widget): 42 """An equalizer widget. The origin is set using ``x`` and ``y``. 43 44 :param int x: x position of the plane origin 45 :param int y: y position of the plane origin 46 47 :param int width: requested width, in pixels. 48 :param int height: requested height, in pixels. 49 50 :param int background_color: background color to use defaults to black (0x000000) 51 52 :para int number_bars: number of bar in the equalizer. Defaults to 1. 53 :para bool bar_best_fit: Allows selecting the best fit for the bar in the given width 54 :param int bar_width: width in pixels of the equalizers bar. Defaults to 10. 55 56 :param int pad_x: pixels number to move the bars to the right 57 58 :param int number_segments: number of segments in each bar 59 :param int segments_height: height in pixel of each bar equalizer segment 60 :param bool seg_best_fit: When True it will calculate segment height automatically 61 Defaults to True. 62 63 64 **Quickstart: Importing and using Equalizer** 65 66 Here is one way of importing the `Equalizer` class so you can use it as 67 the name ``Equal``: 68 69 .. code-block:: python 70 71 from adafruit_displayio_layout.widgets.cartesian import Equalizer as Equal 72 73 Now you can create an equalizer at pixel position x=20, y=30 using: 74 75 .. code-block:: python 76 77 my_equalizer=Equal(x=20, y=30) # instance the equalizer at x=20, y=30 78 79 Once you setup your display, you can now add ``my_equalizer`` to your display using: 80 81 .. code-block:: python 82 83 display.show(my_equalizer) # add the group to the display 84 85 If you want to have multiple display elements, you can create a group and then 86 append the plane and the other elements to the group. Then, you can add the full 87 group to the display as in this example: 88 89 .. code-block:: python 90 91 my_equalizer= Equal(20, 30) # instance the equalizer at x=20, y=30 92 my_group = displayio.Group() # make a group 93 my_group.append(my_equalizer) # Add my_equalizer to the group 94 95 # 96 # Append other display elements to the group 97 # 98 99 display.show(my_group) # add the group to the display 100 101 **Summary: Cartesian Features and input variables** 102 103 The `cartesian` widget has some options for controlling its position, visible appearance, 104 and scale through a collection of input variables: 105 106 - **position**: ``x``, ``y`` 107 108 - **size**: ``width`` and ``height`` 109 110 - **color**: color is controlled internally, to ease the use of the widget 111 112 - **background color**: ``background_color`` 113 114 - **range**: range is controlled internally to ease the use of the widget and is set 115 to 100. To have other ranges, normalize your values first and the pass them to the 116 updater. 117 118 119 .. figure:: equalizer.gif 120 :scale: 100 % 121 :figwidth: 50% 122 :align: center 123 :alt: Picture of the equalizer widget in motion. 124 125 This shows the equalizer capabilities. 126 127 128 """ 129 130 def __init__( 131 self, 132 background_color: int = 0x000000, 133 number_bars: int = 1, 134 bar_best_fit: bool = True, 135 bar_width: int = 10, 136 pad_x: int = 0, 137 number_segments: int = 2, 138 segments_height: int = 10, 139 seg_best_fit: bool = True, 140 **kwargs, 141 ) -> None: 142 143 super().__init__(**kwargs) 144 145 self._background_color = background_color 146 147 if self.width < 42: 148 print("Equalizer minimum width is 40. Defaulting to 40") 149 self._width = 40 150 151 self._number_bars = number_bars 152 self._bar_width = bar_width 153 self._pad_x = pad_x 154 self._bar_best_fit = bar_best_fit 155 156 self._number_segments = number_segments 157 self._segments_height = segments_height 158 self._seg_best_fit = seg_best_fit 159 160 self._screen_bitmap = displayio.Bitmap(self.width, self.height, 5) 161 self._screen_bitmap.fill(10) 162 self._screen_palette = displayio.Palette(11) 163 self._screen_palette[10] = self._background_color 164 165 self._bar_inventory = [] 166 self._segment_inventory = [] 167 self._hor_bar_setup() 168 169 self._screen_tilegrid = displayio.TileGrid( 170 self._screen_bitmap, 171 pixel_shader=self._screen_palette, 172 x=0, 173 y=0, 174 ) 175 176 self.append(self._screen_tilegrid) 177 178 def _hor_bar_setup(self): 179 if self._bar_best_fit: 180 self._bar_width = ( 181 self.width - 2 * (self._number_bars + 1) 182 ) // self._number_bars 183 else: 184 total_width = self._number_bars * self._bar_width + ( 185 (self._number_bars + 1) * 2 186 ) 187 188 if total_width > self.width: 189 print("Equalizer setup could not be displayed. Adjusting bar widths") 190 self._bar_width = ( 191 self.width - ((self._number_bars + 1) * 2) 192 ) // self._number_bars 193 194 widths_bars = self._number_bars * self._bar_width 195 width_free = self.width - widths_bars 196 separationx = width_free // (self._number_bars + 1) 197 x_local = separationx + self._pad_x 198 199 if self._seg_best_fit: 200 self._segments_height = (self.height - 2) // self._number_segments 201 else: 202 total_height = self._number_segments * self._segments_height + 6 203 if total_height > self.height: 204 print( 205 "Equalizer setup could not be displayed. Adjusting segments heights" 206 ) 207 self._segments_height = ( 208 self.height - ((self._number_segments + 1) * 2) 209 ) // self._number_segments 210 211 heights_segs = self._number_segments * self._segments_height 212 height_free = self.height - heights_segs 213 self._separationy = height_free // (self._number_segments + 1) 214 215 for col in range(self._number_bars): 216 self._bar_inventory.append((col, x_local)) 217 x_local = x_local + separationx + self._bar_width 218 219 y_local = self.height - self._separationy - self._segments_height 220 delta = 100 // self._number_segments 221 trigger_value = 0 222 223 for row in range(self._number_segments): 224 self._segment_inventory.append((row, y_local, trigger_value, 0)) 225 y_local = y_local - self._separationy - self._segments_height 226 trigger_value = trigger_value + delta 227 228 for i, item in enumerate(self._segment_inventory): 229 prgb_color = rgb(item[1], 0, 100) 230 color_buffer = fancy.CRGB(prgb_color[0], prgb_color[1], prgb_color[2]) 231 self._screen_palette[i] = color_buffer.pack() 232 233 def show_bars(self, values) -> None: 234 """ 235 :parm values: Tuple of values to update the equlizer bars 236 """ 237 for j, element in enumerate(self._segment_inventory): 238 for i, _ in enumerate(self._bar_inventory): 239 if element[2] < values[i]: 240 241 rectangle_helper( 242 self._bar_inventory[i][1], 243 self._segment_inventory[j][1], 244 self._segments_height, 245 self._bar_width, 246 self._screen_bitmap, 247 j, 248 self._screen_palette, 249 ) 250 else: 251 rectangle_helper( 252 self._bar_inventory[i][1], 253 self._segment_inventory[j][1], 254 self._segments_height, 255 self._bar_width, 256 self._screen_bitmap, 257 10, 258 self._screen_palette, 259 )