Skip to content

Commit

Permalink
Added SharpOSC, fixed isHRBeat
Browse files Browse the repository at this point in the history
  • Loading branch information
DangerKiddy committed Aug 11, 2024
1 parent 30ca9d7 commit 504d94c
Show file tree
Hide file tree
Showing 5 changed files with 39 additions and 100 deletions.
Binary file added Dependencies/SharpOSC.dll
Binary file not shown.
7 changes: 7 additions & 0 deletions HeartRateMonitorVRC.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="SharpOSC, Version=0.1.1.0, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>Dependencies\SharpOSC.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Data" />
<Reference Include="System.Xml" />
Expand Down Expand Up @@ -102,5 +106,8 @@
<ItemGroup>
<None Include="App.config" />
</ItemGroup>
<ItemGroup>
<Content Include="SharpOSC.dll" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>
42 changes: 26 additions & 16 deletions MainWindow.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,21 +26,20 @@ private enum Fade
private static OSC osc;

private static int lastSentBpm = 0;
private static int currentBpm = 0;
private static int currentBpm = 60;

private static DeviceInformationCollection pairedDevices;

public MainWindow()
{
InitializeComponent();

osc = new OSC();
SelectDeviceButton.IsEnabled = false;

ScanForDevicesAsync();
UpdateBPMText();
EmulateBeatEffect();

osc = new OSC();
}

private async void UpdateBPMText()
Expand All @@ -59,19 +58,31 @@ private async void EmulateBeatEffect()
{
var nextBeat = currentBpm == 0 ? 100 : (int)((1f / currentBpm) * 1000) * 60;

if (osc != null && hrDevice != null && hrGatt != null)
if (ShouldEmulateBeatEffect())
{
var showAnim = new DoubleAnimation();
showAnim.From = 1;
showAnim.To = .25f;
showAnim.Duration = TimeSpan.FromMilliseconds(nextBeat);
BPMText.BeginAnimation(OpacityProperty, showAnim);

osc.Send("/avatar/parameters/isHRBeat", true);
nextBeat -= 50;

await Task.Delay(50);

osc.Send("/avatar/parameters/isHRBeat", false);
}

await Task.Delay(nextBeat);
}
}

private bool ShouldEmulateBeatEffect()
{
return osc != null && hrDevice != null && hrGatt != null;
}

private void Debug(string text, bool ignoreStatus = false)
{
Trace.WriteLine(text);
Expand Down Expand Up @@ -100,7 +111,6 @@ private async Task<BluetoothLEDevice> ConnectToDeviceAsync(string deviceId)
Debug("Failed connecting to device.");
return null;
}


device.ConnectionStatusChanged += Device_ConnectionStatusChanged;

Expand Down Expand Up @@ -190,27 +200,27 @@ private void SendBPMToVRChat(int bpm)
lastSentBpm = bpm;

float hr01 = Remap(bpm, 0, 255, 0, 1);
osc.SendFloat("/avatar/parameters/Heartrate2", hr01);
osc.SendFloat("/avatar/parameters/HRPercent", hr01);
osc.SendFloat("/avatar/parameters/FullHRPercent", Remap(bpm, 0, 255, -1, 1));
osc.SendInt("/avatar/parameters/HR", bpm);
osc.Send("/avatar/parameters/Heartrate2", hr01);
osc.Send("/avatar/parameters/HRPercent", hr01);
osc.Send("/avatar/parameters/FullHRPercent", Remap(bpm, 0, 255, -1, 1));
osc.Send("/avatar/parameters/HR", bpm);

string hrAsStr = bpm.ToString();
if (bpm > 99)
{
osc.SendInt("/avatar/parameters/onesHR", int.Parse(hrAsStr[2].ToString()));
osc.SendInt("/avatar/parameters/tensHR", int.Parse(hrAsStr[1].ToString()));
osc.SendInt("/avatar/parameters/hundredsHR", int.Parse(hrAsStr[0].ToString()));
osc.Send("/avatar/parameters/onesHR", int.Parse(hrAsStr[2].ToString()));
osc.Send("/avatar/parameters/tensHR", int.Parse(hrAsStr[1].ToString()));
osc.Send("/avatar/parameters/hundredsHR", int.Parse(hrAsStr[0].ToString()));
}
else
{
osc.SendInt("/avatar/parameters/onesHR", int.Parse(hrAsStr[1].ToString()));
osc.SendInt("/avatar/parameters/tensHR", int.Parse(hrAsStr[0].ToString()));
osc.Send("/avatar/parameters/onesHR", int.Parse(hrAsStr[1].ToString()));
osc.Send("/avatar/parameters/tensHR", int.Parse(hrAsStr[0].ToString()));
}

// Sending it all the time in case if avatar was changed and it should receive new data
osc.SendBool("/avatar/parameters/isHRConnected", true);
osc.SendBool("/avatar/parameters/isHRActive", true);
osc.Send("/avatar/parameters/isHRConnected", true);
osc.Send("/avatar/parameters/isHRActive", true);
}

private int ParseHeartRate(byte[] data)
Expand Down
90 changes: 6 additions & 84 deletions OSC.cs
Original file line number Diff line number Diff line change
@@ -1,102 +1,24 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
using HeartRateMonitorVRC.Properties;
using SharpOSC;

namespace HeartRateMonitorVRC
{
internal class OSC
{
private UdpClient udp;
private static UDPSender oscSender;

public OSC()
{
udp = new UdpClient();
oscSender = new UDPSender("127.0.0.1", 9000);
}

public void SendFloat(string address, float value)
{
var sendBack = address + '\0';
AlignStringBytes(ref sendBack);

sendBack += ",f";
AlignStringBytes(ref sendBack);

var buffer = Encoding.ASCII.GetBytes(sendBack);
var finalBuffer = new byte[buffer.Length + 4];
Array.Copy(buffer, finalBuffer, buffer.Length);

var bytes = BitConverter.GetBytes(value);
finalBuffer[buffer.Length + 0] = bytes[3];
finalBuffer[buffer.Length + 1] = bytes[2];
finalBuffer[buffer.Length + 2] = bytes[1];
finalBuffer[buffer.Length + 3] = bytes[0];

var endPoint = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 9000);
udp.Send(finalBuffer, finalBuffer.Length, endPoint);
}

public void SendInt(string address, int value)
public void Send(string address, object value)
{
var sendBack = address + '\0';
AlignStringBytes(ref sendBack);

sendBack += ",i";
AlignStringBytes(ref sendBack);

var buffer = Encoding.ASCII.GetBytes(sendBack);
var finalBuffer = new byte[buffer.Length + 4];
Array.Copy(buffer, finalBuffer, buffer.Length);

var bytes = BitConverter.GetBytes(value);
finalBuffer[buffer.Length + 0] = bytes[3];
finalBuffer[buffer.Length + 1] = bytes[2];
finalBuffer[buffer.Length + 2] = bytes[1];
finalBuffer[buffer.Length + 3] = bytes[0];

var endPoint = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 9000);
udp.Send(finalBuffer, finalBuffer.Length, endPoint);
}

// TODO: Fix byte alignment, since with specific addresses it aligns bytes wrong. Most likely happens to SendFloat and SendInt as well
public void SendBool(string address, bool value)
{
var sendBack = address + '\0';
AlignStringBytes(ref sendBack);

sendBack += ",";
sendBack += value ? "T" : "F";
AlignStringBytes(ref sendBack);

var buffer = Encoding.ASCII.GetBytes(sendBack);

Trace.WriteLine($"{address}, {buffer.Length}");

var endPoint = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 9000);
udp.Send(buffer, buffer.Length, endPoint);
}

private static void AlignStringBytes(ref string str)
{
int strLen = str.Length;
if (strLen % 4 != 0)
{
strLen += 4 - (strLen % 4);
}
else
{
strLen += 4;
}

for (int i = str.Length; i < strLen; i++)
{
str += '\0';
}
var message = new OscMessage(address, value);
oscSender.Send(message);
}
}
}
Binary file added SharpOSC.dll
Binary file not shown.

0 comments on commit 504d94c

Please sign in to comment.