Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
116 commits
Select commit Hold shift + click to select a range
d2f5dbc
Automatic id / throwing errors changed to log error
MS-crew Feb 27, 2026
bae2994
f
MS-crew Feb 27, 2026
be87e9f
.
MS-crew Feb 27, 2026
ee82708
s
MS-crew Feb 27, 2026
36428d6
gf
MS-crew Feb 27, 2026
841d20d
eğH
MS-crew Feb 27, 2026
a08e3ff
added new özellik
MS-crew Mar 1, 2026
c6fe071
update
MS-crew Mar 1, 2026
2d9feea
update log
MS-crew Mar 1, 2026
8ec3cde
color on wrong order
MS-crew Mar 1, 2026
670dc1a
wwait i will fix
MS-crew Mar 1, 2026
527613c
finished
MS-crew Mar 1, 2026
26909f6
fix
MS-crew Mar 1, 2026
6da8cd2
pool clear
MS-crew Mar 1, 2026
952200f
fAHH!
MS-crew Mar 1, 2026
06e8ffd
Fahh
MS-crew Mar 1, 2026
8cab48f
better and more functionally
MS-crew Mar 2, 2026
699eb56
Return to pool doc
MS-crew Mar 2, 2026
ec11415
Cleanup & Breaking Changes for exiled 10
MS-crew Mar 2, 2026
0c4371f
Update Speaker.cs
MS-crew Mar 2, 2026
b5edd7a
Update Speaker.cs
MS-crew Mar 2, 2026
03e9a4e
Update Speaker.cs
MS-crew Mar 2, 2026
44e41b8
little refactor
MS-crew Mar 2, 2026
6a1bfc3
Performance improvement & fix Architectural problems in pooling
MS-crew Mar 2, 2026
d80362b
wth
MS-crew Mar 2, 2026
f38df4e
.
MS-crew Mar 3, 2026
8ff2180
fix return pool
MS-crew Mar 5, 2026
6639d1f
Merge branch 'ExMod-Team:master' into SpeakerApiRereborn
MS-crew Mar 5, 2026
e926b58
.
MS-crew Mar 6, 2026
0385267
Merge branch 'dev' into SpeakerApiRereborn
louis1706 Mar 6, 2026
575ec40
d
MS-crew Mar 7, 2026
87eeace
Merge branch 'SpeakerApiRereborn' of https://github.com/MS-crew/EXILE…
MS-crew Mar 7, 2026
b2bdac1
removed Hard Coded values
MS-crew Mar 7, 2026
d79555b
Update Speaker.cs
MS-crew Mar 8, 2026
0ea7bd0
fAHHHHH
MS-crew Mar 8, 2026
2d8ccae
Merge branch 'SpeakerApiRereborn' of https://github.com/MS-crew/EXILE…
MS-crew Mar 8, 2026
ad14a12
fix
MS-crew Mar 8, 2026
817b778
f
MS-crew Mar 8, 2026
b5d5d7b
Merge branch 'dev' into SpeakerApiRereborn
louis1706 Mar 8, 2026
55a8a83
izabel
MS-crew Mar 8, 2026
f32d65a
Merge branch 'SpeakerApiRereborn' of https://github.com/MS-crew/EXILE…
MS-crew Mar 8, 2026
a476bb8
Release controller ID on pool return to prevent ID exhaustion
MS-crew Mar 8, 2026
cdb193d
Merge branch 'dev' into SpeakerApiRereborn
MS-crew Mar 8, 2026
c4f2290
remove network chechks
MS-crew Mar 15, 2026
0c4f76c
for another pr
MS-crew Mar 16, 2026
1bfe67f
locals
MS-crew Mar 16, 2026
370ec83
fix: prevent controller ID conflicts by removing pooled speakers from…
MS-crew Mar 18, 2026
a7c7bc7
s
MS-crew Mar 18, 2026
d2ad795
Merge branch 'ExMod-Team:master' into SpeakerApiRereborn
MS-crew Mar 18, 2026
47f9a41
base check for return pool
MS-crew Mar 19, 2026
040d690
ffff
MS-crew Mar 20, 2026
054a206
Merge branch 'dev' into SpeakerApiRereborn
MS-crew Mar 20, 2026
db12dc5
NOT MY CHANGES
MS-crew Mar 20, 2026
0db8c88
Added Fade Volume Method, Time left property & setter for PlaybackPro…
MS-crew Mar 21, 2026
15ce66d
Merge branch 'SpeakerApiRereborn' of https://github.com/MS-crew/EXILE…
MS-crew Mar 21, 2026
28c072a
fade in for play
MS-crew Mar 21, 2026
b512bf1
TrackData & fade in for Play from pool
MS-crew Mar 21, 2026
f69c973
encoder clean for current time setter
MS-crew Mar 21, 2026
464ec65
Queue Track
MS-crew Mar 21, 2026
9f42fb3
added Static Events
MS-crew Mar 21, 2026
d476c4a
Audio Time Events
MS-crew Mar 21, 2026
2443a57
fix Action errors break corrutine
MS-crew Mar 21, 2026
b0a91d3
Action id
MS-crew Mar 21, 2026
0053b13
more more C#18 YOLOOOOOOO
MS-crew Mar 21, 2026
35dfb11
reorder
MS-crew Mar 21, 2026
c770cee
lazy instance for lists
MS-crew Mar 21, 2026
355317d
shuffle
MS-crew Mar 21, 2026
f73eeba
give credit to Fisher-Yates
MS-crew Mar 21, 2026
d8851b9
i forgot to delete this
MS-crew Mar 21, 2026
bd5e246
natural Fade
MS-crew Mar 21, 2026
3a5edd6
add linear fade option
MS-crew Mar 21, 2026
32510ad
Fix after fade volume stuck 0
MS-crew Mar 21, 2026
d287513
fixes
MS-crew Mar 21, 2026
5da1136
squash
MS-crew Mar 21, 2026
8d5d7b3
fade corrutine fix maybe, im drowing help me
MS-crew Mar 22, 2026
56699bb
Update Speaker.cs
MS-crew Mar 22, 2026
0954f8c
Update Speaker.cs
MS-crew Mar 22, 2026
09dfcc1
simplify api & null safety & fix fade & add stop fade function
MS-crew Mar 22, 2026
e16b627
.
MS-crew Mar 22, 2026
faaef76
Remove Fade out because its doing api dirty and complicated
MS-crew Mar 22, 2026
d720c88
Clean Api & new Event
MS-crew Mar 22, 2026
749f01d
remove thing which im added
MS-crew Mar 22, 2026
786091e
remeove old arg
MS-crew Mar 22, 2026
5cca492
doc
MS-crew Mar 22, 2026
306f1f1
f
MS-crew Mar 22, 2026
45d5a52
renameing
MS-crew Mar 22, 2026
8f227dd
Standalone System
MS-crew Mar 22, 2026
7a90945
remove useles using which is i added
MS-crew Mar 22, 2026
cc004bc
bool return
MS-crew Mar 22, 2026
3c31f83
Add Filter
MS-crew Mar 22, 2026
0dd20cd
Open Modular Api + Url play + Player play
MS-crew Mar 23, 2026
9e0af85
\
MS-crew Mar 24, 2026
cee0a6c
add filter samples
MS-crew Mar 24, 2026
a3afd6c
nh
MS-crew Mar 24, 2026
ecedc1b
.
MS-crew Mar 26, 2026
496ad28
fix: 14.2.6 update (#781)
Someone-193 Mar 25, 2026
a6c5e42
Bump to v9.13.3
louis1706 Mar 25, 2026
a82cef2
t
MS-crew Mar 28, 2026
498671f
Merge branch 'dev' into SpeakerApiRereborn
MS-crew Mar 28, 2026
33c5880
directorys
MS-crew Mar 29, 2026
eb31c22
Merge branch 'SpeakerApiRereborn' of https://github.com/MS-crew/EXILE…
MS-crew Mar 29, 2026
4a19539
cache logic#
MS-crew Mar 29, 2026
9d6c91b
fix file name
MS-crew Mar 29, 2026
c261148
pcm method
MS-crew Mar 29, 2026
3b1970a
helper for url
MS-crew Mar 29, 2026
a33d8b3
refactor cache & add Playbacksettings class for too long param
MS-crew Mar 29, 2026
85f2dcb
It seems to get more complicated each time.
MS-crew Mar 29, 2026
2b79d88
d
MS-crew Mar 29, 2026
78038a5
change ai filter to more optimized ai filter
MS-crew Mar 29, 2026
3816886
simplfy playervoicesource
MS-crew Mar 30, 2026
b11500f
Update Speaker.cs
MS-crew Mar 30, 2026
777e652
old way tasks maybe later
MS-crew Mar 30, 2026
4021700
filter reset + change useless Concurrent
MS-crew Mar 30, 2026
61975ca
Merge branch 'dev' into SpeakerApiRereborn
MS-crew Mar 30, 2026
7729ed9
class to struct
MS-crew Mar 31, 2026
892d4a8
fix memory leak
MS-crew Mar 31, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions EXILED/Exiled.API/Exiled.API.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
<Reference Include="UnityEngine.PhysicsModule" HintPath="$(EXILED_REFERENCES)\UnityEngine.PhysicsModule.dll" Private="false" />
<Reference Include="UnityEngine.AudioModule" HintPath="$(EXILED_REFERENCES)\UnityEngine.AudioModule.dll" Private="false" />
<Reference Include="UnityEngine.UI" HintPath="$(EXILED_REFERENCES)\UnityEngine.UI.dll" Private="false" />
<Reference Include="UnityEngine.UnityWebRequestModule" HintPath="$(EXILED_REFERENCES)\UnityEngine.UnityWebRequestModule.dll" Private="false" />
<Reference Include="YamlDotNet" HintPath="$(EXILED_REFERENCES)\YamlDotNet.dll" Private="false" />
<Reference Include="mscorlib" HintPath="$(EXILED_REFERENCES)\mscorlib.dll" Private="false" />
<Reference Include="System" HintPath="$(EXILED_REFERENCES)\System.dll" Private="false" />
Expand Down
213 changes: 213 additions & 0 deletions EXILED/Exiled.API/Features/Audio/AudioDataStorage.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,213 @@
// -----------------------------------------------------------------------
// <copyright file="AudioDataStorage.cs" company="ExMod Team">
// Copyright (c) ExMod Team. All rights reserved.
// Licensed under the CC BY-SA 3.0 license.
// </copyright>
// -----------------------------------------------------------------------

namespace Exiled.API.Features.Audio
{
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;

using Exiled.API.Structs.Audio;

using MEC;

using RoundRestarting;

using UnityEngine.Networking;

/// <summary>
/// Manages a global in-memory storage of decoded PCM audio data. Once stored, audio can be played using <see cref="PcmSources.CachedPcmSource"/>.
/// </summary>
public static class AudioDataStorage
{
static AudioDataStorage()
{
AudioStorage = new();
RoundRestart.OnRestartTriggered += OnRoundRestart;
}

/// <summary>
/// Gets the underlying storage, keyed by name.
/// </summary>
public static ConcurrentDictionary<string, AudioData> AudioStorage { get; }

/// <summary>
/// Gets or sets a value indicating whether the storage is automatically cleared when a round restart is triggered.
/// </summary>
public static bool ClearOnRoundRestart { get; set; } = true;

/// <summary>
/// Loads and stores a local .wav file under the specified name.
/// </summary>
/// <param name="name">The unique storage key to assign to this audio.</param>
/// <param name="path">The absolute path to the local .wav file.</param>
/// <returns><c>true</c> if the file was successfully loaded and stored; otherwise, <c>false</c>.</returns>
public static bool Add(string name, string path)
{
if (!ValidateName(name))
return false;

if (AudioStorage.ContainsKey(name))
{
Log.Warn($"[AudioDataStorage] An entry with the key '{name}' already exists. Skipping add.");
return false;
}

if (path.StartsWith("http"))
{
Log.Error($"[AudioDataStorage] '{path}' is a URL. Use AudioDataStorage.AddUrl() for web sources.");
return false;
}

if (!File.Exists(path))
{
Log.Error($"[AudioDataStorage] Local file not found: '{path}'");
return false;
}

try
{
AudioData parsed = WavUtility.WavToPcm(path);
return AudioStorage.TryAdd(name, parsed);
}
catch (Exception ex)
{
Log.Error($"[AudioDataStorage] Failed to load '{path}' into storage:\n{ex}");
return false;
}
}

/// <summary>
/// Stores raw PCM audio samples under the specified name.
/// </summary>
/// <param name="name">The unique storage key to assign.</param>
/// <param name="pcm">The raw PCM float array to store.</param>
/// <returns><c>true</c> if successfully added; otherwise, <c>false</c>.</returns>
public static bool Add(string name, float[] pcm)
{
if (pcm == null)
{
Log.Error($"[AudioDataStorage] Cannot store null array for key '{name}'.");
return false;
}

TrackData trackInfo = new()
{
Title = name,
Duration = (double)pcm.Length / VoiceChat.VoiceChatSettings.SampleRate,
};

return Add(name, new AudioData(pcm, trackInfo));
}

/// <summary>
/// Stores a fully constructed <see cref="AudioData"/> under the specified name.
/// </summary>
/// <param name="name">The unique storage key to assign.</param>
/// <param name="audioData">The <see cref="AudioData"/> to store.</param>
/// <returns><c>true</c> if successfully added; otherwise, <c>false</c>.</returns>
public static bool Add(string name, AudioData audioData)
{
if (!ValidateName(name))
return false;

if (audioData.Pcm == null || audioData.Pcm.Length == 0)
{
Log.Error($"[AudioDataStorage] AudioData for key '{name}' has null or empty PCM.");
return false;
}

if (AudioStorage.ContainsKey(name))
{
Log.Warn($"[AudioDataStorage] An entry with the key '{name}' already exists. Skipping add.");
return false;
}

return AudioStorage.TryAdd(name, audioData);
}

/// <summary>
/// Starts an asynchronous download of a .wav file from the specified URL and adds it to the storage.
/// </summary>
/// <param name="name">The unique storage key to assign.</param>
/// <param name="url">The HTTP or HTTPS URL pointing to a valid .wav file.</param>
/// <returns>A <see cref="CoroutineHandle"/> for the running download coroutine.</returns>
public static CoroutineHandle AddUrl(string name, string url) => Timing.RunCoroutine(AddUrlCoroutine(name, url));

/// <summary>
/// Starts an asynchronous download of a .wav file from the specified URL and adds it to the storage.
/// </summary>
/// <param name="name">The unique storage key to assign.</param>
/// <param name="url">The HTTP or HTTPS URL pointing to a valid .wav file.</param>
/// <returns>A MEC-compatible <see cref="IEnumerator{T}"/> of <see cref="float"/>.</returns>
public static IEnumerator<float> AddUrlCoroutine(string name, string url)
{
if (!ValidateName(name))
yield break;

if (string.IsNullOrEmpty(url) || !url.StartsWith("http"))
{
Log.Error($"[AudioDataStorage] Invalid URL for key '{name}': '{url}'. Must start with http/https.");
yield break;
}

if (AudioStorage.ContainsKey(name))
{
Log.Warn($"[AudioDataStorage] An entry with the key '{name}' already exists. Skipping download.");
yield break;
}

using UnityWebRequest www = UnityWebRequest.Get(url);
yield return Timing.WaitUntilDone(www.SendWebRequest());

if (www.result != UnityWebRequest.Result.Success)
{
Log.Error($"[AudioDataStorage] Download failed for '{url}': {www.error}");
yield break;
}

try
{
AudioData parsed = WavUtility.WavToPcm(www.downloadHandler.data);
parsed.TrackInfo.Path = url;
AudioStorage.TryAdd(name, parsed);
}
catch (Exception ex)
{
Log.Error($"[AudioDataStorage] Failed to parse downloaded WAV from '{url}':\n{ex}");
}
}

/// <summary>
/// Removes a stored audio entry by name.
/// </summary>
/// <param name="name">The storage name/key to remove.</param>
/// <returns><c>true</c> if the entry was found and removed; otherwise, <c>false</c>.</returns>
public static bool Remove(string name) => AudioStorage.TryRemove(name, out _);

/// <summary>
/// Clears all entries from the audio storage, freeing all associated memory.
/// </summary>
public static void Clear() => AudioStorage.Clear();

private static bool ValidateName(string name)
{
if (!string.IsNullOrEmpty(name))
return true;

Log.Error("[AudioDataStorage] Storage name (key) cannot be null or empty.");
return false;
}

private static void OnRoundRestart()
{
if (ClearOnRoundRestart)
Clear();
}
}
}
Loading
Loading