diff --git a/Content.Client/Audio/Jukebox/JukeboxBoundUserInterface.cs b/Content.Client/Audio/Jukebox/JukeboxBoundUserInterface.cs
index 865dfc478d0..184957b5553 100644
--- a/Content.Client/Audio/Jukebox/JukeboxBoundUserInterface.cs
+++ b/Content.Client/Audio/Jukebox/JukeboxBoundUserInterface.cs
@@ -44,6 +44,7 @@ protected override void Open()
_menu.OnSongSelected += SelectSong;
_menu.SetTime += SetTime;
+ _menu.SetVolume += SetVolume;
PopulateMusic();
Reload();
}
@@ -57,6 +58,7 @@ public void Reload()
return;
_menu.SetAudioStream(jukebox.AudioStream);
+ _menu.SetVolumeSlider(jukebox.Volume);
if (_protoManager.TryIndex(jukebox.SelectedSongId, out var songProto))
{
@@ -97,5 +99,18 @@ public void SetTime(float time)
SendMessage(new JukeboxSetTimeMessage(sentTime));
}
+ public void SetVolume(float volume)
+ {
+ var sentVolume = volume;
+
+ // Prediction
+ if (EntMan.TryGetComponent(Owner, out JukeboxComponent? jukebox) &&
+ EntMan.TryGetComponent(jukebox.AudioStream, out AudioComponent? audioComp))
+ {
+ audioComp.Volume = SharedJukeboxSystem.MapToRange(volume, jukebox.MinSlider, jukebox.MaxSlider, jukebox.MinVolume, jukebox.MaxVolume);
+ }
+
+ SendMessage(new JukeboxSetVolumeMessage(sentVolume));
+ }
}
diff --git a/Content.Client/Audio/Jukebox/JukeboxMenu.xaml b/Content.Client/Audio/Jukebox/JukeboxMenu.xaml
index e8d39a9b119..f322289f08e 100644
--- a/Content.Client/Audio/Jukebox/JukeboxMenu.xaml
+++ b/Content.Client/Audio/Jukebox/JukeboxMenu.xaml
@@ -1,18 +1,31 @@
+ SetSize="600 500" Title="{Loc 'jukebox-menu-title'}">
-
+
+
+
+
+
+
+
+
+
+
-
-
-
-
+
+
+
+
+
+
diff --git a/Content.Client/Audio/Jukebox/JukeboxMenu.xaml.cs b/Content.Client/Audio/Jukebox/JukeboxMenu.xaml.cs
index e0904eece86..3c49995d688 100644
--- a/Content.Client/Audio/Jukebox/JukeboxMenu.xaml.cs
+++ b/Content.Client/Audio/Jukebox/JukeboxMenu.xaml.cs
@@ -30,6 +30,7 @@ public sealed partial class JukeboxMenu : FancyWindow
public event Action? OnStopPressed;
public event Action>? OnSongSelected;
public event Action? SetTime;
+ public event Action? SetVolume;
private EntityUid? _audio;
@@ -61,6 +62,9 @@ public JukeboxMenu()
OnStopPressed?.Invoke();
};
PlaybackSlider.OnReleased += PlaybackSliderKeyUp;
+ VolumeSlider.OnReleased += VolumeSliderKeyUp;
+
+ VolumeSlider.MaxValue = 100f;
SetPlayPauseButton(_audioSystem.IsPlaying(_audio), force: true);
}
@@ -81,6 +85,12 @@ private void PlaybackSliderKeyUp(Slider args)
_lockTimer = 0.5f;
}
+ private void VolumeSliderKeyUp(Slider args)
+ {
+ SetVolume?.Invoke(VolumeSlider.Value);
+ _lockTimer = 0.5f;
+ }
+
///
/// Re-populates the list of jukebox prototypes available.
///
@@ -92,6 +102,7 @@ public void Populate(IEnumerable jukeboxProtos)
{
MusicList.AddItem(entry.Name, metadata: entry.ID);
}
+ MusicList.SortItemsByText();
}
public void SetPlayPauseButton(bool playing, bool force = false)
@@ -117,6 +128,12 @@ public void SetSelectedSong(string name, float length)
PlaybackSlider.SetValueWithoutEvent(0);
}
+ public void SetVolumeSlider(float volume)
+ {
+ VolumeSlider.Value = volume;
+ }
+
+
protected override void FrameUpdate(FrameEventArgs args)
{
base.FrameUpdate(args);
@@ -127,6 +144,7 @@ protected override void FrameUpdate(FrameEventArgs args)
}
PlaybackSlider.Disabled = _lockTimer > 0f;
+ VolumeSlider.Disabled = _lockTimer > 0f;
if (_entManager.TryGetComponent(_audio, out AudioComponent? audio))
{
@@ -137,9 +155,14 @@ protected override void FrameUpdate(FrameEventArgs args)
DurationLabel.Text = $"00:00 / 00:00";
}
+ VolumeNumberLabel.Text = $"{VolumeSlider.Value.ToString("0.##")} %";
+
if (PlaybackSlider.Grabbed)
return;
+ if (VolumeSlider.Grabbed)
+ return;
+
if (audio != null || _entManager.TryGetComponent(_audio, out audio))
{
PlaybackSlider.SetValueWithoutEvent(audio.PlaybackPosition);
diff --git a/Content.Server/Audio/Jukebox/JukeboxSystem.cs b/Content.Server/Audio/Jukebox/JukeboxSystem.cs
index 3535f6b2382..bc3e1a28b74 100644
--- a/Content.Server/Audio/Jukebox/JukeboxSystem.cs
+++ b/Content.Server/Audio/Jukebox/JukeboxSystem.cs
@@ -26,6 +26,7 @@ public override void Initialize()
SubscribeLocalEvent(OnJukeboxPause);
SubscribeLocalEvent(OnJukeboxStop);
SubscribeLocalEvent(OnJukeboxSetTime);
+ SubscribeLocalEvent(OnJukeboxSetVolume);
SubscribeLocalEvent(OnComponentInit);
SubscribeLocalEvent(OnComponentShutdown);
@@ -56,7 +57,7 @@ private void OnJukeboxPlay(EntityUid uid, JukeboxComponent component, ref Jukebo
return;
}
- component.AudioStream = Audio.PlayPvs(jukeboxProto.Path, uid, AudioParams.Default.WithMaxDistance(10f))?.Entity;
+ component.AudioStream = Audio.PlayPvs(jukeboxProto.Path, uid, AudioParams.Default.WithMaxDistance(10f).WithVolume(MapToRange(component.Volume, component.MinSlider, component.MaxSlider, component.MinVolume, component.MaxVolume)))?.Entity;
Dirty(uid, component);
}
}
@@ -74,6 +75,15 @@ private void OnJukeboxSetTime(EntityUid uid, JukeboxComponent component, Jukebox
Audio.SetPlaybackPosition(component.AudioStream, args.SongTime + offset);
}
}
+ private void OnJukeboxSetVolume(EntityUid uid, JukeboxComponent component, JukeboxSetVolumeMessage args)
+ {
+ SetJukeboxVolume(uid, component, args.Volume);
+
+ if (!TryComp(component.AudioStream, out var audioComponent))
+ return;
+
+ Audio.SetVolume(component.AudioStream, MapToRange(args.Volume, component.MinSlider, component.MaxSlider, component.MinVolume, component.MaxVolume));
+ }
private void OnPowerChanged(Entity entity, ref PowerChangedEvent args)
{
@@ -129,6 +139,11 @@ public override void Update(float frameTime)
}
}
}
+ private void SetJukeboxVolume(EntityUid uid, JukeboxComponent component, float volume)
+ {
+ component.Volume = volume;
+ Dirty(uid, component);
+ }
private void OnComponentShutdown(EntityUid uid, JukeboxComponent component, ComponentShutdown args)
{
diff --git a/Content.Shared/Audio/Jukebox/JukeboxComponent.cs b/Content.Shared/Audio/Jukebox/JukeboxComponent.cs
index f9bb385f520..e40cb20452f 100644
--- a/Content.Shared/Audio/Jukebox/JukeboxComponent.cs
+++ b/Content.Shared/Audio/Jukebox/JukeboxComponent.cs
@@ -37,6 +37,14 @@ public sealed partial class JukeboxComponent : Component
[ViewVariables]
public float SelectAccumulator;
+
+ [ViewVariables, AutoNetworkedField]
+ public float Volume = 50f;
+
+ public float MinVolume = -30f;
+ public float MaxVolume = 0f;
+ public float MinSlider = 0f;
+ public float MaxSlider = 100f;
}
[Serializable, NetSerializable]
@@ -60,6 +68,12 @@ public sealed class JukeboxSetTimeMessage(float songTime) : BoundUserInterfaceMe
public float SongTime { get; } = songTime;
}
+[Serializable, NetSerializable]
+public sealed class JukeboxSetVolumeMessage(float volume) : BoundUserInterfaceMessage
+{
+ public float Volume { get; } = volume;
+}
+
[Serializable, NetSerializable]
public enum JukeboxVisuals : byte
{
diff --git a/Content.Shared/Audio/Jukebox/SharedJukeboxSystem.cs b/Content.Shared/Audio/Jukebox/SharedJukeboxSystem.cs
index 1a8f9cb3bb8..1bdd076e5a8 100644
--- a/Content.Shared/Audio/Jukebox/SharedJukeboxSystem.cs
+++ b/Content.Shared/Audio/Jukebox/SharedJukeboxSystem.cs
@@ -5,4 +5,8 @@ namespace Content.Shared.Audio.Jukebox;
public abstract class SharedJukeboxSystem : EntitySystem
{
[Dependency] protected readonly SharedAudioSystem Audio = default!;
+ public static float MapToRange(float value, float leftMin, float leftMax, float rightMin, float rightMax)
+ {
+ return rightMin + (value - leftMin) * (rightMax - rightMin) / (leftMax - leftMin);
+ }
}