SCPSL-AudioManagerAPI
2.2.0
See the version list below for details.
dotnet add package SCPSL-AudioManagerAPI --version 2.2.0
NuGet\Install-Package SCPSL-AudioManagerAPI -Version 2.2.0
<PackageReference Include="SCPSL-AudioManagerAPI" Version="2.2.0" />
<PackageVersion Include="SCPSL-AudioManagerAPI" Version="2.2.0" />
<PackageReference Include="SCPSL-AudioManagerAPI" />
paket add SCPSL-AudioManagerAPI --version 2.2.0
#r "nuget: SCPSL-AudioManagerAPI, 2.2.0"
#:package SCPSL-AudioManagerAPI@2.2.0
#addin nuget:?package=SCPSL-AudioManagerAPI&version=2.2.0
#tool nuget:?package=SCPSL-AudioManagerAPI&version=2.2.0
SCPSL-AudioManagerAPI
A lightweight, high-performance, and robust C# library for managing audio playback in SCP: Secret Laboratory (SCP:SL) plugins using LabAPI.
Designed to integrate seamlessly with Northwood’s LabAPI ecosystem, AudioManagerAPI introduces a powerful Session-Based Architecture. It abstracts away hardware limitations, providing a reliable system for loading, caching, and playing audio with advanced control (volume, position, range, spatialization, zero-allocation player filtering, and hardware eviction recovery).
⚠️ Important: V2.0.0 Breaking Changes
Version 2.0.0 is a major architectural overhaul. Hardware Controller IDs (byte) have been completely replaced by abstract Session IDs (int). Direct manipulation ofISpeakeris no longer permitted in the public API to ensure thread safety and state persistence during hardware eviction. See the Migration Guide below.
Powered by AudioManagerAPI
🏗️ Architecture Overview (V2.1.0)
The core of the architecture relies on the separation between Abstract Sessions and Physical Hardware:
Session IDs (
int)
When you play audio, you are allocated a virtual Session ID. This session holds your state (volume, position, filters, queue, PCM buffers).Hardware Eviction
LabAPI is limited to 254 physical controllers. If the server runs out of physical speakers, theControllerIdManagerwill safely evict lower-priority physical speakers. Your session remains alive in the background.The Router
AudioManageracts as a router. When you send a command (e.g.,Pause(sessionId)), the router updates the abstract state and routes the command to the physical LabAPI speaker only if it is currently allocated.Real‑Time Streaming Layer (NEW in 2.1.0)
Sessions can now receive raw PCM buffers dynamically. The router forwards PCM to hardware when available, or queues it for later playback if the session is temporarily evicted.
🚀 Quick Start
1. Registering Audio
Before playing audio, register your audio files. AudioCache automatically decodes WAV (16/24/32‑bit PCM or IEEE float), downmixes to mono, and resamples to 48 kHz.
The asynchronous AudioCache will handle lazy-loading without blocking the server thread.
using AudioManagerAPI.Defaults;
using System.Reflection;
public void OnPluginStart()
{
DefaultAudioManager.RegisterAudio("my_custom_sound", () =>
Assembly.GetExecutingAssembly().GetManifestResourceStream("MyPlugin.Audio.sound.wav"));
}
AudioCache automatically handles:
- WAV PCM 16/24/32‑bit
- WAV IEEE float
- automatic mono downmix
- automatic resampling to 48 kHz
- always returns float[] samples
- MP3 decoding (via built‑in lightweight decoder)
2. Playing Audio
Use the DefaultAudioManager facade to play audio. It returns an int Session ID.
// Play audio globally for all players
int sessionId = DefaultAudioManager.Play("my_custom_sound", queue: false, fadeInDuration: 1.5f);
// Pause and Resume
DefaultAudioManager.Pause(sessionId);
DefaultAudioManager.Resume(sessionId);
// Stop and Destroy Session
DefaultAudioManager.Stop(sessionId);
3. Advanced Playback (Spatial & Filtered)
For 3D audio or targeting specific players, use the Instance directly.
using AudioManagerAPI.Defaults;
using AudioManagerAPI.Features.Enums;
using AudioManagerAPI.Features.Filters;
int spatialSessionId = DefaultAudioManager.Instance.PlayAudio(
key: "my_custom_sound",
position: new Vector3(10f, 0f, 10f),
loop: false,
volume: 0.8f,
minDistance: 5f,
maxDistance: 25f,
isSpatial: true,
priority: AudioPriority.High,
validPlayersFilter: AudioFilters.ByRole(RoleTypeId.ClassD) // Zero-allocation filter
);
🎤 Real‑Time PCM Streaming (NEW in 2.1.0)
AudioManagerAPI 2.1.0 introduces native real‑time PCM streaming, allowing plugins to push raw audio data directly into an active session.
This feature is ideal for:
- proximity voice chat
- live microphone input
- AI-generated speech
- dynamic sound effects
- procedural audio systems
PCM Format Requirements (Float‑Native)
- 48 kHz
- Mono
- Normalized float PCM (-1.0 to 1.0)
- Provided as a
float[]buffer
A legacy short[] overload exists for backward compatibility.
Example: 🚀 Creating a Stream‑Only Audio Session
int sessionId = DefaultAudioManager.Instance.CreateStreamSession(
position: player.Position,
isSpatial: true,
minDistance: 0.05f,
maxDistance: 20f,
volume: 1f,
priority: AudioPriority.High,
validPlayersFilter: p => p.PlayerId != player.PlayerId
);
This creates a fully initialized audio session with:
- a physical speaker (if available)
- spatialization
- distance attenuation
- player filtering
- no static audio clip
- ready to receive PCM
Example: 🔊 Streaming PCM Into an Existing Session
Whenever new PCM arrives (e.g., from an Opus decoder):
float[] pcmBuffer = GetDecodedFloatPcm(); // normalized float PCM (-1..1)
DefaultAudioManager.Instance.AppendPcmData(sessionId, pcmBuffer);
Legacy short[] example (deprecated):
short[] legacy = GetDecodedShortPcm();
DefaultAudioManager.Instance.AppendPcmData(sessionId, legacy);
The PCM is:
- queued in
SpeakerState.PcmQueue - forwarded to the physical speaker (if allocated)
- played immediately in real time
Example: 🔁 Updating the Speaker Position
For moving audio sources (e.g., players), update the session position:
DefaultAudioManager.Instance.SetSessionPosition(sessionId, player.Position);
Example: 🧹 Cleaning Up
To stop a streaming session:
DefaultAudioManager.Instance.DestroySession(sessionId);
NOTES
- Stream‑only sessions do not require any audio key.
CreateStreamSessionis the recommended way to implement proximity voice or any dynamic audio pipeline.- Float PCM (-1..1) is processed in real time and does not require pre‑loaded audio clips.
- Works seamlessly with
ISpeakerWithPlayerFilterfor per‑player visibility.
🎧 Float‑Native Audio Pipeline (NEW in 2.2.0)
AudioManagerAPI now uses a fully float‑native audio pipeline:
- AudioCache decodes all audio into float[]
- Real‑time streaming uses float[] buffers
- ISpeaker.AppendPcm(float[]) is the primary low‑level API
- short[] overloads remain only for backward compatibility
- MP3 files are automatically decoded and converted to float[]
This ensures maximum audio quality with no quantization, clipping, or integer PCM artifacts.
⚙️ Configuration
On the first launch, the API generates Configs/AudioConfig.json in the server root.
This file is fault-tolerant and will safely fall back to defaults if misconfigured.
{
"CacheSize": 50,
"UseDefaultSpeakerFactory": true,
"DefaultFadeInDuration": 1.0,
"DefaultFadeOutDuration": 1.0
}
Note: CacheSize determines how many decoded WAV files are kept in the LRU RAM cache.
🔄 Migration Guide
Migration from V1.9.x to V2.0.0
Change ID Types
Replace allbyte controllerIdvariables withint sessionId.Update API Calls
PlayGlobalAudioandPlayAudionow returnint.Update API Calls
ChangeDestroySpeaker(id)toStop(id)orDestroySession(id).Remove
Action<ISpeaker>Configurations
Direct access toISpeakerduring instantiation is removed. Instead of passing a configure delegate, use the dedicatedvalidPlayersFilterparameter natively supported by thePlayAudiomethod.Use
AudioFiltersSafely
Custom filters are nowFunc<Player, bool>. If you use dynamic conditions, wrap them properly to avoid pass-by-value bugs (e.g.,AudioFilters.IsConditionTrue(() => Round.IsStarted)).
Migration from V2.0.0 to V2.2.0
Version 2.2.0 is backwards compatible with 2.0.0 and 2.1.0 for all existing static audio use-cases.
New functionality is opt-in and focused on real-time PCM streaming.
New in 2.1.0 - 2.2.0:
IAudioManager.AppendPcmData(int sessionId, short[] pcm)- Allows streaming raw PCM buffers into an existing session.
- Intended for proximity voice, synthesized speech, and other dynamic audio sources.
- The caller should provide normalized float PCM (-1..1). A short[] overload remains available for backward compatibility.
ISpeaker.AppendPcm(float[] samples) — primary methodISpeaker.AppendPcm(short[] pcm) — legacy wrapper- Low-level method used by the router to forward PCM directly to the hardware speaker buffer.
- Implemented by the default LabAPI-based speaker adapter.
SpeakerState.PcmQueue- SpeakerState.PcmQueue — A FIFO queue of float[] PCM buffers (normalized -1..1) used to store audio when a session is temporarily evicted from hardware.
- Ensures that no audio is lost during hardware eviction and recovery.
No breaking changes:
- Existing code using
Play,PlayAudio,PlayGlobalAudio,Pause,Resume,FadeIn,FadeOut,Stop,DestroySessioncontinues to work without modification. - Real-time streaming is an additive feature.
📄 License
This project is licensed under the MIT License. See the LICENSE file for details.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET Framework | net48 is compatible. net481 was computed. |
-
.NETFramework 4.8
- NAudio (>= 2.3.0)
- Northwood.LabAPI (>= 1.1.7)
- System.Text.Json (>= 10.0.8)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 2.3.3 | 0 | 6/9/2026 |
| 2.3.2 | 45 | 6/9/2026 |
| 2.3.1 | 51 | 6/9/2026 |
| 2.3.0 | 52 | 6/9/2026 |
| 2.2.0 | 66 | 6/3/2026 |
| 2.1.2 | 63 | 6/3/2026 |
| 2.1.1 | 59 | 6/3/2026 |
| 2.1.0 | 65 | 6/3/2026 |
| 2.0.3 | 104 | 6/1/2026 |
| 2.0.2 | 137 | 3/8/2026 |
| 2.0.1 | 109 | 3/8/2026 |
| 2.0.0 | 113 | 3/8/2026 |
| 1.9.0 | 123 | 2/27/2026 |
| 1.8.0 | 214 | 9/26/2025 |
| 1.7.0 | 225 | 7/28/2025 |
| 1.6.0 | 175 | 7/27/2025 |
| 1.5.2 | 306 | 7/26/2025 |
| 1.5.1 | 323 | 7/26/2025 |
| 1.5.0 | 543 | 7/24/2025 |
| 1.4.2 | 533 | 7/24/2025 |