From 617eb08f2d80215b9a0581a87de03bd60e97364a Mon Sep 17 00:00:00 2001 From: Michael Paul Coder Date: Wed, 17 Aug 2022 23:23:45 +1000 Subject: [PATCH] Add option to configure wave forms --- Halovision/Halovision.csproj | 3 +- Halovision/PluginHandler.cs | 44 +++++++-- Halovision/Properties/AssemblyInfo.cs | 4 +- Halovision/SineWaveProvider.cs | 75 --------------- Halovision/VisionForm.cs | 41 ++++++++ Halovision/VisionForm.designer.cs | 51 +++++++--- Halovision/VisionForm.resx | 2 +- Halovision/WaveProvider.cs | 117 +++++++++++++++++++++++ Halovision/WaveType.cs | 18 ++++ Installer/Lucid Scribe Halovision.vdproj | 8 +- 10 files changed, 258 insertions(+), 105 deletions(-) delete mode 100644 Halovision/SineWaveProvider.cs create mode 100644 Halovision/WaveProvider.cs create mode 100644 Halovision/WaveType.cs diff --git a/Halovision/Halovision.csproj b/Halovision/Halovision.csproj index 6cdeef0..a3ba222 100644 --- a/Halovision/Halovision.csproj +++ b/Halovision/Halovision.csproj @@ -216,7 +216,7 @@ True Resources.resx - + Form @@ -229,6 +229,7 @@ + diff --git a/Halovision/PluginHandler.cs b/Halovision/PluginHandler.cs index c0894e2..38f30fe 100644 --- a/Halovision/PluginHandler.cs +++ b/Halovision/PluginHandler.cs @@ -150,6 +150,14 @@ public static bool Auralize return visionForm.Auralize; } } + + public static WaveType WaveForm + { + get + { + return visionForm.WaveForm; + } + } } namespace EyeMin @@ -261,7 +269,10 @@ public override double Value if (tossValue == 999 - tossHalfLife) { - PlayGlitch(); + if (Device.Auralize) + { + PlayGlitch(); + } } Device.SetTossValue(tossValue); @@ -297,7 +308,9 @@ namespace Vision public class PluginHandler : lucidcode.LucidScribe.Interface.LucidPluginBase { private IWavePlayer player; - private SineWaveProvider sineProvider; + private WaveProvider waveProvider; + private WaveType waveForm; + private WaveOutEvent waveOutEvent; public override string Name { @@ -309,13 +322,14 @@ public override string Name public override bool Initialize() { - sineProvider = new SineWaveProvider(); + waveForm = Device.WaveForm; + waveProvider = new WaveProvider(waveForm); - var waveOutEvent = new WaveOutEvent(); + waveOutEvent = new WaveOutEvent(); waveOutEvent.NumberOfBuffers = 2; waveOutEvent.DesiredLatency = 100; player = waveOutEvent; - player.Init(new SampleToWaveProvider(sineProvider)); + player.Init(new SampleToWaveProvider(waveProvider)); return Device.Initialize(); } @@ -334,7 +348,8 @@ public override double Value player.Play(); } Auralize(vision); - } else + } + else { if (player.PlaybackState == PlaybackState.Playing) { @@ -348,8 +363,21 @@ public override double Value private void Auralize(double frequency) { - if (frequency > 0) frequency += 128; - sineProvider.Frequency = frequency; + if (waveForm == WaveType.Sin) + { + if (frequency > 0) frequency += 256; + } + + if (waveForm != Device.WaveForm) + { + waveForm = Device.WaveForm; + waveProvider = new WaveProvider(waveForm); + player.Stop(); + player = waveOutEvent; + player.Init(new SampleToWaveProvider(waveProvider)); + player.Play(); + } + waveProvider.Frequency = frequency / 2; } public override void Dispose() diff --git a/Halovision/Properties/AssemblyInfo.cs b/Halovision/Properties/AssemblyInfo.cs index 062a0f3..0d5253b 100644 --- a/Halovision/Properties/AssemblyInfo.cs +++ b/Halovision/Properties/AssemblyInfo.cs @@ -32,5 +32,5 @@ // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.2.5.0")] -[assembly: AssemblyFileVersion("1.2.5.0")] +[assembly: AssemblyVersion("1.2.6.0")] +[assembly: AssemblyFileVersion("1.2.6.0")] diff --git a/Halovision/SineWaveProvider.cs b/Halovision/SineWaveProvider.cs deleted file mode 100644 index 1b7aaff..0000000 --- a/Halovision/SineWaveProvider.cs +++ /dev/null @@ -1,75 +0,0 @@ -using NAudio.Wave; -using System; - -namespace lucidcode.LucidScribe.Plugin.Halovision -{ - internal class SineWaveProvider : ISampleProvider - { - private readonly float[] waveTable; - private double phase; - private double currentPhaseStep; - private double targetPhaseStep; - private double frequency; - private double phaseStepDelta; - private bool seekFreq; - - public SineWaveProvider(int sampleRate = 44100) - { - WaveFormat = WaveFormat.CreateIeeeFloatWaveFormat(sampleRate, 1); - waveTable = new float[sampleRate]; - for (int index = 0; index < sampleRate; ++index) - waveTable[index] = (float)Math.Sin(2 * Math.PI * (double)index / sampleRate); - Frequency = 1000f; - Volume = 1f; - PortamentoTime = 0.1; - } - - public double PortamentoTime { get; set; } - - public double Frequency - { - get - { - return frequency; - } - set - { - frequency = value; - seekFreq = true; - } - } - - public float Volume { get; set; } - - public WaveFormat WaveFormat { get; private set; } - - public int Read(float[] buffer, int offset, int count) - { - if (seekFreq) - { - targetPhaseStep = waveTable.Length * (frequency / WaveFormat.SampleRate); - - phaseStepDelta = (targetPhaseStep - currentPhaseStep) / (WaveFormat.SampleRate * PortamentoTime); - seekFreq = false; - } - var vol = Volume; - for (int n = 0; n < count; ++n) - { - int waveTableIndex = (int)phase % waveTable.Length; - buffer[n + offset] = waveTable[waveTableIndex] * vol; - phase += currentPhaseStep; - if (phase > waveTable.Length) - phase -= waveTable.Length; - if (currentPhaseStep != targetPhaseStep) - { - currentPhaseStep += phaseStepDelta; - if (phaseStepDelta > 0.0 && currentPhaseStep > targetPhaseStep) - currentPhaseStep = targetPhaseStep; - else if (phaseStepDelta < 0.0 && currentPhaseStep < targetPhaseStep) - currentPhaseStep = targetPhaseStep; - } - } - return count; - } - } -} diff --git a/Halovision/VisionForm.cs b/Halovision/VisionForm.cs index 585de83..9900500 100644 --- a/Halovision/VisionForm.cs +++ b/Halovision/VisionForm.cs @@ -52,6 +52,7 @@ public partial class VisionForm : Form public int DashThreshold = 600; public bool Auralize = false; + public WaveType WaveForm = WaveType.Square; private VideoCaptureDevice videoSource; private Rectangle[] faceRegions; @@ -229,6 +230,7 @@ private void LoadSettings() defaultSettings += "200"; defaultSettings += "600"; defaultSettings += "None"; + defaultSettings += "Sqaure"; defaultSettings += ""; defaultSettings += ""; File.WriteAllText(settingsFilePath, defaultSettings); @@ -278,6 +280,15 @@ private void LoadSettings() cmbClassifier.Text = xmlSettings.DocumentElement.SelectSingleNode("//Classifier").InnerText; } + if (xmlSettings.DocumentElement.SelectSingleNode("//WaveForm") != null) + { + cmbWaveForm.Text = xmlSettings.DocumentElement.SelectSingleNode("//WaveForm").InnerText; + } + else + { + cmbWaveForm.Text = "Triangle"; + } + if (xmlSettings.DocumentElement.SelectSingleNode("//TopMost") != null && xmlSettings.DocumentElement.SelectSingleNode("//TopMost").InnerText == "0") { chkTopMost.Checked = false; @@ -906,6 +917,7 @@ private void SaveSettings() } settings += "" + cmbClassifier.Text + ""; + settings += "" + cmbWaveForm.Text + ""; if (chkTopMost.Checked) { @@ -1051,5 +1063,34 @@ private void chkCopyFromScreen_CheckedChanged(object sender, EventArgs e) CopyFromScreen = chkCopyFromScreen.Checked; SaveSettings(); } + + private void cmbWaveForm_SelectedIndexChanged(object sender, EventArgs e) + { + switch (cmbWaveForm.Text) + { + case "Sin": + WaveForm = WaveType.Sin; + break; + case "SawTooth": + WaveForm = WaveType.SawTooth; + break; + case "Sqaure": + WaveForm = WaveType.Square; + break; + case "Triangle": + WaveForm = WaveType.Triangle; + break; + case "Sweep": + WaveForm = WaveType.Sweep; + break; + case "Pink": + WaveForm = WaveType.Pink; + break; + default: + WaveForm = WaveType.Square; + break; + } + SaveSettings(); + } } } diff --git a/Halovision/VisionForm.designer.cs b/Halovision/VisionForm.designer.cs index 08f470a..42c56a6 100644 --- a/Halovision/VisionForm.designer.cs +++ b/Halovision/VisionForm.designer.cs @@ -85,6 +85,7 @@ private void InitializeComponent() this.panel3D5 = new lucidcode.Controls.Panel3D(); this.label3 = new System.Windows.Forms.Label(); this.label4 = new System.Windows.Forms.Label(); + this.cmbWaveForm = new System.Windows.Forms.ComboBox(); this.pnlPlugins.SuspendLayout(); ((System.ComponentModel.ISupportInitialize)(this.pbDisplay)).BeginInit(); this.mnuReconnect.SuspendLayout(); @@ -266,6 +267,7 @@ private void InitializeComponent() this.panel3D3.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.panel3D3.BackColor = System.Drawing.Color.LightSteelBlue; + this.panel3D3.Controls.Add(this.cmbWaveForm); this.panel3D3.Controls.Add(this.chkAuralize); this.panel3D3.Controls.Add(this.chkCopyFromScreen); this.panel3D3.Controls.Add(this.dotThresholdInput); @@ -312,20 +314,20 @@ private void InitializeComponent() // // chkAuralize // - this.chkAuralize.Location = new System.Drawing.Point(9, 167); + this.chkAuralize.Location = new System.Drawing.Point(9, 139); this.chkAuralize.Name = "chkAuralize"; this.chkAuralize.Size = new System.Drawing.Size(84, 22); - this.chkAuralize.TabIndex = 15; + this.chkAuralize.TabIndex = 11; this.chkAuralize.Text = "Auralize"; this.chkAuralize.UseVisualStyleBackColor = true; this.chkAuralize.CheckedChanged += new System.EventHandler(this.chkAuralize_CheckedChanged); // // chkCopyFromScreen // - this.chkCopyFromScreen.Location = new System.Drawing.Point(99, 139); + this.chkCopyFromScreen.Location = new System.Drawing.Point(99, 167); this.chkCopyFromScreen.Name = "chkCopyFromScreen"; this.chkCopyFromScreen.Size = new System.Drawing.Size(145, 22); - this.chkCopyFromScreen.TabIndex = 12; + this.chkCopyFromScreen.TabIndex = 16; this.chkCopyFromScreen.Text = "Copy From Screen"; this.chkCopyFromScreen.UseVisualStyleBackColor = true; this.chkCopyFromScreen.CheckedChanged += new System.EventHandler(this.chkCopyFromScreen_CheckedChanged); @@ -347,7 +349,7 @@ private void InitializeComponent() 0}); this.dotThresholdInput.Name = "dotThresholdInput"; this.dotThresholdInput.Size = new System.Drawing.Size(54, 21); - this.dotThresholdInput.TabIndex = 19; + this.dotThresholdInput.TabIndex = 21; this.dotThresholdInput.Value = new decimal(new int[] { 200, 0, @@ -367,7 +369,7 @@ private void InitializeComponent() 0}); this.dashThresholdInput.Name = "dashThresholdInput"; this.dashThresholdInput.Size = new System.Drawing.Size(54, 21); - this.dashThresholdInput.TabIndex = 20; + this.dashThresholdInput.TabIndex = 22; this.dashThresholdInput.Value = new decimal(new int[] { 600, 0, @@ -432,9 +434,9 @@ private void InitializeComponent() 0}); this.idleTicksInput.Name = "idleTicksInput"; this.idleTicksInput.Size = new System.Drawing.Size(54, 21); - this.idleTicksInput.TabIndex = 304; + this.idleTicksInput.TabIndex = 18; this.idleTicksInput.Value = new decimal(new int[] { - 18, + 8, 0, 0, 0}); @@ -453,10 +455,10 @@ private void InitializeComponent() // // chkTopMost // - this.chkTopMost.Location = new System.Drawing.Point(9, 139); + this.chkTopMost.Location = new System.Drawing.Point(9, 167); this.chkTopMost.Name = "chkTopMost"; this.chkTopMost.Size = new System.Drawing.Size(84, 22); - this.chkTopMost.TabIndex = 11; + this.chkTopMost.TabIndex = 15; this.chkTopMost.Text = "Top Most"; this.chkTopMost.UseVisualStyleBackColor = true; this.chkTopMost.CheckedChanged += new System.EventHandler(this.chkTopMost_CheckedChanged); @@ -597,10 +599,10 @@ private void InitializeComponent() // // chkRecordVideo // - this.chkRecordVideo.Location = new System.Drawing.Point(99, 166); + this.chkRecordVideo.Location = new System.Drawing.Point(99, 193); this.chkRecordVideo.Name = "chkRecordVideo"; this.chkRecordVideo.Size = new System.Drawing.Size(145, 22); - this.chkRecordVideo.TabIndex = 16; + this.chkRecordVideo.TabIndex = 20; this.chkRecordVideo.Text = "Record Video"; this.chkRecordVideo.UseVisualStyleBackColor = true; this.chkRecordVideo.CheckedChanged += new System.EventHandler(this.chkRecordVideo_CheckedChanged); @@ -1064,10 +1066,10 @@ private void InitializeComponent() // // chkTCMP // - this.chkTCMP.Location = new System.Drawing.Point(9, 195); + this.chkTCMP.Location = new System.Drawing.Point(9, 193); this.chkTCMP.Name = "chkTCMP"; this.chkTCMP.Size = new System.Drawing.Size(145, 22); - this.chkTCMP.TabIndex = 18; + this.chkTCMP.TabIndex = 19; this.chkTCMP.Text = "TCMP"; this.chkTCMP.UseVisualStyleBackColor = true; this.chkTCMP.CheckedChanged += new System.EventHandler(this.chkTCMP_CheckedChanged); @@ -1582,6 +1584,26 @@ private void InitializeComponent() this.label4.Text = "Settings"; this.label4.TextAlign = System.Drawing.ContentAlignment.MiddleLeft; // + // cmbWaveForm + // + this.cmbWaveForm.AccessibleName = "Wave Form"; + this.cmbWaveForm.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.cmbWaveForm.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.cmbWaveForm.FormattingEnabled = true; + this.cmbWaveForm.Items.AddRange(new object[] { + "Sin", + "SawTooth", + "Sqaure", + "Triangle", + "Sweep", + "Pink"}); + this.cmbWaveForm.Location = new System.Drawing.Point(99, 138); + this.cmbWaveForm.Name = "cmbWaveForm"; + this.cmbWaveForm.Size = new System.Drawing.Size(145, 21); + this.cmbWaveForm.TabIndex = 12; + this.cmbWaveForm.SelectedIndexChanged += new System.EventHandler(this.cmbWaveForm_SelectedIndexChanged); + // // VisionForm // this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 13F); @@ -1676,5 +1698,6 @@ private void InitializeComponent() internal System.Windows.Forms.Label label18; private System.Windows.Forms.CheckBox chkCopyFromScreen; private System.Windows.Forms.CheckBox chkAuralize; + private System.Windows.Forms.ComboBox cmbWaveForm; } } \ No newline at end of file diff --git a/Halovision/VisionForm.resx b/Halovision/VisionForm.resx index ee22611..e73c6b5 100644 --- a/Halovision/VisionForm.resx +++ b/Halovision/VisionForm.resx @@ -128,7 +128,7 @@ AAEAAAD/////AQAAAAAAAAAMAgAAAFdTeXN0ZW0uV2luZG93cy5Gb3JtcywgVmVyc2lvbj00LjAuMC4w LCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPWI3N2E1YzU2MTkzNGUwODkFAQAAACZTeXN0 ZW0uV2luZG93cy5Gb3Jtcy5JbWFnZUxpc3RTdHJlYW1lcgEAAAAERGF0YQcCAgAAAAkDAAAADwMAAACW - DQAAAk1TRnQBSQFMAwEBAAEgAQIBIAECAR8BAAEcAQAE/wEZAQAI/wFCAU0BNgcAATYDAAEoAwABfAMA + DQAAAk1TRnQBSQFMAwEBAAEoAQIBKAECAR8BAAEcAQAE/wEZAQAI/wFCAU0BNgcAATYDAAEoAwABfAMA ARwDAAEBAQABGAUAAbABKBIAXf//ABgABv8B6QHWAccB6QHWAccB6QHWAccB6QHWAccB6QHWAccB6QHW AccB6QHWAccB6QHWAccB6QHWAccB6QHWAccB6QHWAccB6QHWAccB6QHWAccB6QHWAccB6QHWAccB6QHW AccB6QHWAccB6QHWAccB6QHWAccB6QHWAccB6QHWAccB6QHWAccB6QHWAccB6QHWAccB6QHWAccB6AHX diff --git a/Halovision/WaveProvider.cs b/Halovision/WaveProvider.cs new file mode 100644 index 0000000..2bf1f2d --- /dev/null +++ b/Halovision/WaveProvider.cs @@ -0,0 +1,117 @@ +using NAudio.Wave; +using System; + +namespace lucidcode.LucidScribe.Plugin.Halovision +{ + internal class WaveProvider : ISampleProvider + { + private readonly float[] waveTable; + private double phase; + private double currentPhaseStep; + private double targetPhaseStep; + private double frequency; + private double phaseStepDelta; + private bool seekFreq; + private Random random = new Random(); + + public WaveProvider(WaveType waveType) + { + int sampleRate = 44100; + WaveFormat = WaveFormat.CreateIeeeFloatWaveFormat(sampleRate, 1); + waveTable = new float[sampleRate]; + double[] pinkNoiseBuffer = new double[7]; + for (int index = 0; index < sampleRate; ++index) + { + switch (waveType) + { + case WaveType.Sin: + waveTable[index] = (float)Math.Sin(2 * Math.PI * index / sampleRate); + break; + case WaveType.Square: + waveTable[index] = ((float)Math.Sin(2 * Math.PI * index / sampleRate)) > 0 ? 1 : -1; + break; + case WaveType.Triangle: + waveTable[index] = (float)(Math.Asin(Math.Sin((2 * Math.PI * index / sampleRate))) * (2.0 / Math.PI)); + break; + case WaveType.SawTooth: + waveTable[index] = (float)index / sampleRate; + break; + + case WaveType.Sweep: + waveTable[index] = (float)(Math.Sin(2 * Math.PI * (double)index / sampleRate) + + Math.Sin(4 * Math.PI * (double)index / sampleRate) + + Math.Sin(6 * Math.PI * (double)index / sampleRate) + + Math.Sin(8 * Math.PI * (double)index / sampleRate)); + break; + + case WaveType.Pink: + double white = 2 * random.NextDouble() - 1; + pinkNoiseBuffer[0] = 0.99886 * pinkNoiseBuffer[0] + white * 0.0555179; + pinkNoiseBuffer[1] = 0.99332 * pinkNoiseBuffer[1] + white * 0.0750759; + pinkNoiseBuffer[2] = 0.96900 * pinkNoiseBuffer[2] + white * 0.1538520; + pinkNoiseBuffer[3] = 0.86650 * pinkNoiseBuffer[3] + white * 0.3104856; + pinkNoiseBuffer[4] = 0.55000 * pinkNoiseBuffer[4] + white * 0.5329522; + pinkNoiseBuffer[5] = -0.7616 * pinkNoiseBuffer[5] - white * 0.0168980; + double pink = pinkNoiseBuffer[0] + pinkNoiseBuffer[1] + pinkNoiseBuffer[2] + pinkNoiseBuffer[3] + pinkNoiseBuffer[4] + pinkNoiseBuffer[5] + pinkNoiseBuffer[6] + white * 0.5362; + pinkNoiseBuffer[6] = white * 0.115926; + waveTable[index] = (float)(pink / 5); + break; + default: + waveTable[index] = 0; + break; + } + } + + Frequency = 1000f; + Volume = 0.5f; + PortamentoTime = 0.02; + } + + public double PortamentoTime { get; set; } + + public double Frequency + { + get + { + return frequency; + } + set + { + frequency = value; + seekFreq = true; + } + } + + public float Volume { get; set; } + + public WaveFormat WaveFormat { get; private set; } + + public int Read(float[] buffer, int offset, int count) + { + if (seekFreq) + { + targetPhaseStep = waveTable.Length * (frequency / WaveFormat.SampleRate); + phaseStepDelta = (targetPhaseStep - currentPhaseStep) / (WaveFormat.SampleRate * PortamentoTime); + seekFreq = false; + } + var vol = Volume; + for (int n = 0; n < count; ++n) + { + int waveTableIndex = (int)phase % waveTable.Length; + buffer[n + offset] = waveTable[waveTableIndex] * vol; + phase += currentPhaseStep; + if (phase > waveTable.Length) + phase -= waveTable.Length; + if (currentPhaseStep != targetPhaseStep) + { + currentPhaseStep += phaseStepDelta; + if (phaseStepDelta > 0.0 && currentPhaseStep > targetPhaseStep) + currentPhaseStep = targetPhaseStep; + else if (phaseStepDelta < 0.0 && currentPhaseStep < targetPhaseStep) + currentPhaseStep = targetPhaseStep; + } + } + return count; + } + } +} diff --git a/Halovision/WaveType.cs b/Halovision/WaveType.cs new file mode 100644 index 0000000..b2efe0b --- /dev/null +++ b/Halovision/WaveType.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace lucidcode.LucidScribe.Plugin.Halovision +{ + public enum WaveType + { + Sin = 0, + SawTooth = 1, + Square = 2, + Triangle = 3, + Sweep = 4, + Pink = 5 + } +} diff --git a/Installer/Lucid Scribe Halovision.vdproj b/Installer/Lucid Scribe Halovision.vdproj index 1fcfb01..2b3cfcb 100644 --- a/Installer/Lucid Scribe Halovision.vdproj +++ b/Installer/Lucid Scribe Halovision.vdproj @@ -3056,7 +3056,7 @@ { "Name" = "8:.NET Framework" "Message" = "8:[VSDNETMSG]" - "FrameworkVersion" = "8:3.5.30729 " + "FrameworkVersion" = "8:3.5.30729 " "AllowLaterVersions" = "11:FALSE" "InstallUrl" = "8:http://go.microsoft.com/fwlink/?LinkId=76617" } @@ -11352,15 +11352,15 @@ { "Name" = "8:Microsoft Visual Studio" "ProductName" = "8:Lucid Scribe Halovision" - "ProductCode" = "8:{0D9CC507-CE76-4FD9-99D0-774734E8C965}" - "PackageCode" = "8:{B2C9CCE1-2D02-44FC-96D5-D34D5898A95A}" + "ProductCode" = "8:{ABC56BE3-FB17-44DB-AD5C-401721AD5623}" + "PackageCode" = "8:{A90843F2-091D-4DAA-B5D3-814734A16D07}" "UpgradeCode" = "8:{CAAB2187-AD23-435C-A3DB-568744247625}" "AspNetVersion" = "8:4.0.30319.0" "RestartWWWService" = "11:FALSE" "RemovePreviousVersions" = "11:TRUE" "DetectNewerInstalledVersion" = "11:TRUE" "InstallAllUsers" = "11:TRUE" - "ProductVersion" = "8:1.2.5" + "ProductVersion" = "8:1.2.6" "Manufacturer" = "8:lucidcode" "ARPHELPTELEPHONE" = "8:" "ARPHELPLINK" = "8:http://www.lucidcode.com/Contact"