Skip to content

Commit

Permalink
Merge branch 'dev' into main
Browse files Browse the repository at this point in the history
  • Loading branch information
VolcanicArts committed Dec 26, 2022
2 parents 15589dd + f0bea6f commit 354e9aa
Show file tree
Hide file tree
Showing 6 changed files with 149 additions and 63 deletions.
4 changes: 2 additions & 2 deletions VRCOSC.Desktop/VRCOSC.Desktop.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,12 @@
<ApplicationIcon>game.ico</ApplicationIcon>
<ApplicationManifest>app.manifest</ApplicationManifest>
<Version>0.0.0</Version>
<FileVersion>2022.1219.0</FileVersion>
<FileVersion>2022.1226.0</FileVersion>
<Title>VRCOSC</Title>
<Authors>VolcanicArts</Authors>
<Company>VolcanicArts</Company>
<Nullable>enable</Nullable>
<AssemblyVersion>2022.1219.0</AssemblyVersion>
<AssemblyVersion>2022.1226.0</AssemblyVersion>
</PropertyGroup>
<ItemGroup Label="Project References">
<ProjectReference Include="..\VRCOSC.Game\VRCOSC.Game.csproj" />
Expand Down
6 changes: 3 additions & 3 deletions VRCOSC.Game/Modules/Modules/Heartrate/HeartRateModule.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ public abstract partial class HeartRateModule : ChatBoxModule
protected override int ChatBoxPriority => 1;

protected override bool DefaultChatBoxDisplay => false;
protected override string DefaultChatBoxFormat => "Heartrate %hr% bpm";
protected override string DefaultChatBoxFormat => "Heartrate %hr% bpm";
protected override IEnumerable<string> ChatBoxFormatValues => new[] { "%hr%" };

protected HeartRateProvider? HeartRateProvider;
Expand All @@ -35,7 +35,7 @@ protected override void CreateAttributes()
{
base.CreateAttributes();
CreateParameter<bool>(HeartrateParameter.Enabled, ParameterMode.Write, "VRCOSC/Heartrate/Enabled", "Whether this module is attempting to emit values");
CreateParameter<float>(HeartrateParameter.Normalised, ParameterMode.Write, "VRCOSC/Heartrate/Normalised", "The heartrate value normalised to 60bpm");
CreateParameter<float>(HeartrateParameter.Normalised, ParameterMode.Write, "VRCOSC/Heartrate/Normalised", "The heartrate value normalised to 240bpm");
CreateParameter<float>(HeartrateParameter.Units, ParameterMode.Write, "VRCOSC/Heartrate/Units", "The units digit 0-9 mapped to a float");
CreateParameter<float>(HeartrateParameter.Tens, ParameterMode.Write, "VRCOSC/Heartrate/Tens", "The tens digit 0-9 mapped to a float");
CreateParameter<float>(HeartrateParameter.Hundreds, ParameterMode.Write, "VRCOSC/Heartrate/Hundreds", "The hundreds digit 0-9 mapped to a float");
Expand Down Expand Up @@ -99,7 +99,7 @@ protected virtual void HandleHeartRateUpdate(int heartrate)
lastHeartrate = heartrate;
lastHeartrateTime = DateTimeOffset.Now;

var normalisedHeartRate = heartrate / 60.0f;
var normalisedHeartRate = heartrate / 240.0f;
var individualValues = toDigitArray(heartrate, 3);

SendParameter(HeartrateParameter.Enabled, true);
Expand Down
48 changes: 23 additions & 25 deletions VRCOSC.Game/Modules/Modules/Media/MediaModule.cs
Original file line number Diff line number Diff line change
Expand Up @@ -51,21 +51,19 @@ protected override void CreateAttributes()
if (!mediaProvider.State.IsPlaying)
return GetSetting<MediaPausedBehaviour>(MediaSetting.PausedBehaviour) == MediaPausedBehaviour.Empty ? null : GetSetting<string>(MediaSetting.PausedText);

mediaProvider.State.Position = mediaProvider.Controller?.GetTimelineProperties();

var formattedText = GetSetting<string>(ChatBoxSetting.ChatBoxFormat)
.Replace("%title%", mediaProvider.State.Title)
.Replace("%artist%", mediaProvider.State.Artist)
.Replace("%curtime%", mediaProvider.State.Position?.Position.ToString(@"mm\:ss") ?? "00:00")
.Replace("%duration%", mediaProvider.State.Position?.EndTime.ToString(@"mm\:ss") ?? "00:00");
.Replace("%curtime%", mediaProvider.State.Position?.Position.ToString(@"mm\:ss"))
.Replace("%duration%", mediaProvider.State.Position?.EndTime.ToString(@"mm\:ss"));

return formattedText;
}

protected override void OnModuleStart()
{
base.OnModuleStart();
mediaProvider.OnMediaUpdate += OnMediaUpdate;
mediaProvider.OnPlaybackStateUpdate += onPlaybackStateUpdate;
mediaProvider.StartMediaHook();
startProcesses();
}
Expand All @@ -85,7 +83,7 @@ private void startProcesses()
protected override void OnModuleStop()
{
mediaProvider.StopMediaHook();
mediaProvider.OnMediaUpdate -= OnMediaUpdate;
mediaProvider.OnPlaybackStateUpdate -= onPlaybackStateUpdate;
}

protected override void OnAvatarChange()
Expand All @@ -96,7 +94,25 @@ protected override void OnAvatarChange()

protected override void OnModuleUpdate()
{
sendVolumeParameters();
if (mediaProvider.Controller is not null) sendVolumeParameters();
}

private void onPlaybackStateUpdate()
{
sendMediaParameters();
}

private void sendMediaParameters()
{
SendParameter(MediaParameter.Play, mediaProvider.State.IsPlaying);
SendParameter(MediaParameter.Shuffle, mediaProvider.State.IsShuffle);
SendParameter(MediaParameter.Repeat, (int)mediaProvider.State.RepeatMode);
}

private void sendVolumeParameters()
{
SendParameter(MediaParameter.Volume, mediaProvider.State.Volume);
SendParameter(MediaParameter.Muted, mediaProvider.State.Muted);
}

protected override void OnFloatParameterReceived(Enum key, float value)
Expand Down Expand Up @@ -149,24 +165,6 @@ protected override void OnIntParameterReceived(Enum key, int value)
}
}

private void OnMediaUpdate()
{
sendMediaParameters();
}

private void sendMediaParameters()
{
SendParameter(MediaParameter.Play, mediaProvider.State.IsPlaying);
SendParameter(MediaParameter.Shuffle, mediaProvider.State.IsShuffle);
SendParameter(MediaParameter.Repeat, (int)mediaProvider.State.RepeatMode);
}

private void sendVolumeParameters()
{
SendParameter(MediaParameter.Volume, mediaProvider.State.Volume);
SendParameter(MediaParameter.Muted, mediaProvider.State.Muted);
}

private enum MediaSetting
{
PausedBehaviour,
Expand Down
74 changes: 42 additions & 32 deletions VRCOSC.Game/Modules/Modules/Media/MediaProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,71 +5,81 @@
using Windows.Media;
using Windows.Media.Control;
using VRCOSC.Game.Processes;
using WindowsMediaController;

namespace VRCOSC.Game.Modules.Modules.Media;

public class MediaProvider
{
private MediaManager? mediaManager;
private readonly WindowsMediaInterface mediaInterface;

public MediaState State { get; private set; } = null!;

public GlobalSystemMediaTransportControlsSession? Controller => mediaManager?.GetFocusedSession()?.ControlSession;
public GlobalSystemMediaTransportControlsSession? Controller => mediaInterface?.CurrentSession;

public Action? OnMediaUpdate;
public Action? OnPlaybackStateUpdate;

public MediaProvider()
{
mediaInterface = new WindowsMediaInterface();
mediaInterface.OnAnyPlaybackInfoChanged += onAnyPlaybackStateChanged;
mediaInterface.OnAnyMediaPropertiesChanged += onAnyMediaPropertyChanged;
mediaInterface.OnAnyTimelinePropertiesChanged += onAnyTimelinePropertiesChanged;
mediaInterface.OnCurrentSessionChanged += onCurrentSessionChanged;
mediaInterface.Initialise();
}

public void StartMediaHook()
{
State = new MediaState();

mediaManager = new MediaManager();
mediaManager.OnAnyPlaybackStateChanged += MediaManager_OnAnyPlaybackStateChanged;
mediaManager.OnAnyMediaPropertyChanged += MediaManager_OnAnyMediaPropertyChanged;
mediaManager.OnFocusedSessionChanged += MediaManager_OnFocusedSessionChanged;
mediaManager.Start();
mediaInterface.Hook();
}

public void StopMediaHook()
{
mediaManager?.Dispose();
mediaManager = null;
mediaInterface.UnHook();
}

private void MediaManager_OnAnyPlaybackStateChanged(MediaManager.MediaSession sender, GlobalSystemMediaTransportControlsSessionPlaybackInfo args)
private bool isFocusedSession(GlobalSystemMediaTransportControlsSession session) => session.SourceAppUserModelId == Controller?.SourceAppUserModelId;

private void onAnyPlaybackStateChanged(GlobalSystemMediaTransportControlsSession session, GlobalSystemMediaTransportControlsSessionPlaybackInfo args)
{
State.ProcessId = Controller?.SourceAppUserModelId;
State.IsShuffle = args.IsShuffleActive ?? false;
State.RepeatMode = args.AutoRepeatMode ?? 0;
if (!isFocusedSession(session)) return;

State.IsShuffle = args.IsShuffleActive ?? default;
State.RepeatMode = args.AutoRepeatMode ?? default;
State.Status = args.PlaybackStatus;

OnMediaUpdate?.Invoke();
OnPlaybackStateUpdate?.Invoke();
}

private void MediaManager_OnAnyMediaPropertyChanged(MediaManager.MediaSession sender, GlobalSystemMediaTransportControlsSessionMediaProperties args)
private void onAnyMediaPropertyChanged(GlobalSystemMediaTransportControlsSession session, GlobalSystemMediaTransportControlsSessionMediaProperties args)
{
State.ProcessId = Controller?.SourceAppUserModelId;
if (!isFocusedSession(session)) return;

State.Title = args.Title;
State.Artist = args.Artist;

OnMediaUpdate?.Invoke();
}

private async void MediaManager_OnFocusedSessionChanged(MediaManager.MediaSession? sender)
private void onAnyTimelinePropertiesChanged(GlobalSystemMediaTransportControlsSession session, GlobalSystemMediaTransportControlsSessionTimelineProperties args)
{
if (sender is null) return;
if (!isFocusedSession(session)) return;

var properties = await sender.ControlSession.TryGetMediaPropertiesAsync();
var playbackInfo = sender.ControlSession.GetPlaybackInfo();
State.Position = args;
}

private async void onCurrentSessionChanged(GlobalSystemMediaTransportControlsSession? session)
{
if (session is null)
{
State = new MediaState();
return;
}

State.ProcessId = Controller?.SourceAppUserModelId;
State.Title = properties.Title;
State.Artist = properties.Artist;
State.IsShuffle = playbackInfo.IsShuffleActive ?? false;
State.RepeatMode = playbackInfo.AutoRepeatMode ?? 0;
State.Status = playbackInfo.PlaybackStatus;
State.ProcessId = session.SourceAppUserModelId;

OnMediaUpdate?.Invoke();
onAnyPlaybackStateChanged(session, session.GetPlaybackInfo());
onAnyMediaPropertyChanged(session, await session.TryGetMediaPropertiesAsync());
onAnyTimelinePropertiesChanged(session, session.GetTimelineProperties());
}
}

Expand Down
79 changes: 79 additions & 0 deletions VRCOSC.Game/Modules/WindowsMediaInterface.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
// Copyright (c) VolcanicArts. Licensed under the GPL-3.0 License.
// See the LICENSE file in the repository root for full license text.

using System;
using System.Collections.Generic;
using System.Linq;
using Windows.Media.Control;
using osu.Framework.Extensions.IEnumerableExtensions;

namespace VRCOSC.Game.Modules;

public class WindowsMediaInterface
{
public Action<GlobalSystemMediaTransportControlsSession?>? OnCurrentSessionChanged;
public Action<GlobalSystemMediaTransportControlsSession, GlobalSystemMediaTransportControlsSessionPlaybackInfo>? OnAnyPlaybackInfoChanged;
public Action<GlobalSystemMediaTransportControlsSession, GlobalSystemMediaTransportControlsSessionMediaProperties>? OnAnyMediaPropertiesChanged;
public Action<GlobalSystemMediaTransportControlsSession, GlobalSystemMediaTransportControlsSessionTimelineProperties>? OnAnyTimelinePropertiesChanged;

private readonly List<GlobalSystemMediaTransportControlsSession> currentSessions = new();
private GlobalSystemMediaTransportControlsSessionManager sessionManager = null!;

public GlobalSystemMediaTransportControlsSession? CurrentSession => sessionManager.GetCurrentSession();

public async void Initialise()
{
sessionManager = await GlobalSystemMediaTransportControlsSessionManager.RequestAsync();
}

public void Hook()
{
sessionManager.CurrentSessionChanged += currentSessionChanged;
sessionManager.SessionsChanged += sessionsChanged;

auditSessions();
OnCurrentSessionChanged?.Invoke(CurrentSession);
}

public void UnHook()
{
sessionManager.CurrentSessionChanged -= currentSessionChanged;
sessionManager.SessionsChanged -= sessionsChanged;

currentSessions.Clear();
}

private void currentSessionChanged(GlobalSystemMediaTransportControlsSessionManager _, CurrentSessionChangedEventArgs _2)
{
OnCurrentSessionChanged?.Invoke(CurrentSession);
}

private void sessionsChanged(GlobalSystemMediaTransportControlsSessionManager _, SessionsChangedEventArgs _2)
{
auditSessions();

// Session changes can occur due to a session closing
// To avoid repeat code, we can just detect what may have closed rather than listening for the closed event and checking for closed sessions
removeClosedSessions();
}

private void auditSessions()
{
sessionManager.GetSessions().Where(controlSession => !currentSessions.Contains(controlSession)).ForEach(addControlSession);
}

private void removeClosedSessions()
{
var sessions = sessionManager.GetSessions();
var closedSessions = currentSessions.Where(session => !sessions.Contains(session)).ToList();
if (closedSessions.Any()) currentSessions.Remove(closedSessions.First());
}

private void addControlSession(GlobalSystemMediaTransportControlsSession controlSession)
{
controlSession.PlaybackInfoChanged += (_, _) => OnAnyPlaybackInfoChanged?.Invoke(controlSession, controlSession.GetPlaybackInfo());
controlSession.MediaPropertiesChanged += async (_, _) => OnAnyMediaPropertiesChanged?.Invoke(controlSession, await controlSession.TryGetMediaPropertiesAsync());
controlSession.TimelinePropertiesChanged += (_, _) => OnAnyTimelinePropertiesChanged?.Invoke(controlSession, controlSession.GetTimelineProperties());
currentSessions.Add(controlSession);
}
}
1 change: 0 additions & 1 deletion VRCOSC.Game/VRCOSC.Game.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
<ProjectReference Include="..\VRCOSC.Resources\VRCOSC.Resources.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Dubya.WindowsMediaController" Version="2.4.0" />
<PackageReference Include="LibreHardwareMonitorLib" Version="0.9.1" />
<PackageReference Include="VolcanicArts.Libs.OpenVR" Version="1.23.7" />
<PackageReference Include="ppy.osu.Framework" Version="2022.1219.0" />
Expand Down

0 comments on commit 354e9aa

Please sign in to comment.