Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 18 additions & 3 deletions api/audio.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ The Audio module provides a **NES-inspired** synthesis stack (Pulse, Triangle, N
- `SINE`: Band-limited sine via LUT.
- `SAW`: Sawtooth from a linear phase ramp.

All `WaveType` values share the same **`MAX_VOICES`** pool. Under contention the implementation may steal a voice (shortest remaining time) to make room for a new event. Internally, `VoiceType` mirrors these for allocation logic.
All `WaveType` values share the same **`MAX_VOICES`** pool. Under contention the implementation may steal a voice (lowest steal score; looping SFX score 0) to make room for a new event. Internally, `VoiceType` mirrors these for allocation logic.

### Predefined Instrument Presets

Expand All @@ -49,20 +49,35 @@ evt.duty = 0.5f;
audio.playEvent(evt);
```

Sweep example (pulse or triangle only; ignored for `NOISE`):
Sweep example (melodic or noise period sweep):

```cpp
AudioEvent sweep{};
sweep.type = WaveType::PULSE;
sweep.frequency = 2000.0f; // start Hz
sweep.sweepEndHz = 400.0f; // end Hz
sweep.sweepEndHz = 400.0f; // end Hz (melodic) or target noise clock Hz for NOISE
sweep.sweepDurationSec = 0.15f; // active when both this and sweepEndHz > 0
sweep.duration = 0.25f;
sweep.volume = 0.7f;
sweep.duty = 0.5f;
audio.playEvent(sweep);
```

Looping SFX (motor, ambience) — stop explicitly when done:

```cpp
AudioEvent motor{};
motor.type = WaveType::TRIANGLE;
motor.frequency = 110.0f;
motor.loop = true;
motor.volume = 0.35f;
motor.preset = &INSTR_TRIANGLE_BASS;
audio.playEvent(motor);
// Later: STOP_CHANNEL on the voice slot, or stop all SFX voices when leaving the scene.
```

Under SFX pool saturation, **looping voices are preferred steal targets** (steal score 0) so new one-shots are not starved.

### Playing Music

```cpp
Expand Down
126 changes: 124 additions & 2 deletions api/generated/audio/ApuCore.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,10 @@

Shared NES-style APU core used by every AudioScheduler.

Owns the 4 channels (2x PULSE, 1x TRIANGLE, 1x NOISE), the SPSC command
queue and the music sequencer. Platform-specific schedulers
Owns an eight-voice pool (MAX_VOICES = 8), the SPSC command queue and the
music sequencer. Voice slots 0–3 are reserved for sequencer tracks
(trackIdx maps 1:1 to slot); slots 4–7 are reserved for PLAY_EVENT / SFX
and sequencer percussion hits (shared subpool, steal policy confined to 4–7). Platform-specific schedulers
(DefaultAudioScheduler, ESP32AudioScheduler, NativeAudioScheduler) are
thin orchestrators that decide *when* generateSamples() runs; all
synthesis, mixing and sequencing lives here to eliminate the three-way
Expand Down Expand Up @@ -165,4 +167,124 @@ Reads and clears all profile entries.

### `size_t countEnabledVoicesForTesting() const`

**Description:**

Test-only: counts voices with enabled==true.

**Returns:** Active voice count in [0, MAX_VOICES].

::: tip
Available only when UNIT_TEST is defined (native_test). Not for game code.
:::

### `size_t getSequencerMainNoteIndexForTesting() const`

**Description:**

Test-only: main music track note index after the last sequencer run.

**Returns:** Index into track 0's note array.

::: tip
Available only when UNIT_TEST is defined (native_test). Not for game code.
:::

### `bool isVoiceEnabledForTesting(int slot) const`

**Description:**

Test-only: reports whether a voice slot is currently synthesizing.

**Parameters:**

- `slot`: Voice index [0, MAX_VOICES); out-of-range returns false.

**Returns:** true if voices[slot].enabled.

::: tip
Available only when UNIT_TEST is defined (native_test). Not for game code.
:::

### `bool isMusicTrackVoiceActiveForTesting(size_t track_index) const`

**Description:**

Test-only: reports whether a music track has an active melodic gate.

**Parameters:**

- `track_index`: Sequencer track [0, MAX_MUSIC_TRACKS); out-of-range returns false.

**Returns:** true after a melodic note until Rest note-off or lifecycle reset.

::: tip
Available only when UNIT_TEST is defined (native_test). Not for game code.
:::

### `uint32_t getVoiceNoisePeriodForTesting(int slot) const`

**Description:**

Test-only: NOISE LFSR period in samples for a voice slot.

**Parameters:**

- `slot`: Voice index [0, MAX_VOICES); out-of-range returns 0.

**Returns:** NOISE LFSR period in samples if slot is valid, 0 otherwise.

### `uint64_t getVoiceRemainingSamplesForTesting(int slot) const`

**Description:**

Test-only: remaining sample gate for a voice slot.

**Parameters:**

- `slot`: Voice index [0, MAX_VOICES); out-of-range returns 0.

**Returns:** remaining samples if slot is valid, 0 otherwise.

### `bool isVoiceLoopForTesting(int slot) const`

**Description:**

Test-only: whether a voice slot is in continuous loop mode.

**Parameters:**

- `slot`: Voice index [0, MAX_VOICES); out-of-range returns false.

**Returns:** true if voice is in loop mode.

### `float getVoiceFrequencyForTesting(int slot) const`

**Description:**

Test-only: current voice frequency in Hz (melodic / noise clock).

**Parameters:**

- `slot`: Voice index [0, MAX_VOICES); out-of-range returns 0.

**Returns:** Frequency in Hz if slot is valid, 0 otherwise.

### `float getVoiceDutyCycleForTesting(int slot) const`

**Description:**

Test-only: current PULSE duty cycle [0,1].

**Parameters:**

- `slot`: Voice index [0, MAX_VOICES); out-of-range returns 0.

### `float getVoiceDutySweepPerSampleForTesting(int slot) const`

**Description:**

Test-only: continuous dutySweep delta per sample (0 when duty stepped).

**Parameters:**

- `slot`: Voice index [0, MAX_VOICES); out-of-range returns 0.
12 changes: 8 additions & 4 deletions api/generated/audio/AudioChannel.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,14 @@ Designed to be static and memory-efficient.
|------|------|-------------|
| `sweepSamplesTotal` | `uint32_t` | Total samples for the sweep. |
| `sweepSamplesRemaining` | `uint32_t` | Samples remaining in the sweep. |
| `sweepStartHz` | `float` | Starting frequency in Hz. |
| `sweepEndHz` | `float` | Ending frequency in Hz. |
| `sweepStartIncQ32` | `uint32_t` | Q32 phase increment at sweep start. |
| `sweepEndIncQ32` | `uint32_t` | Q32 phase increment at sweep end. |
| `sweepStartHz` | `float` | Starting frequency in Hz (NOISE: LFSR clock). |
| `sweepEndHz` | `float` | Ending frequency in Hz (NOISE: LFSR clock). |
| `sweepStartIncQ32` | `uint32_t` | Melodic: Q32 phase inc start; NOISE: start period. |
| `sweepEndIncQ32` | `uint32_t` | Melodic: Q32 phase inc end; NOISE: end period. |
| `sweepCurve` | `SweepCurve` | Active sweep curve (may fallback to Linear). |
| `sweepLogRatio` | `float` | FPU Exponential: logf(endHz/startHz). |
| `sweepLogStartQ16` | `int32_t` | Q15 path Exponential: log2(start) in Q16. |
| `sweepLogDeltaQ16` | `int32_t` | Q15 path Exponential: log2(end/start) in Q16. |

## Methods

Expand Down
15 changes: 8 additions & 7 deletions api/generated/audio/AudioCommandQueue.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,26 +6,27 @@

## Description

Multi-Producer Single-Consumer (MPSC) lock-free ring buffer for AudioCommands.
Single-Producer Single-Consumer (SPSC) lock-free ring buffer for AudioCommands.

Fixed-size, zero-allocation queue designed for real-time audio thread communication.
Supports multiple concurrent producer threads (e.g., game logic, music sequencer)
and a single consumer thread (the audio thread).
Supports one producer (game/logic thread) and a single consumer (the audio thread).
Concurrent multi-producer use is not supported by this algorithm.

Drop policy: When the queue is full, the newest command is silently dropped and
the droppedCommands counter is incremented. Callers can monitor this via
getDroppedCommands() for diagnostics.

Thread-safety: Uses compare-and-swap (CAS) for atomic ring index advancement.
The producer path is wait-free; the consumer path is lock-free.
Thread-safety: Atomic head/tail loads and stores for SPSC handoff.
Safe for one producer and one consumer; not wait-free under contention from
multiple producers.

## Methods

### `bool enqueue(const AudioCommand& cmd)`

**Description:**

Enqueues a command. Thread-safe for multiple producers.
Enqueues a command. Safe for a single producer thread.

**Parameters:**

Expand Down Expand Up @@ -56,4 +57,4 @@ Checks if the queue is empty.
**Description:**

Returns the count of dropped commands due to queue full.
Thread-safe for concurrent reads from multiple producers.
Safe to read from the producer or consumer thread.
10 changes: 10 additions & 0 deletions api/generated/audio/AudioEngine.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,16 @@ Usage:

Initializes the engine and its internal scheduler.

### `void reinitSampleRate(int freq)`

**Description:**

Re-initializes the underlying ApuCore at a new sample rate.

**Parameters:**

- `freq`: The confirmed device sample rate, in Hz.

### `void generateSamples(int16_t* stream, int length)`

**Description:**
Expand Down
19 changes: 19 additions & 0 deletions api/generated/audio/SfxBreakpoint.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# SfxBreakpoint

<Badge type="info" text="Struct" />

**Source:** `AudioTypes.h`

## Description

Timed automation point for SFX duty steps or pitch envelope.

`value` is duty in [0,1] for duty steps, or frequency/clock Hz for pitch.
Tables are static/constexpr in exported banks; AudioEvent holds pointer+count.

## Properties

| Name | Type | Description |
|------|------|-------------|
| `timeSec` | `float` | Offset from voice start (seconds); non-decreasing in a table. |
| `value` | `float` | Duty [0,1] or Hz > 0 depending on table context. |
3 changes: 2 additions & 1 deletion api/generated/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ Auto-generated API documentation from C++ header files.
- [AudioBackend](./audio/AudioBackend.md) — Abstract interface for platform-specific audio drivers.
- [AudioChannel](./audio/AudioChannel.md) — Represents the internal state of a single audio channel.
- [AudioCommand](./audio/AudioCommand.md) — Internal command to communicate between game and audio threads.
- [AudioCommandQueue](./audio/AudioCommandQueue.md) — Multi-Producer Single-Consumer (MPSC) lock-free ring buffer for AudioCommands.
- [AudioCommandQueue](./audio/AudioCommandQueue.md) — Single-Producer Single-Consumer (SPSC) lock-free ring buffer for AudioCommands.
- [AudioConfig](./audio/AudioConfig.md) — Configuration for the Audio subsystem.
- [AudioEngine](./audio/AudioEngine.md) — Facade class for the NES-style audio subsystem.
- [AudioEvent](./audio/AudioEvent.md) — A fire-and-forget sound event triggered by the game.
Expand All @@ -19,6 +19,7 @@ Auto-generated API documentation from C++ header files.
- [LfoState](./audio/LfoState.md) — Holds LFO (Low-Frequency Oscillator) state for pitch or volume modulation.
- [MusicNote](./audio/MusicNote.md) — Represents a single note in a melody.
- [MusicPlayer](./audio/MusicPlayer.md) — Simple sequencer to play MusicTracks using the AudioEngine.
- [SfxBreakpoint](./audio/SfxBreakpoint.md) — Timed automation point for SFX duty steps or pitch envelope.

## Core

Expand Down
4 changes: 3 additions & 1 deletion api/generated/physics/CollisionSystem.md
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ Performs a swept collision test between a circle and an AABB.

**Returns:** True if a collision occurs.

### `bool validateOneWayPlatform( pixelroot32::core::PhysicsActor* actor, pixelroot32::core::PhysicsActor* platform, const pixelroot32::math::Vector2& collisionNormal )`
### `bool validateOneWayPlatform( pixelroot32::core::PhysicsActor* actor, pixelroot32::core::PhysicsActor* platform, const pixelroot32::math::Vector2& collisionNormal, pixelroot32::math::Scalar motionStartHitboxBottom = pixelroot32::math::toScalar(-1.0f)`

**Description:**

Expand All @@ -133,5 +133,7 @@ Validates if a collision with a one-way platform should occur.
- `actor`: The moving actor.
- `platform`: The one-way platform.
- `collisionNormal`: The contact normal.
- `motionStartHitboxBottom`: Hitbox bottom at the start of the current move probe
(used by kinematic moveAndCollide). Negative to use previousPosition instead.

**Returns:** True if the collision is valid and should be resolved.
16 changes: 16 additions & 0 deletions api/generated/physics/KinematicActor.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,22 @@ Clears floor velocity and state.

Returns true if the body collided with a wall.

### `void setStrictTopSurfaceFloor(bool strict)`

**Description:**

Enables or disables top-surface validation for floor state and kinematic carry.

**Parameters:**

- `strict`: When true (default), floor contact requires horizontal overlap on the top face.

### `inline bool isStrictTopSurfaceFloor() const`

**Description:**

Returns whether top-surface floor validation is active.

### `void draw(pixelroot32::graphics::Renderer& renderer)`

**Description:**
Expand Down
Loading
Loading