Skip to content

Conversation

@BeanRepo
Copy link
Contributor

@BeanRepo BeanRepo commented Nov 6, 2025

Wave Generator Brick

A continuous wave generator for real-time audio synthesis. Generates various waveforms (sine, square, sawtooth, triangle) and streams them to a USB speaker with smooth frequency and amplitude transitions.

Features

  • Multiple waveforms: sine, square, sawtooth, triangle
  • Smooth transitions: Configurable glide (portamento), attack, and release times
  • Real-time control: Change frequency, amplitude, and waveform on the fly
  • Efficient: Pre-allocated buffers and NumPy vectorization
  • Thread-safe: Safe to call from multiple threads

Usage

Basic Example

from arduino.app_bricks.wave_generator import WaveGenerator
from arduino.app_utils import App

# Create wave generator with default settings
wave_gen = WaveGenerator()

# Start generation
App.start_brick(wave_gen)

# Control the generator
wave_gen.set_frequency(440.0)  # A4 note
wave_gen.set_amplitude(0.8)    # 80% amplitude

# Keep app running
App.run()

Advanced Configuration

from arduino.app_bricks.wave_generator import WaveGenerator
from arduino.app_utils import App

# Create with custom settings
wave_gen = WaveGenerator(
    sample_rate=16000,
    wave_type="square",       # Initial waveform
    block_duration=0.03,      # 30ms blocks
    attack=0.01,              # 10ms attack time
    release=0.03,             # 30ms release time
    glide=0.02,               # 20ms frequency glide
)

App.start_brick(wave_gen)

# Change waveform type
wave_gen.set_wave_type("triangle")

# Set frequency and amplitude
wave_gen.set_frequency(880.0)  # A5 note
wave_gen.set_amplitude(0.5)

# Adjust envelope
wave_gen.set_envelope_params(attack=0.05, release=0.1, glide=0.05)

App.run()

Theremin-Style Controller

import time
from arduino.app_bricks.wave_generator import WaveGenerator
from arduino.app_utils import App

wave_gen = WaveGenerator(wave_type="sine", glide=0.02)
App.start_brick(wave_gen)

def theremin_loop():
    """Simulate theremin-style frequency sweeps."""
    for freq in range(220, 880, 10):
        wave_gen.set_frequency(float(freq))
        wave_gen.set_amplitude(0.7)
        time.sleep(0.05)
    
    # Fade out
    wave_gen.set_amplitude(0.0)
    time.sleep(2)

App.run(user_loop=theremin_loop)

With WebUI Control

from arduino.app_bricks.wave_generator import WaveGenerator
from arduino.app_bricks.web_ui import WebUI
from arduino.app_utils import App

wave_gen = WaveGenerator()
ui = WebUI()

def on_frequency_change(sid, data):
    freq = float(data.get('frequency', 440))
    wave_gen.set_frequency(freq)

def on_amplitude_change(sid, data):
    amp = float(data.get('amplitude', 0.5))
    wave_gen.set_amplitude(amp)

def on_waveform_change(sid, data):
    wave_type = data.get('wave_type', 'sine')
    wave_gen.set_wave_type(wave_type)

ui.on_message('set_frequency', on_frequency_change)
ui.on_message('set_amplitude', on_amplitude_change)
ui.on_message('set_waveform', on_waveform_change)

App.run()

API Reference

Constructor

WaveGenerator(
    sample_rate: int = 16000,
    wave_type: WaveType = "sine",
    block_duration: float = 0.03,
    attack: float = 0.01,
    release: float = 0.03,
    glide: float = 0.02,
    speaker_device: str = Speaker.USB_SPEAKER_1,
    speaker_format: str = "FLOAT_LE",
)

Parameters:

  • sample_rate: Audio sample rate in Hz (default: 16000)
  • wave_type: Initial waveform - "sine", "square", "sawtooth", "triangle" (default: "sine")
  • block_duration: Audio block duration in seconds (default: 0.03)
  • attack: Amplitude attack time in seconds (default: 0.01)
  • release: Amplitude release time in seconds (default: 0.03)
  • glide: Frequency glide time (portamento) in seconds (default: 0.02)
  • speaker_device: Speaker device identifier (default: USB_SPEAKER_1)
  • speaker_format: Audio format (default: "FLOAT_LE")

Methods

set_frequency(frequency: float)

Set target output frequency with smooth glide transition.

Parameters:

  • frequency: Target frequency in Hz (typically 20-8000 Hz)

set_amplitude(amplitude: float)

Set target output amplitude with smooth attack/release.

Parameters:

  • amplitude: Target amplitude in range [0.0, 1.0]

set_wave_type(wave_type: WaveType)

Change the waveform type.

Parameters:

  • wave_type: One of "sine", "square", "sawtooth", "triangle"

set_volume(volume: float)

Set master volume level.

Parameters:

  • volume: Master volume in range [0.0, 1.0]

set_envelope_params(attack=None, release=None, glide=None)

Update envelope parameters.

Parameters:

  • attack: Attack time in seconds (optional)
  • release: Release time in seconds (optional)
  • glide: Frequency glide time in seconds (optional)

get_state() -> dict

Get current generator state.

Returns:

  • Dictionary with keys: frequency, amplitude, wave_type, master_volume, phase

Waveform Types

Sine Wave

Classic smooth sine wave, ideal for pure tones and musical applications.

wave_gen.set_wave_type("sine")

Square Wave

Sharp square wave with odd harmonics, creates a "hollow" or "clarinet-like" sound.

wave_gen.set_wave_type("square")

Sawtooth Wave

Bright sawtooth wave with all harmonics, creates a "buzzy" or "brassy" sound.

wave_gen.set_wave_type("sawtooth")

Triangle Wave

Softer than square, contains only odd harmonics with lower amplitude.

wave_gen.set_wave_type("triangle")

Envelope Parameters

Attack Time

Time to rise from current amplitude to target amplitude when increasing.

Typical values:

  • 0.001 - 1ms: Very fast, almost instant
  • 0.01 - 10ms: Fast, percussive
  • 0.1 - 100ms: Slow, pad-like

Release Time

Time to fall from current amplitude to target amplitude when decreasing.

Typical values:

  • 0.01 - 10ms: Short decay
  • 0.05 - 50ms: Medium decay
  • 0.5 - 500ms: Long decay, reverb-like

Glide Time (Portamento)

Time to smoothly transition from current frequency to target frequency.

Typical values:

  • 0.0 - Disabled: Instant frequency changes (may cause clicks)
  • 0.005 - 5ms: Minimal, just removes clicks
  • 0.02 - 20ms: Natural, smooth transitions (recommended)
  • 0.05 - 50ms: Noticeable portamento effect
  • 0.1+ - 100ms+: Very "slidey", theremin-like

Hardware Requirements

  • Arduino UNO Q (or compatible)
  • USB-C® hub with external power
  • USB audio device (USB speaker, wireless dongle, or USB-C → 3.5mm adapter)
  • Power supply (5V, 3A) for USB hub

Note: Must run in Network Mode or SBC Mode as the USB-C port is needed for the hub.

Troubleshooting

No Sound Output

  • Check USB speaker is connected and powered
  • Verify amplitude is > 0: wave_gen.set_amplitude(0.5)
  • Check master volume: wave_gen.set_volume(0.8)

Choppy or Clicking Audio

  • Increase glide time: wave_gen.set_envelope_params(glide=0.05)
  • Reduce block duration for lower latency: WaveGenerator(block_duration=0.02)
  • Close other CPU-intensive applications

"No USB speaker found" Error

  • Ensure USB-C hub is connected with 5V/3A power supply
  • Connect USB audio device to the hub
  • Restart the application

@CLAassistant
Copy link

CLAassistant commented Nov 6, 2025

CLA assistant check
All committers have signed the CLA.

@dsammaruga
Copy link

Recent Updates to WaveGenerator Brick

This update refines the WaveGenerator brick's API and improves hardware volume control, along with comprehensive testing and documentation updates.

Changes Summary

1. External Speaker Instance Support (b7b09a1)

  • Added ability to pass a pre-configured Speaker instance to WaveGenerator
  • Enables custom speaker device selection and audio format configuration
  • WaveGenerator now manages speaker lifecycle (start/stop) automatically
  • Added example 06_external_speaker.py demonstrating external speaker usage
  • Updated README with external speaker configuration guide

Benefits:

  • Flexibility to use specific USB speakers (USB_SPEAKER_1, USB_SPEAKER_2, etc.)
  • Support for different audio formats (S16_LE, FLOAT_LE, etc.)
  • Explicit device name configuration when auto-detection isn't suitable

2. Speaker Mixer Selection Fix (4ad8c42)

  • Fixed ALSA mixer detection to correctly identify playback volume controls
  • Simplified mixer selection logic from ~50 lines to ~18 lines
  • Changed to directly load 'Headset' mixer for UNO Q's UH34 audio device
  • Removed complex priority-based fallback system in favor of targeted approach

Impact:

  • Volume control now works correctly on Arduino UNO Q hardware
  • More reliable mixer detection for USB audio devices

3. Volume Handling Improvements (546c51b)

  • Breaking Change: Unified volume control to hardware-only (0-100 integer range)
  • Removed software master_volume parameter (was 0.0-1.0 float)
  • Added get_volume() method to retrieve current hardware volume
  • get_state() now returns volume instead of master_volume
  • Automatic volume initialization to 100% on start
  • Improved logging: changed default level from DEBUG to INFO

API Changes:

  • set_volume(volume: int) - now accepts 0-100 instead of 0.0-1.0
  • get_volume() -> int - new method, returns current hardware volume
  • get_state() - returns dict with volume key (0-100) instead of master_volume

4. Comprehensive Testing (02ce879)

  • Added 25 unit tests covering all WaveGenerator functionality
  • Tests include: initialization, waveform generation, envelope control, thread safety
  • Mock Speaker implementation for hardware-independent testing
  • All tests passing in CI/CD pipeline

Test Coverage:

  • Basic initialization and configuration
  • All waveform types (sine, square, sawtooth, triangle)
  • Frequency glide and amplitude envelope
  • Volume control and state management
  • Thread-safe concurrent access
  • Buffer pre-allocation and phase continuity

5. Test Adaptation (853f677)

  • Updated tests to reflect new volume API (0-100 integer range)
  • Added FakeMixer mock for hardware volume simulation
  • Removed obsolete master_volume tests
  • Updated all _generate_block() calls to match new signature (no master_volume param)

6. Documentation and Examples Update (a01edeb)

  • Updated README.md with correct volume API documentation
  • Fixed all code examples to use set_volume(80) instead of set_volume(0.8)
  • Updated API reference section with get_volume() method
  • Corrected troubleshooting section to reflect hardware volume control
  • Updated 3 examples: 01_basic_tone.py, 04_musical_scale.py, 05_envelope_control.py

Migration Guide

For Existing Code

If you're using WaveGenerator with volume control, update:

Before:

wave_gen.set_volume(0.8)  # Float 0.0-1.0

After:

wave_gen.set_volume(80)  # Integer 0-100

Constructor Changes

No changes to constructor parameters - speaker parameter added but optional.

New optional pattern:

from arduino.app_peripherals.speaker import Speaker

# Custom speaker configuration
speaker = Speaker(device=Speaker.USB_SPEAKER_2, format="S16_LE")
wave_gen = WaveGenerator(speaker=speaker)

@rjtokenring rjtokenring marked this pull request as ready for review November 7, 2025 12:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants