Initial commit — WPF guitar note name tutor using FFT frequency detection
This commit is contained in:
@@ -0,0 +1,72 @@
|
||||
using System;
|
||||
|
||||
namespace SoundAnalysis
|
||||
{
|
||||
/// <summary>
|
||||
/// Complex number.
|
||||
/// </summary>
|
||||
struct ComplexNumber
|
||||
{
|
||||
public double Re;
|
||||
public double Im;
|
||||
|
||||
public ComplexNumber(double re)
|
||||
{
|
||||
Re = re;
|
||||
Im = 0;
|
||||
}
|
||||
|
||||
public ComplexNumber(double re, double im)
|
||||
{
|
||||
Re = re;
|
||||
Im = im;
|
||||
}
|
||||
|
||||
public static ComplexNumber operator *(ComplexNumber n1, ComplexNumber n2)
|
||||
{
|
||||
return new ComplexNumber(n1.Re * n2.Re - n1.Im * n2.Im,
|
||||
n1.Im * n2.Re + n1.Re * n2.Im);
|
||||
}
|
||||
|
||||
public static ComplexNumber operator +(ComplexNumber n1, ComplexNumber n2)
|
||||
{
|
||||
return new ComplexNumber(n1.Re + n2.Re, n1.Im + n2.Im);
|
||||
}
|
||||
|
||||
public static ComplexNumber operator -(ComplexNumber n1, ComplexNumber n2)
|
||||
{
|
||||
return new ComplexNumber(n1.Re - n2.Re, n1.Im - n2.Im);
|
||||
}
|
||||
|
||||
public static ComplexNumber operator -(ComplexNumber n)
|
||||
{
|
||||
return new ComplexNumber(-n.Re, -n.Im);
|
||||
}
|
||||
|
||||
public static implicit operator ComplexNumber(double n)
|
||||
{
|
||||
return new ComplexNumber(n, 0);
|
||||
}
|
||||
|
||||
public ComplexNumber PoweredE()
|
||||
{
|
||||
double e = Math.Exp(Re);
|
||||
return new ComplexNumber(e * Math.Cos(Im), e * Math.Sin(Im));
|
||||
}
|
||||
|
||||
public double Power2()
|
||||
{
|
||||
return Re * Re - Im * Im;
|
||||
}
|
||||
|
||||
public double AbsPower2()
|
||||
{
|
||||
return Re * Re + Im * Im;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return String.Format("{0}+i*{1}", Re, Im);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
using System;
|
||||
|
||||
namespace SoundAnalysis
|
||||
{
|
||||
/// <summary>
|
||||
/// Cooley-Tukey FFT algorithm.
|
||||
/// </summary>
|
||||
public static class FftAlgorithm
|
||||
{
|
||||
/// <summary>
|
||||
/// Calculates FFT using Cooley-Tukey FFT algorithm.
|
||||
/// </summary>
|
||||
/// <param name="x">input data</param>
|
||||
/// <returns>spectrogram of the data</returns>
|
||||
/// <remarks>
|
||||
/// If amount of data items not equal a power of 2, then algorithm
|
||||
/// automatically pad with 0s to the lowest amount of power of 2.
|
||||
/// </remarks>
|
||||
public static double[] Calculate(double[] x)
|
||||
{
|
||||
int length;
|
||||
int bitsInLength;
|
||||
if (IsPowerOfTwo(x.Length))
|
||||
{
|
||||
length = x.Length;
|
||||
bitsInLength = Log2(length) - 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
bitsInLength = Log2(x.Length);
|
||||
length = 1 << bitsInLength;
|
||||
// the items will be pad with zeros
|
||||
}
|
||||
|
||||
// bit reversal
|
||||
ComplexNumber[] data = new ComplexNumber[length];
|
||||
for (int i = 0; i < x.Length; i++)
|
||||
{
|
||||
int j = ReverseBits(i, bitsInLength);
|
||||
data[j] = new ComplexNumber(x[i]);
|
||||
}
|
||||
|
||||
// Cooley-Turkey
|
||||
for (int i = 0; i < bitsInLength; i++)
|
||||
{
|
||||
int m = 1 << i;
|
||||
int n = m * 2;
|
||||
double alpha = -(2 * Math.PI / n);
|
||||
|
||||
for (int k = 0; k < m; k++)
|
||||
{
|
||||
// e^(-2*pi*i/N*k)
|
||||
ComplexNumber oddPartMultiplier = new ComplexNumber(0, alpha * k).PoweredE();
|
||||
|
||||
for (int j = k; j < length; j += n)
|
||||
{
|
||||
ComplexNumber evenPart = data[j];
|
||||
ComplexNumber oddPart = oddPartMultiplier * data[j + m];
|
||||
data[j] = evenPart + oddPart;
|
||||
data[j + m] = evenPart - oddPart;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// calculate spectrogram
|
||||
double[] spectrogram = new double[length];
|
||||
for (int i = 0; i < spectrogram.Length; i++)
|
||||
{
|
||||
spectrogram[i] = data[i].AbsPower2();
|
||||
}
|
||||
return spectrogram;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets number of significat bytes.
|
||||
/// </summary>
|
||||
/// <param name="n">Number</param>
|
||||
/// <returns>Amount of minimal bits to store the number.</returns>
|
||||
private static int Log2(int n)
|
||||
{
|
||||
int i = 0;
|
||||
while (n > 0)
|
||||
{
|
||||
++i; n >>= 1;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reverses bits in the number.
|
||||
/// </summary>
|
||||
/// <param name="n">Number</param>
|
||||
/// <param name="bitsCount">Significant bits in the number.</param>
|
||||
/// <returns>Reversed binary number.</returns>
|
||||
private static int ReverseBits(int n, int bitsCount)
|
||||
{
|
||||
int reversed = 0;
|
||||
for (int i = 0; i < bitsCount; i++)
|
||||
{
|
||||
int nextBit = n & 1;
|
||||
n >>= 1;
|
||||
|
||||
reversed <<= 1;
|
||||
reversed |= nextBit;
|
||||
}
|
||||
return reversed;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if number is power of 2.
|
||||
/// </summary>
|
||||
/// <param name="n">number</param>
|
||||
/// <returns>true if n=2^k and k is positive integer</returns>
|
||||
private static bool IsPowerOfTwo(int n)
|
||||
{
|
||||
return n > 1 && (n & (n - 1)) == 0;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
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("SoundAnalysis")]
|
||||
[assembly: AssemblyDescription("Sound anslysis library: FFT")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("notmasteryet")]
|
||||
[assembly: AssemblyProduct("FftGuitarTuner")]
|
||||
[assembly: AssemblyCopyright("Copyright © notmasteryet 2009")]
|
||||
[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("52c3b811-84a7-4a5b-92e6-64f3c16752bb")]
|
||||
|
||||
// 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")]
|
||||
@@ -0,0 +1,49 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="3.5" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProductVersion>9.0.21022</ProductVersion>
|
||||
<SchemaVersion>2.0</SchemaVersion>
|
||||
<ProjectGuid>{ABA54DC3-324B-49DE-B79E-C4F573306E4F}</ProjectGuid>
|
||||
<OutputType>Library</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>SoundAnalysis</RootNamespace>
|
||||
<AssemblyName>SoundAnalysis</AssemblyName>
|
||||
<TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="System" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="ComplexNumber.cs" />
|
||||
<Compile Include="FftCalc.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
<Target Name="BeforeBuild">
|
||||
</Target>
|
||||
<Target Name="AfterBuild">
|
||||
</Target>
|
||||
-->
|
||||
</Project>
|
||||
Reference in New Issue
Block a user