Matrix audio visualizer

This commit is contained in:
Serge
2023-05-18 17:42:19 +02:00
parent c66c8e9030
commit 0eb6209eda
9 changed files with 339 additions and 150 deletions

View File

@@ -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);
}
}
}
}

View File

@@ -1,6 +1,7 @@
// Source thanks to https://github.com/vddCore/Starlight with some adjustments from me // Source thanks to https://github.com/vddCore/Starlight with some adjustments from me
using Starlight.Communication; using Starlight.Communication;
using System.Diagnostics;
using System.Drawing.Drawing2D; using System.Drawing.Drawing2D;
using System.Drawing.Text; using System.Drawing.Text;
using System.Globalization; using System.Globalization;
@@ -98,6 +99,9 @@ namespace Starlight.AnimeMatrix
private static AnimeType _model = AnimeType.GA402; private static AnimeType _model = AnimeType.GA402;
private static long lastPresent;
private static List<double> maxes = new List<double>();
public AnimeMatrixDevice() public AnimeMatrixDevice()
: base(0x0B05, 0x193B, 640) : base(0x0B05, 0x193B, 640)
{ {
@@ -357,6 +361,50 @@ namespace Starlight.AnimeMatrix
PresentTextDiagonal(time); 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 = "") public void PresentText(string text1, string text2 = "")
{ {
using (Bitmap bmp = new Bitmap(MaxColumns * 3, MaxRows)) using (Bitmap bmp = new Bitmap(MaxColumns * 3, MaxRows))

View File

@@ -166,7 +166,13 @@ namespace GHelper
memory = current_memory; memory = current_memory;
} }
labelGPU.Text = nvControl.FullName; try
{
labelGPU.Text = nvControl.FullName;
} catch
{
}
//} //}

View File

@@ -59,8 +59,10 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="FftSharp" Version="2.0.0" />
<PackageReference Include="hidlibrary" Version="3.3.40" /> <PackageReference Include="hidlibrary" Version="3.3.40" />
<PackageReference Include="HidSharpCore" Version="1.2.1.1" /> <PackageReference Include="HidSharpCore" Version="1.2.1.1" />
<PackageReference Include="NAudio" Version="2.1.0" />
<PackageReference Include="System.Management" Version="7.0.1" /> <PackageReference Include="System.Management" Version="7.0.1" />
<PackageReference Include="TaskScheduler" Version="2.10.1" /> <PackageReference Include="TaskScheduler" Version="2.10.1" />
<PackageReference Include="WinForms.DataVisualization" Version="1.8.0" /> <PackageReference Include="WinForms.DataVisualization" Version="1.8.0" />

View File

@@ -42,7 +42,7 @@ namespace GHelper
Thread.CurrentThread.CurrentUICulture = CultureInfo.CurrentUICulture; Thread.CurrentThread.CurrentUICulture = CultureInfo.CurrentUICulture;
Debug.WriteLine(CultureInfo.CurrentUICulture); Debug.WriteLine(CultureInfo.CurrentUICulture);
//Thread.CurrentThread.CurrentUICulture = CultureInfo.CreateSpecificCulture("es"); //Thread.CurrentThread.CurrentUICulture = CultureInfo.CreateSpecificCulture("de");
CheckProcesses(); CheckProcesses();
@@ -159,7 +159,7 @@ namespace GHelper
} }
settingsForm.AutoKeyboard(); settingsForm.AutoKeyboard();
settingsForm.SetMatrix(); settingsForm.matrix.SetMatrix();
} }
private static void SystemEvents_PowerModeChanged(object sender, PowerModeChangedEventArgs e) private static void SystemEvents_PowerModeChanged(object sender, PowerModeChangedEventArgs e)

View File

@@ -520,7 +520,7 @@ namespace GHelper.Properties {
} }
/// <summary> /// <summary>
/// Looks up a localized string similar to Core Clock. /// Looks up a localized string similar to Core Clock Offset.
/// </summary> /// </summary>
internal static string GPUCoreClockOffset { internal static string GPUCoreClockOffset {
get { get {
@@ -672,6 +672,15 @@ namespace GHelper.Properties {
} }
} }
/// <summary>
/// Looks up a localized string similar to Audio Visualizer.
/// </summary>
internal static string MatrixAudio {
get {
return ResourceManager.GetString("MatrixAudio", resourceCulture);
}
}
/// <summary> /// <summary>
/// Looks up a localized string similar to Binary Banner. /// Looks up a localized string similar to Binary Banner.
/// </summary> /// </summary>

View File

@@ -321,6 +321,9 @@
<data name="Logo" xml:space="preserve"> <data name="Logo" xml:space="preserve">
<value>Logo</value> <value>Logo</value>
</data> </data>
<data name="MatrixAudio" xml:space="preserve">
<value>Audio Visualizer</value>
</data>
<data name="MatrixBanner" xml:space="preserve"> <data name="MatrixBanner" xml:space="preserve">
<value>Binary Banner</value> <value>Binary Banner</value>
</data> </data>

View File

@@ -185,7 +185,7 @@ namespace GHelper
comboMatrixRunning.Font = new Font("Segoe UI", 9F, FontStyle.Regular, GraphicsUnit.Point); comboMatrixRunning.Font = new Font("Segoe UI", 9F, FontStyle.Regular, GraphicsUnit.Point);
comboMatrixRunning.FormattingEnabled = true; comboMatrixRunning.FormattingEnabled = true;
comboMatrixRunning.ItemHeight = 32; 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.Location = new Point(261, 10);
comboMatrixRunning.Margin = new Padding(4, 10, 4, 8); comboMatrixRunning.Margin = new Padding(4, 10, 4, 8);
comboMatrixRunning.Name = "comboMatrixRunning"; comboMatrixRunning.Name = "comboMatrixRunning";

View File

@@ -1,7 +1,11 @@
using CustomControls; using CustomControls;
using GHelper.AnimeMatrix;
using GHelper.Gpu; using GHelper.Gpu;
using NAudio.CoreAudioApi;
using NAudio.Wave;
using Starlight.AnimeMatrix; using Starlight.AnimeMatrix;
using System.Diagnostics; using System.Diagnostics;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging; using System.Drawing.Imaging;
using System.Net; using System.Net;
using System.Reflection; using System.Reflection;
@@ -21,16 +25,15 @@ namespace GHelper
public static System.Timers.Timer aTimer = default!; public static System.Timers.Timer aTimer = default!;
public static Point trayPoint; public static Point trayPoint;
static System.Timers.Timer matrixTimer = default!;
public string versionUrl = "http://github.com/seerge/g-helper/releases"; public string versionUrl = "http://github.com/seerge/g-helper/releases";
public string perfName = "Balanced"; public string perfName = "Balanced";
public AniMatrix matrix;
public Fans fans; public Fans fans;
public Extra keyb; public Extra keyb;
static AnimeMatrixDevice mat;
static long lastRefresh; static long lastRefresh;
private bool customFans = false; private bool customFans = false;
@@ -504,84 +507,9 @@ namespace GHelper
if (sender is null) return; if (sender is null) return;
CheckBox check = (CheckBox)sender; CheckBox check = (CheckBox)sender;
Program.config.setConfig("matrix_auto", check.Checked ? 1 : 0); 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) private void ButtonMatrix_Click(object? sender, EventArgs e)
@@ -608,7 +536,7 @@ namespace GHelper
Program.config.setConfig("matrix_picture", fileName); Program.config.setConfig("matrix_picture", fileName);
Program.config.setConfig("matrix_running", 2); Program.config.setConfig("matrix_running", 2);
SetMatrixPicture(fileName); matrix?.SetMatrixPicture(fileName);
BeginInvoke(delegate BeginInvoke(delegate
{ {
comboMatrixRunning.SelectedIndex = 2; comboMatrixRunning.SelectedIndex = 2;
@@ -621,72 +549,16 @@ namespace GHelper
private void ComboMatrixRunning_SelectedValueChanged(object? sender, EventArgs e) private void ComboMatrixRunning_SelectedValueChanged(object? sender, EventArgs e)
{ {
Program.config.setConfig("matrix_running", comboMatrixRunning.SelectedIndex); Program.config.setConfig("matrix_running", comboMatrixRunning.SelectedIndex);
SetMatrix(); matrix?.SetMatrix();
} }
private void ComboMatrix_SelectedValueChanged(object? sender, EventArgs e) private void ComboMatrix_SelectedValueChanged(object? sender, EventArgs e)
{ {
Program.config.setConfig("matrix_brightness", comboMatrix.SelectedIndex); 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) private void LabelCPUFan_Click(object? sender, EventArgs e)
{ {
@@ -785,13 +657,9 @@ namespace GHelper
public void InitMatrix() public void InitMatrix()
{ {
try matrix = new AniMatrix();
{
mat = new AnimeMatrixDevice(); if (matrix is null)
matrixTimer = new System.Timers.Timer(100);
matrixTimer.Elapsed += MatrixTimer_Elapsed;
}
catch
{ {
panelMatrix.Visible = false; panelMatrix.Visible = false;
return; return;