From 0eb6209eda70cdfbaa92da1a9061d475d4adaa7e Mon Sep 17 00:00:00 2001 From: Serge <5920850+seerge@users.noreply.github.com> Date: Thu, 18 May 2023 17:42:19 +0200 Subject: [PATCH] Matrix audio visualizer --- app/AnimeMatrix/AniMatrix.cs | 253 +++++++++++++++++++++++++++ app/AnimeMatrix/AnimeMatrixDevice.cs | 48 +++++ app/Fans.cs | 8 +- app/GHelper.csproj | 2 + app/Program.cs | 4 +- app/Properties/Strings.Designer.cs | 11 +- app/Properties/Strings.resx | 3 + app/Settings.Designer.cs | 2 +- app/Settings.cs | 158 ++--------------- 9 files changed, 339 insertions(+), 150 deletions(-) create mode 100644 app/AnimeMatrix/AniMatrix.cs diff --git a/app/AnimeMatrix/AniMatrix.cs b/app/AnimeMatrix/AniMatrix.cs new file mode 100644 index 00000000..8a821ea9 --- /dev/null +++ b/app/AnimeMatrix/AniMatrix.cs @@ -0,0 +1,253 @@ +using NAudio.CoreAudioApi; +using NAudio.Wave; +using Starlight.AnimeMatrix; +using System.Diagnostics; +using System.Drawing.Imaging; +using System.Timers; + +namespace GHelper.AnimeMatrix +{ + + public class AniMatrix + { + static System.Timers.Timer matrixTimer = default!; + static AnimeMatrixDevice mat; + + static double[] AudioValues; + static WasapiCapture AudioDevice; + + public static bool IsValid => mat != null; + + public AniMatrix() + { + try + { + mat = new AnimeMatrixDevice(); + matrixTimer = new System.Timers.Timer(100); + matrixTimer.Elapsed += MatrixTimer_Elapsed; + } + catch + { + mat.Dispose(); + mat = null; + } + } + + public void SetMatrix() + { + + if (!IsValid) return; + + int brightness = Program.config.getConfig("matrix_brightness"); + int running = Program.config.getConfig("matrix_running"); + + bool auto = Program.config.getConfig("matrix_auto") == 1; + + if (brightness < 0) brightness = 0; + if (running < 0) running = 0; + + BuiltInAnimation animation = new BuiltInAnimation( + (BuiltInAnimation.Running)running, + BuiltInAnimation.Sleeping.Starfield, + BuiltInAnimation.Shutdown.SeeYa, + BuiltInAnimation.Startup.StaticEmergence + ); + + StopMatrixTimer(); + StopMatrixAudio(); + + mat.SetProvider(); + + if (brightness == 0 || (auto && SystemInformation.PowerStatus.PowerLineStatus != PowerLineStatus.Online)) + { + mat.SetDisplayState(false); + Logger.WriteLine("Matrix Off"); + } + else + { + mat.SetDisplayState(true); + mat.SetBrightness((BrightnessMode)brightness); + + switch (running) + { + case 2: + SetMatrixPicture(Program.config.getConfigString("matrix_picture")); + break; + case 3: + StartMatrixTimer(1000); + Logger.WriteLine("Matrix Clock"); + break; + case 4: + SetMatrixAudio(); + break; + default: + mat.SetBuiltInAnimation(true, animation); + Logger.WriteLine("Matrix builtin " + animation.AsByte); + break; + + } + + //mat.SetBrightness((BrightnessMode)brightness); + } + + } + private static void StartMatrixTimer(int interval = 100) + { + matrixTimer.Interval = interval; + matrixTimer.Enabled = true; + } + + private static void StopMatrixTimer() + { + matrixTimer.Enabled = false; + } + + + private static void MatrixTimer_Elapsed(object? sender, ElapsedEventArgs e) + { + if (!IsValid) return; + + switch (Program.config.getConfig("matrix_running")) + { + case 2: + mat.PresentNextFrame(); + break; + case 3: + mat.PresentClock(); + break; + } + + } + + void StopMatrixAudio() + { + if (AudioDevice is not null) + { + try + { + AudioDevice.StopRecording(); + AudioDevice.Dispose(); + } + catch (Exception ex) + { + Logger.WriteLine(ex.ToString()); + } + } + } + + void SetMatrixAudio() + { + if (!IsValid) return; + + mat.SetBuiltInAnimation(false); + StopMatrixTimer(); + StopMatrixAudio(); + + try + { + using (var enumerator = new MMDeviceEnumerator()) + using (MMDevice device = enumerator.GetDefaultAudioEndpoint(DataFlow.Render, Role.Console)) + { + AudioDevice = new WasapiLoopbackCapture(device); + WaveFormat fmt = AudioDevice.WaveFormat; + + AudioValues = new double[fmt.SampleRate / 1000]; + AudioDevice.DataAvailable += WaveIn_DataAvailable; + AudioDevice.StartRecording(); + Logger.WriteLine("Matrix Audio"); + } + } + catch (Exception ex) + { + Logger.WriteLine(ex.ToString()); + } + + } + + private static void WaveIn_DataAvailable(object? sender, WaveInEventArgs e) + { + int bytesPerSamplePerChannel = AudioDevice.WaveFormat.BitsPerSample / 8; + int bytesPerSample = bytesPerSamplePerChannel * AudioDevice.WaveFormat.Channels; + int bufferSampleCount = e.Buffer.Length / bytesPerSample; + + if (bufferSampleCount >= AudioValues.Length) + { + bufferSampleCount = AudioValues.Length; + } + + if (bytesPerSamplePerChannel == 2 && AudioDevice.WaveFormat.Encoding == WaveFormatEncoding.Pcm) + { + for (int i = 0; i < bufferSampleCount; i++) + AudioValues[i] = BitConverter.ToInt16(e.Buffer, i * bytesPerSample); + } + else if (bytesPerSamplePerChannel == 4 && AudioDevice.WaveFormat.Encoding == WaveFormatEncoding.Pcm) + { + for (int i = 0; i < bufferSampleCount; i++) + AudioValues[i] = BitConverter.ToInt32(e.Buffer, i * bytesPerSample); + } + else if (bytesPerSamplePerChannel == 4 && AudioDevice.WaveFormat.Encoding == WaveFormatEncoding.IeeeFloat) + { + for (int i = 0; i < bufferSampleCount; i++) + AudioValues[i] = BitConverter.ToSingle(e.Buffer, i * bytesPerSample); + } + + double[] paddedAudio = FftSharp.Pad.ZeroPad(AudioValues); + double[] fftMag = FftSharp.Transform.FFTmagnitude(paddedAudio); + + mat.PresentAudio(fftMag); + } + + + public void SetMatrixPicture(string fileName) + { + + if (!IsValid) return; + StopMatrixTimer(); + + Image image; + + try + { + using (var fs = new FileStream(fileName, FileMode.Open)) + { + var ms = new MemoryStream(); + fs.CopyTo(ms); + ms.Position = 0; + image = Image.FromStream(ms); + } + } + catch + { + Debug.WriteLine("Error loading picture"); + return; + } + + mat.SetBuiltInAnimation(false); + mat.ClearFrames(); + + FrameDimension dimension = new FrameDimension(image.FrameDimensionsList[0]); + int frameCount = image.GetFrameCount(dimension); + + if (frameCount > 1) + { + for (int i = 0; i < frameCount; i++) + { + image.SelectActiveFrame(dimension, i); + mat.GenerateFrame(image); + mat.AddFrame(); + } + + StartMatrixTimer(); + Logger.WriteLine("Matrix GIF " + fileName); + } + else + { + mat.GenerateFrame(image); + mat.Present(); + Logger.WriteLine("Matrix " + fileName); + } + } + + + } +} diff --git a/app/AnimeMatrix/AnimeMatrixDevice.cs b/app/AnimeMatrix/AnimeMatrixDevice.cs index dbf19944..bcd2cca0 100644 --- a/app/AnimeMatrix/AnimeMatrixDevice.cs +++ b/app/AnimeMatrix/AnimeMatrixDevice.cs @@ -1,6 +1,7 @@ // Source thanks to https://github.com/vddCore/Starlight with some adjustments from me using Starlight.Communication; +using System.Diagnostics; using System.Drawing.Drawing2D; using System.Drawing.Text; using System.Globalization; @@ -98,6 +99,9 @@ namespace Starlight.AnimeMatrix private static AnimeType _model = AnimeType.GA402; + private static long lastPresent; + private static List maxes = new List(); + public AnimeMatrixDevice() : base(0x0B05, 0x193B, 640) { @@ -357,6 +361,50 @@ namespace Starlight.AnimeMatrix PresentTextDiagonal(time); } + private void DrawBar(int pos, double h) + { + int dx = pos*2; + int dy = 20; + + byte color; + + for (int y = 0; y < h - (h % 2); y++) + for (int x = 0; x < 2 - (y % 2); x++) + { + //color = (byte)(Math.Min(1,(h - y - 2)*2) * 255); + SetLedPlanar(x + dx, dy + y, (byte)(h* 255 / 30) ); + SetLedPlanar(x + dx, dy - y, 255); + } + } + + public void PresentAudio(double[] audio) + { + + if (Math.Abs(DateTimeOffset.Now.ToUnixTimeMilliseconds() - lastPresent) < 70) return; + lastPresent = DateTimeOffset.Now.ToUnixTimeMilliseconds(); + + Clear(); + + int size = 20; + double[] bars = new double[size]; + double max = 2, maxAverage; + + for (int i = 0; i < size; i++) + { + bars[i] = Math.Sqrt(audio[i] * 10000); + if (bars[i] > max) max = bars[i]; + } + + maxes.Add(max); + if (maxes.Count > 20) maxes.RemoveAt(0); + maxAverage = maxes.Average(); + + for (int i = 0; i < size; i++) DrawBar(20 - i, bars[i] / maxAverage * 20); + + Present(); + } + + public void PresentText(string text1, string text2 = "") { using (Bitmap bmp = new Bitmap(MaxColumns * 3, MaxRows)) diff --git a/app/Fans.cs b/app/Fans.cs index fd6f3e26..522361f0 100644 --- a/app/Fans.cs +++ b/app/Fans.cs @@ -166,7 +166,13 @@ namespace GHelper memory = current_memory; } - labelGPU.Text = nvControl.FullName; + try + { + labelGPU.Text = nvControl.FullName; + } catch + { + + } //} diff --git a/app/GHelper.csproj b/app/GHelper.csproj index d4321764..02bc85bf 100644 --- a/app/GHelper.csproj +++ b/app/GHelper.csproj @@ -59,8 +59,10 @@ + + diff --git a/app/Program.cs b/app/Program.cs index acdfd961..e2a93f3a 100644 --- a/app/Program.cs +++ b/app/Program.cs @@ -42,7 +42,7 @@ namespace GHelper Thread.CurrentThread.CurrentUICulture = CultureInfo.CurrentUICulture; Debug.WriteLine(CultureInfo.CurrentUICulture); - //Thread.CurrentThread.CurrentUICulture = CultureInfo.CreateSpecificCulture("es"); + //Thread.CurrentThread.CurrentUICulture = CultureInfo.CreateSpecificCulture("de"); CheckProcesses(); @@ -159,7 +159,7 @@ namespace GHelper } settingsForm.AutoKeyboard(); - settingsForm.SetMatrix(); + settingsForm.matrix.SetMatrix(); } private static void SystemEvents_PowerModeChanged(object sender, PowerModeChangedEventArgs e) diff --git a/app/Properties/Strings.Designer.cs b/app/Properties/Strings.Designer.cs index f407ae2a..d20f1153 100644 --- a/app/Properties/Strings.Designer.cs +++ b/app/Properties/Strings.Designer.cs @@ -520,7 +520,7 @@ namespace GHelper.Properties { } /// - /// Looks up a localized string similar to Core Clock. + /// Looks up a localized string similar to Core Clock Offset. /// internal static string GPUCoreClockOffset { get { @@ -672,6 +672,15 @@ namespace GHelper.Properties { } } + /// + /// Looks up a localized string similar to Audio Visualizer. + /// + internal static string MatrixAudio { + get { + return ResourceManager.GetString("MatrixAudio", resourceCulture); + } + } + /// /// Looks up a localized string similar to Binary Banner. /// diff --git a/app/Properties/Strings.resx b/app/Properties/Strings.resx index 6c735d5d..d2d4496e 100644 --- a/app/Properties/Strings.resx +++ b/app/Properties/Strings.resx @@ -321,6 +321,9 @@ Logo + + Audio Visualizer + Binary Banner diff --git a/app/Settings.Designer.cs b/app/Settings.Designer.cs index 612cde2f..49363f73 100644 --- a/app/Settings.Designer.cs +++ b/app/Settings.Designer.cs @@ -185,7 +185,7 @@ namespace GHelper comboMatrixRunning.Font = new Font("Segoe UI", 9F, FontStyle.Regular, GraphicsUnit.Point); comboMatrixRunning.FormattingEnabled = true; comboMatrixRunning.ItemHeight = 32; - comboMatrixRunning.Items.AddRange(new object[] { Properties.Strings.MatrixBanner, Properties.Strings.MatrixLogo, Properties.Strings.MatrixPicture, Properties.Strings.MatrixClock }); + comboMatrixRunning.Items.AddRange(new object[] { Properties.Strings.MatrixBanner, Properties.Strings.MatrixLogo, Properties.Strings.MatrixPicture, Properties.Strings.MatrixClock, Properties.Strings.MatrixAudio }); comboMatrixRunning.Location = new Point(261, 10); comboMatrixRunning.Margin = new Padding(4, 10, 4, 8); comboMatrixRunning.Name = "comboMatrixRunning"; diff --git a/app/Settings.cs b/app/Settings.cs index fb857464..d410570f 100644 --- a/app/Settings.cs +++ b/app/Settings.cs @@ -1,7 +1,11 @@ using CustomControls; +using GHelper.AnimeMatrix; using GHelper.Gpu; +using NAudio.CoreAudioApi; +using NAudio.Wave; using Starlight.AnimeMatrix; using System.Diagnostics; +using System.Drawing.Drawing2D; using System.Drawing.Imaging; using System.Net; using System.Reflection; @@ -21,16 +25,15 @@ namespace GHelper public static System.Timers.Timer aTimer = default!; public static Point trayPoint; - static System.Timers.Timer matrixTimer = default!; - public string versionUrl = "http://github.com/seerge/g-helper/releases"; public string perfName = "Balanced"; + public AniMatrix matrix; + public Fans fans; public Extra keyb; - static AnimeMatrixDevice mat; static long lastRefresh; private bool customFans = false; @@ -504,84 +507,9 @@ namespace GHelper if (sender is null) return; CheckBox check = (CheckBox)sender; Program.config.setConfig("matrix_auto", check.Checked ? 1 : 0); + matrix?.SetMatrix(); } - private static void StartMatrixTimer(int interval = 100) - { - matrixTimer.Interval = interval; - matrixTimer.Enabled = true; - } - - private static void StopMatrixTimer() - { - matrixTimer.Enabled = false; - } - - private static void MatrixTimer_Elapsed(object? sender, ElapsedEventArgs e) - { - if (mat is null) return; - - switch (Program.config.getConfig("matrix_running")) - { - case 2: - mat.PresentNextFrame(); - break; - case 3: - mat.PresentClock(); - break; - } - - } - - void SetMatrixPicture(string fileName) - { - - if (mat is null) return; - StopMatrixTimer(); - - Image image; - - try - { - using (var fs = new FileStream(fileName, FileMode.Open)) - { - var ms = new MemoryStream(); - fs.CopyTo(ms); - ms.Position = 0; - image = Image.FromStream(ms); - } - } - catch - { - Debug.WriteLine("Error loading picture"); - return; - } - - mat.SetBuiltInAnimation(false); - mat.ClearFrames(); - - FrameDimension dimension = new FrameDimension(image.FrameDimensionsList[0]); - int frameCount = image.GetFrameCount(dimension); - - if (frameCount > 1) - { - for (int i = 0; i < frameCount; i++) - { - image.SelectActiveFrame(dimension, i); - mat.GenerateFrame(image); - mat.AddFrame(); - } - - StartMatrixTimer(); - Logger.WriteLine("Matrix GIF " + fileName); - } - else - { - mat.GenerateFrame(image); - mat.Present(); - Logger.WriteLine("Matrix " + fileName); - } - } private void ButtonMatrix_Click(object? sender, EventArgs e) @@ -608,7 +536,7 @@ namespace GHelper Program.config.setConfig("matrix_picture", fileName); Program.config.setConfig("matrix_running", 2); - SetMatrixPicture(fileName); + matrix?.SetMatrixPicture(fileName); BeginInvoke(delegate { comboMatrixRunning.SelectedIndex = 2; @@ -621,72 +549,16 @@ namespace GHelper private void ComboMatrixRunning_SelectedValueChanged(object? sender, EventArgs e) { Program.config.setConfig("matrix_running", comboMatrixRunning.SelectedIndex); - SetMatrix(); + matrix?.SetMatrix(); } private void ComboMatrix_SelectedValueChanged(object? sender, EventArgs e) { Program.config.setConfig("matrix_brightness", comboMatrix.SelectedIndex); - SetMatrix(); + matrix?.SetMatrix(); } - public void SetMatrix() - { - - if (mat is null) return; - - int brightness = Program.config.getConfig("matrix_brightness"); - int running = Program.config.getConfig("matrix_running"); - bool auto = Program.config.getConfig("matrix_auto") == 1; - - if (brightness < 0) brightness = 0; - if (running < 0) running = 0; - - BuiltInAnimation animation = new BuiltInAnimation( - (BuiltInAnimation.Running)running, - BuiltInAnimation.Sleeping.Starfield, - BuiltInAnimation.Shutdown.SeeYa, - BuiltInAnimation.Startup.StaticEmergence - ); - - StopMatrixTimer(); - - mat.SetProvider(); - - if (brightness == 0 || (auto && SystemInformation.PowerStatus.PowerLineStatus != PowerLineStatus.Online)) - { - mat.SetDisplayState(false); - Logger.WriteLine("Matrix Off"); - } - else - { - mat.SetDisplayState(true); - mat.SetBrightness((BrightnessMode)brightness); - - switch (running) - { - case 2: - SetMatrixPicture(Program.config.getConfigString("matrix_picture")); - break; - case 3: - mat.SetBuiltInAnimation(false); - StartMatrixTimer(1000); - Logger.WriteLine("Matrix Clock"); - break; - default: - mat.SetBuiltInAnimation(true, animation); - Logger.WriteLine("Matrix builtin " + animation.AsByte); - break; - - } - - //mat.SetBrightness((BrightnessMode)brightness); - } - - } - - private void LabelCPUFan_Click(object? sender, EventArgs e) { @@ -785,13 +657,9 @@ namespace GHelper public void InitMatrix() { - try - { - mat = new AnimeMatrixDevice(); - matrixTimer = new System.Timers.Timer(100); - matrixTimer.Elapsed += MatrixTimer_Elapsed; - } - catch + matrix = new AniMatrix(); + + if (matrix is null) { panelMatrix.Visible = false; return;