Skip to content

Modern demo #39

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 16 commits into from
Oct 27, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
158 changes: 82 additions & 76 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,98 +11,110 @@
### Quickstart

```cs
// Start with some data
double[] audio = FftSharp.SampleData.SampleAudio1();
// Begin with an array containing sample data
double[] signal = FftSharp.SampleData.SampleAudio1();

// Shape the signal using a Hanning window
var window = new FftSharp.Windows.Hanning();
window.ApplyInPlace(signal);

// Window your signal
double[] window = FftSharp.Window.Hanning(audio.Length);
FftSharp.Window.ApplyInPlace(window, audio);
// Calculate the FFT as an array of complex numbers
Complex[] fftRaw = FftSharp.Transform.FFT(signal);

// Calculate power spectral density (dB)
double[] fftPower = FftSharp.Transform.FFTpower(audio);
// or get the magnitude (units²) or power (dB) as real numbers
double[] fftMag = FftSharp.Transform.FFTmagnitude(signal);
double[] fftPwr = FftSharp.Transform.FFTpower(signal);
```

Audio | Windowed | FFT
Signal | Windowed Signal | FFT
---|---|---
![](src/FftSharp.Quickstart/output/audio.png)|![](src/FftSharp.Quickstart/output/audio-windowed.png)|![](src/FftSharp.Quickstart/output/fft-windowed.png)

A quickstart application ([Program.cs](src/FftSharp.Quickstart/Program.cs)) demonstrates common FftSharp features. The quickstart program generates the graphs shown here, but plotting-related code has been omitted from these code samples.

## Usage

### Installation

FftSharp is available on NuGet:
* https://www.nuget.org/packages/FftSharp/

![](dev/quickstart/audio.png)|![](dev/quickstart/audio-windowed.png)|![](dev/quickstart/fft-windowed.png)

### Sample Data
## Sample Data

```cs
// sample audio with tones at 2, 10, and 20 kHz plus white noise
double[] audio = FftSharp.SampleData.SampleAudio1();
int sampleRate = 48000;
double[] signal = FftSharp.SampleData.SampleAudio1();
int sampleRate = 48_000;

// plot the sample audio
var plt = new ScottPlot.Plot(400, 200);
plt.AddSignal(signal, sampleRate / 1000.0);
plt.YLabel("Amplitude");
plt.Margins(0);
plt.SaveFig("time-series.png");
```

<div align="center">

![](src/FftSharp.Quickstart/output/audio.png)
![](dev/quickstart/time-series.png)

</div>

### Calculate the Power Spectral Density (PSD)

Most people interested in FFT software are attempting to calculate the [power spectral density (PSD)](https://en.wikipedia.org/wiki/Spectral_density) of a signal, usually reported in [dB](https://en.wikipedia.org/wiki/Decibel) units. Knowing this, FftSharp makes it easy to go straight from signal to power spectrum:
## Spectral Magnitude and Power Density

```cs
double[] fftPower = FftSharp.Transform.FFTpower(audio);
```
Most people performing FFT operations are interested in calculating magnitude or power of their signal with respect to frequency. Magnitude units are the square of the original units, and power is in decibels.

Knowing you're interested in how these data line-up with frequencies, we also make it easy to get the list of frequencies corresponding to each point on the power spectrum:
Frequency of each point is a linear range between zero and half the sample rage (Nyquist frequency). A helper function makes it easy to get an array of frequencies (Hz units) to match the FFT that was generated.

```cs
double[] freqs = FftSharp.Transform.FFTfreq(sampleRate, fftPower.Length);
// sample audio with tones at 2, 10, and 20 kHz plus white noise
double[] signal = FftSharp.SampleData.SampleAudio1();
int sampleRate = 48_000;

// calculate the power spectral density using FFT
double[] psd = FftSharp.Transform.FFTpower(signal);
double[] freq = FftSharp.Transform.FFTfreq(sampleRate, psd.Length);

// plot the sample audio
var plt = new ScottPlot.Plot(400, 200);
plt.AddScatterLines(freq, psd);
plt.YLabel("Power (dB)");
plt.XLabel("Frequency (Hz)");
plt.Margins(0);
plt.SaveFig("periodogram.png");
```

Power vs. frequency can then be plotted to yield a [periodogram](https://en.wikipedia.org/wiki/Periodogram):

<div align="center">

![](src/FftSharp.Quickstart/output/fft.png)
![](dev/quickstart/periodogram.png)

</div>

### Calculate the FFT using Complex Numbers
## FFT using Complex Numbers

If you enjoy working with real and imaginary components of complex numbers, you can build your own complex array and call `FFT()` directly which performs the transformation _in-place_ on a `Complex[]` array:
If you are writing a performance application or just enjoy working with real and imaginary components of complex numbers, you can build your own complex array perform FFT operations on it in place:

```cs
// Start with some data
double[] audio = FftSharp.SampleData.SampleAudio1();
Complex[] buffer =
{
new Complex(42, 0),
new Complex(96, 0),
new Complex(13, 0),
new Complex(99, 0),
};

// convert the data to an array of complex numbers
Complex[] buffer = new Complex[audio.Length];
for (int i=0; i<buffer.Length; i++)
buffer[i] = new Complex(audio[i], 0);

// compute the FFT in-place
FftSharp.Transform.FFT(buffer);
```

## Filtering

The `FftSharp.Filter` module has methods to apply low-pass, high-pass, band-pass, and band-stop filtering.
The `FftSharp.Filter` module has methods to apply low-pass, high-pass, band-pass, and band-stop filtering. This works by converting signals to the frequency domain (using FFT), zeroing-out the desired ranges, performing the inverse FFT (iFFT), and returning the result.

```cs
double[] audio = FftSharp.SampleData.SampleAudio1();
double[] filtered = FftSharp.Filter.LowPass(audio, sampleRate, maxFrequency: 2000);
```

<div align="center">

![](dev/lowpass.png)

</div>

## Windowing

Signals are often are _windowed_ prior to FFT analysis. Windowing is essentially multiplying the waveform by a bell-shaped curve prior to analysis. The `FftSharp.Window` module provides easy access to many common window functions.
Signals are often are _windowed_ prior to FFT analysis. Windowing is essentially multiplying the waveform by a bell-shaped curve prior to analysis, improving frequency resolution of the FFT output.

<div align="center">

Expand All @@ -113,65 +125,59 @@ Signals are often are _windowed_ prior to FFT analysis. Windowing is essentially
The Hanning window is the most common window for general-purpose FFT analysis. Other window functions may have different _scallop loss_ or _spectral leakage_ properties. For more information review [window functions](https://en.wikipedia.org/wiki/Window_function) on Wikipedia.

```cs
// Apply a Hanning window to the audio prior to FFT analysis
double[] window = FftSharp.Window.Hanning(audio.Length);
FftSharp.Window.ApplyInPlace(window, audio);
double[] signal = FftSharp.SampleData.SampleAudio1();

var window = new FftSharp.Windows.Hanning();
double[] windowed = window.Apply(signal);
```

Hanning Window | Power Spectral Density
---|---
![](src/FftSharp.Quickstart/output/audio-windowed.png)|![](src/FftSharp.Quickstart/output/fft-windowed.png)
![](dev/quickstart/audio-windowed.png)|![](dev/quickstart/fft-windowed.png)

Windowing signals prior to calculating the FFT improves signal-to-noise ratio at lower frequencies, making power spectrum peaks easier to resolve.

No Window | Power Spectral Density
---|---
![](src/FftSharp.Quickstart/output/audio.png)|![](src/FftSharp.Quickstart/output/fft.png)

## Mel Scaling
![](dev/quickstart/audio.png)|![](dev/quickstart/fft.png)

Analyses aimed at achieving maximum frequency resolution present power spectral density using linear scaling, where every point 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.
### Window Functions

![](dev/mel-scale.png)
This chart (adapted from [](https://www.egr.msu.edu/classes/me451/me451_labs/Fall_2013/Understanding_FFT_Windows.pdf)) summarizes windows commonly used for FFT analysis.

To visualize frequency in a way that mimics human perception we scale data so lower frequencies have more resolution than higher frequencies. The [Mel Scale](https://en.wikipedia.org/wiki/Mel_scale) is commonly used to represent power spectral density this way, and the resulting _Mel Periodogram_ has greatly reduced total frequency resolution but is a better representation of human frequency perception.

Several methods starting with `FftSharp.Transform.Mel` facilitate conversion between a linear frequency scale and the Mel scale. The image above was produced using the following code:

```cs
double[] audio = SampleData.SampleAudio1();
int sampleRate = 48000;
int melBinCount = 20;

double[] fftMag = FftSharp.Transform.FFTmagnitude(audio);
double[] fftMagMel = FftSharp.Transform.MelScale(fftMag, sampleRate, melBinCount);
```
Window | Use Case | Frequency Resolution | Spectral Leakage | Amplitude Accuracy
-----------------|----------------|------|------|-----
Barlett | Random | Good | Fair | Fair
Blackman | Random / mixed | Poor | Best | Good
Flat Top | Sine waves | Poor | Good | Best
Hanning | Random | Good | Good | Fair
Hamming | Random | Good | Fair | Fair
Kaiser-Bessel | Random | Fair | Good | Good
None / Boxcar | Transient / synchronous | Best | Poor Poor
Tukey | Random | Good | Poor | Poor
Welch | Random | Good | Good | Fair

## Demo Application

A graphical demo application is included in this project which uses [ScottPlot](https://swharden.com/scottplot/) to interactively display an audio signal next to its FFT.
A sample application is included with this project that interactively displays an audio signal next to its FFT using different windowing functions.

<div align="center">

![](src/FftSharp.Demo/screenshot2.png)
![](dev/demo.png)

</div>

## Microphone Demo
### Microphone Demo

This project also contains a realtime FFT microphone demo which continuously monitors a sound card input device and calculates the FFT and displays it in real time. This demo uses [NAudio](https://github.com/naudio/NAudio) to interface the sound card, [FftSharp](https://github.com/swharden/FftSharp) to calculate the FFT, and [ScottPlot](https://github.com/swharden/ScottPlot) to display to result.
One of the demos included is a FFT microphone analyzer which continuously monitors a sound card input device and calculates the FFT and displays it in real time.

![](dev/microphone-fft.gif)

You can download this program as a click-to-run EXE to try-out this library without having to compile this project from source:

* Download for Windows: [FftSharp-demo.zip](https://github.com/swharden/FftSharp/raw/master/dev/FftSharp-demo.zip)

## Spectrogram

A spectrogram is a visual representation of the spectrum of frequencies of a signal as it varies with time. Spectrograms are created by computing power spectral density of a small window of an audio signal, moving the window forward in time, and repeating until the end of the signal is reached. In a spectrogram the horizontal axis represents time, the vertical axis represents frequency, and the pixel intensity represents spectral magnitude or power.

[Spectrogram](https://github.com/swharden/Spectrogram) is a .NET library for creating spectrograms.
[**Spectrogram**](https://github.com/swharden/Spectrogram) is a .NET library for creating spectrograms.

<div align="center">

Expand Down
Binary file added dev/demo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added dev/quickstart/audio-windowed.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added dev/quickstart/audio.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added dev/quickstart/fft-windowed.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added dev/quickstart/fft.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added dev/quickstart/periodogram.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added dev/quickstart/time-series.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added dev/quickstart/windows.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 0 additions & 6 deletions src/FftSharp.Demo/App.config

This file was deleted.

Loading