/ equalizer / equalizer.py
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                      )