From 3828e2f6d36750590ed76318bd2c165fb799b216 Mon Sep 17 00:00:00 2001 From: Scott W Harden Date: Thu, 16 Jun 2022 18:15:08 -0400 Subject: [PATCH 1/3] csproj: upgrade dependencies --- src/Spectrogram.MicrophoneDemo/Spectrogram.Demo.csproj | 2 +- src/Spectrogram/Spectrogram.csproj | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Spectrogram.MicrophoneDemo/Spectrogram.Demo.csproj b/src/Spectrogram.MicrophoneDemo/Spectrogram.Demo.csproj index bb31c5c..0aee820 100644 --- a/src/Spectrogram.MicrophoneDemo/Spectrogram.Demo.csproj +++ b/src/Spectrogram.MicrophoneDemo/Spectrogram.Demo.csproj @@ -10,7 +10,7 @@ - + diff --git a/src/Spectrogram/Spectrogram.csproj b/src/Spectrogram/Spectrogram.csproj index 8d6100a..6e28915 100644 --- a/src/Spectrogram/Spectrogram.csproj +++ b/src/Spectrogram/Spectrogram.csproj @@ -29,10 +29,10 @@ - + - + runtime; build; native; contentfiles; analyzers; buildtransitive all From dd54c7eb5c484e61ea9f4e159a407f2fade20dd6 Mon Sep 17 00:00:00 2001 From: Scott W Harden Date: Thu, 16 Jun 2022 18:37:01 -0400 Subject: [PATCH 2/3] SFF: deprecate resolves #44 --- README.md | 52 ------------- src/Spectrogram.Tests/FileFormat.cs | 97 ------------------------- src/Spectrogram/SFF.cs | 5 +- src/Spectrogram/SpectrogramGenerator.cs | 4 + src/Spectrogram/Tools.cs | 13 ++++ 5 files changed, 21 insertions(+), 150 deletions(-) delete mode 100644 src/Spectrogram.Tests/FileFormat.cs diff --git a/README.md b/README.md index 417ebd8..3c160f0 100644 --- a/README.md +++ b/README.md @@ -153,58 +153,6 @@ Bitmap bmp = sg.GetBitmapMel(melSizePoints: 250); bmp.Save("halMel.png", ImageFormat.Png); ``` -## Spectrogram File Format (SFF) - -The Spectrogram library has methods which can read and write SFF files, a file format specifically designed for storing spectrogram data. SFF files contain 2D spectrogram data (repeated FFTs) with a [small header](dev/sff) describing the audio and FFT settings suitable for deriving scale information. - -SFF files store `double` values (8-byte floating-point data) which is far superior to saving spectrograms as indexed color images (which represent intensity with a single `byte` per pixel). - -SFF files be saved using `Complex` data format (with real and imaginary values for each point) to faithfully represent the FFT output, or `double` format to represent magnitude (with an optional pre-conversion to Decibels to represent power). - -### Create SFF Files with C# - -This example creates a spectrogram but saves it using the SFF file format instead of saving it as an image. The SFF file can then be read in any language. - -```cs -(double[] audio, int sampleRate) = ReadWavMono("hal.wav"); -var sg = new SpectrogramGenerator(sampleRate, fftSize: 4096, stepSize: 700, maxFreq: 2000); -sg.Add(audio); -sg.SaveData("hal.sff"); -``` - -### Display SFF Files with C# -Spectrogram data can be loaded from SFF files to facilitate rapid recall of data which can otherwise be resource-intensive to calculate. Spectrogram's `SFF` module facilitates this operation and has methods which can directly convert spectrograms to Bitmaps with options to customize the colormap, intensity, and Decibel scaling. - -![](dev/sff/SffViewer/screenshot.png) - -A simple SFF file viewer has been added to [dev/sff](dev/sff) and serves as a demonstration of how the `SFF` module can be used to generate spectrogram images from SFF files. - -### Read SFF Files with Python -A Python module to read SFF files has been created (in [dev/sff/python](dev/sff/python)) which allows Spectrograms created by this library and stored in SFF format to be loaded as 2D numpy arrays in Python. - -This example demonstrates how the SFF file created in the previous C# example can be loaded into Python and displayed with matplotlib. This example has a few lines related to styling omitted for brevity, but the full Python demo can be found in [dev/sff/python](dev/sff/python). - -```python -import matplotlib.pyplot as plt -import sffLib - -# load spectrogram data as a 2D numpy array -sf = sffLib.SpectrogramFile("hal.sff") - -# display the spectrogram as a pseudocolor mesh -plt.pcolormesh(freqs, times, sf.values) -plt.colorbar() -plt.show() -``` - -![](dev/sff/python/hal.sff.png) - -## Resources -* [FftSharp](https://github.com/swharden/FftSharp) - the module which actually performs the FFT and related transformations -* [MP3Sharp](https://github.com/ZaneDubya/MP3Sharp) - a library I use to read MP3 files during testing -* [FSKview](https://github.com/swharden/FSKview) - a real-time spectrogram for viewing frequency-shift-keyed (FSK) signals from audio transmitted over radio frequency. -* [NAudio](https://github.com/naudio/NAudio) - an open source .NET library which makes it easy to get samples from the microphone or sound card in real time - ## Read data from a WAV File You should customize your file-reading method to suit your specific application. I frequently use the NAudio package to read data from WAV and MP3 files. This function reads audio data from a mono WAV file and will be used for the examples on this page. diff --git a/src/Spectrogram.Tests/FileFormat.cs b/src/Spectrogram.Tests/FileFormat.cs deleted file mode 100644 index 7a3b8d9..0000000 --- a/src/Spectrogram.Tests/FileFormat.cs +++ /dev/null @@ -1,97 +0,0 @@ -using NUnit.Framework; -using System; -using System.Collections.Generic; -using System.Drawing; -using System.Text; - -namespace Spectrogram.Tests -{ - class FileFormat - { - [Test] - public void Test_SFF_Linear() - { - (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); - spec.SaveData("../../../../../dev/sff/hal.sff"); - - var spec2 = new SFF("../../../../../dev/sff/hal.sff"); - Assert.AreEqual(spec.SampleRate, spec2.SampleRate); - Assert.AreEqual(spec.StepSize, spec2.StepSize); - Assert.AreEqual(spec.Width, spec2.Width); - Assert.AreEqual(spec.FftSize, spec2.FftSize); - Assert.AreEqual(spec.NextColumnIndex, spec2.FftFirstIndex); - Assert.AreEqual(spec.Height, spec2.Height); - Assert.AreEqual(spec.OffsetHz, spec2.OffsetHz); - } - - [Test] - public void Test_SFF_Mel() - { - (double[] audio, int sampleRate) = AudioFile.ReadWAV("../../../../../data/cant-do-that-44100.wav"); - int fftSize = 1 << 12; - var spec = new SpectrogramGenerator(sampleRate, fftSize, stepSize: 700); - var window = new FftSharp.Windows.Hanning(); - spec.SetWindow(window.Create(fftSize / 3)); // sharper window than typical - spec.Add(audio); - - Bitmap bmp = spec.GetBitmapMel(250); - bmp.Save("../../../../../dev/sff/halMel.png", System.Drawing.Imaging.ImageFormat.Png); - spec.SaveData("../../../../../dev/sff/halMel.sff", melBinCount: 250); - - var spec2 = new SFF("../../../../../dev/sff/halMel.sff"); - Assert.AreEqual(spec.SampleRate, spec2.SampleRate); - Assert.AreEqual(spec.StepSize, spec2.StepSize); - Assert.AreEqual(spec.Width, spec2.Width); - Assert.AreEqual(spec.FftSize, spec2.FftSize); - Assert.AreEqual(spec.NextColumnIndex, spec2.FftFirstIndex); - Assert.AreEqual(spec.Height, spec2.Height); - Assert.AreEqual(spec.OffsetHz, spec2.OffsetHz); - } - - [Test] - public void Test_SFF_Linear2() - { - // test creating SFF file from 16-bit 48kHz mono WAV file - - // read the wav file - (double[] audio, int sampleRate) = AudioFile.ReadWAV("../../../../../data/03-02-03-01-02-01-19.wav"); - Assert.AreEqual(48000, sampleRate); - - // save the SFF - int fftSize = 1 << 12; - var spec = new SpectrogramGenerator(sampleRate, fftSize, stepSize: 300, maxFreq: 2000); - spec.Add(audio); - spec.SaveData("testDoor.sff"); - - // load the SFF and verify all the values are the same - var spec2 = new SFF("testDoor.sff"); - Assert.AreEqual(spec.SampleRate, spec2.SampleRate); - Assert.AreEqual(spec.StepSize, spec2.StepSize); - Assert.AreEqual(spec.Width, spec2.Width); - Assert.AreEqual(spec.FftSize, spec2.FftSize); - Assert.AreEqual(spec.NextColumnIndex, spec2.FftFirstIndex); - Assert.AreEqual(spec.Height, spec2.Height); - Assert.AreEqual(spec.OffsetHz, spec2.OffsetHz); - Assert.AreEqual("SFF 701x170", spec2.ToString()); - } - - [Test] - public void Test_SFF_LinearBigMaxFreq() - { - // test creating SFF file from 16-bit 48kHz mono WAV file - - (double[] audio, int sampleRate) = AudioFile.ReadWAV("../../../../../data/03-02-03-01-02-01-19.wav"); - Assert.AreEqual(48000, sampleRate); - - int fftSize = 1 << 12; - var spec = new SpectrogramGenerator(sampleRate, fftSize, stepSize: 300, maxFreq: 7999); - spec.Add(audio); - spec.SaveData("testDoorBig.sff"); - } - } -} diff --git a/src/Spectrogram/SFF.cs b/src/Spectrogram/SFF.cs index 8d68d9b..d64bb51 100644 --- a/src/Spectrogram/SFF.cs +++ b/src/Spectrogram/SFF.cs @@ -8,7 +8,10 @@ namespace Spectrogram { - // Spectrogram File Format reader/writer + [Obsolete("The SFF file format is obsolete. " + + "Users are encouraged to write their own IO routines specific to their application. "+ + "To get a copy of the original SFF reader/writer see https://github.com/swharden/Spectrogram/issues/44", + error: true)] public class SFF { public readonly byte VersionMajor = 1; diff --git a/src/Spectrogram/SpectrogramGenerator.cs b/src/Spectrogram/SpectrogramGenerator.cs index 76afc8e..367d91d 100644 --- a/src/Spectrogram/SpectrogramGenerator.cs +++ b/src/Spectrogram/SpectrogramGenerator.cs @@ -350,6 +350,10 @@ public Bitmap GetBitmapMax(double intensity = 1, bool dB = false, double dBScale return Image.GetBitmap(ffts2, Colormap, intensity, dB, dBScale, roll, NextColumnIndex); } + [Obsolete("The SFF file format is obsolete. " + + "Users are encouraged to write their own IO routines specific to their application. " + + "To get a copy of the original SFF reader/writer see https://github.com/swharden/Spectrogram/issues/44", + error: true)] /// /// Export spectrogram data using the Spectrogram File Format (SFF) /// diff --git a/src/Spectrogram/Tools.cs b/src/Spectrogram/Tools.cs index 2186644..6300194 100644 --- a/src/Spectrogram/Tools.cs +++ b/src/Spectrogram/Tools.cs @@ -7,6 +7,10 @@ namespace Spectrogram { public static class Tools { + [Obsolete("The SFF file format is obsolete. " + + "Users are encouraged to write their own IO routines specific to their application. " + + "To get a copy of the original SFF reader/writer see https://github.com/swharden/Spectrogram/issues/44", + error: true)] /// /// Collapse the 2D spectrogram into a 1D array (mean power of each frequency) /// @@ -31,6 +35,11 @@ public static double[] SffMeanFFT(SFF sff, bool dB = false) return mean; } + + [Obsolete("The SFF file format is obsolete. " + + "Users are encouraged to write their own IO routines specific to their application. " + + "To get a copy of the original SFF reader/writer see https://github.com/swharden/Spectrogram/issues/44", + error: true)] /// /// Collapse the 2D spectrogram into a 1D array (mean power of each time point) /// @@ -48,6 +57,10 @@ public static double[] SffMeanPower(SFF sff, bool dB = false) return power; } + [Obsolete("The SFF file format is obsolete. " + + "Users are encouraged to write their own IO routines specific to their application. " + + "To get a copy of the original SFF reader/writer see https://github.com/swharden/Spectrogram/issues/44", + error: true)] public static double GetPeakFrequency(SFF sff, bool firstFftOnly = false) { double[] freqs = firstFftOnly ? sff.Ffts[0] : SffMeanFFT(sff, false); From 1ee81a91234aad800e36d96c9798e945471502aa Mon Sep 17 00:00:00 2001 From: Scott W Harden Date: Thu, 16 Jun 2022 18:37:42 -0400 Subject: [PATCH 3/3] Spectrogram 1.5.0 --- src/Spectrogram/Spectrogram.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Spectrogram/Spectrogram.csproj b/src/Spectrogram/Spectrogram.csproj index 6e28915..028ff85 100644 --- a/src/Spectrogram/Spectrogram.csproj +++ b/src/Spectrogram/Spectrogram.csproj @@ -2,7 +2,7 @@ netstandard2.0 - 1.4.4 + 1.5.0 A .NET Standard library for creating spectrograms Scott Harden Harden Technologies, LLC