diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml
index 2966d92..8a2d2bd 100644
--- a/.github/workflows/ci.yaml
+++ b/.github/workflows/ci.yaml
@@ -17,31 +17,24 @@ jobs:
runs-on: windows-latest
steps:
- name: 🛒 Checkout
- uses: actions/checkout@v2
-
- - name: ✨ Setup .NET 6
- uses: actions/setup-dotnet@v1
+ uses: actions/checkout@v4
+ - name: ✨ Setup .NET 8
+ uses: actions/setup-dotnet@v4
with:
- dotnet-version: "6.0.x"
-
+ dotnet-version: "8.0.x"
- name: 🚚 Restore
run: dotnet restore src
-
- name: 🛠️ Build
- run: dotnet build src --configuration Release --no-restore
-
+ run: dotnet build src --configuration Release
- name: 🧪 Test
- run: dotnet test src --configuration Release --no-build
-
+ run: dotnet test src --configuration Release
- name: 📦 Pack
- run: dotnet pack src --configuration Release --no-build
-
+ run: dotnet pack src --configuration Release
- name: 🔑 Configure Secrets
if: github.event_name == 'release'
uses: nuget/setup-nuget@v1
with:
nuget-api-key: ${{ secrets.NUGET_API_KEY }}
-
- - name: 🚀 Deploy Package
+ - name: 🚀 Deploy NuGet Package
if: github.event_name == 'release'
run: nuget push "src\Spectrogram\bin\Release\*.nupkg" -SkipDuplicate -Source https://api.nuget.org/v3/index.json
diff --git a/src/Spectrogram.MicrophoneDemo/App.config b/src/Spectrogram.MicrophoneDemo/App.config
deleted file mode 100644
index 56efbc7..0000000
--- a/src/Spectrogram.MicrophoneDemo/App.config
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-
-
-
-
\ No newline at end of file
diff --git a/src/Spectrogram.MicrophoneDemo/FormMicrophone.cs b/src/Spectrogram.MicrophoneDemo/FormMicrophone.cs
index e6b4964..975c5b1 100644
--- a/src/Spectrogram.MicrophoneDemo/FormMicrophone.cs
+++ b/src/Spectrogram.MicrophoneDemo/FormMicrophone.cs
@@ -9,6 +9,7 @@
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
+using SkiaSharp.Views.Desktop;
namespace Spectrogram.MicrophoneDemo
{
@@ -64,7 +65,7 @@ private void StartListening()
pbSpectrogram.Height = spec.Height;
pbScaleVert.Image?.Dispose();
- pbScaleVert.Image = spec.GetVerticalScale(pbScaleVert.Width);
+ pbScaleVert.Image = spec.GetVerticalScale(pbScaleVert.Width).ToBitmap();
pbScaleVert.Height = spec.Height;
}
@@ -85,7 +86,7 @@ private void timer1_Tick(object sender, EventArgs e)
using (var gfx = Graphics.FromImage(bmpSpec))
using (var pen = new Pen(Color.White))
{
- gfx.DrawImage(bmpSpecIndexed, 0, 0);
+ gfx.DrawImage(bmpSpecIndexed.ToBitmap(), 0, 0);
if (cbRoll.Checked)
{
gfx.DrawLine(pen, spec.NextColumnIndex, 0, spec.NextColumnIndex, pbSpectrogram.Height);
diff --git a/src/Spectrogram.MicrophoneDemo/Program.cs b/src/Spectrogram.MicrophoneDemo/Program.cs
index e1a34c4..821c47b 100644
--- a/src/Spectrogram.MicrophoneDemo/Program.cs
+++ b/src/Spectrogram.MicrophoneDemo/Program.cs
@@ -1,16 +1,11 @@
using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Threading.Tasks;
+using System.Runtime.Versioning;
using System.Windows.Forms;
namespace Spectrogram.MicrophoneDemo
{
static class Program
{
- ///
- /// The main entry point for the application.
- ///
[STAThread]
static void Main()
{
diff --git a/src/Spectrogram.MicrophoneDemo/Properties/AssemblyInfo.cs b/src/Spectrogram.MicrophoneDemo/Properties/AssemblyInfo.cs
deleted file mode 100644
index 5eaa0f3..0000000
--- a/src/Spectrogram.MicrophoneDemo/Properties/AssemblyInfo.cs
+++ /dev/null
@@ -1,36 +0,0 @@
-using System.Reflection;
-using System.Runtime.CompilerServices;
-using System.Runtime.InteropServices;
-
-// General Information about an assembly is controlled through the following
-// set of attributes. Change these attribute values to modify the information
-// associated with an assembly.
-[assembly: AssemblyTitle("Spectrogram.MicrophoneDemo")]
-[assembly: AssemblyDescription("")]
-[assembly: AssemblyConfiguration("")]
-[assembly: AssemblyCompany("")]
-[assembly: AssemblyProduct("Spectrogram.MicrophoneDemo")]
-[assembly: AssemblyCopyright("Copyright © 2020")]
-[assembly: AssemblyTrademark("")]
-[assembly: AssemblyCulture("")]
-
-// Setting ComVisible to false makes the types in this assembly not visible
-// to COM components. If you need to access a type in this assembly from
-// COM, set the ComVisible attribute to true on that type.
-[assembly: ComVisible(false)]
-
-// The following GUID is for the ID of the typelib if this project is exposed to COM
-[assembly: Guid("d51abc6a-53f4-4620-88a1-14ea1d779538")]
-
-// Version information for an assembly consists of the following four values:
-//
-// Major Version
-// Minor Version
-// Build Number
-// Revision
-//
-// 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.0.0.0")]
-[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/src/Spectrogram.MicrophoneDemo/Properties/Resources.Designer.cs b/src/Spectrogram.MicrophoneDemo/Properties/Resources.Designer.cs
deleted file mode 100644
index 1a71b5a..0000000
--- a/src/Spectrogram.MicrophoneDemo/Properties/Resources.Designer.cs
+++ /dev/null
@@ -1,71 +0,0 @@
-//------------------------------------------------------------------------------
-//
-// This code was generated by a tool.
-// Runtime Version:4.0.30319.42000
-//
-// Changes to this file may cause incorrect behavior and will be lost if
-// the code is regenerated.
-//
-//------------------------------------------------------------------------------
-
-namespace Spectrogram.MicrophoneDemo.Properties
-{
-
-
- ///
- /// A strongly-typed resource class, for looking up localized strings, etc.
- ///
- // This class was auto-generated by the StronglyTypedResourceBuilder
- // class via a tool like ResGen or Visual Studio.
- // To add or remove a member, edit your .ResX file then rerun ResGen
- // with the /str option, or rebuild your VS project.
- [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
- [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
- [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
- internal class Resources
- {
-
- private static global::System.Resources.ResourceManager resourceMan;
-
- private static global::System.Globalization.CultureInfo resourceCulture;
-
- [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
- internal Resources()
- {
- }
-
- ///
- /// Returns the cached ResourceManager instance used by this class.
- ///
- [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
- internal static global::System.Resources.ResourceManager ResourceManager
- {
- get
- {
- if ((resourceMan == null))
- {
- global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Spectrogram.MicrophoneDemo.Properties.Resources", typeof(Resources).Assembly);
- resourceMan = temp;
- }
- return resourceMan;
- }
- }
-
- ///
- /// Overrides the current thread's CurrentUICulture property for all
- /// resource lookups using this strongly typed resource class.
- ///
- [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
- internal static global::System.Globalization.CultureInfo Culture
- {
- get
- {
- return resourceCulture;
- }
- set
- {
- resourceCulture = value;
- }
- }
- }
-}
diff --git a/src/Spectrogram.MicrophoneDemo/Properties/Resources.resx b/src/Spectrogram.MicrophoneDemo/Properties/Resources.resx
deleted file mode 100644
index af7dbeb..0000000
--- a/src/Spectrogram.MicrophoneDemo/Properties/Resources.resx
+++ /dev/null
@@ -1,117 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- text/microsoft-resx
-
-
- 2.0
-
-
- System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
-
-
- System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
-
-
\ No newline at end of file
diff --git a/src/Spectrogram.MicrophoneDemo/Properties/Settings.Designer.cs b/src/Spectrogram.MicrophoneDemo/Properties/Settings.Designer.cs
deleted file mode 100644
index 37d8130..0000000
--- a/src/Spectrogram.MicrophoneDemo/Properties/Settings.Designer.cs
+++ /dev/null
@@ -1,30 +0,0 @@
-//------------------------------------------------------------------------------
-//
-// This code was generated by a tool.
-// Runtime Version:4.0.30319.42000
-//
-// Changes to this file may cause incorrect behavior and will be lost if
-// the code is regenerated.
-//
-//------------------------------------------------------------------------------
-
-namespace Spectrogram.MicrophoneDemo.Properties
-{
-
-
- [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
- [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")]
- internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase
- {
-
- private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
-
- public static Settings Default
- {
- get
- {
- return defaultInstance;
- }
- }
- }
-}
diff --git a/src/Spectrogram.MicrophoneDemo/Properties/Settings.settings b/src/Spectrogram.MicrophoneDemo/Properties/Settings.settings
deleted file mode 100644
index 3964565..0000000
--- a/src/Spectrogram.MicrophoneDemo/Properties/Settings.settings
+++ /dev/null
@@ -1,7 +0,0 @@
-
-
-
-
-
-
-
diff --git a/src/Spectrogram.MicrophoneDemo/Spectrogram.Demo.csproj b/src/Spectrogram.MicrophoneDemo/Spectrogram.Demo.csproj
index 344f096..e58c01a 100644
--- a/src/Spectrogram.MicrophoneDemo/Spectrogram.Demo.csproj
+++ b/src/Spectrogram.MicrophoneDemo/Spectrogram.Demo.csproj
@@ -1,19 +1,15 @@
- net6.0-windows
+ net8.0-windows
WinExe
- false
true
true
+ NU1701
-
-
-
-
-
-
-
+
+
+
\ No newline at end of file
diff --git a/src/Spectrogram.MicrophoneDemo/packages.config b/src/Spectrogram.MicrophoneDemo/packages.config
deleted file mode 100644
index 64d4d6d..0000000
--- a/src/Spectrogram.MicrophoneDemo/packages.config
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-
-
-
-
\ No newline at end of file
diff --git a/src/Spectrogram.Tests/AudioFileTests.cs b/src/Spectrogram.Tests/AudioFileTests.cs
index 60248fc..ef99c25 100644
--- a/src/Spectrogram.Tests/AudioFileTests.cs
+++ b/src/Spectrogram.Tests/AudioFileTests.cs
@@ -1,7 +1,5 @@
-using NUnit.Framework;
-using System;
-using System.Collections.Generic;
-using System.Text;
+using FluentAssertions;
+using NUnit.Framework;
namespace Spectrogram.Tests
{
@@ -20,8 +18,8 @@ public void Test_AudioFile_LengthAndSampleRate(string filename, int knownRate, i
string filePath = $"../../../../../data/{filename}";
(double[] audio, int sampleRate) = AudioFile.ReadWAV(filePath);
- Assert.AreEqual(knownRate, sampleRate);
- Assert.AreEqual(knownLength, audio.Length / channels);
+ sampleRate.Should().Be(knownRate);
+ (audio.Length / channels).Should().Be(knownLength);
}
}
}
diff --git a/src/Spectrogram.Tests/ColormapExamples.cs b/src/Spectrogram.Tests/ColormapExamples.cs
deleted file mode 100644
index 276b3f1..0000000
--- a/src/Spectrogram.Tests/ColormapExamples.cs
+++ /dev/null
@@ -1,41 +0,0 @@
-using NUnit.Framework;
-using System;
-using System.Diagnostics;
-
-namespace Spectrogram.Tests
-{
- class ColormapExamples
- {
- [Test]
- public void Test_Make_CommonColormaps()
- {
- (double[] audio, int sampleRate) = AudioFile.ReadWAV("../../../../../data/cant-do-that-44100.wav");
- int fftSize = 1 << 12;
- var spec = new SpectrogramGenerator(sampleRate, fftSize, stepSize: 700, maxFreq: 2000);
- var window = new FftSharp.Windows.Hanning();
- spec.SetWindow(window.Create(fftSize / 3)); // sharper window than typical
- spec.Add(audio);
-
- // delete old colormap files
- foreach (var filePath in System.IO.Directory.GetFiles("../../../../../dev/graphics/", "hal-*.png"))
- System.IO.File.Delete(filePath);
-
- foreach (var cmap in Colormap.GetColormaps())
- {
- spec.Colormap = cmap;
- spec.SaveImage($"../../../../../dev/graphics/hal-{cmap.Name}.png");
- Debug.WriteLine($"");
- }
- }
-
- [Test]
- public void Test_Colormaps_ByName()
- {
- string[] names = Colormap.GetColormapNames();
- Console.WriteLine(string.Join(", ", names));
-
- Colormap viridisCmap = Colormap.GetColormap("viridis");
- Assert.AreEqual("Viridis", viridisCmap.Name);
- }
- }
-}
diff --git a/src/Spectrogram.Tests/ColormapValues.cs b/src/Spectrogram.Tests/ColormapValues.cs
deleted file mode 100644
index 9c5d9d1..0000000
--- a/src/Spectrogram.Tests/ColormapValues.cs
+++ /dev/null
@@ -1,361 +0,0 @@
-using Microsoft.VisualStudio.TestPlatform.Utilities;
-using NUnit.Framework;
-using System;
-using System.Collections.Generic;
-using System.Drawing;
-using System.Text;
-
-namespace Spectrogram.Tests
-{
- class ColormapValues
- {
- [Test]
- public void Test_Colormap_ExtendedFractionsReturnEdgeValues()
- {
- var cmap = Colormap.Viridis;
-
- Random rand = new Random(0);
- for (double frac = -3; frac < 3; frac += rand.NextDouble() * .2)
- {
- Console.WriteLine($"{frac}: {cmap.GetRGB(frac)}");
-
- if (frac <= 0)
- Assert.AreEqual(cmap.GetRGB(0), cmap.GetRGB(frac));
-
- if (frac >= 1)
- Assert.AreEqual(cmap.GetRGB(1.0), cmap.GetRGB(frac));
- }
- }
-
- [Test]
- public void Test_Colormap_IntegerMatchesRGBColors()
- {
- var cmap = Colormap.Viridis;
-
- byte pixelIntensity = 123;
- var (r, g, b) = cmap.GetRGB(pixelIntensity);
- int int32 = cmap.GetInt32(pixelIntensity);
-
- Color color1 = Color.FromArgb(255, r, g, b);
- Color color2 = Color.FromArgb(int32);
- Color color3 = cmap.GetColor(pixelIntensity);
-
- Assert.AreEqual(color1, color2);
- Assert.AreEqual(color1, color3);
- }
-
- [Test]
- public void Test_colorLookup_integerMatchesTriplet()
- {
- for (int i = 0; i < 256; i++)
- {
- byte[] bytes = BitConverter.GetBytes(ints[i]);
-
- Color color1 = Color.FromArgb(bytes[2], bytes[1], bytes[0]);
-
- Color color2 = Color.FromArgb(rgb[i, 0], rgb[i, 1], rgb[i, 2]);
-
- Assert.AreEqual(color2.R, color1.R, 1);
- Assert.AreEqual(color2.G, color1.G, 1);
- Assert.AreEqual(color2.B, color1.B, 1);
- }
- }
-
- private readonly int[] ints =
- {
- 04456788, 04457045, 04457303, 04523352, 04523610, 04524123, 04589916, 04590430,
- 04590687, 04591201, 04656994, 04657507, 04657765, 04658278, 04658535, 04658793,
- 04659306, 04725099, 04725356, 04725870, 04726127, 04726384, 04726897, 04727154,
- 04727411, 04727668, 04662645, 04662902, 04663159, 04663416, 04663929, 04664186,
- 04664443, 04599164, 04599676, 04599933, 04600190, 04534911, 04535423, 04535680,
- 04535937, 04470657, 04471170, 04405891, 04406147, 04406404, 04341124, 04341381,
- 04341893, 04276614, 04276870, 04211591, 04211847, 04146567, 04147080, 04081800,
- 04082057, 04016777, 04017033, 04017289, 03952010, 03952266, 03887242, 03887498,
- 03822219, 03822475, 03757195, 03757451, 03692171, 03692428, 03627148, 03627404,
- 03562124, 03562380, 03497100, 03497356, 03432077, 03432333, 03367053, 03367309,
- 03302029, 03302285, 03237005, 03237261, 03237517, 03172237, 03172493, 03107213,
- 03107469, 03042190, 03042446, 03042702, 02977422, 02977678, 02912398, 02912654,
- 02912910, 02847630, 02847886, 02782606, 02782862, 02783118, 02717838, 02718094,
- 02652814, 02652814, 02653070, 02587790, 02588046, 02588302, 02523022, 02523278,
- 02523534, 02458254, 02458509, 02393229, 02393485, 02393741, 02328461, 02328717,
- 02328973, 02263437, 02263693, 02263949, 02198669, 02198924, 02199180, 02133900,
- 02134156, 02134412, 02069132, 02069387, 02069643, 02069899, 02070155, 02004874,
- 02005130, 02005386, 02005386, 02005641, 02005897, 02006153, 02006408, 02006664,
- 02006920, 02007175, 02072967, 02073222, 02073478, 02139269, 02139525, 02205317,
- 02205572, 02271108, 02336899, 02337154, 02402946, 02468737, 02534529, 02600320,
- 02666111, 02731903, 02797694, 02863485, 02929021, 03060348, 03126139, 03191930,
- 03323258, 03389049, 03520376, 03586167, 03717494, 03783030, 03914357, 04045684,
- 04111475, 04242802, 04374129, 04505200, 04570991, 04702318, 04833645, 04964972,
- 05096043, 05227369, 05358696, 05490023, 05621350, 05752421, 05883748, 06015074,
- 06211937, 06343008, 06474335, 06605661, 06802524, 06933595, 07064921, 07196248,
- 07392854, 07524181, 07655508, 07852114, 07983441, 08180303, 08311374, 08508236,
- 08639307, 08836169, 08967495, 09164102, 09295428, 09492035, 09623361, 09819967,
- 09951294, 10147900, 10344762, 10475832, 10672695, 10869301, 11000627, 11197234,
- 11394096, 11525166, 11722028, 11918635, 12049705, 12246567, 12443174, 12574500,
- 12771106, 12967713, 13099039, 13295646, 13492253, 13623580, 13820187, 13951258,
- 14148121, 14344728, 14475800, 14672664, 14803736, 15000344, 15197209, 15328281,
- 15524890, 15656219, 15852828, 15983902, 16180767, 16311841, 16442914, 16639780,
-
- };
-
- private readonly byte[,] rgb =
- {
- {68, 1, 84},
- {68, 2, 86},
- {69, 4, 87},
- {69, 5, 89},
- {70, 7, 90},
- {70, 8, 92},
- {70, 10, 93},
- {70, 11, 94},
- {71, 13, 96},
- {71, 14, 97},
- {71, 16, 99},
- {71, 17, 100},
- {71, 19, 101},
- {72, 20, 103},
- {72, 22, 104},
- {72, 23, 105},
- {72, 24, 106},
- {72, 26, 108},
- {72, 27, 109},
- {72, 28, 110},
- {72, 29, 111},
- {72, 31, 112},
- {72, 32, 113},
- {72, 33, 115},
- {72, 35, 116},
- {72, 36, 117},
- {72, 37, 118},
- {72, 38, 119},
- {72, 40, 120},
- {72, 41, 121},
- {71, 42, 122},
- {71, 44, 122},
- {71, 45, 123},
- {71, 46, 124},
- {71, 47, 125},
- {70, 48, 126},
- {70, 50, 126},
- {70, 51, 127},
- {70, 52, 128},
- {69, 53, 129},
- {69, 55, 129},
- {69, 56, 130},
- {68, 57, 131},
- {68, 58, 131},
- {68, 59, 132},
- {67, 61, 132},
- {67, 62, 133},
- {66, 63, 133},
- {66, 64, 134},
- {66, 65, 134},
- {65, 66, 135},
- {65, 68, 135},
- {64, 69, 136},
- {64, 70, 136},
- {63, 71, 136},
- {63, 72, 137},
- {62, 73, 137},
- {62, 74, 137},
- {62, 76, 138},
- {61, 77, 138},
- {61, 78, 138},
- {60, 79, 138},
- {60, 80, 139},
- {59, 81, 139},
- {59, 82, 139},
- {58, 83, 139},
- {58, 84, 140},
- {57, 85, 140},
- {57, 86, 140},
- {56, 88, 140},
- {56, 89, 140},
- {55, 90, 140},
- {55, 91, 141},
- {54, 92, 141},
- {54, 93, 141},
- {53, 94, 141},
- {53, 95, 141},
- {52, 96, 141},
- {52, 97, 141},
- {51, 98, 141},
- {51, 99, 141},
- {50, 100, 142},
- {50, 101, 142},
- {49, 102, 142},
- {49, 103, 142},
- {49, 104, 142},
- {48, 105, 142},
- {48, 106, 142},
- {47, 107, 142},
- {47, 108, 142},
- {46, 109, 142},
- {46, 110, 142},
- {46, 111, 142},
- {45, 112, 142},
- {45, 113, 142},
- {44, 113, 142},
- {44, 114, 142},
- {44, 115, 142},
- {43, 116, 142},
- {43, 117, 142},
- {42, 118, 142},
- {42, 119, 142},
- {42, 120, 142},
- {41, 121, 142},
- {41, 122, 142},
- {41, 123, 142},
- {40, 124, 142},
- {40, 125, 142},
- {39, 126, 142},
- {39, 127, 142},
- {39, 128, 142},
- {38, 129, 142},
- {38, 130, 142},
- {38, 130, 142},
- {37, 131, 142},
- {37, 132, 142},
- {37, 133, 142},
- {36, 134, 142},
- {36, 135, 142},
- {35, 136, 142},
- {35, 137, 142},
- {35, 138, 141},
- {34, 139, 141},
- {34, 140, 141},
- {34, 141, 141},
- {33, 142, 141},
- {33, 143, 141},
- {33, 144, 141},
- {33, 145, 140},
- {32, 146, 140},
- {32, 146, 140},
- {32, 147, 140},
- {31, 148, 140},
- {31, 149, 139},
- {31, 150, 139},
- {31, 151, 139},
- {31, 152, 139},
- {31, 153, 138},
- {31, 154, 138},
- {30, 155, 138},
- {30, 156, 137},
- {30, 157, 137},
- {31, 158, 137},
- {31, 159, 136},
- {31, 160, 136},
- {31, 161, 136},
- {31, 161, 135},
- {31, 162, 135},
- {32, 163, 134},
- {32, 164, 134},
- {33, 165, 133},
- {33, 166, 133},
- {34, 167, 133},
- {34, 168, 132},
- {35, 169, 131},
- {36, 170, 131},
- {37, 171, 130},
- {37, 172, 130},
- {38, 173, 129},
- {39, 173, 129},
- {40, 174, 128},
- {41, 175, 127},
- {42, 176, 127},
- {44, 177, 126},
- {45, 178, 125},
- {46, 179, 124},
- {47, 180, 124},
- {49, 181, 123},
- {50, 182, 122},
- {52, 182, 121},
- {53, 183, 121},
- {55, 184, 120},
- {56, 185, 119},
- {58, 186, 118},
- {59, 187, 117},
- {61, 188, 116},
- {63, 188, 115},
- {64, 189, 114},
- {66, 190, 113},
- {68, 191, 112},
- {70, 192, 111},
- {72, 193, 110},
- {74, 193, 109},
- {76, 194, 108},
- {78, 195, 107},
- {80, 196, 106},
- {82, 197, 105},
- {84, 197, 104},
- {86, 198, 103},
- {88, 199, 101},
- {90, 200, 100},
- {92, 200, 99},
- {94, 201, 98},
- {96, 202, 96},
- {99, 203, 95},
- {101, 203, 94},
- {103, 204, 92},
- {105, 205, 91},
- {108, 205, 90},
- {110, 206, 88},
- {112, 207, 87},
- {115, 208, 86},
- {117, 208, 84},
- {119, 209, 83},
- {122, 209, 81},
- {124, 210, 80},
- {127, 211, 78},
- {129, 211, 77},
- {132, 212, 75},
- {134, 213, 73},
- {137, 213, 72},
- {139, 214, 70},
- {142, 214, 69},
- {144, 215, 67},
- {147, 215, 65},
- {149, 216, 64},
- {152, 216, 62},
- {155, 217, 60},
- {157, 217, 59},
- {160, 218, 57},
- {162, 218, 55},
- {165, 219, 54},
- {168, 219, 52},
- {170, 220, 50},
- {173, 220, 48},
- {176, 221, 47},
- {178, 221, 45},
- {181, 222, 43},
- {184, 222, 41},
- {186, 222, 40},
- {189, 223, 38},
- {192, 223, 37},
- {194, 223, 35},
- {197, 224, 33},
- {200, 224, 32},
- {202, 225, 31},
- {205, 225, 29},
- {208, 225, 28},
- {210, 226, 27},
- {213, 226, 26},
- {216, 226, 25},
- {218, 227, 25},
- {221, 227, 24},
- {223, 227, 24},
- {226, 228, 24},
- {229, 228, 25},
- {231, 228, 25},
- {234, 229, 26},
- {236, 229, 27},
- {239, 229, 28},
- {241, 229, 29},
- {244, 230, 30},
- {246, 230, 32},
- {248, 230, 33},
- {251, 231, 35},
- {253, 231, 37},
- };
- }
-}
diff --git a/src/Spectrogram.Tests/ImageTests.cs b/src/Spectrogram.Tests/ImageTests.cs
index d02a218..c0fe5ab 100644
--- a/src/Spectrogram.Tests/ImageTests.cs
+++ b/src/Spectrogram.Tests/ImageTests.cs
@@ -1,4 +1,5 @@
using NUnit.Framework;
+using SkiaSharp;
namespace Spectrogram.Tests;
@@ -12,10 +13,10 @@ public void Test_Image_Rotations()
SpectrogramGenerator sg = new(sampleRate, 4096, 500, maxFreq: 3000);
sg.Add(audio);
- System.Drawing.Bitmap bmp1 = sg.GetBitmap(rotate: false);
- bmp1.Save("test-image-original.png");
+ SKBitmap bmp1 = sg.GetBitmap(rotate: false);
+ bmp1.SaveTo("test-image-original.png", SKEncodedImageFormat.Png);
- System.Drawing.Bitmap bmp2 = sg.GetBitmap(rotate: true);
- bmp2.Save("test-image-rotated.png");
+ SKBitmap bmp2 = sg.GetBitmap(rotate: true);
+ bmp2.SaveTo("test-image-rotated.png", SKEncodedImageFormat.Png);
}
}
diff --git a/src/Spectrogram.Tests/Mel.cs b/src/Spectrogram.Tests/Mel.cs
index 43f0d56..be6b6f3 100644
--- a/src/Spectrogram.Tests/Mel.cs
+++ b/src/Spectrogram.Tests/Mel.cs
@@ -1,9 +1,6 @@
using NUnit.Framework;
using System;
-using System.Collections.Generic;
-using System.Drawing;
-using System.Drawing.Imaging;
-using System.Text;
+using SkiaSharp;
namespace Spectrogram.Tests
{
@@ -17,16 +14,38 @@ public void Test_MelSpectrogram_MelScale()
var sg = new SpectrogramGenerator(sampleRate, fftSize, stepSize: 500);
sg.Add(audio);
- Bitmap bmpMel = sg.GetBitmapMel(250);
- bmpMel.Save("../../../../../dev/graphics/halMel-MelScale.png", ImageFormat.Png);
+ // Ottieni l'immagine Mel-scaled come SKBitmap
+ SKBitmap bmpMel = sg.GetBitmapMel(250); // Presuppone che sg abbia un metodo GetSKBitmapMel
+ using (var image = SKImage.FromBitmap(bmpMel))
+ using (var data = image.Encode(SKEncodedImageFormat.Png, 100))
+ {
+ // Salva l'immagine Mel-scaled
+ using (var stream = System.IO.File.OpenWrite("../../../../../dev/graphics/halMel-MelScale.png"))
+ {
+ data.SaveTo(stream);
+ }
+ }
+
+ // Ottieni l'immagine originale come SKBitmap
+ SKBitmap bmpRaw = sg.GetBitmap(); // Presuppone che sg abbia un metodo GetSKBitmap
+ SKBitmap bmpCropped = new SKBitmap(bmpRaw.Width, bmpMel.Height);
- Bitmap bmpRaw = sg.GetBitmap();
- Bitmap bmpCropped = new Bitmap(bmpRaw.Width, bmpMel.Height);
- using (Graphics gfx = Graphics.FromImage(bmpCropped))
+ // Disegna bmpRaw su bmpCropped usando SKCanvas
+ using (var canvas = new SKCanvas(bmpCropped))
{
- gfx.DrawImage(bmpRaw, 0, bmpMel.Height - bmpRaw.Height);
+ canvas.Clear(SKColors.Transparent);
+ canvas.DrawBitmap(bmpRaw, new SKRect(0, bmpMel.Height - bmpRaw.Height, bmpRaw.Width, bmpMel.Height));
+ }
+
+ using (var imageCropped = SKImage.FromBitmap(bmpCropped))
+ using (var dataCropped = imageCropped.Encode(SKEncodedImageFormat.Png, 100))
+ {
+ // Salva l'immagine croppata
+ using (var streamCropped = System.IO.File.OpenWrite("../../../../../dev/graphics/halMel-LinearCropped.png"))
+ {
+ dataCropped.SaveTo(streamCropped);
+ }
}
- bmpCropped.Save("../../../../../dev/graphics/halMel-LinearCropped.png", ImageFormat.Png);
}
[Test]
@@ -37,11 +56,11 @@ public void Test_Mel_Graph()
double maxMel = 2595 * Math.Log10(1 + maxFreq / 700);
Random rand = new Random(1);
- double[] freq = ScottPlot.DataGen.Consecutive(specPoints, maxFreq / specPoints);
- double[] power = ScottPlot.DataGen.RandomWalk(rand, specPoints, .02, .5);
+ double[] freq = ScottPlot.Generate.Consecutive(specPoints, maxFreq / specPoints);
+ double[] power = ScottPlot.Generate.RandomWalk(specPoints, .02, .5);
- var plt1 = new ScottPlot.Plot(800, 300);
- plt1.AddScatter(freq, power, markerSize: 0);
+ var plt1 = new ScottPlot.Plot();
+ plt1.Add.ScatterLine(freq, power);
int filterSize = 25;
@@ -64,10 +83,9 @@ public void Test_Mel_Graph()
double freqCenter = binStartFreqs[binIndex + 1];
double freqHigh = binStartFreqs[binIndex + 2];
- var sctr = plt1.AddScatter(
- xs: new double[] { freqLow, freqCenter, freqHigh },
- ys: new double[] { 0, 1, 0 },
- markerSize: 0, lineWidth: 2);
+ double[] xs = [freqLow, freqCenter, freqHigh];
+ double[] ys = [0, 1, 0];
+ var sctr = plt1.Add.ScatterLine(xs, ys);
int indexLow = (int)(specPoints * freqLow / maxFreq);
int indexHigh = (int)(specPoints * freqHigh / maxFreq);
@@ -84,10 +102,10 @@ public void Test_Mel_Graph()
binValue += power[indexLow + i] * frac;
}
binValue /= binScaleSum;
- plt1.AddPoint(freqCenter, binValue, sctr.Color, 10);
+ plt1.Add.Marker(freqCenter, binValue, ScottPlot.MarkerShape.FilledCircle, 10, sctr.Color);
}
- plt1.SaveFig("mel1.png");
+ plt1.SavePng("mel1.png", 800, 300);
}
[Test]
diff --git a/src/Spectrogram.Tests/SkExtensions.cs b/src/Spectrogram.Tests/SkExtensions.cs
new file mode 100644
index 0000000..f59a544
--- /dev/null
+++ b/src/Spectrogram.Tests/SkExtensions.cs
@@ -0,0 +1,13 @@
+using SkiaSharp;
+
+namespace Spectrogram.Tests;
+
+internal static class SkExtensions
+{
+ internal static void SaveTo(this SKBitmap bitmap, string fileName, SKEncodedImageFormat format, int quality = 100)
+ {
+ using var data = bitmap.Encode(format, quality);
+ using var stream = System.IO.File.OpenWrite(fileName);
+ data.SaveTo(stream);
+ }
+}
\ No newline at end of file
diff --git a/src/Spectrogram.Tests/Spectrogram.Tests.csproj b/src/Spectrogram.Tests/Spectrogram.Tests.csproj
index 7754517..83fa10d 100644
--- a/src/Spectrogram.Tests/Spectrogram.Tests.csproj
+++ b/src/Spectrogram.Tests/Spectrogram.Tests.csproj
@@ -1,17 +1,18 @@
-
+
- net6.0
+ net8.0
false
+
-
-
-
-
-
+
+
+
+
+
diff --git a/src/Spectrogram/Colormap.cs b/src/Spectrogram/Colormap.cs
index 1972fa6..1553712 100644
--- a/src/Spectrogram/Colormap.cs
+++ b/src/Spectrogram/Colormap.cs
@@ -1,95 +1,91 @@
using System;
-using System.Drawing;
using System.Linq;
+using SkiaSharp;
-namespace Spectrogram
+namespace Spectrogram;
+
+public class Colormap(ScottPlot.IColormap colormap)
{
- public class Colormap
+ private ScottPlot.IColormap _Colormap { get; } = colormap;
+
+ public string Name => _Colormap.Name;
+
+ public override string ToString() => _Colormap.ToString();
+
+ public static Colormap[] GetColormaps() => ScottPlot.Colormap.GetColormaps()
+ .Select(x => new Colormap(x))
+ .ToArray();
+
+ public static string[] GetColormapNames() => ScottPlot.Colormap.GetColormaps()
+ .Select(x => new Colormap(x).Name)
+ .ToArray();
+
+ public static Colormap GetColormap(string colormapName)
{
- public static Colormap Argo => new Colormap(new Colormaps.Argo());
- public static Colormap Blues => new Colormap(new Colormaps.Blues());
- public static Colormap Grayscale => new Colormap(new Colormaps.Grayscale());
- public static Colormap GrayscaleReversed => new Colormap(new Colormaps.GrayscaleR());
- public static Colormap Greens => new Colormap(new Colormaps.Greens());
- public static Colormap Inferno => new Colormap(new Colormaps.Inferno());
- public static Colormap Lopora => new Colormap(new Colormaps.Lopora());
- public static Colormap Magma => new Colormap(new Colormaps.Magma());
- public static Colormap Plasma => new Colormap(new Colormaps.Plasma());
- public static Colormap Turbo => new Colormap(new Colormaps.Turbo());
- public static Colormap Viridis => new Colormap(new Colormaps.Viridis());
-
- private readonly IColormap cmap;
- public readonly string Name;
- public Colormap(IColormap colormap)
- {
- cmap = colormap ?? new Colormaps.Grayscale();
- Name = cmap.GetType().Name;
- }
+ foreach (Colormap cmap in GetColormaps())
+ if (string.Equals(cmap.Name, colormapName, StringComparison.InvariantCultureIgnoreCase))
+ return cmap;
- public override string ToString()
- {
- return $"Colormap {Name}";
- }
+ throw new ArgumentException($"Colormap does not exist: {colormapName}");
+ }
- public static Colormap[] GetColormaps() =>
- typeof(Colormap).GetProperties()
- .Select(x => (Colormap)x.GetValue(x.Name))
- .ToArray();
+ public (byte r, byte g, byte b) GetRGB(byte value) => GetRGB(value / 255.0);
- public static string[] GetColormapNames()
- {
- return GetColormaps().Select(x => x.Name).ToArray();
- }
+ public (byte r, byte g, byte b) GetRGB(double fraction)
+ {
+ ScottPlot.Color color = _Colormap.GetColor(fraction);
+ return (color.R, color.G, color.B);
+ }
- public static Colormap GetColormap(string colormapName)
- {
- foreach (Colormap cmap in GetColormaps())
- if (string.Equals(cmap.Name, colormapName, StringComparison.InvariantCultureIgnoreCase))
- return cmap;
+ public int GetInt32(byte value)
+ {
+ var (r, g, b) = GetRGB(value);
+ return 255 << 24 | r << 16 | g << 8 | b;
+ }
- throw new ArgumentException($"Colormap does not exist: {colormapName}");
- }
+ public int GetInt32(double fraction)
+ {
+ var (r, g, b) = GetRGB(fraction);
+ return 255 << 24 | r << 16 | g << 8 | b;
+ }
- public (byte r, byte g, byte b) GetRGB(byte value)
- {
- return cmap.GetRGB(value);
- }
+ public SKColor GetColor(byte value)
+ {
+ var color = GetInt32(value);
+ return new SKColor((uint)color);
+ }
- public (byte r, byte g, byte b) GetRGB(double fraction)
- {
- fraction = Math.Max(fraction, 0);
- fraction = Math.Min(fraction, 1);
- return cmap.GetRGB((byte)(fraction * 255));
- }
+ public SKColor GetColor(double fraction)
+ {
+ var color = GetInt32(fraction);
+ return new SKColor((uint)color);
+ }
- public int GetInt32(byte value)
- {
- var (r, g, b) = GetRGB(value);
- return 255 << 24 | r << 16 | g << 8 | b;
- }
+ public SKBitmap ApplyFilter(SKBitmap bmp)
+ {
+ SKImageInfo info = new(bmp.Width, bmp.Height, SKColorType.Rgba8888);
+ SKBitmap newBitmap = new(info);
+ using SKCanvas canvas = new(newBitmap);
+ canvas.Clear();
- public int GetInt32(double fraction)
- {
- var (r, g, b) = GetRGB(fraction);
- return 255 << 24 | r << 16 | g << 8 | b;
- }
+ using SKPaint paint = new SKPaint();
- public Color GetColor(byte value)
- {
- return Color.FromArgb(GetInt32(value));
- }
+ byte[] A = new byte[256];
+ byte[] R = new byte[256];
+ byte[] G = new byte[256];
+ byte[] B = new byte[256];
- public Color GetColor(double fraction)
+ for (int i = 0; i < 256; i++)
{
- return Color.FromArgb(GetInt32(fraction));
+ var color = GetColor((byte)i);
+ A[i] = color.Alpha;
+ R[i] = color.Red;
+ G[i] = color.Green;
+ B[i] = color.Blue;
}
+ paint.ColorFilter = SKColorFilter.CreateTable(A, R, G, B);
- public void Apply(Bitmap bmp)
- {
- System.Drawing.Imaging.ColorPalette pal = bmp.Palette;
- for (int i = 0; i < 256; i++)
- pal.Entries[i] = GetColor((byte)i);
- bmp.Palette = pal;
- }
+ canvas.DrawBitmap(bmp, 0, 0, paint);
+ return newBitmap;
}
}
diff --git a/src/Spectrogram/Colormaps/Argo.cs b/src/Spectrogram/Colormaps/Argo.cs
deleted file mode 100644
index ac40f2d..0000000
--- a/src/Spectrogram/Colormaps/Argo.cs
+++ /dev/null
@@ -1,55 +0,0 @@
-/* Argo is a closed-source weak signal spectrogram.
- * This colormap was created to mimic the colors used by Argo.
- * https://www.i2phd.org/argo/index.html
- * https://digilander.libero.it/i2phd/argo/
- */
-
-using System;
-
-namespace Spectrogram.Colormaps
-{
- class Argo : IColormap
- {
- public (byte r, byte g, byte b) GetRGB(byte value)
- {
- byte[] bytes = BitConverter.GetBytes(rgb[value]);
- return (bytes[2], bytes[1], bytes[0]);
- }
-
- private readonly int[] rgb =
- {
- 00000000, 00000004, 00000264, 00000267, 00000527, 00000530, 00000789, 00066328,
- 00066588, 00066591, 00066849, 00132388, 00132647, 00132650, 00132908, 00198447,
- 00198706, 00198708, 00264503, 00264505, 00330299, 00330557, 00330560, 00396354,
- 00396612, 00462150, 00462408, 00527946, 00528204, 00593998, 00594000, 00659794,
- 00660052, 00725590, 00791383, 00791641, 00857435, 00857437, 00923230, 00989024,
- 00989026, 01054819, 01120613, 01120870, 01186408, 01252201, 01252459, 01318252,
- 01384046, 01384047, 01449841, 01515634, 01515892, 01581429, 01647222, 01713016,
- 01713273, 01779066, 01844860, 01910397, 01976190, 01976447, 02042241, 02108034,
- 02173827, 02239364, 02239621, 02305415, 02371208, 02437001, 02502794, 02568587,
- 02568844, 02634381, 02700174, 02765968, 02831761, 02897554, 02963347, 03029140,
- 03029397, 03095190, 03160983, 03226520, 03292313, 03358106, 03423899, 03489692,
- 03555485, 03621278, 03687071, 03752864, 03818656, 03884449, 03950242, 04016035,
- 04081828, 04147621, 04147878, 04213671, 04279464, 04345256, 04411049, 04476842,
- 04542635, 04608428, 04739757, 04805550, 04871342, 04937135, 05002928, 05068721,
- 05134514, 05200306, 05266099, 05331892, 05397685, 05463477, 05529270, 05595063,
- 05660856, 05726648, 05792441, 05858234, 05924027, 05989819, 06121148, 06186941,
- 06252734, 06318526, 06384319, 06450112, 06515904, 06581953, 06647746, 06779074,
- 06844867, 06910660, 06976452, 07042245, 07108038, 07173830, 07239623, 07370952,
- 07436744, 07502793, 07568586, 07634378, 07700171, 07765964, 07897292, 07963085,
- 08028877, 08094670, 08160719, 08226511, 08357840, 08423632, 08489425, 08555218,
- 08621010, 08752339, 08818387, 08884180, 08949973, 09015765, 09147094, 09212886,
- 09278679, 09344727, 09410520, 09541849, 09607641, 09673434, 09739226, 09870555,
- 09936603, 10002396, 10068188, 10133981, 10265309, 10331102, 10397150, 10462943,
- 10594272, 10660064, 10725857, 10791905, 10923234, 10989026, 11054819, 11120611,
- 11251940, 11317988, 11383781, 11515109, 11580902, 11646694, 11712743, 11844071,
- 11909864, 11975656, 12106985, 12173033, 12238826, 12304618, 12435947, 12501995,
- 12567787, 12699116, 12764908, 12830701, 12962285, 13028078, 13093870, 13159663,
- 13291247, 13357040, 13422832, 13554161, 13619953, 13686001, 13817330, 13883122,
- 13948915, 14080499, 14146292, 14212084, 14343412, 14409461, 14475253, 14606582,
- 14672374, 14738423, 14869751, 14935543, 15066872, 15132920, 15198713, 15330041,
- 15396090, 15461882, 15593210, 15659003, 15725051, 15856380, 15922172, 16053500,
- 16119549, 16185341, 16316670, 16382718, 16448510, 16579839, 16645631, 16777215,
- };
- }
-}
diff --git a/src/Spectrogram/Colormaps/Blues.cs b/src/Spectrogram/Colormaps/Blues.cs
deleted file mode 100644
index d917d9c..0000000
--- a/src/Spectrogram/Colormaps/Blues.cs
+++ /dev/null
@@ -1,50 +0,0 @@
-// This colormap was created by Scott Harden on 2020-06-16 and is released under a MIT license.
-using System;
-
-namespace Spectrogram.Colormaps
-{
- class Blues : IColormap
- {
- public (byte r, byte g, byte b) GetRGB(byte value)
- {
- byte[] bytes = BitConverter.GetBytes(argb[value]);
- return (bytes[2], bytes[1], bytes[0]);
- }
-
- private readonly int[] argb =
- {
- -16767403, -16767402, -16767144, -16766887, -16701093, -16700835, -16700578, -16634784,
- -16634527, -16634269, -16568476, -16568218, -16568216, -16567959, -16502165, -16501908,
- -16501650, -16435856, -16435599, -16435341, -16369548, -16369290, -16369033, -16303495,
- -16303238, -16302980, -16237186, -16236929, -16236671, -16236414, -16170620, -16170363,
- -16170105, -16104312, -16104054, -16103797, -16038259, -16038002, -16037745, -15971951,
- -15971694, -15971436, -15905643, -15905385, -15905128, -15839335, -15839077, -15838820,
- -15773027, -15772769, -15772512, -15706718, -15706717, -15706460, -15706203, -15640409,
- -15640152, -15574359, -15574101, -15573844, -15508051, -15507794, -15507537, -15441743,
- -15441486, -15441229, -15375436, -15375179, -15309386, -15309128, -15308871, -15243078,
- -15242821, -15177284, -15177027, -15111234, -15110977, -15045184, -15044927, -14979134,
- -14978877, -14913084, -14912827, -14847034, -14781241, -14780984, -14715191, -14715190,
- -14649397, -14583605, -14517812, -14517555, -14451762, -14385969, -14320176, -14319920,
- -14254383, -14188590, -14122797, -14057005, -13991212, -13925419, -13859626, -13859626,
- -13793833, -13662505, -13596712, -13530919, -13465383, -13399590, -13333797, -13268005,
- -13202212, -13136676, -13005347, -12939555, -12873762, -12808226, -12676897, -12611105,
- -12545312, -12414240, -12348447, -12282655, -12151327, -12085790, -12019998, -11888669,
- -11822877, -11757341, -11626012, -11560220, -11429148, -11363355, -11232027, -11166235,
- -11035162, -10969370, -10903578, -10772505, -10706713, -10575385, -10509592, -10378520,
- -10312728, -10181400, -10115863, -09984535, -09918743, -09787414, -09721878, -09590550,
- -09524758, -09393429, -09327893, -09262101, -09130773, -09065237, -08933908, -08868116,
- -08736788, -08671252, -08605459, -08474131, -08408339, -08277267, -08211475, -08145682,
- -08014354, -07948818, -07817490, -07751698, -07685905, -07554833, -07489041, -07357713,
- -07291921, -07226128, -07095056, -07029264, -06897936, -06832144, -06766351, -06635279,
- -06569487, -06503695, -06372367, -06306575, -06175502, -06109710, -06043918, -05912590,
- -05846798, -05781261, -05649933, -05584141, -05518349, -05387021, -05321485, -05190156,
- -05124364, -05058572, -04927244, -04861452, -04730123, -04664587, -04598795, -04467467,
- -04401675, -04335883, -04204554, -04139018, -04007690, -03941898, -03876106, -03744777,
- -03678985, -03547657, -03481865, -03416329, -03285000, -03219208, -03087880, -03022088,
- -02956296, -02824968, -02759175, -02628103, -02562311, -02430983, -02365191, -02299398,
- -02168070, -02102278, -01970950, -01905158, -01839621, -01708293, -01642501, -01511173,
- -01445381, -01314052, -01248260, -01182468, -01051140, -00985604, -00854275, -00788483,
- -00657155, -00591363, -00525571, -00394242, -00328450, -00197122, -00131330, -00000001,
- };
- }
-}
diff --git a/src/Spectrogram/Colormaps/Grayscale.cs b/src/Spectrogram/Colormaps/Grayscale.cs
deleted file mode 100644
index f8d092a..0000000
--- a/src/Spectrogram/Colormaps/Grayscale.cs
+++ /dev/null
@@ -1,12 +0,0 @@
-// This colormap was created by Scott Harden on 2020-06-16 and is released under a MIT license.
-
-namespace Spectrogram.Colormaps
-{
- class Grayscale : IColormap
- {
- public (byte r, byte g, byte b) GetRGB(byte value)
- {
- return (value, value, value);
- }
- }
-}
diff --git a/src/Spectrogram/Colormaps/GrayscaleR.cs b/src/Spectrogram/Colormaps/GrayscaleR.cs
deleted file mode 100644
index 6c499e5..0000000
--- a/src/Spectrogram/Colormaps/GrayscaleR.cs
+++ /dev/null
@@ -1,13 +0,0 @@
-// This colormap was created by Scott Harden on 2020-06-16 and is released under a MIT license.
-
-namespace Spectrogram.Colormaps
-{
- public class GrayscaleR : IColormap
- {
- public (byte r, byte g, byte b) GetRGB(byte value)
- {
- value = (byte)(255 - value);
- return (value, value, value);
- }
- }
-}
\ No newline at end of file
diff --git a/src/Spectrogram/Colormaps/Greens.cs b/src/Spectrogram/Colormaps/Greens.cs
deleted file mode 100644
index 0ee2358..0000000
--- a/src/Spectrogram/Colormaps/Greens.cs
+++ /dev/null
@@ -1,51 +0,0 @@
-// This colormap was created by Scott Harden on 2020-06-16 and is released under a MIT license.
-using System;
-
-namespace Spectrogram.Colormaps
-{
- class Greens : IColormap
- {
- public (byte r, byte g, byte b) GetRGB(byte value)
- {
- byte[] bytes = BitConverter.GetBytes(argb[value]);
- return (bytes[2], bytes[1], bytes[0]);
- }
-
- private readonly int[] argb =
- {
- -16761088, -16760832, -16760575, -16760318, -16760061, -16759804, -16759547, -16759290,
- -16759033, -16758776, -16758519, -16758006, -16757749, -16757492, -16757235, -16756979,
- -16756722, -16756465, -16756208, -16755951, -16755694, -16755437, -16755180, -16754667,
- -16754410, -16688617, -16688360, -16688104, -16687847, -16687590, -16687333, -16687076,
- -16621283, -16621026, -16620769, -16620512, -16620256, -16554463, -16554206, -16553949,
- -16553692, -16487899, -16487642, -16487385, -16421336, -16421080, -16420823, -16355030,
- -16354773, -16288980, -16288723, -16222930, -16222930, -16222673, -16156880, -16156623,
- -16090830, -16025038, -16024781, -15958988, -15958731, -15892938, -15827145, -15826889,
- -15761096, -15695303, -15695046, -15629254, -15563461, -15497924, -15497667, -15431875,
- -15366082, -15300289, -15234496, -15234240, -15168447, -15102654, -15037118, -14971325,
- -14905532, -14839740, -14773947, -14708154, -14642618, -14576825, -14511033, -14445240,
- -14379447, -14313655, -14248118, -14182326, -14116533, -14050741, -13985204, -13919412,
- -13853619, -13722291, -13656498, -13590962, -13525169, -13459377, -13328048, -13262512,
- -13196720, -13130927, -13065391, -12934063, -12868270, -12802478, -12671406, -12605613,
- -12539821, -12408749, -12342957, -12277164, -12146092, -12080300, -12014508, -11883435,
- -11817643, -11686315, -11620779, -11554986, -11423914, -11358122, -11226794, -11161258,
- -11029929, -10964137, -10833065, -10767273, -10636201, -10570408, -10439080, -10373544,
- -10242216, -10176680, -10045351, -09914023, -09848487, -09717159, -09651623, -09520294,
- -09389222, -09323430, -09192102, -09061029, -08995237, -08864165, -08798372, -08667300,
- -08535972, -08404643, -08339107, -08207779, -08076706, -08010914, -07879842, -07748513,
- -07682977, -07551648, -07420576, -07289248, -07223711, -07092383, -06961054, -06895518,
- -06764189, -06633116, -06501788, -06436251, -06304923, -06173850, -06108057, -05976984,
- -05845656, -05714583, -05648790, -05517717, -05386389, -05320596, -05189523, -05123730,
- -04992657, -04861328, -04795791, -04664462, -04598925, -04467596, -04336523, -04270730,
- -04139400, -04073863, -03942534, -03876997, -03745667, -03680130, -03614337, -03483007,
- -03417470, -03286140, -03220603, -03154809, -03023479, -02957942, -02892148, -02826610,
- -02760817, -02629487, -02563949, -02498155, -02432617, -02366823, -02301029, -02235491,
- -02169697, -02103903, -02038365, -01972571, -01906777, -01841239, -01775444, -01709650,
- -01644112, -01578318, -01512523, -01446985, -01381191, -01315396, -01249858, -01249599,
- -01183805, -01118266, -01052472, -00986678, -00986675, -00920880, -00855086, -00789547,
- -00723753, -00723494, -00657956, -00592161, -00526367, -00526108, -00460569, -00394775,
- -00394516, -00328977, -00263183, -00197388, -00197385, -00131591, -00065796, -00000001,
-
- };
- }
-}
diff --git a/src/Spectrogram/Colormaps/Inferno.cs b/src/Spectrogram/Colormaps/Inferno.cs
deleted file mode 100644
index 0b45ed5..0000000
--- a/src/Spectrogram/Colormaps/Inferno.cs
+++ /dev/null
@@ -1,57 +0,0 @@
-/* Inferno is a colormap by Nathaniel J. Smith and Stefan van der Walt
- * https://bids.github.io/colormap/
- * https://github.com/BIDS/colormap/blob/master/colormaps.py
- *
- * This colormap is provided under the CC0 license / public domain dedication
- * http://creativecommons.org/publicdomain/zero/1.0/
- */
-
-using System;
-
-namespace Spectrogram.Colormaps
-{
- class Inferno : IColormap
- {
- public (byte r, byte g, byte b) GetRGB(byte value)
- {
- byte[] bytes = BitConverter.GetBytes(rgb[value]);
- return (bytes[2], bytes[1], bytes[0]);
- }
-
- private readonly int[] rgb =
- {
- 00000003, 00000004, 00000006, 00065543, 00065801, 00065803, 00131342, 00131600,
- 00197138, 00262932, 00262934, 00328728, 00394267, 00460061, 00525855, 00591393,
- 00657187, 00722726, 00854056, 00919594, 00985389, 01050927, 01182258, 01247796,
- 01313590, 01444665, 01510203, 01641278, 01706816, 01838147, 01903685, 02034759,
- 02100298, 02231116, 02362190, 02493264, 02558802, 02689876, 02820694, 02951768,
- 03017306, 03148380, 03279197, 03410271, 03475808, 03606881, 03737954, 03869028,
- 03934565, 04065638, 04196710, 04262247, 04393576, 04524649, 04590185, 04721514,
- 04852586, 04918379, 05049451, 05180780, 05246316, 05377644, 05443181, 05574509,
- 05705581, 05771373, 05902701, 05968238, 06099566, 06230638, 06296430, 06427758,
- 06493294, 06624622, 06690158, 06821486, 06952814, 07018350, 07149678, 07215214,
- 07346542, 07477613, 07543405, 07674733, 07740269, 07871597, 08002669, 08068460,
- 08199532, 08265324, 08396651, 08462187, 08593515, 08724586, 08790378, 08921450,
- 08987241, 09118313, 09249641, 09315432, 09446504, 09512295, 09643367, 09774694,
- 09840230, 09971557, 10037348, 10168420, 10234211, 10365283, 10496610, 10562401,
- 10693473, 10759264, 10890335, 10956127, 11087454, 11218525, 11284316, 11415643,
- 11481435, 11612506, 11678297, 11809624, 11875159, 12006486, 12072278, 12203605,
- 12269396, 12400467, 12466258, 12532049, 12663376, 12729167, 12860494, 12926285,
- 13057612, 13123147, 13188938, 13320265, 13386056, 13451847, 13583430, 13649220,
- 13715011, 13780802, 13912129, 13977920, 14043711, 14109502, 14241085, 14306875,
- 14372666, 14438457, 14504504, 14570295, 14636086, 14702132, 14833459, 14899250,
- 14965297, 15031088, 15096878, 15097389, 15163180, 15229227, 15295018, 15361064,
- 15426855, 15492902, 15558693, 15559203, 15625250, 15691041, 15757087, 15757342,
- 15823389, 15889436, 15889690, 15955737, 15956248, 16022038, 16088085, 16088596,
- 16154642, 16154897, 16220944, 16221454, 16287501, 16287756, 16288267, 16354313,
- 16354824, 16355336, 16421127, 16421638, 16422150, 16422662, 16488710, 16489222,
- 16489734, 16489991, 16490503, 16491016, 16491530, 16492043, 16492557, 16493070,
- 16493584, 16494098, 16494612, 16494870, 16495384, 16495898, 16496412, 16496926,
- 16431905, 16432419, 16432933, 16433448, 16368426, 16368940, 16369455, 16304433,
- 16304948, 16305463, 16240442, 16240956, 16175935, 16176450, 16111429, 16111944,
- 16046923, 16047183, 15982162, 15982678, 15983193, 15918173, 15918688, 15853668,
- 15853928, 15854444, 15854960, 15855220, 15855737, 15856253, 15922049, 15922309,
- 15988361, 16054157, 16119953, 16186005, 16251801, 16383133, 16448928, 16580260,
- };
- }
-}
diff --git a/src/Spectrogram/Colormaps/Lopora.cs b/src/Spectrogram/Colormaps/Lopora.cs
deleted file mode 100644
index a250f7b..0000000
--- a/src/Spectrogram/Colormaps/Lopora.cs
+++ /dev/null
@@ -1,55 +0,0 @@
-/* Lopora is an open-source weak signal spectrogram by Onno Hoekstra (PA2OHH)
- * This colormap was created to mimic the default colors used by Lopora.
- * https://www.qsl.net/pa2ohh/11lop.htm
- * https://github.com/swharden/Lopora/blob/20afe72416579f8b7d3c8861532c71a95b904066/src/LOPORA-v5a.py#L828-L872
- */
-
-using System;
-
-namespace Spectrogram.Colormaps
-{
- class Lopora : IColormap
- {
- public (byte r, byte g, byte b) GetRGB(byte value)
- {
- byte[] bytes = BitConverter.GetBytes(rgb[value]);
- return (bytes[2], bytes[1], bytes[0]);
- }
-
- private readonly int[] rgb =
- {
- 0000000000, 0000069696, 0000137036, 0000203860, 0000270426, 0000336991, 0000403300, 0000469608,
- 0000535915, 0000602222, 0000668273, 0000734580, 0000800631, 0000866681, 0000932987, 0000999037,
- 0001065088, 0001131137, 0001197187, 0001262981, 0001329031, 0001395080, 0001461130, 0001526924,
- 0001592973, 0001659023, 0001724816, 0001790865, 0001856659, 0001922708, 0001988501, 0002054550,
- 0002120344, 0002186393, 0002252186, 0002317979, 0002384028, 0002449821, 0002515614, 0002581663,
- 0002647456, 0002713249, 0002779042, 0002845091, 0002910884, 0002976677, 0003042470, 0003108263,
- 0003174056, 0003240105, 0003305898, 0003371690, 0003437483, 0003503276, 0003569069, 0003634862,
- 0003700654, 0003766447, 0003832240, 0003898033, 0003963825, 0004029618, 0004095411, 0004161204,
- 0004227252, 0004292789, 0004358582, 0004424374, 0004490167, 0004555960, 0004621752, 0004687545,
- 0004753338, 0004819130, 0004884923, 0004950716, 0005016508, 0005082301, 0005148093, 0005213886,
- 0005279679, 0005345215, 0005411008, 0005476800, 0005542593, 0005608386, 0005674178, 0005739971,
- 0005805763, 0005871300, 0005937092, 0006002885, 0006068677, 0006134470, 0006200263, 0006265799,
- 0006331592, 0006397384, 0006463177, 0006528969, 0006594506, 0006660298, 0006726091, 0006791883,
- 0006857676, 0006923212, 0006989005, 0007054797, 0007120590, 0007186126, 0007251918, 0007317711,
- 0007383503, 0007449040, 0007514832, 0007580625, 0007646417, 0007711954, 0007777746, 0007843539,
- 0007909331, 0007974867, 0008040660, 0008106452, 0008171989, 0008237781, 0008303574, 0008369366,
- 0008434902, 0008435159, 0008500951, 0008566488, 0008632280, 0008698072, 0008763609, 0008829401,
- 0008895194, 0008960986, 0009026522, 0009092315, 0009158107, 0009223644, 0009289436, 0009355228,
- 0009420765, 0009486557, 0009552350, 0009617886, 0009683678, 0009749471, 0009815007, 0009880799,
- 0009946336, 0010012128, 0010077921, 0010143457, 0010209249, 0010275042, 0010340578, 0010406370,
- 0010472163, 0010537699, 0010603491, 0010669028, 0010734820, 0010800612, 0010866149, 0010931941,
- 0010997734, 0011063270, 0011129062, 0011194599, 0011260391, 0011326183, 0011391720, 0011457512,
- 0011523048, 0011588841, 0011654633, 0011720169, 0011785962, 0011851498, 0011917290, 0011983082,
- 0012048619, 0012114411, 0012179947, 0012245740, 0012311532, 0012377068, 0012442861, 0012508397,
- 0012574189, 0012639726, 0012705518, 0012771310, 0012836847, 0012902639, 0012968175, 0013033967,
- 0013099504, 0013165296, 0013231088, 0013296625, 0013362417, 0013427953, 0013493746, 0013559282,
- 0013625074, 0013690610, 0013756403, 0013822195, 0013887731, 0013953524, 0014019060, 0014084852,
- 0014150388, 0014216181, 0014281717, 0014347509, 0014413046, 0014478838, 0014544374, 0014610166,
- 0014675959, 0014741495, 0014807287, 0014872823, 0014938616, 0015004152, 0015069944, 0015135481,
- 0015201273, 0015266809, 0015332601, 0015398138, 0015463930, 0015529466, 0015595258, 0015660795,
- 0015726587, 0015792123, 0015857915, 0015923452, 0015989244, 0016054780, 0016120572, 0016186109,
- 0016251901, 0016317437, 0016383229, 0016448766, 0016514558, 0016580350, 0016645887, 0016711679,
- };
- }
-}
diff --git a/src/Spectrogram/Colormaps/Magma.cs b/src/Spectrogram/Colormaps/Magma.cs
deleted file mode 100644
index 105a177..0000000
--- a/src/Spectrogram/Colormaps/Magma.cs
+++ /dev/null
@@ -1,57 +0,0 @@
-/* Magma is a colormap by Nathaniel J. Smith and Stefan van der Walt
- * https://bids.github.io/colormap/
- * https://github.com/BIDS/colormap/blob/master/colormaps.py
- *
- * This colormap is provided under the CC0 license / public domain dedication
- * http://creativecommons.org/publicdomain/zero/1.0/
- */
-
-using System;
-
-namespace Spectrogram.Colormaps
-{
- class Magma : IColormap
- {
- public (byte r, byte g, byte b) GetRGB(byte value)
- {
- byte[] bytes = BitConverter.GetBytes(rgb[value]);
- return (bytes[2], bytes[1], bytes[0]);
- }
-
- private readonly int[] rgb =
- {
- 00000003, 00000004, 00000006, 00065543, 00065801, 00065803, 00131597, 00131599,
- 00197393, 00262931, 00263189, 00328727, 00394521, 00460059, 00525853, 00591647,
- 00657186, 00722980, 00788774, 00854568, 00920106, 00985900, 01051695, 01117233,
- 01183027, 01314101, 01379896, 01445434, 01511228, 01576767, 01708097, 01773636,
- 01839174, 01970249, 02036043, 02101581, 02232656, 02298194, 02429269, 02494807,
- 02625881, 02756956, 02822494, 02953312, 03084386, 03149925, 03280999, 03412072,
- 03477354, 03608428, 03739502, 03870575, 03936113, 04067186, 04198259, 04329332,
- 04394869, 04525942, 04657015, 04722808, 04853881, 04919417, 05050746, 05181819,
- 05247611, 05378684, 05444476, 05575549, 05706877, 05772670, 05903742, 05969534,
- 06100862, 06166399, 06297727, 06363263, 06494591, 06625920, 06691456, 06822784,
- 06888576, 07019648, 07085440, 07216769, 07282305, 07413633, 07544705, 07610497,
- 07741825, 07807361, 07938689, 08004225, 08135553, 08266881, 08332417, 08463745,
- 08529281, 08660609, 08726145, 08857473, 08988801, 09054337, 09185664, 09251200,
- 09382528, 09513600, 09579392, 09710464, 09776256, 09907327, 10038655, 10104191,
- 10235519, 10366590, 10432382, 10563454, 10694782, 10760317, 10891645, 10957181,
- 11088508, 11219836, 11285371, 11416699, 11547771, 11613562, 11744634, 11875961,
- 11941497, 12072824, 12138360, 12269687, 12401015, 12466550, 12597877, 12728949,
- 12794740, 12926068, 12991603, 13122930, 13254258, 13319793, 13451120, 13516912,
- 13648239, 13714030, 13845101, 13910893, 14042220, 14108011, 14239338, 14305129,
- 14436457, 14502248, 14568039, 14699366, 14765158, 14830949, 14962276, 15028323,
- 15094114, 15159906, 15225953, 15357280, 15423072, 15489119, 15554911, 15620958,
- 15621469, 15687261, 15753309, 15819100, 15885148, 15951196, 15951707, 16017499,
- 16083547, 16084059, 16150107, 16150619, 16216411, 16216924, 16282972, 16283484,
- 16349532, 16350045, 16350557, 16416606, 16416862, 16417375, 16483424, 16483936,
- 16484449, 16484962, 16551011, 16551523, 16552036, 16552549, 16552806, 16618855,
- 16619368, 16619881, 16620394, 16620907, 16621420, 16621934, 16622191, 16622704,
- 16688753, 16689267, 16689780, 16690293, 16690806, 16691064, 16691577, 16692091,
- 16692604, 16693117, 16693631, 16694144, 16694402, 16694915, 16695429, 16695942,
- 16696456, 16696969, 16697227, 16697741, 16698254, 16633232, 16633746, 16634259,
- 16634517, 16635031, 16635544, 16636058, 16636572, 16637085, 16637343, 16637857,
- 16638371, 16573349, 16573862, 16574120, 16574634, 16575148, 16575662, 16576176,
- 16576689, 16576947, 16577461, 16577975, 16512953, 16513467, 16513725, 16514239,
- };
- }
-}
diff --git a/src/Spectrogram/Colormaps/Plasma.cs b/src/Spectrogram/Colormaps/Plasma.cs
deleted file mode 100644
index b03dc59..0000000
--- a/src/Spectrogram/Colormaps/Plasma.cs
+++ /dev/null
@@ -1,57 +0,0 @@
-/* Plasma is a colormap by Nathaniel J. Smith and Stefan van der Walt
- * https://bids.github.io/colormap/
- * https://github.com/BIDS/colormap/blob/master/colormaps.py
- *
- * This colormap is provided under the CC0 license / public domain dedication
- * http://creativecommons.org/publicdomain/zero/1.0/
- */
-
-using System;
-
-namespace Spectrogram.Colormaps
-{
- class Plasma : IColormap
- {
- public (byte r, byte g, byte b) GetRGB(byte value)
- {
- byte[] bytes = BitConverter.GetBytes(rgb[value]);
- return (bytes[2], bytes[1], bytes[0]);
- }
-
- private readonly int[] rgb =
- {
- 00788358, 01050503, 01246857, 01377930, 01574539, 01771148, 01902221, 02033038,
- 02164111, 02295184, 02426257, 02557330, 02688403, 02819476, 02950292, 03081365,
- 03212438, 03343511, 03409048, 03540120, 03671193, 03802266, 03867546, 03998619,
- 04129692, 04195228, 04326301, 04457374, 04522910, 04653727, 04784799, 04850336,
- 04981409, 05112481, 05178018, 05308834, 05374371, 05505443, 05636515, 05702052,
- 05833124, 05898405, 06029477, 06160549, 06226086, 06357158, 06422694, 06553767,
- 06619303, 06750375, 06815911, 06946983, 07078056, 07143592, 07274664, 07340200,
- 07471272, 07536808, 07667880, 07733672, 07864744, 07930280, 08061608, 08127143,
- 08258471, 08324007, 08455335, 08520871, 08652198, 08717990, 08783782, 08914853,
- 08980645, 09111972, 09177764, 09309348, 09375139, 09440931, 09572258, 09638049,
- 09769377, 09835168, 09900960, 10032287, 10098078, 10164126, 10295453, 10361244,
- 10427035, 10492827, 10624154, 10689945, 10755736, 10821527, 10953111, 11018902,
- 11084693, 11150484, 11281811, 11347602, 11413393, 11479184, 11545231, 11611023,
- 11676814, 11808141, 11873932, 11939723, 12005514, 12071561, 12137352, 12203143,
- 12268934, 12334725, 12400516, 12466307, 12532098, 12598145, 12663936, 12729728,
- 12795519, 12861310, 12927101, 12992892, 13058683, 13124730, 13190521, 13256312,
- 13322103, 13387894, 13453685, 13519477, 13585268, 13651315, 13717106, 13717361,
- 13783152, 13848943, 13914734, 13980525, 14046573, 14112364, 14112619, 14178410,
- 14244201, 14309992, 14375783, 14441830, 14442086, 14507877, 14573668, 14639459,
- 14639714, 14705761, 14771552, 14837344, 14903135, 14903390, 14969437, 15035228,
- 15035483, 15101274, 15167066, 15233113, 15233368, 15299159, 15364950, 15365205,
- 15431252, 15497044, 15497299, 15563090, 15563601, 15629392, 15695183, 15695438,
- 15761485, 15761741, 15827532, 15893579, 15893834, 15959625, 15959880, 16025927,
- 16026183, 16091974, 16092485, 16158276, 16158531, 16159042, 16224833, 16225089,
- 16291136, 16291391, 16291902, 16357693, 16357948, 16423995, 16424250, 16424762,
- 16425017, 16491064, 16491319, 16491574, 16557621, 16557877, 16558388, 16558643,
- 16559154, 16559409, 16625457, 16625712, 16626223, 16626478, 16626989, 16627245,
- 16627756, 16628011, 16628523, 16628778, 16629289, 16629801, 16630056, 16630568,
- 16630823, 16631334, 16566054, 16566566, 16567077, 16567333, 16567845, 16502820,
- 16503076, 16503588, 16438564, 16438820, 16439332, 16374052, 16374564, 16309540,
- 16310052, 16244772, 16245285, 16180261, 16180517, 16115494, 16116006, 16050726,
- 15985702, 15986214, 15921190, 15921446, 15856422, 15791397, 15791651, 15726625,
- };
- }
-}
diff --git a/src/Spectrogram/Colormaps/Turbo.cs b/src/Spectrogram/Colormaps/Turbo.cs
deleted file mode 100644
index e935210..0000000
--- a/src/Spectrogram/Colormaps/Turbo.cs
+++ /dev/null
@@ -1,53 +0,0 @@
-// This colormap was created by Scott Harden on 2020-06-16 and is released under a MIT license.
-// It was designed to mimic Turbo, but is not a copy of or derived from Turbo source code.
-// https://ai.googleblog.com/2019/08/turbo-improved-rainbow-colormap-for.html
-
-using System;
-
-namespace Spectrogram.Colormaps
-{
- class Turbo : IColormap
- {
- public (byte r, byte g, byte b) GetRGB(byte value)
- {
- byte[] bytes = BitConverter.GetBytes(argb[value]);
- return (bytes[2], bytes[1], bytes[0]);
- }
-
- private readonly int[] argb =
- {
- -13559489, -13493436, -13427382, -13361328, -13295018, -13228964, -13162911, -13096857,
- -13030547, -12964493, -12898440, -12832130, -12766077, -12700023, -12633970, -12567660,
- -12501607, -12435554, -12369245, -12303192, -12237139, -12171086, -12170313, -12104260,
- -12038208, -12037436, -11971383, -11905331, -11904559, -11838507, -11837991, -11771940,
- -11771168, -11770653, -11770138, -11703831, -11703316, -11702801, -11702287, -11701517,
- -11701003, -11700489, -11765255, -11764742, -11764228, -11828995, -11828482, -11893506,
- -11892737, -11957761, -12022785, -12022017, -12087042, -12152067, -12217092, -12347397,
- -12412423, -12477448, -12542218, -12672780, -12737806, -12802577, -12933139, -12998166,
- -13128729, -13193500, -13324063, -13389091, -13519654, -13584682, -13714989, -13780017,
- -13910581, -13975609, -14106173, -14171201, -14301765, -14366793, -14431822, -14562386,
- -14627414, -14692442, -14757471, -14822499, -14887527, -14952556, -14952048, -15017332,
- -15082361, -15081853, -15147137, -15146629, -15146121, -15145869, -15145361, -15145109,
- -15079065, -15078812, -15013024, -15012515, -14946726, -14880938, -14749356, -14683567,
- -14617778, -14486453, -14355127, -14223801, -14092475, -13961405, -13764543, -13633217,
- -13436355, -13239748, -13108422, -12911559, -12714952, -12518089, -12255946, -12059339,
- -11862476, -11600333, -11403725, -11141582, -10879182, -10682575, -10420431, -10158287,
- -09896143, -09633999, -09372111, -09175503, -08913359, -08651215, -08389327, -08127183,
- -07865294, -07603150, -07341262, -07079117, -06817229, -06555341, -06293452, -06031308,
- -05834955, -05573067, -05311178, -05114826, -04852937, -04656585, -04394952, -04198600,
- -04002247, -03740358, -03544262, -03347910, -03217349, -03020997, -02824900, -02628804,
- -02497987, -02301891, -02171331, -02040770, -01910210, -01779394, -01648834, -01518273,
- -01387713, -01257153, -01126849, -01061824, -00931264, -00866240, -00801216, -00670656,
- -00605888, -00540864, -00475840, -00411072, -00346048, -00346560, -00281792, -00282304,
- -00217536, -00218048, -00153280, -00153793, -00154561, -00155073, -00155841, -00156354,
- -00157122, -00157634, -00158403, -00224451, -00225219, -00291524, -00292036, -00358341,
- -00424389, -00490694, -00491206, -00557511, -00623559, -00755400, -00821705, -00887754,
- -00954058, -01085643, -01151948, -01283533, -01349837, -01481422, -01613263, -01679312,
- -01811153, -01942738, -02074579, -02206164, -02337749, -02469590, -02601175, -02733016,
- -02930137, -03061978, -03193563, -03390940, -03522526, -03654111, -03851488, -03983073,
- -04180450, -04377572, -04509157, -04706534, -04838119, -05035497, -05232618, -05429739,
- -05561580, -05758702, -05956079, -06153200, -06350322, -06482163, -06679284, -06876662,
- -07073783, -07270904, -07468282, -07665403, -07862524, -08059902, -08257023, -08388608,
- };
- }
-}
diff --git a/src/Spectrogram/Colormaps/Viridis.cs b/src/Spectrogram/Colormaps/Viridis.cs
deleted file mode 100644
index a6e0bb1..0000000
--- a/src/Spectrogram/Colormaps/Viridis.cs
+++ /dev/null
@@ -1,57 +0,0 @@
-/* Viridis is a colormap by Nathaniel J. Smith, Stefan van der Walt, and Eric Firing
- * https://bids.github.io/colormap/
- * https://github.com/BIDS/colormap/blob/master/colormaps.py
- *
- * This colormap is provided under the CC0 license / public domain dedication
- * http://creativecommons.org/publicdomain/zero/1.0/
- */
-
-using System;
-
-namespace Spectrogram.Colormaps
-{
- class Viridis : IColormap
- {
- public (byte r, byte g, byte b) GetRGB(byte value)
- {
- byte[] bytes = BitConverter.GetBytes(rgb[value]);
- return (bytes[2], bytes[1], bytes[0]);
- }
-
- private readonly int[] rgb =
- {
- 04456788, 04457045, 04457303, 04523352, 04523610, 04524123, 04589916, 04590430,
- 04590687, 04591201, 04656994, 04657507, 04657765, 04658278, 04658535, 04658793,
- 04659306, 04725099, 04725356, 04725870, 04726127, 04726384, 04726897, 04727154,
- 04727411, 04727668, 04662645, 04662902, 04663159, 04663416, 04663929, 04664186,
- 04664443, 04599164, 04599676, 04599933, 04600190, 04534911, 04535423, 04535680,
- 04535937, 04470657, 04471170, 04405891, 04406147, 04406404, 04341124, 04341381,
- 04341893, 04276614, 04276870, 04211591, 04211847, 04146567, 04147080, 04081800,
- 04082057, 04016777, 04017033, 04017289, 03952010, 03952266, 03887242, 03887498,
- 03822219, 03822475, 03757195, 03757451, 03692171, 03692428, 03627148, 03627404,
- 03562124, 03562380, 03497100, 03497356, 03432077, 03432333, 03367053, 03367309,
- 03302029, 03302285, 03237005, 03237261, 03237517, 03172237, 03172493, 03107213,
- 03107469, 03042190, 03042446, 03042702, 02977422, 02977678, 02912398, 02912654,
- 02912910, 02847630, 02847886, 02782606, 02782862, 02783118, 02717838, 02718094,
- 02652814, 02652814, 02653070, 02587790, 02588046, 02588302, 02523022, 02523278,
- 02523534, 02458254, 02458509, 02393229, 02393485, 02393741, 02328461, 02328717,
- 02328973, 02263437, 02263693, 02263949, 02198669, 02198924, 02199180, 02133900,
- 02134156, 02134412, 02069132, 02069387, 02069643, 02069899, 02070155, 02004874,
- 02005130, 02005386, 02005386, 02005641, 02005897, 02006153, 02006408, 02006664,
- 02006920, 02007175, 02072967, 02073222, 02073478, 02139269, 02139525, 02205317,
- 02205572, 02271108, 02336899, 02337154, 02402946, 02468737, 02534529, 02600320,
- 02666111, 02731903, 02797694, 02863485, 02929021, 03060348, 03126139, 03191930,
- 03323258, 03389049, 03520376, 03586167, 03717494, 03783030, 03914357, 04045684,
- 04111475, 04242802, 04374129, 04505200, 04570991, 04702318, 04833645, 04964972,
- 05096043, 05227369, 05358696, 05490023, 05621350, 05752421, 05883748, 06015074,
- 06211937, 06343008, 06474335, 06605661, 06802524, 06933595, 07064921, 07196248,
- 07392854, 07524181, 07655508, 07852114, 07983441, 08180303, 08311374, 08508236,
- 08639307, 08836169, 08967495, 09164102, 09295428, 09492035, 09623361, 09819967,
- 09951294, 10147900, 10344762, 10475832, 10672695, 10869301, 11000627, 11197234,
- 11394096, 11525166, 11722028, 11918635, 12049705, 12246567, 12443174, 12574500,
- 12771106, 12967713, 13099039, 13295646, 13492253, 13623580, 13820187, 13951258,
- 14148121, 14344728, 14475800, 14672664, 14803736, 15000344, 15197209, 15328281,
- 15524890, 15656219, 15852828, 15983902, 16180767, 16311841, 16442914, 16639780,
- };
- }
-}
diff --git a/src/Spectrogram/IColormap.cs b/src/Spectrogram/IColormap.cs
deleted file mode 100644
index 5c9c79e..0000000
--- a/src/Spectrogram/IColormap.cs
+++ /dev/null
@@ -1,11 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Text;
-
-namespace Spectrogram
-{
- public interface IColormap
- {
- (byte r, byte g, byte b) GetRGB(byte value);
- }
-}
diff --git a/src/Spectrogram/Image.cs b/src/Spectrogram/Image.cs
index 283543b..7413b38 100644
--- a/src/Spectrogram/Image.cs
+++ b/src/Spectrogram/Image.cs
@@ -1,16 +1,11 @@
-using System;
-using System.Collections.Generic;
-using System.Drawing;
-using System.Drawing.Imaging;
-using System.Runtime.InteropServices;
-using System.Text;
-using System.Threading.Tasks;
+using System.Collections.Generic;
+using SkiaSharp;
namespace Spectrogram
{
public static class Image
{
- public static Bitmap GetBitmap(List ffts, Colormap cmap, double intensity = 1,
+ public static SKBitmap GetBitmap(List ffts, Colormap cmap, double intensity = 1,
bool dB = false, double dBScale = 1, bool roll = false, int rollOffset = 0, bool rotate = false)
{
diff --git a/src/Spectrogram/ImageMaker.cs b/src/Spectrogram/ImageMaker.cs
index dd4348f..c55ef99 100644
--- a/src/Spectrogram/ImageMaker.cs
+++ b/src/Spectrogram/ImageMaker.cs
@@ -1,10 +1,8 @@
using System;
using System.Collections.Generic;
-using System.Drawing;
-using System.Drawing.Imaging;
using System.Runtime.InteropServices;
-using System.Text;
using System.Threading.Tasks;
+using SkiaSharp;
namespace Spectrogram
{
@@ -53,37 +51,35 @@ public ImageMaker()
{
}
-
- public Bitmap GetBitmap(List ffts)
+
+ public SKBitmap GetBitmap(List ffts)
{
if (ffts.Count == 0)
throw new ArgumentException("Not enough data in FFTs to generate an image yet.");
- int Width = IsRotated ? ffts[0].Length : ffts.Count;
- int Height = IsRotated ? ffts.Count : ffts[0].Length;
-
- Bitmap bmp = new(Width, Height, PixelFormat.Format8bppIndexed);
- Colormap.Apply(bmp);
+ int width = IsRotated ? ffts[0].Length : ffts.Count;
+ int height = IsRotated ? ffts.Count : ffts[0].Length;
- Rectangle lockRect = new(0, 0, Width, Height);
- BitmapData bitmapData = bmp.LockBits(lockRect, ImageLockMode.ReadOnly, bmp.PixelFormat);
- int stride = bitmapData.Stride;
+ var imageInfo = new SKImageInfo(width, height, SKColorType.Gray8);
+ var bitmap = new SKBitmap(imageInfo);
+
+ int pixelCount = width * height;
+ byte[] pixelBuffer = new byte[pixelCount];
- byte[] bytes = new byte[bitmapData.Stride * bmp.Height];
- Parallel.For(0, Width, col =>
+ Parallel.For(0, width, col =>
{
int sourceCol = col;
if (IsRoll)
{
- sourceCol += Width - RollOffset % Width;
- if (sourceCol >= Width)
- sourceCol -= Width;
+ sourceCol += width - RollOffset % width;
+ if (sourceCol >= width)
+ sourceCol -= width;
}
- for (int row = 0; row < Height; row++)
+ for (int row = 0; row < height; row++)
{
double value = IsRotated
- ? ffts[Height - row - 1][sourceCol]
+ ? ffts[height - row - 1][sourceCol]
: ffts[sourceCol][row];
if (IsDecibel)
@@ -91,15 +87,18 @@ public Bitmap GetBitmap(List ffts)
value *= Intensity;
value = Math.Min(value, 255);
- int bytePosition = (Height - 1 - row) * stride + col;
- bytes[bytePosition] = (byte)value;
+
+ int bytePosition = (height - 1 - row) * width + col;
+ pixelBuffer[bytePosition] = (byte)value;
}
});
- Marshal.Copy(bytes, 0, bitmapData.Scan0, bytes.Length);
- bmp.UnlockBits(bitmapData);
+ IntPtr pixelPtr = bitmap.GetPixels();
+ Marshal.Copy(pixelBuffer, 0, pixelPtr, pixelBuffer.Length);
- return bmp;
+ SKBitmap newBitmap = Colormap.ApplyFilter(bitmap);
+ bitmap.Dispose();
+ return newBitmap;
}
}
}
diff --git a/src/Spectrogram/Scale.cs b/src/Spectrogram/Scale.cs
index 767b21b..e4a7788 100644
--- a/src/Spectrogram/Scale.cs
+++ b/src/Spectrogram/Scale.cs
@@ -1,60 +1,58 @@
-using System;
+using SkiaSharp;
using System.Collections.Generic;
-using System.Drawing;
-using System.Drawing.Imaging;
-using System.Linq;
-using System.Text;
-namespace Spectrogram
+namespace Spectrogram;
+
+static class Scale
{
- static class Scale
+ public static SKBitmap Vertical(int width, Settings settings, int offsetHz = 0, int tickSize = 3, int reduction = 1)
{
- public static Bitmap Vertical(int width, Settings settings, int offsetHz = 0, int tickSize = 3, int reduction = 1)
+ double tickHz = 1;
+ int minSpacingPx = 50;
+ double[] multipliers = { 2, 2.5, 2 };
+ int multiplier = 0;
+
+ while (true)
+ {
+ tickHz *= multipliers[multiplier++ % multipliers.Length];
+ double tickCount = settings.FreqSpan / tickHz;
+ double pxBetweenTicks = settings.Height / tickCount;
+ if (pxBetweenTicks >= minSpacingPx * reduction)
+ break;
+ }
+
+ var imageInfo = new SKImageInfo(width, settings.Height / reduction, SKColorType.Rgba8888);
+ var bitmap = new SKBitmap(imageInfo);
+ using var canvas = new SKCanvas(bitmap);
+ canvas.Clear(SKColors.White);
+
+ var paint = new SKPaint
+ {
+ Color = SKColors.Black,
+ TextSize = 10,
+ IsAntialias = true,
+ Typeface = SKTypeface.FromFamilyName("Monospace")
+ };
+
+ List freqs = new List();
+ for (double f = settings.FreqMin; f <= settings.FreqMax; f += tickHz)
+ freqs.Add(f);
+
+ if (freqs.Count >= 2)
{
- double tickHz = 1;
- int minSpacingPx = 50;
- double[] multipliers = { 2, 2.5, 2 };
- int multiplier = 0;
- while (true)
- {
- tickHz *= multipliers[multiplier++ % multipliers.Length];
- double tickCount = settings.FreqSpan / tickHz;
- double pxBetweenTicks = settings.Height / tickCount;
- if (pxBetweenTicks >= minSpacingPx * reduction)
- break;
- }
-
- Bitmap bmp = new Bitmap(width, settings.Height / reduction, PixelFormat.Format32bppPArgb);
-
- using (var gfx = Graphics.FromImage(bmp))
- using (var pen = new Pen(Color.Black))
- using (var brush = new SolidBrush(Color.Black))
- using (var font = new Font(FontFamily.GenericMonospace, 10))
- using (var sf = new StringFormat() { LineAlignment = StringAlignment.Center })
- {
- gfx.Clear(Color.White);
-
- List freqs = new List();
-
- for (double f = settings.FreqMin; f <= settings.FreqMax; f += tickHz)
- freqs.Add(f);
-
- // don't show first or last tick
- if (freqs.Count >= 2)
- {
- freqs.RemoveAt(0);
- freqs.RemoveAt(freqs.Count - 1);
- }
-
- foreach (var freq in freqs)
- {
- int y = settings.PixelY(freq) / reduction;
- gfx.DrawLine(pen, 0, y, tickSize, y);
- gfx.DrawString($"{freq + offsetHz:N0} Hz", font, brush, tickSize, y, sf);
- }
- }
-
- return bmp;
+ freqs.RemoveAt(0);
+ freqs.RemoveAt(freqs.Count - 1);
}
+
+ foreach (var freq in freqs)
+ {
+ int y = settings.PixelY(freq) / reduction;
+ canvas.DrawLine(0, y, tickSize, y, paint);
+
+ var text = $"{freq + offsetHz:N0} Hz";
+ canvas.DrawText(text, tickSize + 2, y + 5, paint);
+ }
+
+ return bitmap;
}
-}
\ No newline at end of file
+}
diff --git a/src/Spectrogram/Settings.cs b/src/Spectrogram/Settings.cs
index a799260..3ec35f1 100644
--- a/src/Spectrogram/Settings.cs
+++ b/src/Spectrogram/Settings.cs
@@ -31,7 +31,9 @@ class Settings
public Settings(int sampleRate, int fftSize, int stepSize, double minFreq, double maxFreq, int offsetHz)
{
- if (FftSharp.Transform.IsPowerOfTwo(fftSize) == false)
+ static bool IsPowerOfTwo(int x) => ((x & (x - 1)) == 0) && (x > 0);
+
+ if (IsPowerOfTwo(fftSize) == false)
throw new ArgumentException("FFT size must be a power of 2");
// FFT info
diff --git a/src/Spectrogram/Spectrogram.csproj b/src/Spectrogram/Spectrogram.csproj
index 4804cff..4bfc42f 100644
--- a/src/Spectrogram/Spectrogram.csproj
+++ b/src/Spectrogram/Spectrogram.csproj
@@ -1,8 +1,7 @@
-
- netstandard2.0;net6.0
- 1.6.1
+ netstandard2.0
+ 2.0.0-alpha
A .NET Standard library for creating spectrograms
Scott Harden
Harden Technologies, LLC
@@ -23,20 +22,13 @@
true
latest
-
-
-
-
-
-
- runtime; build; native; contentfiles; analyzers; buildtransitive
- all
-
+
+
+
-
-
+
\ No newline at end of file
diff --git a/src/Spectrogram/SpectrogramGenerator.cs b/src/Spectrogram/SpectrogramGenerator.cs
index 68a9c3b..edb85b4 100644
--- a/src/Spectrogram/SpectrogramGenerator.cs
+++ b/src/Spectrogram/SpectrogramGenerator.cs
@@ -1,446 +1,408 @@
using System;
using System.Collections.Generic;
-using System.Drawing;
-using System.Drawing.Imaging;
using System.IO;
using System.Threading.Tasks;
+using SkiaSharp;
-namespace Spectrogram
+namespace Spectrogram;
+
+public class SpectrogramGenerator
{
- public class SpectrogramGenerator
+ ///
+ /// Number of pixel columns (FFT samples) in the spectrogram image
+ ///
+ public int Width { get => FFTs.Count; }
+
+ ///
+ /// Number of pixel rows (frequency bins) in the spectrogram image
+ ///
+ public int Height { get => Settings.Height; }
+
+ ///
+ /// Number of samples to use for each FFT (must be a power of 2)
+ ///
+ public int FftSize { get => Settings.FftSize; }
+
+ ///
+ /// Vertical resolution (frequency bin size depends on FftSize and SampleRate)
+ ///
+ public double HzPerPx { get => Settings.HzPerPixel; }
+
+ ///
+ /// Horizontal resolution (seconds per pixel depends on StepSize)
+ ///
+ public double SecPerPx { get => Settings.StepLengthSec; }
+
+ ///
+ /// Number of FFTs that remain to be processed for data which has been added but not yet analyzed
+ ///
+ public int FftsToProcess { get => (UnprocessedData.Count - Settings.FftSize) / Settings.StepSize; }
+
+ ///
+ /// Total number of FFT steps processed
+ ///
+ public int FftsProcessed { get; private set; }
+
+ ///
+ /// Index of the pixel column which will be populated next. Location of vertical line for wrap-around displays.
+ ///
+ public int NextColumnIndex { get => Width > 0 ? (FftsProcessed + rollOffset) % Width : 0; }
+
+ ///
+ /// This value is added to displayed frequency axis tick labels
+ ///
+ public int OffsetHz { get => Settings.OffsetHz; set { Settings.OffsetHz = value; } }
+
+ ///
+ /// Number of samples per second
+ ///
+ public int SampleRate { get => Settings.SampleRate; }
+
+ ///
+ /// Number of samples to step forward after each FFT is processed.
+ /// This value controls the horizontal resolution of the spectrogram.
+ ///
+ public int StepSize { get => Settings.StepSize; }
+
+ ///
+ /// The spectrogram is trimmed to cut-off frequencies below this value.
+ ///
+ public double FreqMax { get => Settings.FreqMax; }
+
+ ///
+ /// The spectrogram is trimmed to cut-off frequencies above this value.
+ ///
+ public double FreqMin { get => Settings.FreqMin; }
+
+ ///
+ /// This module contains detailed FFT/Spectrogram settings
+ ///
+ private readonly Settings Settings;
+
+ ///
+ /// This is the list of FFTs which is translated to the spectrogram image when it is requested.
+ /// The length of this list is the spectrogram width.
+ /// The length of the arrays in this list is the spectrogram height.
+ ///
+ private readonly List FFTs = [];
+
+ ///
+ /// This list contains data values which have not yet been processed.
+ /// Process() processes all unprocessed data.
+ /// This list may not be empty after processing if there aren't enough values to fill a full FFT (FftSize).
+ ///
+ private readonly List UnprocessedData;
+
+ ///
+ /// Colormap to use when generating future FFTs.
+ ///
+ public Colormap Colormap = new(new ScottPlot.Colormaps.Viridis());
+
+ ///
+ /// Instantiate a spectrogram generator.
+ /// This module calculates the FFT over a moving window as data comes in.
+ /// Using the Add() method to load new data and process it as it arrives.
+ ///
+ /// Number of samples per second (Hz)
+ /// Number of samples to use for each FFT operation. This value must be a power of 2.
+ /// Number of samples to step forward
+ /// Frequency data lower than this value (Hz) will not be stored
+ /// Frequency data higher than this value (Hz) will not be stored
+ /// Spectrogram output will always be sized to this width (column count)
+ /// This value will be added to displayed frequency axis tick labels
+ /// Analyze this data immediately (alternative to calling Add() later)
+ public SpectrogramGenerator(
+ int sampleRate,
+ int fftSize,
+ int stepSize,
+ double minFreq = 0,
+ double maxFreq = double.PositiveInfinity,
+ int? fixedWidth = null,
+ int offsetHz = 0,
+ List initialAudioList = null)
{
- ///
- /// Number of pixel columns (FFT samples) in the spectrogram image
- ///
- public int Width { get => FFTs.Count; }
-
- ///
- /// Number of pixel rows (frequency bins) in the spectrogram image
- ///
- public int Height { get => Settings.Height; }
-
- ///
- /// Number of samples to use for each FFT (must be a power of 2)
- ///
- public int FftSize { get => Settings.FftSize; }
-
- ///
- /// Vertical resolution (frequency bin size depends on FftSize and SampleRate)
- ///
- public double HzPerPx { get => Settings.HzPerPixel; }
-
- ///
- /// Horizontal resolution (seconds per pixel depends on StepSize)
- ///
- public double SecPerPx { get => Settings.StepLengthSec; }
-
- ///
- /// Number of FFTs that remain to be processed for data which has been added but not yet analuyzed
- ///
- public int FftsToProcess { get => (UnprocessedData.Count - Settings.FftSize) / Settings.StepSize; }
-
- ///
- /// Total number of FFT steps processed
- ///
- public int FftsProcessed { get; private set; }
-
- ///
- /// Index of the pixel column which will be populated next. Location of vertical line for wrap-around displays.
- ///
- public int NextColumnIndex { get => Width > 0 ? (FftsProcessed + rollOffset) % Width : 0; }
-
- ///
- /// This value is added to displayed frequency axis tick labels
- ///
- public int OffsetHz { get => Settings.OffsetHz; set { Settings.OffsetHz = value; } }
-
- ///
- /// Number of samples per second
- ///
- public int SampleRate { get => Settings.SampleRate; }
-
- ///
- /// Number of samples to step forward after each FFT is processed.
- /// This value controls the horizontal resolution of the spectrogram.
- ///
- public int StepSize { get => Settings.StepSize; }
-
- ///
- /// The spectrogram is trimmed to cut-off frequencies below this value.
- ///
- public double FreqMax { get => Settings.FreqMax; }
-
- ///
- /// The spectrogram is trimmed to cut-off frequencies above this value.
- ///
- public double FreqMin { get => Settings.FreqMin; }
-
- ///
- /// This module contains detailed FFT/Spectrogram settings
- ///
- private readonly Settings Settings;
-
- ///
- /// This is the list of FFTs which is translated to the spectrogram image when it is requested.
- /// The length of this list is the spectrogram width.
- /// The length of the arrays in this list is the spectrogram height.
- ///
- private readonly List FFTs = new List();
-
- ///
- /// This list contains data values which have not yet been processed.
- /// Process() processes all unprocessed data.
- /// This list may not be empty after processing if there aren't enough values to fill a full FFT (FftSize).
- ///
- private readonly List UnprocessedData;
-
- ///
- /// Colormap to use when generating future FFTs.
- ///
- public Colormap Colormap = Colormap.Viridis;
-
- ///
- /// Instantiate a spectrogram generator.
- /// This module calculates the FFT over a moving window as data comes in.
- /// Using the Add() method to load new data and process it as it arrives.
- ///
- /// Number of samples per second (Hz)
- /// Number of samples to use for each FFT operation. This value must be a power of 2.
- /// Number of samples to step forward
- /// Frequency data lower than this value (Hz) will not be stored
- /// Frequency data higher than this value (Hz) will not be stored
- /// Spectrogram output will always be sized to this width (column count)
- /// This value will be added to displayed frequency axis tick labels
- /// Analyze this data immediately (alternative to calling Add() later)
- public SpectrogramGenerator(
- int sampleRate,
- int fftSize,
- int stepSize,
- double minFreq = 0,
- double maxFreq = double.PositiveInfinity,
- int? fixedWidth = null,
- int offsetHz = 0,
- List initialAudioList = null)
- {
- Settings = new Settings(sampleRate, fftSize, stepSize, minFreq, maxFreq, offsetHz);
-
- UnprocessedData = initialAudioList ?? new List();
-
- if (fixedWidth.HasValue)
- SetFixedWidth(fixedWidth.Value);
- }
+ Settings = new Settings(sampleRate, fftSize, stepSize, minFreq, maxFreq, offsetHz);
- public override string ToString()
- {
- double processedSamples = FFTs.Count * Settings.StepSize + Settings.FftSize;
- double processedSec = processedSamples / Settings.SampleRate;
- string processedTime = (processedSec < 60) ? $"{processedSec:N2} sec" : $"{processedSec / 60.0:N2} min";
-
- return $"Spectrogram ({Width}, {Height})" +
- $"\n Vertical ({Height} px): " +
- $"{Settings.FreqMin:N0} - {Settings.FreqMax:N0} Hz, " +
- $"FFT size: {Settings.FftSize:N0} samples, " +
- $"{Settings.HzPerPixel:N2} Hz/px" +
- $"\n Horizontal ({Width} px): " +
- $"{processedTime}, " +
- $"window: {Settings.FftLengthSec:N2} sec, " +
- $"step: {Settings.StepLengthSec:N2} sec, " +
- $"overlap: {Settings.StepOverlapFrac * 100:N0}%";
- }
-
- [Obsolete("Assign to the Colormap field")]
- ///
- /// Set the colormap to use for future renders
- ///
- public void SetColormap(Colormap cmap)
- {
- Colormap = cmap ?? this.Colormap;
- }
+ UnprocessedData = initialAudioList ?? new List();
- ///
- /// Load a custom window kernel to multiply against each FFT sample prior to processing.
- /// Windows must be at least the length of FftSize and typically have a sum of 1.0.
- ///
- public void SetWindow(double[] newWindow)
- {
- if (newWindow.Length > Settings.FftSize)
- throw new ArgumentException("window length cannot exceed FFT size");
+ if (fixedWidth.HasValue)
+ SetFixedWidth(fixedWidth.Value);
+ }
- for (int i = 0; i < Settings.FftSize; i++)
- Settings.Window[i] = 0;
+ ///
+ /// Load a custom window kernel to multiply against each FFT sample prior to processing.
+ /// Windows must be at least the length of FftSize and typically have a sum of 1.0.
+ ///
+ public void SetWindow(double[] newWindow)
+ {
+ if (newWindow.Length > Settings.FftSize)
+ throw new ArgumentException("window length cannot exceed FFT size");
- int offset = (Settings.FftSize - newWindow.Length) / 2;
- Array.Copy(newWindow, 0, Settings.Window, offset, newWindow.Length);
- }
+ for (int i = 0; i < Settings.FftSize; i++)
+ Settings.Window[i] = 0;
- [Obsolete("use the Add() method", true)]
- public void AddExtend(float[] values) { }
+ int offset = (Settings.FftSize - newWindow.Length) / 2;
+ Array.Copy(newWindow, 0, Settings.Window, offset, newWindow.Length);
+ }
- [Obsolete("use the Add() method", true)]
- public void AddCircular(float[] values) { }
+ ///
+ /// Load new data into the spectrogram generator
+ ///
+ public void Add(IEnumerable audio, bool process = true)
+ {
+ UnprocessedData.AddRange(audio);
+ if (process)
+ Process();
+ }
- [Obsolete("use the Add() method", true)]
- public void AddScroll(float[] values) { }
+ ///
+ /// The roll offset is used to calculate NextColumnIndex and can be set to a positive number
+ /// to begin adding new columns to the center of the spectrogram.
+ /// This can also be used to artificially move the next column index to zero even though some
+ /// data has already been accumulated.
+ ///
+ private int rollOffset = 0;
+
+ ///
+ /// Reset the next column index such that the next processed FFT will appear at the far left of the spectrogram.
+ ///
+ ///
+ public void RollReset(int offset = 0)
+ {
+ rollOffset = -FftsProcessed + offset;
+ }
- ///
- /// Load new data into the spectrogram generator
- ///
- public void Add(IEnumerable audio, bool process = true)
- {
- UnprocessedData.AddRange(audio);
- if (process)
- Process();
- }
+ ///
+ /// Perform FFT analysis on all unprocessed data
+ ///
+ public double[][] Process()
+ {
+ if (FftsToProcess < 1)
+ return null;
- ///
- /// The roll offset is used to calculate NextColumnIndex and can be set to a positive number
- /// to begin adding new columns to the center of the spectrogram.
- /// This can also be used to artificially move the next column index to zero even though some
- /// data has already been accumulated.
- ///
- private int rollOffset = 0;
-
- ///
- /// Reset the next column index such that the next processed FFT will appear at the far left of the spectrogram.
- ///
- ///
- public void RollReset(int offset = 0)
- {
- rollOffset = -FftsProcessed + offset;
- }
+ int newFftCount = FftsToProcess;
+ double[][] newFfts = new double[newFftCount][];
- ///
- /// Perform FFT analysis on all unprocessed data
- ///
- public double[][] Process()
+ Parallel.For(0, newFftCount, newFftIndex =>
{
- if (FftsToProcess < 1)
- return null;
-
- int newFftCount = FftsToProcess;
- double[][] newFfts = new double[newFftCount][];
-
- Parallel.For(0, newFftCount, newFftIndex =>
- {
- FftSharp.Complex[] buffer = new FftSharp.Complex[Settings.FftSize];
- int sourceIndex = newFftIndex * Settings.StepSize;
- for (int i = 0; i < Settings.FftSize; i++)
- buffer[i].Real = UnprocessedData[sourceIndex + i] * Settings.Window[i];
+ var buffer = new System.Numerics.Complex[Settings.FftSize];
+ int sourceIndex = newFftIndex * Settings.StepSize;
+ for (int i = 0; i < Settings.FftSize; i++)
+ buffer[i] = new(UnprocessedData[sourceIndex + i] * Settings.Window[i], 0);
- FftSharp.Transform.FFT(buffer);
+ FftSharp.FFT.Forward(buffer);
- newFfts[newFftIndex] = new double[Settings.Height];
- for (int i = 0; i < Settings.Height; i++)
- newFfts[newFftIndex][i] = buffer[Settings.FftIndex1 + i].Magnitude / Settings.FftSize;
- });
+ newFfts[newFftIndex] = new double[Settings.Height];
+ for (int i = 0; i < Settings.Height; i++)
+ newFfts[newFftIndex][i] = buffer[Settings.FftIndex1 + i].Magnitude / Settings.FftSize;
+ });
- foreach (var newFft in newFfts)
- FFTs.Add(newFft);
- FftsProcessed += newFfts.Length;
+ foreach (var newFft in newFfts)
+ FFTs.Add(newFft);
+ FftsProcessed += newFfts.Length;
- UnprocessedData.RemoveRange(0, newFftCount * Settings.StepSize);
- PadOrTrimForFixedWidth();
+ UnprocessedData.RemoveRange(0, newFftCount * Settings.StepSize);
+ PadOrTrimForFixedWidth();
- return newFfts;
- }
+ return newFfts;
+ }
- ///
- /// Return a list of the mel-scaled FFTs contained in this spectrogram
- ///
- /// Total number of output bins to use. Choose a value significantly smaller than Height.
- public List GetMelFFTs(int melBinCount)
- {
- if (Settings.FreqMin != 0)
- throw new InvalidOperationException("cannot get Mel spectrogram unless minimum frequency is 0Hz");
+ ///
+ /// Return a list of the mel-scaled FFTs contained in this spectrogram
+ ///
+ /// Total number of output bins to use. Choose a value significantly smaller than Height.
+ public List GetMelFFTs(int melBinCount)
+ {
+ if (Settings.FreqMin != 0)
+ throw new InvalidOperationException("cannot get Mel spectrogram unless minimum frequency is 0Hz");
- var fftsMel = new List();
- foreach (var fft in FFTs)
- fftsMel.Add(FftSharp.Transform.MelScale(fft, SampleRate, melBinCount));
+ var fftsMel = new List();
+ foreach (var fft in FFTs)
+ fftsMel.Add(FftSharp.Mel.Scale(fft, SampleRate, melBinCount));
- return fftsMel;
- }
+ return fftsMel;
+ }
- ///
- /// Create and return a spectrogram bitmap from the FFTs stored in memory.
- ///
- /// Multiply the output by a fixed value to change its brightness.
- /// If true, output will be log-transformed.
- /// If dB scaling is in use, this multiplier will be applied before log transformation.
- /// Behavior of the spectrogram when it is full of data.
- /// If True, the image will be rotated so time flows from top to bottom (rather than left to right).
- /// Roll (true) adds new columns on the left overwriting the oldest ones.
- /// Scroll (false) slides the whole image to the left and adds new columns to the right.
- public Bitmap GetBitmap(double intensity = 1, bool dB = false, double dBScale = 1, bool roll = false, bool rotate = false)
- {
- if (FFTs.Count == 0)
- throw new InvalidOperationException("Not enough data to create an image. " +
- $"Ensure {nameof(Width)} is >0 before calling {nameof(GetBitmap)}().");
+ ///
+ /// Create and return a spectrogram bitmap from the FFTs stored in memory.
+ ///
+ /// Multiply the output by a fixed value to change its brightness.
+ /// If true, output will be log-transformed.
+ /// If dB scaling is in use, this multiplier will be applied before log transformation.
+ /// Behavior of the spectrogram when it is full of data.
+ /// If True, the image will be rotated so time flows from top to bottom (rather than left to right).
+ /// Roll (true) adds new columns on the left overwriting the oldest ones.
+ /// Scroll (false) slides the whole image to the left and adds new columns to the right.
+ public SKBitmap GetBitmap(double intensity = 1, bool dB = false, double dBScale = 1, bool roll = false, bool rotate = false)
+ {
+ if (FFTs.Count == 0)
+ throw new InvalidOperationException("Not enough data to create an image. " +
+ $"Ensure {nameof(Width)} is >0 before calling {nameof(GetBitmap)}().");
- return Image.GetBitmap(FFTs, Colormap, intensity, dB, dBScale, roll, NextColumnIndex, rotate);
- }
+ return Image.GetBitmap(FFTs, Colormap, intensity, dB, dBScale, roll, NextColumnIndex, rotate);
+ }
- ///
- /// Create a Mel-scaled spectrogram.
- ///
- /// Total number of output bins to use. Choose a value significantly smaller than Height.
- /// Multiply the output by a fixed value to change its brightness.
- /// If true, output will be log-transformed.
- /// If dB scaling is in use, this multiplier will be applied before log transformation.
- /// Behavior of the spectrogram when it is full of data.
- /// Roll (true) adds new columns on the left overwriting the oldest ones.
- /// Scroll (false) slides the whole image to the left and adds new columns to the right.
- public Bitmap GetBitmapMel(int melBinCount = 25, double intensity = 1, bool dB = false, double dBScale = 1, bool roll = false) =>
- Image.GetBitmap(GetMelFFTs(melBinCount), Colormap, intensity, dB, dBScale, roll, NextColumnIndex);
-
- [Obsolete("use SaveImage()", true)]
- public void SaveBitmap(Bitmap bmp, string fileName) { }
-
- ///
- /// Generate the spectrogram and save it as an image file.
- ///
- /// Path of the file to save.
- /// Multiply the output by a fixed value to change its brightness.
- /// If true, output will be log-transformed.
- /// If dB scaling is in use, this multiplier will be applied before log transformation.
- /// Behavior of the spectrogram when it is full of data.
- /// Roll (true) adds new columns on the left overwriting the oldest ones.
- /// Scroll (false) slides the whole image to the left and adds new columns to the right.
- public void SaveImage(string fileName, double intensity = 1, bool dB = false, double dBScale = 1, bool roll = false)
- {
- if (FFTs.Count == 0)
- throw new InvalidOperationException("Spectrogram contains no data. Use Add() to add signal data.");
-
- string extension = Path.GetExtension(fileName).ToLower();
-
- ImageFormat fmt;
- if (extension == ".bmp")
- fmt = ImageFormat.Bmp;
- else if (extension == ".png")
- fmt = ImageFormat.Png;
- else if (extension == ".jpg" || extension == ".jpeg")
- fmt = ImageFormat.Jpeg;
- else if (extension == ".gif")
- fmt = ImageFormat.Gif;
- else
- throw new ArgumentException("unknown file extension");
-
- Image.GetBitmap(FFTs, Colormap, intensity, dB, dBScale, roll, NextColumnIndex).Save(fileName, fmt);
- }
+ ///
+ /// Create a Mel-scaled spectrogram.
+ ///
+ /// Total number of output bins to use. Choose a value significantly smaller than Height.
+ /// Multiply the output by a fixed value to change its brightness.
+ /// If true, output will be log-transformed.
+ /// If dB scaling is in use, this multiplier will be applied before log transformation.
+ /// Behavior of the spectrogram when it is full of data.
+ /// Roll (true) adds new columns on the left overwriting the oldest ones.
+ /// Scroll (false) slides the whole image to the left and adds new columns to the right.
+ public SKBitmap GetBitmapMel(int melBinCount = 25, double intensity = 1, bool dB = false, double dBScale = 1, bool roll = false) =>
+ Image.GetBitmap(GetMelFFTs(melBinCount), Colormap, intensity, dB, dBScale, roll, NextColumnIndex);
+
+ ///
+ /// Generate the spectrogram and save it as an image file.
+ ///
+ /// Path of the file to save.
+ /// Multiply the output by a fixed value to change its brightness.
+ /// If true, output will be log-transformed.
+ /// If dB scaling is in use, this multiplier will be applied before log transformation.
+ /// Behavior of the spectrogram when it is full of data.
+ /// Roll (true) adds new columns on the left overwriting the oldest ones.
+ /// Scroll (false) slides the whole image to the left and adds new columns to the right.
+ public void SaveImage(string fileName, double intensity = 1, bool dB = false, double dBScale = 1, bool roll = false)
+ {
+ if (FFTs.Count == 0)
+ throw new InvalidOperationException("Spectrogram contains no data. Use Add() to add signal data.");
+
+ string extension = Path.GetExtension(fileName).ToLower();
+
+ SKEncodedImageFormat fmt;
+ if (extension == ".bmp")
+ fmt = SKEncodedImageFormat.Bmp;
+ else if (extension == ".png")
+ fmt = SKEncodedImageFormat.Png;
+ else if (extension == ".jpg" || extension == ".jpeg")
+ fmt = SKEncodedImageFormat.Jpeg;
+ else if (extension == ".gif")
+ fmt = SKEncodedImageFormat.Gif;
+ else
+ throw new ArgumentException("unknown file extension");
+
+ using var image = Image.GetBitmap(FFTs, Colormap, intensity, dB, dBScale, roll, NextColumnIndex);
+ using var encodedImage = image.Encode(fmt, 80);
+ using var fileStream = new FileStream(fileName, FileMode.Create);
+ encodedImage.SaveTo(fileStream);
+ }
- ///
- /// Create and return a spectrogram bitmap from the FFTs stored in memory.
- /// The output will be scaled-down vertically by binning according to a reduction factor and keeping the brightest pixel value in each bin.
- ///
- /// Multiply the output by a fixed value to change its brightness.
- /// If true, output will be log-transformed.
- /// If dB scaling is in use, this multiplier will be applied before log transformation.
- /// Behavior of the spectrogram when it is full of data.
- ///
- public Bitmap GetBitmapMax(double intensity = 1, bool dB = false, double dBScale = 1, bool roll = false, int reduction = 4)
+ ///
+ /// Create and return a spectrogram bitmap from the FFTs stored in memory.
+ /// The output will be scaled-down vertically by binning according to a reduction factor and keeping the brightest pixel value in each bin.
+ ///
+ /// Multiply the output by a fixed value to change its brightness.
+ /// If true, output will be log-transformed.
+ /// If dB scaling is in use, this multiplier will be applied before log transformation.
+ /// Behavior of the spectrogram when it is full of data.
+ ///
+ public SKBitmap GetBitmapMax(double intensity = 1, bool dB = false, double dBScale = 1, bool roll = false, int reduction = 4)
+ {
+ List ffts2 = new List();
+ for (int i = 0; i < FFTs.Count; i++)
{
- List ffts2 = new List();
- for (int i = 0; i < FFTs.Count; i++)
- {
- double[] d1 = FFTs[i];
- double[] d2 = new double[d1.Length / reduction];
- for (int j = 0; j < d2.Length; j++)
- for (int k = 0; k < reduction; k++)
- d2[j] = Math.Max(d2[j], d1[j * reduction + k]);
- ffts2.Add(d2);
- }
- return Image.GetBitmap(ffts2, Colormap, intensity, dB, dBScale, roll, NextColumnIndex);
+ double[] d1 = FFTs[i];
+ double[] d2 = new double[d1.Length / reduction];
+ for (int j = 0; j < d2.Length; j++)
+ for (int k = 0; k < reduction; k++)
+ d2[j] = Math.Max(d2[j], d1[j * reduction + k]);
+ ffts2.Add(d2);
}
+ return Image.GetBitmap(ffts2, Colormap, intensity, dB, dBScale, roll, NextColumnIndex);
+ }
- ///
- /// Defines the total number of FFTs (spectrogram columns) to store in memory. Determines Width.
- ///
- private int fixedWidth = 0;
+ ///
+ /// Defines the total number of FFTs (spectrogram columns) to store in memory. Determines Width.
+ ///
+ private int fixedWidth = 0;
- ///
- /// Configure the Spectrogram to maintain a fixed number of pixel columns.
- /// Zeros will be added to padd existing data to achieve this width, and extra columns will be deleted.
- ///
- public void SetFixedWidth(int width)
- {
- fixedWidth = width;
- PadOrTrimForFixedWidth();
- }
+ ///
+ /// Configure the Spectrogram to maintain a fixed number of pixel columns.
+ /// Zeros will be added to pad existing data to achieve this width, and extra columns will be deleted.
+ ///
+ public void SetFixedWidth(int width)
+ {
+ fixedWidth = width;
+ PadOrTrimForFixedWidth();
+ }
- private void PadOrTrimForFixedWidth()
+ private void PadOrTrimForFixedWidth()
+ {
+ if (fixedWidth > 0)
{
- if (fixedWidth > 0)
- {
- int overhang = Width - fixedWidth;
- if (overhang > 0)
- FFTs.RemoveRange(0, overhang);
+ int overhang = Width - fixedWidth;
+ if (overhang > 0)
+ FFTs.RemoveRange(0, overhang);
- while (FFTs.Count < fixedWidth)
- FFTs.Insert(0, new double[Height]);
- }
+ while (FFTs.Count < fixedWidth)
+ FFTs.Insert(0, new double[Height]);
}
+ }
- ///
- /// Get a vertical image containing ticks and tick labels for the frequency axis.
- ///
- /// size (pixels)
- /// number to add to each tick label
- /// length of each tick mark (pixels)
- /// bin size for vertical data reduction
- public Bitmap GetVerticalScale(int width, int offsetHz = 0, int tickSize = 3, int reduction = 1)
- {
- return Scale.Vertical(width, Settings, offsetHz, tickSize, reduction);
- }
+ ///
+ /// Get a vertical image containing ticks and tick labels for the frequency axis.
+ ///
+ /// size (pixels)
+ /// number to add to each tick label
+ /// length of each tick mark (pixels)
+ /// bin size for vertical data reduction
+ public SKBitmap GetVerticalScale(int width, int offsetHz = 0, int tickSize = 3, int reduction = 1)
+ {
+ return Scale.Vertical(width, Settings, offsetHz, tickSize, reduction);
+ }
- ///
- /// Return the vertical position (pixel units) for the given frequency
- ///
- public int PixelY(double frequency, int reduction = 1)
- {
- int pixelsFromZeroHz = (int)(Settings.PxPerHz * frequency / reduction);
- int pixelsFromMinFreq = pixelsFromZeroHz - Settings.FftIndex1 / reduction + 1;
- int pixelRow = Settings.Height / reduction - 1 - pixelsFromMinFreq;
- return pixelRow - 1;
- }
+ ///
+ /// Return the vertical position (pixel units) for the given frequency
+ ///
+ public int PixelY(double frequency, int reduction = 1)
+ {
+ int pixelsFromZeroHz = (int)(Settings.PxPerHz * frequency / reduction);
+ int pixelsFromMinFreq = pixelsFromZeroHz - Settings.FftIndex1 / reduction + 1;
+ int pixelRow = Settings.Height / reduction - 1 - pixelsFromMinFreq;
+ return pixelRow - 1;
+ }
- ///
- /// Return the list of FFTs in memory underlying the spectrogram.
- /// This list may continue to evolve after it is returned.
- ///
- public List GetFFTs()
- {
- return FFTs;
- }
+ ///
+ /// Return the list of FFTs in memory underlying the spectrogram.
+ /// This list may continue to evolve after it is returned.
+ ///
+ public List GetFFTs()
+ {
+ return FFTs;
+ }
- ///
- /// Return frequency and magnitude of the dominant frequency.
- ///
- /// If true, only the latest FFT will be assessed.
- public (double freqHz, double magRms) GetPeak(bool latestFft = true)
- {
- if (FFTs.Count == 0)
- return (double.NaN, double.NaN);
+ ///
+ /// Return frequency and magnitude of the dominant frequency.
+ ///
+ /// If true, only the latest FFT will be assessed.
+ public (double freqHz, double magRms) GetPeak(bool latestFft = true)
+ {
+ if (FFTs.Count == 0)
+ return (double.NaN, double.NaN);
- if (latestFft == false)
- throw new NotImplementedException("peak of mean of all FFTs not yet supported");
+ if (latestFft == false)
+ throw new NotImplementedException("peak of mean of all FFTs not yet supported");
- double[] freqs = FFTs[FFTs.Count - 1];
+ double[] freqs = FFTs[FFTs.Count - 1];
- int peakIndex = 0;
- double peakMagnitude = 0;
- for (int i = 0; i < freqs.Length; i++)
+ int peakIndex = 0;
+ double peakMagnitude = 0;
+ for (int i = 0; i < freqs.Length; i++)
+ {
+ if (freqs[i] > peakMagnitude)
{
- if (freqs[i] > peakMagnitude)
- {
- peakMagnitude = freqs[i];
- peakIndex = i;
- }
+ peakMagnitude = freqs[i];
+ peakIndex = i;
}
+ }
- double maxFreq = SampleRate / 2;
- double peakFreqFrac = peakIndex / (double)freqs.Length;
- double peakFreqHz = maxFreq * peakFreqFrac;
+ double maxFreq = SampleRate / 2;
+ double peakFreqFrac = peakIndex / (double)freqs.Length;
+ double peakFreqHz = maxFreq * peakFreqFrac;
- return (peakFreqHz, peakMagnitude);
- }
+ return (peakFreqHz, peakMagnitude);
}
}