-
Notifications
You must be signed in to change notification settings - Fork 0
Examples
Important
Make sure to dispose of audio resources when you're done using them.
You can invoke the dispose method manually, or use the DisposeOnDestroy extension method
to automatically dispose of the WaveStream when the audio player is destroyed.
Example list:
Tip
Check out the demo to see examples of custom sample providers.
See also: providers and file reading
When the playback ends, the audio player is pooled. Pooling counts as destroying (disables the component), so the stream will be disposed.
using System.IO;
using SecretLabNAudio.Core;
using SecretLabNAudio.Core.Extensions;
using SecretLabNAudio.Core.FileReading;
using SecretLabNAudio.Core.Pools;
public static void PlayMusicOneShot(Vector3 position, string path)
{
if (!File.Exists(path) || !TryCreateAudioReader.Stream(path, out var stream))
return;
AudioPlayerPool.Rent(SpeakerSettings.Default, null, position)
.WithProvider(stream)
.PoolOnEnd()
.DisposeOnDestroy(stream);
}Simply create a SpeakerToy with the same controller ID as the AudioPlayer to mirror its output.
The speaker(s) can be placed anywhere, and they can even have different settings.
using SecretLabNAudio.Core;
using SecretLabNAudio.Core.Extensions;
using SecretLabNAudio.Core.Pools;
using UnityEngine;
public static AudioPlayer CloneOutput(this AudioPlayer player, IEnumerable<Vector3> positions)
{
var settings = SpeakerSettings.From(player);
foreach (var position in positions)
SpeakerToyPool.Rent(player.Id, settings, null, position);
return player;
}
// create player
var player = AudioPlayerPool.Rent(Config.Settings, position: Config.MainSpeakerLocation)
.WithProvider(provider)
.CloneOutput(Config.OtherSpeakerLocations);using System.IO;
using LabApi.Events.Handlers;
using LabApi.Loader;
using LabApi.Loader.Features.Plugins;
using NAudio.Wave;
using SecretLabNAudio.Core;
using SecretLabNAudio.Core.Extensions;
using SecretLabNAudio.Core.FileReading;
using SecretLabNAudio.Core.Pools;
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, 0, 0);
private AudioPlayer? _player;
private WaveStream? _stream;
public override void Enable()
{
ServerEvents.WaitingForPlayers += PlayAudio;
ServerEvents.RoundStarted += StopAudio;
}
public override void Disable()
{
StopAudio();
ServerEvents.WaitingForPlayers -= PlayAudio;
ServerEvents.RoundStarted -= StopAudio;
}
private void PlayAudio()
{
_stream?.Dispose();
var path = Path.Combine(this.GetConfigDirectory().FullName, "lobby.mp3");
if (!File.Exists(path))
return;
_stream = CreateAudioReader.Stream(path);
_player = AudioPlayer.Create(AudioPlayerPool.NextAvailableId, new SpeakerSettings
{
IsSpatial = false,
MinDistance = 10000,
MaxDistance = 15000,
Volume = 0.2f
}).WithProvider(_stream.Loop());
}
private void StopAudio()
{
_stream?.Dispose();
_stream = null;
if (_player != null)
_player.Destroy();
}
}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;
}
private void OnJoined(PlayerJoinedEventArgs ev) => ev.Player.GetDataStore<SoundtrackStore>();Store implementation:
using System.IO;
using LabApi.Features.Stores;
using LabApi.Features.Wrappers;
using NAudio.Wave;
using SecretLabNAudio.Core;
using SecretLabNAudio.Core.Extensions;
using SecretLabNAudio.Core.FileReading;
using SecretLabNAudio.Core.Pools;
using SecretLabNAudio.Core.SendEngines;
public sealed class SoundtrackStore : CustomDataStore<SoundtrackStore>
{
private readonly AudioPlayer _player;
private WaveStream? _currentTrack;
public SoundtrackStore(Player owner) : base(owner)
=> _player = AudioPlayer.Create(AudioPlayerPool.NextAvailableId, new SpeakerSettings
{
IsSpatial = false,
MaxDistance = 1,
Volume = 0.2f
}, owner.GameObject.transform) // audio player is destroyed with the owner
.WithSendEngine(new SpecificPlayerSendEngine(owner));
public void ChangeTrack(string path)
{
_currentTrack?.Dispose();
_currentTrack = null;
if (File.Exists(path) && TryCreateAudioReader.Stream(path, out _currentTrack))
_player.WithProvider(_currentTrack.Loop());
}
protected override void OnInstanceDestroyed()
{
_currentTrack?.Dispose();
_currentTrack = null;
}
}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);
}This example makes the speaker 3D if the player is close to it.
using SecretLabNAudio.Core;
using SecretLabNAudio.Core.Extensions;
using SecretLabNAudio.Core.FileReading;
using SecretLabNAudio.Core.Providers;
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 = new()
{
IsSpatial = false,
MaxDistance = float.MaxValue,
Volume = 0.1f
};
public static void Beep(Vector3 position)
{
if (!ShortClipCache.TryGet("beep", out RawSourceSampleProvider? provider))
return;
var audioPlayer = AudioPlayer.Create(AudioPlayerPool.NextAvailableId, Far, null, position);
var personalization = audioPlayer.AddPersonalization();
audioPlayer.WithLivePersonalizedSendEngine(personalization, (player, current) =>
{
if (Vector3.Distance(player.Position, position) > SpatialRange)
return null; // default (Far) settings
return Near;
})
.WithProvider(provider.Loop());
}