Examples - Axwabo/SecretLabNAudio GitHub Wiki
Examples
Example list:
[!TIP] Check out the demo to see examples of custom sample providers.
See also: providers file reading
Music Playback
When the playback ends, the audio player is pooled. Pooling counts as destroying (disables the component), so the stream will be disposed.
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();
}
Multiple Speakers
When cloning the player's output, SpeakerToys are rented from the pool
Each speaker can have different settings while they play the same audio.
[!TIP] See also: groups
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();
Lobby Music Player
[!CAUTION] Do not use null-conditional operators (
.?) with Unity objects. Why?
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();
}
}
Soundtracks
Register the data store on enable:
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>();
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);
}
Optionally, define an extension method for the player wrapper:
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);
}
Personalization
This example makes the speaker 3D if the player is close to it.
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;
});
}
Mixing
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% volume
[!CAUTION]
MixFilethrows if the file was not found, or if its file type is not supported. Use theMixFileSafeextension 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.
Queueing
To play sources one after another, call one of the Enqueue... extension methods.
The next source will be played when one ends.
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]
EnqueueFilethrows if the file was not found, or if its file type is not supported. Use theEnqueueFileSafeextension method to skip unreadable files.