{"id":19728763,"url":"https://github.com/swharden/spectrogram","last_synced_at":"2025-04-14T22:13:19.918Z","repository":{"id":39898098,"uuid":"202745600","full_name":"swharden/Spectrogram","owner":"swharden","description":".NET library for creating spectrograms (visual representations of frequency spectrum over time)","archived":false,"fork":false,"pushed_at":"2024-11-01T15:29:51.000Z","size":177238,"stargazers_count":332,"open_issues_count":2,"forks_count":56,"subscribers_count":14,"default_branch":"main","last_synced_at":"2025-04-14T22:13:03.790Z","etag":null,"topics":["audio","fft","frequency","spectrogram","visualization"],"latest_commit_sha":null,"homepage":"https://nuget.org/packages/Spectrogram","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/swharden.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null}},"created_at":"2019-08-16T14:49:17.000Z","updated_at":"2025-04-13T11:18:13.000Z","dependencies_parsed_at":"2022-09-07T16:03:36.272Z","dependency_job_id":"f4a9fb16-08cc-45f0-bafa-b2f8bbf9cbd0","html_url":"https://github.com/swharden/Spectrogram","commit_stats":null,"previous_names":[],"tags_count":17,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/swharden%2FSpectrogram","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/swharden%2FSpectrogram/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/swharden%2FSpectrogram/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/swharden%2FSpectrogram/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/swharden","download_url":"https://codeload.github.com/swharden/Spectrogram/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248968917,"owners_count":21191162,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["audio","fft","frequency","spectrogram","visualization"],"created_at":"2024-11-12T00:07:41.836Z","updated_at":"2025-04-14T22:13:19.900Z","avatar_url":"https://github.com/swharden.png","language":"Python","readme":"# Spectrogram\n\n[![CI](https://github.com/swharden/Spectrogram/actions/workflows/ci.yaml/badge.svg)](https://github.com/swharden/Spectrogram/actions/workflows/ci.yaml)\n[![Nuget](https://img.shields.io/nuget/v/Spectrogram?label=NuGet\u0026logo=nuget)](https://www.nuget.org/packages/Spectrogram/)\n\n**Spectrogram** is a .NET library for creating spectrograms from pre-recorded signals or live audio from the sound card.  Spectrogram uses FFT algorithms and window functions provided by the [FftSharp](https://github.com/swharden/FftSharp) project, and it targets .NET Standard so it can be used in .NET Framework and .NET Core projects.\n\n\u003cdiv align=\"center\"\u003e\n\n![](dev/graphics/hal-spectrogram.png)\n\n_\"I'm sorry Dave... I'm afraid I can't do that\"_\n\n\u003c/div\u003e\n\n\n## Quickstart\n\n* This code generates the spectrogram above.\n\n* Source code for the WAV reading method is at the bottom of this page.\n\n```cs\n(double[] audio, int sampleRate) = ReadMono(\"hal.wav\");\nvar sg = new SpectrogramGenerator(sampleRate, fftSize: 4096, stepSize: 500, maxFreq: 3000);\nsg.Add(audio);\nsg.SaveImage(\"hal.png\");\n```\n\n\n## Windows Forms\n\nIf you're using Spectrogram in a graphical application you may find it helpful to retrieve the output as a Bitmap which can be displayed on a Picturebox:\n\n```cs\npictureBox1.Image = sg.GetBitmap();\n```\n\nI find it helpful to put the Picturebox inside a Panel with auto-scroll enabled, so large spectrograms which are bigger than the size of the window can be interactively displayed.\n\n## Real-Time Spectrogram\n\nAn example program is included in this repository which demonstrates how to use NAudio to get samples from the sound card and display them as a spectrogram. Spectrogram was designed to be able to display spectrograms with live or growing data, so this is exceptionally easy to implement.\n\n* **Click-to-run demo** for 64-bit Windows: [SpectrogramDemo.exe](dev/SpectrogramDemo.zip)\n\n![](dev/microphone-spectrogram.gif)\n\nTo do this, keep your Spectrogram at the class level:\n```cs\nSpectrogramGenerator sg;\n\npublic Form1()\n{\n    InitializeComponent();\n    sg = new SpectrogramGenerator(sampleRate, fftSize: 4096, stepSize: 500, maxFreq: 3000);\n}\n```\n\nWhenever an audio buffer gets filled, add the data to your Spectrogram:\n```cs\nprivate void GotNewBuffer(double[] audio)\n{\n    sg.Add(audio);\n}\n```\n\nThen set up a timer to trigger rendering:\n```cs\nprivate void timer1_Tick(object sender, EventArgs e){\n    Bitmap bmp = sg.GetBitmap(intensity: .4);\n    pictureBox1.Image?.Dispose();\n    pictureBox1.Image = bmp;\n}\n```\n\nReview the source code of the demo application for additional details and considerations. You'll found I abstracted the audio interfacing code into its own class, isolating it from the GUI code.\n\n## Song-to-Spectrogram\n\nThis example demonstrates how to convert a MP3 file to a spectrogram image. A sample MP3 audio file in the [data folder](data) contains the audio track from Ken Barker's excellent piano performance of George Frideric Handel's Suite No. 5 in E major for harpsichord ([_The Harmonious Blacksmith_](https://en.wikipedia.org/wiki/The_Harmonious_Blacksmith)). This audio file is included [with permission](dev/Handel%20-%20Air%20and%20Variations.txt), and the [original video can be viewed on YouTube](https://www.youtube.com/watch?v=Mza-xqk770k).\n\n```cs\n(double[] audio, int sampleRate) = ReadMono(\"song.wav\");\n\nint fftSize = 16384;\nint targetWidthPx = 3000;\nint stepSize = audio.Length / targetWidthPx;\n\nvar sg = new SpectrogramGenerator(sampleRate, fftSize, stepSize, maxFreq: 2200);\nsg.Add(audio);\nsg.SaveImage(\"song.png\", intensity: 5, dB: true);\n```\n\nNotice the optional conversion to Decibels while saving the image.\n\n![](dev/graphics/spectrogram-song.png)\n\nIf you [listen to the audio track](https://www.youtube.com/watch?v=Mza-xqk770k) while closely inspecting the spectrogram you can identify individual piano notes and chords, and may be surprised by the interesting patterns that emerge around trills and glissandos.\n\n## Spectrogram Information\n\nThe Spectrogram's `ToString()` method displays detailed information about the spectrogram:\n\n```cs\nConsole.WriteLine(sg);\n```\n\n```\nSpectrogram (2993, 817)\n  Vertical (817 px): 0 - 2,199 Hz, FFT size: 16,384 samples, 2.69 Hz/px\n  Horizontal (2993 px): 2.96 min, window: 0.37 sec, step: 0.06 sec, overlap: 84%\n```\n\n## Colormaps\n\nThese examples demonstrate the identical spectrogram analyzed with a variety of different colormaps. Spectrogram colormaps can be changed by calling the `SetColormap()` method:\n\n```cs\n(double[] audio, int sampleRate) = ReadMono(\"hal.wav\");\nvar sg = new SpectrogramGenerator(sampleRate, fftSize: 8192, stepSize: 200, maxFreq: 3000);\nsg.Add(audio);\nsg.SetColormap(Colormap.Jet);\nsg.SaveImage($\"jet.png\");\n```\n\nViridis | Greens | Blues | Grayscale | GrayscaleR\n---|---|---|---|---\n![](dev/graphics/hal-Viridis.png)|![](dev/graphics/hal-Greens.png)|![](dev/graphics/hal-Blues.png)|![](dev/graphics/hal-Grayscale.png)|![](dev/graphics/hal-GrayscaleR.png)\n\n## Mel Spectrogram\n\nAnalytical spectrograms aimed at achieving maximum frequency resolution are presented using linear scaling, where every row of pixels is evenly spaced in the frequency domain. However, biological sensory systems tend to be logarithmic, and the human ear can differentiate frequency shifts better at lower frequencies than at higher ones. \n\n**To visualize frequency in a way that mimics human perception** we create a spectrogram that represents lower frequencies using a large portion of the image, and condense higher frequency ranges into smaller rows of pixels toward the top of the image. The [Mel Scale](https://en.wikipedia.org/wiki/Mel_scale) is commonly used to represent power spectral density this way, and the resulting _Mel Spectrogram_ has greatly reduced vertical resolution but is a better representation of human frequency perception. \n\nCropped Linear Scale (0-3kHz) | Mel Scale (0-22 kHz)\n---|---\n![](dev/graphics/halMel-LinearCropped.png)|![](dev/graphics/halMel-MelScale.png)\n\nAmplitude perception in humans, like frequency perception, is logarithmic. Therefore, Mel spectrograms typically display log-transformed spectral power and are presented using Decibel units.\n\n```cs\n(double[] audio, int sampleRate) = ReadMono(\"hal.wav\");\nvar sg = new SpectrogramGenerator(sampleRate, fftSize: 4096, stepSize: 500, maxFreq: 3000);\nsg.Add(audio);\n\n// Create a traditional (linear) Spectrogram\nsg.SaveImage(\"hal.png\");\n\n// Create a Mel Spectrogram\nBitmap bmp = sg.GetBitmapMel(melSizePoints: 250);\nbmp.Save(\"halMel.png\", ImageFormat.Png);\n```\n\n## Read Data from an Audio File\n\nYou 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.\n\n```cs\n(double[] audio, int sampleRate) ReadMono(string filePath, double multiplier = 16_000)\n{\n    using var afr = new NAudio.Wave.AudioFileReader(filePath);\n    int sampleRate = afr.WaveFormat.SampleRate;\n    int bytesPerSample = afr.WaveFormat.BitsPerSample / 8;\n    int sampleCount = (int)(afr.Length / bytesPerSample);\n    int channelCount = afr.WaveFormat.Channels;\n    var audio = new List\u003cdouble\u003e(sampleCount);\n    var buffer = new float[sampleRate * channelCount];\n    int samplesRead = 0;\n    while ((samplesRead = afr.Read(buffer, 0, buffer.Length)) \u003e 0)\n        audio.AddRange(buffer.Take(samplesRead).Select(x =\u003e x * multiplier));\n    return (audio.ToArray(), sampleRate);\n}\n```","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fswharden%2Fspectrogram","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fswharden%2Fspectrogram","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fswharden%2Fspectrogram/lists"}