466 lines
17 KiB
C#
466 lines
17 KiB
C#
using HidSliders.Hid;
|
|
using Voicemeeter;
|
|
using VoiceMeeter;
|
|
using Timer = System.Windows.Forms.Timer;
|
|
|
|
namespace HidSliders.Gui
|
|
{
|
|
public partial class Form1 : Form
|
|
{
|
|
private IDisposable? _vmClient;
|
|
private bool _mixerConnected;
|
|
private List<MixerButton> _previousButtons = [];
|
|
private readonly HidInterface _hidInterface;
|
|
private const int ZeroPercent = 90;
|
|
private readonly List<ChannelControlCollection> _channels;
|
|
private bool _suppressCheckboxUpdate = false;
|
|
private Timer _syncTimer = new();
|
|
|
|
public Form1()
|
|
{
|
|
InitializeComponent();
|
|
_channels = new List<ChannelControlCollection>
|
|
{
|
|
new (numericUpDown1, trackBar1, ch1A1Checkbox, ch1A2Checkbox, ch1A3Checkbox, ch1A4Checkbox, ch1A5Checkbox, ch1B1Checkbox, ch1B2Checkbox, ch1B3Checkbox, checkBox1, checkBox2, slider1Label),
|
|
new (numericUpDown2, trackBar2, checkBox12, checkBox11, checkBox10, checkBox9, checkBox8, checkBox7, checkBox6, checkBox5, checkBox4, checkBox3, slider2Label),
|
|
new (numericUpDown3, trackBar3, checkBox22, checkBox21, checkBox20, checkBox19, checkBox18, checkBox17, checkBox16, checkBox15, checkBox14, checkBox13, slider3Label),
|
|
new (numericUpDown4, trackBar4, checkBox32, checkBox31, checkBox30, checkBox29, checkBox28, checkBox27, checkBox26, checkBox25, checkBox24, checkBox23, slider4Label),
|
|
new (numericUpDown5, trackBar5, checkBox42, checkBox41, checkBox40, checkBox39, checkBox38, checkBox37, checkBox36, checkBox35, checkBox34, checkBox33, slider5Label),
|
|
|
|
};
|
|
|
|
switch (Properties.Settings.Default.VMVersion)
|
|
{
|
|
case 1:
|
|
vmRadio.Checked = true;
|
|
break;
|
|
case 2:
|
|
vmBananaRadio.Checked = true;
|
|
break;
|
|
case 3:
|
|
vmPotatoRadio.Checked = true;
|
|
break;
|
|
}
|
|
|
|
_channels[0].ChannelSelector.Value = Properties.Settings.Default.Channel1Strip;
|
|
_channels[1].ChannelSelector.Value = Properties.Settings.Default.Channel2Strip;
|
|
_channels[2].ChannelSelector.Value = Properties.Settings.Default.Channel3Strip;
|
|
_channels[3].ChannelSelector.Value = Properties.Settings.Default.Channel4Strip;
|
|
_channels[4].ChannelSelector.Value = Properties.Settings.Default.Channel5Strip;
|
|
|
|
_syncTimer.Tick += SyncVM;
|
|
_syncTimer.Interval = 100;
|
|
_syncTimer.Enabled = true;
|
|
if (Properties.Settings.Default.AutoConnect)
|
|
{
|
|
autoConnectCheckBox.Checked = true;
|
|
Task.Run(ConnectVoicemeeter).Wait();
|
|
}
|
|
|
|
_hidInterface = new HidInterface();
|
|
mixerLabel.Text = "Mixer not connected";
|
|
|
|
if (Properties.Settings.Default.MixerAutoConnect)
|
|
{
|
|
ConnectHidMixer();
|
|
mixerAutoConnectCheckbox.Checked = true;
|
|
}
|
|
}
|
|
|
|
private void OnSliderValues(int[] values)
|
|
{
|
|
this.BeginInvoke(() =>
|
|
{
|
|
for (int i = 0; i < 5; i++)
|
|
{
|
|
var gain = HidToGain(values[i]);
|
|
SetStripGain(_channels[i].CurrentStrip, gain);
|
|
_channels[i].TrackBar.Value = (int)gain;
|
|
_channels[i].IndicatorLabel.Text = $"{gain} dB";
|
|
}
|
|
});
|
|
}
|
|
|
|
private void HandleButtonPress(MixerButton button)
|
|
{
|
|
var buttonGroup = MixerButtonHelper.GetButtonGroup(button);
|
|
var channelButton = MixerButtonHelper.GetChannelButton(button, buttonGroup);
|
|
|
|
ChannelControlCollection channel;
|
|
switch (buttonGroup)
|
|
{
|
|
case ButtonGroups.Channel1:
|
|
channel = _channels[0];
|
|
break;
|
|
case ButtonGroups.Channel2:
|
|
channel = _channels[1];
|
|
break;
|
|
case ButtonGroups.Channel3:
|
|
channel = _channels[2];
|
|
break;
|
|
case ButtonGroups.Channel4:
|
|
channel = _channels[3];
|
|
break;
|
|
case ButtonGroups.Channel5:
|
|
channel = _channels[4];
|
|
break;
|
|
default:
|
|
throw new Exception();
|
|
}
|
|
|
|
switch (channelButton)
|
|
{
|
|
case ChannelButton.ChannelA1:
|
|
channel.BusA1Checkbox.Checked = !channel.BusA1Checkbox.Checked;
|
|
break;
|
|
case ChannelButton.ChannelA2:
|
|
channel.BusA2Checkbox.Checked = !channel.BusA2Checkbox.Checked;
|
|
break;
|
|
case ChannelButton.ChannelA3:
|
|
channel.BusA3Checkbox.Checked = !channel.BusA3Checkbox.Checked;
|
|
break;
|
|
case ChannelButton.ChannelA4:
|
|
channel.BusA4Checkbox.Checked = !channel.BusA4Checkbox.Checked;
|
|
break;
|
|
case ChannelButton.ChannelA5:
|
|
channel.BusA5Checkbox.Checked = !channel.BusA5Checkbox.Checked;
|
|
break;
|
|
case ChannelButton.ChannelB1:
|
|
channel.BusB1Checkbox.Checked = !channel.BusB1Checkbox.Checked;
|
|
break;
|
|
case ChannelButton.ChannelB2:
|
|
channel.BusB2Checkbox.Checked = !channel.BusB2Checkbox.Checked;
|
|
break;
|
|
case ChannelButton.ChannelB3:
|
|
channel.BusB3Checkbox.Checked = !channel.BusB3Checkbox.Checked;
|
|
break;
|
|
case ChannelButton.ChannelMute:
|
|
channel.MuteCheckBox.Checked = !channel.MuteCheckBox.Checked;
|
|
break;
|
|
default:
|
|
throw new Exception();
|
|
}
|
|
listBox1.Items.Add($"{buttonGroup} {channelButton} pressed");
|
|
}
|
|
|
|
private void OnButtons(List<MixerButton> buttons)
|
|
{
|
|
BeginInvoke(() =>
|
|
{
|
|
var newButtons = buttons.Except(_previousButtons).ToList();
|
|
_previousButtons = buttons;
|
|
|
|
newButtons.ForEach(HandleButtonPress);
|
|
});
|
|
}
|
|
|
|
private void OnSliderComplete()
|
|
{
|
|
BeginInvoke(() =>
|
|
{
|
|
_mixerConnected = false;
|
|
button1.Enabled = true;
|
|
mixerLabel.Text = "Mixer disconnected";
|
|
});
|
|
}
|
|
|
|
private void ConnectHidMixer()
|
|
{
|
|
_mixerConnected = _hidInterface.ConnectDevice(0x0359, 0x6497);
|
|
if (_mixerConnected)
|
|
{
|
|
mixerLabel.Text = "Mixer connected";
|
|
button1.Enabled = false;
|
|
_hidInterface.SliderObservable.Subscribe(OnSliderValues, OnSliderComplete);
|
|
_hidInterface.ButtonsObservable.Subscribe(OnButtons);
|
|
}
|
|
}
|
|
|
|
private async Task ConnectVoicemeeter()
|
|
{
|
|
RunVoicemeeterParam version = RunVoicemeeterParam.None;
|
|
if (vmRadio.Checked)
|
|
{
|
|
version = RunVoicemeeterParam.Voicemeeter;
|
|
}
|
|
else if (vmBananaRadio.Checked)
|
|
{
|
|
version = RunVoicemeeterParam.VoicemeeterBanana;
|
|
}
|
|
else if (vmPotatoRadio.Checked)
|
|
{
|
|
version = RunVoicemeeterParam.VoicemeeterPotato;
|
|
}
|
|
|
|
Properties.Settings.Default.VMVersion = (int)version;
|
|
_vmClient = await Remote.Initialize(version);
|
|
if (_vmClient != null)
|
|
{
|
|
EnableVMControls(version);
|
|
voicemeeterStatusLabel.Text = "Connected";
|
|
_syncTimer.Start();
|
|
}
|
|
}
|
|
|
|
private async void connectButton_Click(object sender, EventArgs e)
|
|
{
|
|
await ConnectVoicemeeter();
|
|
}
|
|
|
|
private void SyncVM(object? sender, EventArgs eventArgs)
|
|
{
|
|
if (_vmClient == null || Remote.IsParametersDirty() != 1) return;
|
|
|
|
foreach (var channelControlCollection in _channels)
|
|
{
|
|
SyncChannelControls(channelControlCollection);
|
|
}
|
|
}
|
|
|
|
private void EnableVMControls(RunVoicemeeterParam version)
|
|
{
|
|
foreach (var channel in _channels)
|
|
{
|
|
channel.TrackBar.Enabled = true;
|
|
channel.SoloCheckBox.Enabled = true;
|
|
channel.MuteCheckBox.Enabled = true;
|
|
channel.EnableBusses(version);
|
|
SyncChannelControls(channel);
|
|
}
|
|
}
|
|
|
|
private void SyncChannelControls(ChannelControlCollection channel)
|
|
{
|
|
_suppressCheckboxUpdate = true;
|
|
channel.BusA1Checkbox.Checked = GetBusEnabled(channel.CurrentStrip, VMBus.A1);
|
|
channel.BusB1Checkbox.Checked = GetBusEnabled(channel.CurrentStrip, VMBus.B1);
|
|
if (vmBananaRadio.Checked || vmPotatoRadio.Checked)
|
|
{
|
|
channel.BusA2Checkbox.Checked = GetBusEnabled(channel.CurrentStrip, VMBus.A2);
|
|
channel.BusA3Checkbox.Checked = GetBusEnabled(channel.CurrentStrip, VMBus.A3);
|
|
channel.BusB2Checkbox.Checked = GetBusEnabled(channel.CurrentStrip, VMBus.B2);
|
|
}
|
|
|
|
if (vmPotatoRadio.Checked)
|
|
{
|
|
channel.BusA4Checkbox.Checked = GetBusEnabled(channel.CurrentStrip, VMBus.A4);
|
|
channel.BusA5Checkbox.Checked = GetBusEnabled(channel.CurrentStrip, VMBus.A5);
|
|
channel.BusB3Checkbox.Checked = GetBusEnabled(channel.CurrentStrip, VMBus.B3);
|
|
}
|
|
|
|
channel.TrackBar.Value = (int)GetStripGain(channel.CurrentStrip);
|
|
channel.SoloCheckBox.Checked = GetChannelSolo(channel.CurrentStrip);
|
|
channel.MuteCheckBox.Checked = GetChannelMute(channel.CurrentStrip);
|
|
_suppressCheckboxUpdate = false;
|
|
}
|
|
|
|
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
|
|
{
|
|
_vmClient?.Dispose();
|
|
Properties.Settings.Default.Save();
|
|
}
|
|
|
|
private void button1_Click(object sender, EventArgs e)
|
|
{
|
|
ConnectHidMixer();
|
|
}
|
|
|
|
private float HidToGain(int hidValue)
|
|
{
|
|
// adjusted regression of yamaha board is approx y = 5.8456 + 17.0486 * ln(x)
|
|
// Truncate to first decimal (move decimal, int truncate, move decimal)
|
|
//var realResult = 5.8456f + 17.0486f * (float)Math.Log(Math.Max(hidValue / Resolution, 0.02));
|
|
//return ((int)(realResult * 10)) / 10.0f;
|
|
return (int)(CalculateGain(hidValue, ZeroPercent) * 10) / 10.0f;
|
|
}
|
|
|
|
public static float CalculateGain(float slider, int zeroPct)
|
|
{
|
|
float minSlider;
|
|
float maxSlider;
|
|
float minGain;
|
|
float maxGain;
|
|
|
|
if (slider >= zeroPct)
|
|
{
|
|
minSlider = zeroPct;
|
|
maxSlider = 100;
|
|
minGain = 0;
|
|
maxGain = 12;
|
|
}
|
|
else
|
|
{
|
|
minSlider = 0;
|
|
maxSlider = zeroPct;
|
|
minGain = -60;
|
|
maxGain = 0;
|
|
}
|
|
|
|
// Calculate the percentage of slider position within the range
|
|
var percentage = (slider - minSlider) / (maxSlider - minSlider);
|
|
|
|
// Interpolate gain within the range based on the percentage
|
|
var calculatedGain = minGain + (percentage * (maxGain - minGain));
|
|
|
|
return calculatedGain;
|
|
}
|
|
|
|
private void trackBar_Scroll(object sender, EventArgs e)
|
|
{
|
|
if (_suppressCheckboxUpdate) return;
|
|
if (sender is TrackBar trackBar && _vmClient != null)
|
|
{
|
|
var channel = _channels.First(c => c.OwnsControl(trackBar));
|
|
SetStripGain(channel.CurrentStrip, trackBar.Value);
|
|
}
|
|
}
|
|
|
|
private void busCheckbox_CheckedChanged(object sender, EventArgs e)
|
|
{
|
|
if (_suppressCheckboxUpdate) return;
|
|
if (sender is CheckBox { Tag: VMBus bus } checkBox && _vmClient != null)
|
|
{
|
|
var channel = _channels.First(c => c.OwnsControl(checkBox));
|
|
SetBusEnabled(channel.CurrentStrip, bus, checkBox.Checked);
|
|
}
|
|
}
|
|
|
|
private void SetStripGain(int strip, float value)
|
|
{
|
|
Remote.SetParameter($"Strip[{strip}].Gain", value);
|
|
}
|
|
|
|
private float GetStripGain(int index)
|
|
{
|
|
return Remote.GetParameter($"Strip[{index}].Gain");
|
|
}
|
|
|
|
private void vmRadio_CheckedChanged(object sender, EventArgs e)
|
|
{
|
|
if (vmRadio.Checked)
|
|
{
|
|
setMaxChannel(2);
|
|
}
|
|
}
|
|
|
|
private void vmBananaRadio_CheckedChanged(object sender, EventArgs e)
|
|
{
|
|
if (vmBananaRadio.Checked)
|
|
{
|
|
setMaxChannel(4);
|
|
}
|
|
}
|
|
|
|
private void setMaxChannel(int max)
|
|
{
|
|
numericUpDown1.Maximum = max;
|
|
numericUpDown2.Maximum = max;
|
|
numericUpDown3.Maximum = max;
|
|
numericUpDown4.Maximum = max;
|
|
numericUpDown5.Maximum = max;
|
|
}
|
|
|
|
private void vmPotatoRadio_CheckedChanged(object sender, EventArgs e)
|
|
{
|
|
if (vmPotatoRadio.Checked)
|
|
{
|
|
setMaxChannel(7);
|
|
}
|
|
}
|
|
|
|
private void SetBusEnabled(int strip, VMBus bus, bool enabled)
|
|
{
|
|
Remote.SetParameter($"Strip[{strip}].{bus}", enabled ? 1 : 0);
|
|
}
|
|
|
|
private bool GetBusEnabled(int strip, VMBus bus)
|
|
{
|
|
return (int)Remote.GetParameter($"Strip[{strip}].{bus}") == 1;
|
|
}
|
|
|
|
private void SetChannelSolo(int strip, bool enabled)
|
|
{
|
|
Remote.SetParameter($"Strip[{strip}].Solo", enabled ? 1 : 0);
|
|
}
|
|
|
|
private bool GetChannelSolo(int strip)
|
|
{
|
|
return (int)Remote.GetParameter($"Strip[{strip}].Solo") == 1;
|
|
}
|
|
|
|
private void SetChannelMute(int strip, bool enabled)
|
|
{
|
|
Remote.SetParameter($"Strip[{strip}].Mute", enabled ? 1 : 0);
|
|
}
|
|
|
|
private bool GetChannelMute(int strip)
|
|
{
|
|
return (int)Remote.GetParameter($"Strip[{strip}].Mute") == 1;
|
|
}
|
|
|
|
private void numericUpDown_ValueChanged(object sender, EventArgs e)
|
|
{
|
|
if (sender is NumericUpDown numericUpDown)
|
|
{
|
|
var channel = _channels.First(c => c.OwnsControl(numericUpDown));
|
|
if (_vmClient != null)
|
|
{
|
|
SyncChannelControls(channel);
|
|
}
|
|
var index = _channels.FindIndex(c => c == channel);
|
|
var settingsType = Properties.Settings.Default.GetType();
|
|
settingsType.GetProperty($"Channel{index + 1}Strip").SetValue(Properties.Settings.Default, (int)numericUpDown.Value);
|
|
}
|
|
}
|
|
|
|
private void soloCheckBox_CheckedChanged(object sender, EventArgs e)
|
|
{
|
|
if (_suppressCheckboxUpdate) return;
|
|
if (sender is CheckBox checkBox && _vmClient != null)
|
|
{
|
|
var channel = _channels.First(c => c.OwnsControl(checkBox));
|
|
SetChannelSolo(channel.CurrentStrip, checkBox.Checked);
|
|
}
|
|
}
|
|
|
|
private void muteCheckBox_CheckedChanged(object sender, EventArgs e)
|
|
{
|
|
if (_suppressCheckboxUpdate) return;
|
|
if (sender is CheckBox checkBox && _vmClient != null)
|
|
{
|
|
var channel = _channels.First(c => c.OwnsControl(checkBox));
|
|
SetChannelMute(channel.CurrentStrip, checkBox.Checked);
|
|
}
|
|
}
|
|
|
|
private void autoConnectCheckBox_CheckedChanged(object sender, EventArgs e)
|
|
{
|
|
Properties.Settings.Default.AutoConnect = autoConnectCheckBox.Checked;
|
|
}
|
|
|
|
private void checkBox43_CheckedChanged(object sender, EventArgs e)
|
|
{
|
|
Properties.Settings.Default.MixerAutoConnect = mixerAutoConnectCheckbox.Checked;
|
|
}
|
|
|
|
private void Form1_Resize(object sender, EventArgs e)
|
|
{
|
|
if (FormWindowState.Minimized == this.WindowState)
|
|
{
|
|
notifyIcon1.Visible = true;
|
|
this.Hide();
|
|
}
|
|
else if (FormWindowState.Normal == this.WindowState)
|
|
{
|
|
notifyIcon1.Visible = false;
|
|
}
|
|
}
|
|
|
|
private void notifyIcon1_Click(object sender, EventArgs e)
|
|
{
|
|
this.Show();
|
|
WindowState = FormWindowState.Normal;
|
|
}
|
|
}
|
|
}
|