WaveViz is a CircuitPython class to create a positionable displayio.TileGrid
graphics widget from a synthio.waveform
wave table or synthio.Envelope
object (or any one-dimensional list for that matter). The class also makes the underlying bitmap and palette objects available for other uses such as saving the widget to an image file.
The WaveViz class currently resides in its GitHub repository and is included in the CircuitPython Community Bundle. The update utility circup can be used to install cedargrove_waveviz.
The long-term objective is to be able to save and import sounds and ADSR envelopes from a library of synthio wave table files and envelope objects. The first step in the process is to create a visualizer to help characterize the sounds graphically. We'll worry about saving and retrieving waveform and envelope objects sometime in the future (watch for a new class, WaveStore to appear soon). For now, let's work on getting images of wave tables and envelopes.
I See What You Did There
When instantiated, WaveViz is given a waveform or envelope object, the screen origin pixel coordinates (x, y), an image size (width, height) in pixels, as well as RGB 24-bit colors for the waveform, border, and background. Internally, the class creates a bitmap image where:
- a waveform wave table fills the image with positive and negative sample values above and below a baseline, or
- an envelope object representing the four phases of attack, decay, sustain, and release with 0.0 to 1.0 amplitude values on the y-axis and relative phase timing on the x-axis.
After the image is created, the class instance becomes a displayio.TileGrid
object. The image is also available as a bitmap (WaveViz.bitmap
, WaveViz.pixel_shader
).
The resultant image can be plotted anywhere on the display in any aspect ratio. If nested layering is required, the background color can also be specified as transparent (back_color=None
).
Simple Test Example Code
The following is a simple test that used a FeatherS2 attached to a 2.4-inch TFT FeatherWing display. The code imports WaveBuilder to construct a synthetic harmonica wave table from five oscillators.
# SPDX-FileCopyrightText: Copyright (c) 2024 JG for Cedar Grove Maker Studios # SPDX-License-Identifier: MIT import board import displayio import synthio import adafruit_ili9341 from cedargrove_wavebuilder import WaveBuilder, WaveShape from cedargrove_waveviz import WaveViz # Define wave table parameters WAVE_TABLE_LENGTH = 512 # The wave table length in samples SAMPLE_MAXIMUM = 32700 # The maximum value of a sample # Instantiate a built-in display # display = board.DISPLAY # Instantiate the FeatherS2 with 2.4-inch TFT FeatherWing display displayio.release_displays() # Release display resources display_bus = displayio.FourWire( board.SPI(), command=board.D6, chip_select=board.D5, reset=None ) display = adafruit_ili9341.ILI9341(display_bus, width=320, height=240) display.rotation = 0 splash = displayio.Group() display.root_group = splash # Define the Harmonica wave shape, overtone ratio, and amplitude tone = [ (WaveShape.Sine, 1.00, 0.10), (WaveShape.Sine, 2.00, 0.48), (WaveShape.Sine, 3.00, 0.28), (WaveShape.Sine, 4.00, 0.02), (WaveShape.Sine, 5.00, 0.12), ] # Create the wave table (wave.wave_table) wave = WaveBuilder( oscillators=tone, table_length=WAVE_TABLE_LENGTH, sample_max=SAMPLE_MAXIMUM, lambda_factor=1.0, loop_smoothing=True, debug=False, ) # Create a synthio.Envelope object env = synthio.Envelope( attack_time=0.05, attack_level=1.0, decay_time=0.1, release_time=0.1, sustain_level=0.5, ) # Display a small version on the bottom layer splash.append( WaveViz(wave.wave_table, x=20, y=80, width=25, height=25, back_color=0x0000A0) ) # Display a full-sized version on the middle layer splash.append( WaveViz(wave.wave_table, x=0, y=0, width=300, height=240, back_color=None) ) # Display the envelope object on the top layer splash.append( WaveViz( env, x=150, y=170, width=80, height=40, plot_color=0xFFFFFF, back_color=0x800080, ) ) while True: pass