-
Notifications
You must be signed in to change notification settings - Fork 0
Examples
List of practical examples:
- Music Playback
- Multiple Speakers
- Lobby Music Plugin
- Soundtracks
- Personalization
- Mixing
- Playlists
- Queueing
When the playback ends, the audio player is pooled. Pooling counts as destroying (disables the component), so the stream will be disposed.
Simple Code
using SecretLabNAudio.Core.Extensions;
using SecretLabNAudio.Core.Pools;
using UnityEngine;
public static void PlayMusicOneShot(Vector3 position, string path)
{
AudioPlayerPool.RentDefault(position)
.PoolOnEnd()
.UseFile(path);
}File Readability Check
This version of the code doesn't create the player if the file is not readable.
using SecretLabNAudio.Core.Extensions;
using SecretLabNAudio.Core.Extensions.Processors;
using SecretLabNAudio.Core.Processors;
using SecretLabNAudio.Core.Pools;
using UnityEngine;
public static void PlayMusicOneShot(Vector3 position, string path)
{
// if not using C# 14, the line below would use
// the StreamProcessorExtensions class
if (!StreamAudioProcessor.TryCreateFromFile(path, false, out var processor))
return;
AudioPlayerPool.RentDefault(position)
// the processor is disposed when the player is pooled/destroyed
.Use(processor)
.PoolOnEnd();
}When cloning the player's output, SpeakerToys are rented from the pool
Each speaker can have different settings while they play the same audio.
using SecretLabNAudio.Core;
using SecretLabNAudio.Core.Extensions;
using SecretLabNAudio.Core.Pools;
var player = AudioPlayerPool.Rent(Config.Settings, position: Config.MainSpeakerLocation)
.Use(processor)
.CloneOutput(Config.OtherSpeakerLocations)
.PoolOnEnd();Tip
See also: groups
Caution
Do not use null-conditional operators (.?) with Unity objects.
Why?
This is a simple implementation of a music player that plays one file.
Plugin Class
using System;
using System.IO;
using LabApi.Events.Handlers;
using LabApi.Loader;
using LabApi.Loader.Features.Plugins;
using SecretLabNAudio.Core;
using SecretLabNAudio.Core.Extensions;
public sealed class LobbyMusic : Plugin
{
public override string Name => "LobbyMusic";
public override string Description => "Plays a song in the lobby";
public override string Author => "Author";
public override Version Version => GetType().Assembly.GetName().Version;
public override Version RequiredApiVersion { get; } = new(1, 1, 5);
private AudioPlayer? _player;
public override void Enable()
{
ServerEvents.WaitingForPlayers += PlayAudio;
ServerEvents.RoundStarted += StopAudio;
}
public override void Disable()
{
StopAudio();
ServerEvents.WaitingForPlayers -= PlayAudio;
ServerEvents.RoundStarted -= StopAudio;
}
private void PlayAudio()
{
var path = Path.Combine(this.GetConfigDirectory().FullName, "lobby.mp3");
var settings = SpeakerSettings.GloballyAudible with {Volume = 0.2f};
_player = AudioPlayer.Create(settings).UseFile(path, true);
}
private void StopAudio()
{
// do not use null-conditional operators (.?) with Unity objects
if (_player != null)
_player.Destroy();
}
}Data Store Implementation
using LabApi.Features.Stores;
using LabApi.Features.Wrappers;
using SecretLabNAudio.Core;
using SecretLabNAudio.Core.Extensions;
using SecretLabNAudio.Core.SendEngines;
public sealed class SoundtrackStore : CustomDataStore<SoundtrackStore>
{
private static readonly SpeakerSettings Settings = new()
{
IsSpatial = false,
MaxDistance = 1,
Volume = 0.2f
};
private readonly AudioPlayer _player;
public SoundtrackStore(Player owner) : base(owner)
=> _player = AudioPlayer.Create(
Settings,
parent: owner.GameObject!.transform // destroy with the owner
).WithSendEngine(new SpecificPlayerSendEngine(owner));
// no exception; the track won't change if the file isn't readable
public void ChangeTrack(string path) => _player.UseFileSafe(path);
}Register Data Store
using LabApi.Events.Arguments.PlayerEvents;
using LabApi.Events.Handlers;
using LabApi.Features.Stores;
public override void Enable()
{
CustomDataStoreManager.RegisterStore<SoundtrackStore>();
PlayerEvents.Joined += OnJoined;
}
// force initialization of the store
private void OnJoined(PlayerJoinedEventArgs ev)
=> ev.Player.GetDataStore<SoundtrackStore>();Optional Extension Method
using LabApi.Features.Stores;
using LabApi.Features.Wrappers;
public static class PlayerExtensions
{
public static void ChangeTrack(this Player player, string path)
=> player.GetDataStore<SoundtrackStore>().ChangeTrack(path);
}This example makes the speaker 3D if the player is close to it.
Code
using SecretLabNAudio.Core;
using SecretLabNAudio.Core.Extensions;
using UnityEngine;
private const float SpatialRange = 10;
private static readonly SpeakerSettings Near = new()
{
IsSpatial = true,
MinDistance = 2,
MaxDistance = SpatialRange + 2,
Volume = 1
};
private static readonly SpeakerSettings Far = SpeakerSettings.GloballyAudible with {Volume = 0.1f};
public static void BeepLoop(Vector3 position)
{
AudioPlayer.Create(Far, position)
.UseShortClip("beep", true)
.WithLivePersonalizedSendEngine((player, _) =>
{
if (Vector3.Distance(player.Position, position) > SpatialRange)
return null; // default (Far) settings
return Near;
});
}To play multiple sources at once, call one of the Mix... extension methods.
using SecretLabNAudio.Core;
using SecretLabNAudio.Core.Extensions;
AudioPlayer.CreateGlobal()
.MixFile("/path/to/first/track.mp3", true, 0.5f) // looped, 50% volume
.MixShortClip("SoundEffect", true) // looped, 100% volume
.MixFile("/path/to/second/track.ogg"); // plays once, 100% volumeCaution
MixFile throws if the file was not found, or if its file type is not supported.
Use the MixFileSafe extension method to skip unreadable files.
Note
In this example, the audio player is only destroyed when the round restarts because the first track and the short clip are being looped.
The LazyPlaylist lets you define a list of items to play one after another, while allowing for shuffling, and repeating one item or the entire playlist.
using SecretLabNAudio.Core;
using SecretLabNAudio.Core.Extensions;
using SecretLabNAudio.Core.Extensions.Processors;
AudioPlayer.CreateGlobal().UsePlaylist(
playlist => playlist.RepeatAll()
// .AutoShuffle()
.AddFile("/path/to/first/track.mp3")
.AddShortClip("Jingle")
.AddFiles(
"/path/to/second/track.wav",
"/path/to/third/track.ogg"
)
);To play sources one after another, call one of the Enqueue... extension methods.
The next source will be played when one ends.
Note
Queueing pre-allocates each processor, meaning more resource usage and open file handles.
using SecretLabNAudio.Core.Extensions;
using SecretLabNAudio.Core.Pools;
AudioPlayerPool.RentGloballyAudible()
.PoolOnEnd() // returns the player even if EnqueueFile threw an exception
.EnqueueFile("/path/to/first/track.mp3")
.EnqueueShortClip("SoundEffect")
.EnqueueFile("/path/to/second/track.ogg");Caution
EnqueueFile throws if the file was not found, or if its file type is not supported.
Use the EnqueueFileSafe extension method to skip unreadable files.
- π Home
- πΌ Digital Audio Basics
- π Examples
- π¦ Supported Formats
- β¬οΈ Migrating from v1
- π AudioPlayer
- πΎ Short Clips
- πΏ Streaming From Disk
- ποΈ Speaker Groups
- π Sample Providers
- β»οΈ Pooling
- π¨ SendEngines
- π§ Personalizing Speakers
- π Monitoring Output
- βοΈ AudioQueue
- πΆ Mixer
- ποΈοΈ ProcessorChain
- π LazyPlaylist
- π§° Intro to FFmpeg
- π© Installation
- ποΈ FFmpegArguments
- πͺ Adding Short Clips
- π FFmpeg Audio Processors
- ποΈ Caches
Caution
v1 is out of support.