Skip to content

Commit

Permalink
Convert the 'Serial' field to a drop-down (#10)
Browse files Browse the repository at this point in the history
* Convert the 'Serial' field to a drop-down

* Reset Status on Disconnect

* When disconnecting, prepare for a new connection.

* Just use mixer.Key
  • Loading branch information
FrostyCoolSlug authored Aug 9, 2024
1 parent 54ea2e6 commit 10f5f5e
Show file tree
Hide file tree
Showing 3 changed files with 96 additions and 10 deletions.
80 changes: 73 additions & 7 deletions FaderSyncPlugin/OBS/GoXlrChannelSyncFilter.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using System.Collections;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text;
Expand Down Expand Up @@ -140,11 +141,22 @@ private static unsafe void GetDefaults(obs_data* settings)
private static unsafe obs_properties* GetProperties(void* data)
{
var properties = ObsProperties.obs_properties_create();
var context = (FilterContext*)data;

// Get the currently configured Serial as a String (we'll need this later)
var serial = Marshal.PtrToStringUTF8((IntPtr)context->DeviceSerial);
var deviceSerialError = "---ERROR---";

fixed (byte*
// device serial input
sDeviceSerialId = "DEVICE_SERIAL"u8.ToArray(),
sDeviceSerialDescription = "Device Serial"u8.ToArray(),

// We need a 'Default' serial in case something is wrong when loading
sDeviceSerialError = Encoding.UTF8.GetBytes(deviceSerialError),
sDeviceSerialDisconnected = "Error Connecting to the GoXLR Utility"u8.ToArray(),
sDeviceSerialNoDevices = "No GoXLR Devices Detected"u8.ToArray(),
sDeviceSerialSelect = "Select GoXLR"u8.ToArray(),

// add channels
sChannelNameId = "CHANNEL_NAME"u8.ToArray(),
Expand Down Expand Up @@ -191,15 +203,69 @@ private static unsafe void GetDefaults(obs_data* settings)
ObsProperties.obs_property_list_add_string(channelList, (sbyte*)sChannelMicMonitor, (sbyte*)sChannelMicMonitorId);
ObsProperties.obs_property_list_add_string(channelList, (sbyte*)sChannelLineOut, (sbyte*)sChannelLineOutId);

// // channel name text field
// ObsProperties.obs_properties_add_text(properties, (sbyte*)sChannelNameId, (sbyte*)sChannelNameDescription,
// obs_text_type.OBS_TEXT_DEFAULT);

// device serial text field
ObsProperties.obs_properties_add_text(properties, (sbyte*)sDeviceSerialId, (sbyte*)sDeviceSerialDescription,
obs_text_type.OBS_TEXT_DEFAULT);
// Create the Serial Dropdown..
var deviceList = ObsProperties.obs_properties_add_list(properties, (sbyte*)sDeviceSerialId, (sbyte*)sDeviceSerialDescription,
obs_combo_type.OBS_COMBO_TYPE_LIST, obs_combo_format.OBS_COMBO_FORMAT_STRING);


// Before we Proceed, we need to fetch a list of the available GoXLRs on the System..
var utility = UtilitySingleton.GetInstance();
var mixers = (JsonObject)utility.Status?["mixers"];
var locatedDevices = new ArrayList();
var forcedSerial = false;

// Iterate the status and add all the currently connected serials to a list.
if (mixers != null) {
foreach (var mixer in mixers) {
locatedDevices.Add(mixer.Key);
}
}

// Get an initial count of devices which we'll use for stuff later!
var locatedDeviceCount = locatedDevices.Count;

// If the user has perviously configured a GoXLR but it's not currently attached to the Utility, we need to
// force the serial into the list to prevent arbitrary device switching later on. We'll also flag this as a
// forced entry so we can appropriately label it.
if (serial != "" && !locatedDevices.Contains(serial)) {
locatedDevices.Add(serial);
forcedSerial = true;
}

if (locatedDevices.Count == 0) {
// We're in some kind of error state. Either the utility connection is broken or there are no GoXLRs attached, and the
// user hasn't previously defined a GoXLR. In this case we'll forcably add the 'Error' serial to the list so we can
// display the problem to the user in the drop-down.
locatedDevices.Add(deviceSerialError);
}

//ObsProperties.obs_properties_add_text(properties, (sbyte*)tWarnTitle, (sbyte*)tWarnMessage, obs_text_type.OBS_TEXT_INFO);
// Start filling out the list..
foreach (var located in locatedDevices) {
fixed (byte* sSerial = Encoding.UTF8.GetBytes((string)located)) {
if (located.Equals(deviceSerialError) && mixers == null) {
// Unable to Connect to the Utility, no GoXLR previously configured in the Filter
ObsProperties.obs_property_list_add_string(deviceList, (sbyte*)sDeviceSerialDisconnected, (sbyte*)sDeviceSerialError);
} else if (located.Equals(deviceSerialError) && locatedDeviceCount == 0) {
// No GoXLR Devices Attached, no GoXLR previously configured in the Filter
ObsProperties.obs_property_list_add_string(deviceList, (sbyte*)sDeviceSerialNoDevices, (sbyte*)sDeviceSerialError);
} else if (located.Equals(deviceSerialError) && locatedDeviceCount > 0) {
// In this scenario we've left an Error State. By not pushing the Error Serial into the list OBS will automatically
// switch the dropdown to the first entry (a real GoXLR Serial) forcing an update and taking us out of the error state.
} else {
var title = (string)located;

// Has this device been forced into the located list due to it being disconnected?
if (forcedSerial && located.Equals(serial)) {
// We can do a *LOT* better than this and potentially check WHY it's disconnected..
title = String.Format("{0} - Disconnected", located);
}
fixed(byte* sTitle = Encoding.UTF8.GetBytes(title)) {
ObsProperties.obs_property_list_add_string(deviceList, (sbyte*)sTitle, (sbyte*)sSerial);
}
}
}
}
}

return properties;
Expand Down
19 changes: 16 additions & 3 deletions UtilityClient/Native/WebsocketClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ private async Task ReceiveMessageTask()

case WebSocketMessageType.Binary:
default:
// This wont occur with the Utility, but we need to trigger DisconnectAsync to perform tidying up!
await _client.CloseAsync(WebSocketCloseStatus.ProtocolError, "Only Text is supported.", CancellationToken.None);
this.OnDisconnected?.Invoke(this, "Connection closed because server tried to send binary or invalid message.");
break;
Expand All @@ -94,8 +95,8 @@ private async Task ConnectionTask()

case WebSocketState.Aborted:
case WebSocketState.Closed:
if (this._isConnected) this.OnDisconnected?.Invoke(this, "Connection closed.");
this._isConnected = false;
// Trigger an internal disconnect to clean resources.
Task.Run(DisconnectAsync);

Check warning on line 99 in UtilityClient/Native/WebsocketClient.cs

View workflow job for this annotation

GitHub Actions / Build for Linux

Because this call is not awaited, execution of the current method continues before the call is completed. Consider applying the 'await' operator to the result of the call.

Check warning on line 99 in UtilityClient/Native/WebsocketClient.cs

View workflow job for this annotation

GitHub Actions / Build for Windows

Because this call is not awaited, execution of the current method continues before the call is completed. Consider applying the 'await' operator to the result of the call.
break;

default:
Expand Down Expand Up @@ -126,7 +127,10 @@ public async Task<bool> ConnectAsync(Uri uri)

public async Task DisconnectAsync()
{
await this._client.CloseAsync(WebSocketCloseStatus.NormalClosure, "", CancellationToken.None);
// Only attempt to close the socket if it's not already closed
if (this._client.State != WebSocketState.Aborted && this._client.State != WebSocketState.Closed) {
await this._client.CloseAsync(WebSocketCloseStatus.NormalClosure, "", CancellationToken.None);
}
this.OnDisconnected?.Invoke(this, "Connection closed.");

this._cancellationTokenSource.Cancel();
Expand All @@ -140,6 +144,15 @@ public async Task DisconnectAsync()

this._receiveMessageTask.Dispose();
this._connectionTask.Dispose();

// Dispose of, and create a new client / Token for future connections
this._client.Dispose();
this._client = new();
this._cancellationTokenSource.Dispose();
this._cancellationTokenSource = new();

// Flag the connection as disconnected
this._isConnected = false;
}

public async Task SendMessage(string message)
Expand Down
7 changes: 7 additions & 0 deletions UtilityClient/Utility.cs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,13 @@ public Utility() : base()
OnException?.Invoke(this, je);
} // nothing
};

base.OnDisconnected += async (object? sender, string message) => {

Check warning on line 51 in UtilityClient/Utility.cs

View workflow job for this annotation

GitHub Actions / Build for Linux

This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread.

Check warning on line 51 in UtilityClient/Utility.cs

View workflow job for this annotation

GitHub Actions / Build for Windows

This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread.
Console.WriteLine("Disconnected from Host: {0}", message);

// Reset the status, so Upstream code knows we're not connected.
this.Status = JsonNode.Parse("{}")!;
};
}

private async Task<JsonNode?> AwaitResponse(uint operationId)
Expand Down

0 comments on commit 10f5f5e

Please sign in to comment.