Files
2026-06-21 21:19:51 -04:00

89 lines
2.9 KiB
Python

# Standalone MicroPython Voice Animation: Speaking Mouth (Audio Spectrum)
# Exposes run(text="", frames=...) and runs automatically if executed directly.
import time
import math
try:
import board_config
display = getattr(board_config, 'display_instance', None)
except Exception:
board_config = None
display = None
def draw_center_text(d, msg, y, w, scale=1, c=1):
char_w = 8 * scale
x = max(0, (w - len(msg) * char_w) // 2)
if hasattr(d, 'text_large') and scale > 1:
try:
d.text_large(msg, x, y, scale, c)
return
except Exception:
pass
d.text(msg, x, y, c)
def run(text="", frames=100):
if not display:
print("board_config.display_instance is not available. Skipping animation.")
return
width = getattr(display, 'width', 400)
height = getattr(display, 'height', 300)
cx, cy = width // 2, height // 2
label = text if text else "SPEAKING..."
frame = 0
try:
while True:
if frames is not None and frames > 0 and frame >= frames:
break
display.clear(0)
# Draw top and bottom boundary lines for the sound waves
display.line(cx - 100, cy - 45, cx + 100, cy - 45, 1)
display.line(cx - 100, cy + 45, cx + 100, cy + 45, 1)
# Draw a series of vertical equalizer bars modulated to simulate speech patterns
# An envelope simulates words (peaks and silences)
word_envelope = abs(math.sin(frame * 0.05))
num_bars = 15
bar_w = 8
gap = 4
total_w = num_bars * bar_w + (num_bars - 1) * gap
start_x = cx - total_w // 2
for i in range(num_bars):
bx = start_x + i * (bar_w + gap)
# Modulate individual bar heights using frame phase offsets
phase = frame * 0.28 + i * 0.45
noise_val = abs(math.sin(phase)) * 0.7 + abs(math.cos(phase * 1.5)) * 0.3
# Height peaks towards the center bars (bell shape)
center_factor = math.cos(((i - num_bars // 2) / (num_bars // 2)) * (math.pi / 2.2))
h = int(6 + 72 * word_envelope * noise_val * center_factor)
# Keep height within boundaries
h = min(80, h)
# Draw vertical bar centered at cy
display.fill_rect(bx, cy - h // 2, bar_w, h, 1)
# Draw centered text label at the bottom
draw_center_text(display, label, height - 30, width, scale=1, c=1)
display.show()
frame += 1
try:
time.sleep_ms(50)
except AttributeError:
time.sleep(0.05)
except KeyboardInterrupt:
print("Animation interrupted by user.")
if __name__ in ('__main__', '__name__'):
run()