55`cedargrove_waveviz`
66===============================================================================
77A CircuitPython class to create a positionable ``displayio.TileGrid`` object
8- from a ``synthio.ReadableBuffer`` wave table. The class inherits all
9- properties of a ``TileGrid`` object including bitmap, pixel_shader, width ,
10- height, x, y.
8+ from a ``synthio.ReadableBuffer`` wave table or ``synthio.Envelope object.
9+ The class inherits the properties of a ``TileGrid`` object including bitmap,
10+ pixel_shader, width, height, x, y.
1111
1212https://github.com/CedarGroveStudios/CircuitPython_WaveViz
1313https://docs.circuitpython.org/en/latest/shared-bindings/displayio/#displayio.TileGrid
2727
2828
2929# pylint: disable=too-few-public-methods
30+ # pylint: disable=too-many-instance-attributes
3031class WaveViz (displayio .TileGrid ):
3132 """
3233 The WaveViz class creates a positionable ``displayio.TileGrid`` object
33- from a ``synthio.ReadableBuffer`` wave table. The class inherits the
34- properties of a ``TileGrid`` object of bitmap, pixel_shader, width ,
35- height, x, y.
34+ from a ``synthio.ReadableBuffer`` wave table or ``synthio.Envelope object.
35+ The class inherits the properties of a ``TileGrid`` object of bitmap,
36+ pixel_shader, width, height, x, y.
3637
37- :param synthio.ReadableBuffer wave_table: The synthio waveform object of type 'h'
38- (signed 16-bit). No default.
38+ :param synthio.ReadableBuffer wave_table: The synthio waveform or envelope
39+ object of type 'h' (signed 16-bit). No default.
3940 :param int x: The tile grid's x-axis coordinate value. No default.
4041 :param int y: The tile grid's y-axis coordinate value. No default.
4142 :param int width: The tile grid's width in pixels. No default.
@@ -45,6 +46,8 @@ class WaveViz(displayio.TileGrid):
4546 :param integer back_color: The grid background color. Defaults to None (transparent).
4647 :param bool auto_scale: Automatically adjust resultant plot to the wave table's
4748 full-scale value. Defaults to True (auto scale enabled).
49+ :param bool envelope_plot: Plot an envelope object. Defaults to False (plot
50+ a wave object).
4851 """
4952
5053 # pylint: disable=too-many-arguments
@@ -59,6 +62,7 @@ def __init__(
5962 grid_color = 0x808080 ,
6063 back_color = None ,
6164 auto_scale = True ,
65+ envelope_plot = False ,
6266 ):
6367 """Instantiate the tile generator class."""
6468 self ._wave_table = wave_table
@@ -70,6 +74,7 @@ def __init__(
7074 self ._auto_scale = auto_scale
7175 self ._max_sample_value = 32767 # Maximum signed 16-bit value
7276 self ._scale_y = 0 # Define for later use
77+ self ._envelope_plot = envelope_plot
7378
7479 self ._palette = displayio .Palette (3 )
7580 self ._palette [1 ] = plot_color
@@ -126,13 +131,66 @@ def max_result(self):
126131 return self ._max_sample_value
127132
128133 def _update_plot (self ):
129- """Clears the bitmap and plots the grid and waveform."""
134+ """Clears the bitmap and plots the grid and waveform or envelope ."""
130135 # Clear the target bitmap
131136 self ._bmp .fill (0 )
132137
133138 # Plot grid and wave table
134139 self ._plot_grid () # Plot the grid
135- self ._plot_wave () # Plot the wave table
140+ if self ._envelope_plot :
141+ self ._plot_envelope ()
142+ else :
143+ self ._plot_wave () # Plot the wave table
144+
145+ def _plot_envelope (self ):
146+ """Plot the wave_table as a bitmap representing an ADSR envelope
147+ object. Y-axis is set at 0.0 to 1.0 and will not automatically
148+ scale. Sustain duration is plotted as an arbitrary value based
149+ on the attack and release time values."""
150+ # Get the five envelope values from the wave table
151+ a_time = self ._wave_table [0 ] # Attack time
152+ a_level = self ._wave_table [1 ] # Attack level
153+ d_time = self ._wave_table [2 ] # Decay time
154+ s_level = self ._wave_table [3 ] # Sustain level
155+ r_time = self ._wave_table [4 ] # Release time
156+
157+ x_points = array ("h" , [])
158+ y_points = array ("h" , [])
159+
160+ # Plot envelope polygon
161+ if s_level != 0 :
162+ # Full ADSR envelope
163+ s_time = 0.3 * (a_time + r_time ) # relative/arbitrary, not actual
164+ time_points = [
165+ 0 ,
166+ a_time ,
167+ a_time + d_time ,
168+ a_time + d_time + s_time ,
169+ a_time + d_time + s_time + r_time ,
170+ ]
171+ level_points = [0 , a_level , s_level , s_level , 0 ]
172+ env_duration = a_time + d_time + s_time + r_time + 0.0001
173+ else :
174+ # AR phases only (plucked or struck instrument)
175+ time_points = [0 , a_time , a_time + r_time ]
176+ level_points = [0 , a_level , 0 ]
177+ env_duration = a_time + r_time + 0.0001
178+
179+ # Scale the lists to fit the plot window size and location in pixels
180+ for time in time_points :
181+ x_points .append (int ((self ._width / env_duration ) * time ))
182+
183+ for level in level_points :
184+ y_points .append (- int (self ._height * level ) + self ._height )
185+
186+ # Draw the envelope polygon
187+ bitmaptools .draw_polygon (
188+ self ._bmp ,
189+ x_points ,
190+ y_points ,
191+ 1 ,
192+ False ,
193+ )
136194
137195 def _plot_wave (self ):
138196 """Plot the wave_table as a bitmap. Extract samples from the wave
@@ -182,12 +240,13 @@ def _plot_grid(self):
182240 2 ,
183241 )
184242
185- # Draw x-axis line
186- bitmaptools .draw_line (
187- self ._bmp ,
188- 0 ,
189- self ._y_offset ,
190- self ._width ,
191- self ._y_offset ,
192- 2 ,
193- )
243+ if not self ._envelope_plot :
244+ # Draw x-axis line for wave plot
245+ bitmaptools .draw_line (
246+ self ._bmp ,
247+ 0 ,
248+ self ._y_offset ,
249+ self ._width ,
250+ self ._y_offset ,
251+ 2 ,
252+ )
0 commit comments