# Audio

> Audio in FlatRedBall2. Use when working with sound effects, background music, AudioManager, loading Song or SoundEffect, volume control, or collision sound triggers.

- Skill: `vchelaru/audio` (Agent Skill)
- Install (CLI): `npx skillmds@latest add vchelaru/audio`
- Raw SKILL.md: https://api.skillmd.com/api/skills/vchelaru/audio/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: vchelaru (https://skillmd.com/u/vchelaru)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/vchelaru/audio

---


# Audio in FlatRedBall2

## Access

```csharp
Engine.Audio  // AudioManager instance on FlatRedBallService
```

## Loading Audio

Two paths. In the Common/Desktop template layout, prefer the **direct path** — it matches how every other raw asset (png, tmx, achx) is handled, with audio files living alongside them in `Common/Content/`. Reach for MGCB only when a format limit forces it (MP3).

### Direct path — load the raw file (default for OGG/WAV)

Drop the file in `Common/Content/` (e.g. `Common/Content/Audio/song.ogg`); it is linked into the Desktop output's `Content/` automatically, no `.mgcb` entry needed. Load by full path with extension.

**Song (music) — OGG only.** `Song.FromUri` accepts only OGG Vorbis on DesktopGL; MP3 fails at runtime (NVorbis backend).

```csharp
var song = Song.FromUri("song", new Uri(Path.GetFullPath("Content/Audio/song.ogg")));
```

**SoundEffect — WAV only.** `SoundEffect.FromStream` accepts only PCM WAV. OGG/MP3 effects need the MGCB path below.

```csharp
using var stream = File.OpenRead("Content/Audio/hit.wav");
var sfx = SoundEffect.FromStream(stream);
Engine.Content.Track(sfx);  // disposes on screen transition
```

Usings: `System.IO`, `Microsoft.Xna.Framework.Audio`, `Microsoft.Xna.Framework.Media`.

### MGCB pipeline — for MP3 or compressed formats

Needed only when the direct path's format limits bite. The `.mgcb` lives in the **`.Desktop` head**, not `Common`: `MonoGame.Content.Builder.Task` runs only on the head, so a mgcb added to `Common` is silently never built. Put the `#begin` entry *and* the source file under `Desktop/Content/`. See `multiplatform-conversion` for the content-split rationale.

```
#begin Audio/song.mp3
/importer:Mp3Importer
/processor:SongProcessor
/build:Audio/song.mp3
```

Load with the asset name **minus the source extension** — `/build:Audio/song.mp3` builds to `Audio/song.xnb`:

```csharp
var song = Engine.Content.Load<Song>("Audio/song");  // not "Audio/song.mp3"
// ContentLoader disposes Load<>'d assets on screen transition — no Track() needed
```

Usings: `Microsoft.Xna.Framework.Audio`, `Microsoft.Xna.Framework.Media`.

## Sound Effects

```csharp
Engine.Audio.Play(sfx);                             // play with defaults
Engine.Audio.Play(sfx, volume: 0.5f, pitch: 0f, pan: 0f);
Engine.Audio.IsPlaying(sfx);                        // true if any instance is active
```

Per-frame dedup: calling `Play(sfx)` multiple times in a single frame (e.g., from `CollisionOccurred` firing on multiple pairs) plays the sound only once. Cross-frame overlap is allowed.

## Background Music

```csharp
Engine.Audio.PlaySong(song);          // loops by default
Engine.Audio.PlaySong(song, loop: false);
Engine.Audio.PauseSong();             // holds position
Engine.Audio.ResumeSong();            // resumes from position
Engine.Audio.StopSong();              // clears position
```

### Playlist

```csharp
Engine.Audio.PlayPlaylist(song1, song2, song3);  // plays sequentially, loops back to start
```

### Pitchable music (runtime pitch/speed control)

`PlaySong`/`PlayPlaylist` are backed by `MediaPlayer`, which exposes no pitch/rate control on any
backend. For a slow-mo effect or similar, use `PlayPitchableSong` instead — it streams a raw OGG
file through a `DynamicSoundEffectInstance`, which does support real-time pitch:

```csharp
Engine.Audio.PlayPitchableSong("Content/Audio/song.ogg");   // loops by default
Engine.Audio.MusicPitch = -0.3f;                            // tape-slowdown; [-1, 1], takes effect immediately
```

`PauseSong`/`ResumeSong`/`StopSong`/`MusicVolume`/`MusicEnabled` all work the same regardless of
whether `PlaySong` or `PlayPitchableSong` is active.

## Volume and Enable/Disable

```csharp
Engine.Audio.SoundVolume = 0.8f;   // [0, 1], default 1
Engine.Audio.MusicVolume = 0.5f;   // [0, 1], default 1; takes effect immediately
Engine.Audio.SoundEnabled = false; // silences new Play() calls; active instances finish naturally
Engine.Audio.MusicEnabled = false; // pauses current song immediately; true resumes it
```

## Gotchas

- **Music does not stop automatically on screen transition** — call `Engine.Audio.StopSong()` in `CustomDestroy`, or music keeps playing into the next screen.
- **`Song.FromUri` only works with OGG** — on DesktopGL (NVorbis), `Song.FromUri` fails at runtime with MP3. Use the MGCB pipeline and `Engine.Content.Load<Song>` for MP3 files.
- **`Load<>` name drops the source extension** — `Load<Song>("Audio/song")`, not `"Audio/song.ogg"`. Passing the extension makes MonoGame look for `song.ogg.xnb` (the pipeline builds `song.xnb`) and throws `FileNotFoundException`. The `.mgcb` defining it must be in the `.Desktop` head, not `Common`.
- **Track SoundEffect only when loaded via `FromStream`** — call `Engine.Content.Track(sfx)` when using `SoundEffect.FromStream` so it is disposed on screen transition. MGCB-loaded assets (`Engine.Content.Load<SoundEffect>`) are disposed automatically by the ContentLoader — do not call `Track` for those.
- **Per-frame dedup in collision handlers** — `Play(sfx)` in a `CollisionOccurred` handler is safe to call unconditionally; it fires at most once per frame regardless of how many pairs collide.
- **`PlayPitchableSong` is OGG-only**, same as `Song.FromUri`. It loads the raw file directly (not via `Song`), so `Engine.Content.Load<Song>`/MGCB assets don't apply here.
- **`MusicPitch` is a silent no-op while a `PlaySong`/`PlayPlaylist` track is active** — `Song`/`MediaPlayer` expose no pitch API on any backend. Switch to `PlayPitchableSong` to get real pitch control.

