Skip to content

Audio Types

Data structures and types for the audio system.

Description

This document describes the data structures used by the audio system, including wave types, audio events, and channel state.

Namespace

namespace pixelroot32::audio {
    // Types and structures
}

WaveType Enum

Defines the types of waveforms available.

Values: - WaveType::PULSE: Pulse wave (square wave with variable duty cycle) - WaveType::TRIANGLE: Triangle wave (smooth, melodic) - WaveType::NOISE: Noise wave (random, percussive)

Example:

pixelroot32::audio::WaveType wave = pixelroot32::audio::WaveType::PULSE;

AudioEvent Structure

A fire-and-forget sound event triggered by the game.

Members: - WaveType type: Type of waveform to use - float frequency: Frequency in Hz - float duration: Duration in seconds - float volume: Volume level (0.0 to 1.0) - float duty: Duty cycle for pulse wave (0.0 to 1.0, pulse only)

Example:

pixelroot32::audio::AudioEvent jumpSound{};
jumpSound.type = pixelroot32::audio::WaveType::PULSE;
jumpSound.frequency = 800.0f;
jumpSound.duration = 0.1f;
jumpSound.volume = 0.7f;
jumpSound.duty = 0.5f;

auto& audio = engine.getAudioEngine();
audio.playEvent(jumpSound);

Common Sound Effects

Jump Sound:

pixelroot32::audio::AudioEvent jump{};
jump.type = pixelroot32::audio::WaveType::PULSE;
jump.frequency = 800.0f;
jump.duration = 0.1f;
jump.volume = 0.7f;
jump.duty = 0.5f;

Hit Sound:

pixelroot32::audio::AudioEvent hit{};
hit.type = pixelroot32::audio::WaveType::NOISE;
hit.frequency = 500.0f;
hit.duration = 0.05f;
hit.volume = 0.5f;

Collect Sound:

pixelroot32::audio::AudioEvent collect{};
collect.type = pixelroot32::audio::WaveType::TRIANGLE;
collect.frequency = 1000.0f;
collect.duration = 0.15f;
collect.volume = 0.6f;

Explosion:

pixelroot32::audio::AudioEvent explosion{};
explosion.type = pixelroot32::audio::WaveType::NOISE;
explosion.frequency = 200.0f;
explosion.duration = 0.3f;
explosion.volume = 0.9f;

AudioChannel Structure

Represents the internal state of a single audio channel.

Members: - bool enabled: Whether the channel is active - WaveType type: Type of waveform - float frequency: Current frequency in Hz - float phase: Current phase (0.0 to 1.0) - float phaseIncrement: Pre-calculated phase increment - float volume: Current volume (0.0 to 1.0) - float targetVolume: Target volume for envelopes - float dutyCycle: Duty cycle for pulse wave (0.0 to 1.0) - uint16_t noiseRegister: LFSR state for noise generation - unsigned long durationMs: Total duration in milliseconds - unsigned long remainingMs: Remaining duration in milliseconds

Methods: - void reset(): Resets the channel to inactive state

Notes: - Internal structure, typically not accessed directly - Managed automatically by AudioEngine - 4 channels total: 2 Pulse, 1 Triangle, 1 Noise

Frequency Reference

Common frequencies for musical notes (A4 = 440 Hz):

  • C4: 261.63 Hz
  • D4: 293.66 Hz
  • E4: 329.63 Hz
  • F4: 349.23 Hz
  • G4: 392.00 Hz
  • A4: 440.00 Hz
  • B4: 493.88 Hz
  • C5: 523.25 Hz

Example:

// Play a C note
pixelroot32::audio::AudioEvent note{};
note.type = pixelroot32::audio::WaveType::PULSE;
note.frequency = 261.63f;  // C4
note.duration = 0.5f;
note.volume = 0.8f;
note.duty = 0.5f;

Duty Cycle (Pulse Wave)

Duty cycle controls the shape of the pulse wave:

  • 0.125 (12.5%): Thin pulse (NES-like)
  • 0.25 (25%): Narrow pulse
  • 0.5 (50%): Square wave (most common)
  • 0.75 (75%): Wide pulse

Example:

// Thin pulse (NES style)
event.duty = 0.125f;

// Square wave (standard)
event.duty = 0.5f;

Usage Examples

Creating Sound Effect Library

namespace SoundEffects {
    // Jump sound
    inline pixelroot32::audio::AudioEvent jump() {
        pixelroot32::audio::AudioEvent evt{};
        evt.type = pixelroot32::audio::WaveType::PULSE;
        evt.frequency = 800.0f;
        evt.duration = 0.1f;
        evt.volume = 0.7f;
        evt.duty = 0.5f;
        return evt;
    }

    // Hit sound
    inline pixelroot32::audio::AudioEvent hit() {
        pixelroot32::audio::AudioEvent evt{};
        evt.type = pixelroot32::audio::WaveType::NOISE;
        evt.frequency = 500.0f;
        evt.duration = 0.05f;
        evt.volume = 0.5f;
        return evt;
    }

    // Collect sound
    inline pixelroot32::audio::AudioEvent collect() {
        pixelroot32::audio::AudioEvent evt{};
        evt.type = pixelroot32::audio::WaveType::TRIANGLE;
        evt.frequency = 1000.0f;
        evt.duration = 0.15f;
        evt.volume = 0.6f;
        return evt;
    }
}

// Usage
auto& audio = engine.getAudioEngine();
audio.playEvent(SoundEffects::jump());
audio.playEvent(SoundEffects::hit());

Frequency Sweep Effect

void playSweepSound() {
    auto& audio = engine.getAudioEngine();

    // Create multiple events for sweep effect
    for (int i = 0; i < 5; i++) {
        pixelroot32::audio::AudioEvent evt{};
        evt.type = pixelroot32::audio::WaveType::PULSE;
        evt.frequency = 400.0f + (i * 200.0f);  // Sweep from 400 to 1200 Hz
        evt.duration = 0.05f;
        evt.volume = 0.6f;
        evt.duty = 0.5f;

        audio.playEvent(evt);
        delay(50);  // Small delay between events
    }
}

Performance Considerations

  • Event creation: Creating events is fast (just struct initialization)
  • Channel allocation: Events are queued and played when channels are available
  • Frequency range: Keep frequencies in reasonable range (100-5000 Hz) for best results
  • Duration: Shorter durations free channels faster

ESP32 Considerations

  • Memory: Events are small structs, safe to create frequently
  • CPU: Audio generation is efficient but limit simultaneous sounds
  • Quality: Lower sample rates reduce CPU usage

See Also