diff --git a/AudioProcessing/AudioBeatDetector.cs b/AudioProcessing/AudioBeatDetector.cs index 9a75a77..deeca8b 100644 --- a/AudioProcessing/AudioBeatDetector.cs +++ b/AudioProcessing/AudioBeatDetector.cs @@ -6,7 +6,7 @@ using System.Linq; namespace BeatDetector.AudioProcessing; -internal class AudioBeatDetector +internal class AudioBeatDetector : IDisposable { public event EventHandler? OnBeat; @@ -20,8 +20,9 @@ internal class AudioBeatDetector private readonly int chunkCount; private readonly BeatChunk[] beatChunks; - public AudioBeatDetector(TimeSpan beatDetectionDelay, int fftLength, int chunkCount) + public AudioBeatDetector(IWaveIn waveIn, TimeSpan beatDetectionDelay, int fftLength, int chunkCount) { + this.waveIn = waveIn; this.beatDetectionDelay = beatDetectionDelay; this.chunkCount = chunkCount; this.fftLength = fftLength; @@ -38,10 +39,12 @@ internal class AudioBeatDetector sampleAggregator.FftCalculated += new EventHandler(FftCalculated); sampleAggregator.PerformFFT = true; - waveIn = new WasapiLoopbackCapture(); waveIn.DataAvailable += OnDataAvailable; + } - waveIn.StartRecording(); + public void Dispose() + { + waveIn.DataAvailable -= OnDataAvailable; } private void OnDataAvailable(object? sender, WaveInEventArgs e) @@ -77,7 +80,7 @@ internal class AudioBeatDetector double averageEnergyLevel = beatChunk.EnergyHistory.Count > 0 ? beatChunk.EnergyHistory.Average() : double.MaxValue; - if (beatChunk.StopWatch.Elapsed >= beatDetectionDelay) + if (beatChunk.StopWatch.Elapsed >= beatDetectionDelay || beatDetectionDelay <= TimeSpan.Zero) { double difference = energyLevel - averageEnergyLevel; @@ -88,6 +91,11 @@ internal class AudioBeatDetector } else { + //if (difference > 0) + //{ + // beatChunk.AverageDifference = (difference + beatChunk.AverageDifference) / 2.00001; + //} + OnNoBeat?.Invoke(this, new BeatDetectorEventArgs(chunkIndex, 0)); } beatChunk.StopWatch.Restart(); diff --git a/AudioProcessing/BeatChunk.cs b/AudioProcessing/BeatChunk.cs index 7e2493c..706dd2a 100644 --- a/AudioProcessing/BeatChunk.cs +++ b/AudioProcessing/BeatChunk.cs @@ -8,4 +8,6 @@ internal class BeatChunk public Stopwatch StopWatch { get; } = new Stopwatch(); public List EnergyHistory { get; } = new List(); public double AverageDifference { get; set; } + + public int NoBeatCount { get; set; } } \ No newline at end of file diff --git a/MainWindow.xaml b/MainWindow.xaml index 1ca76e9..701d099 100644 --- a/MainWindow.xaml +++ b/MainWindow.xaml @@ -5,17 +5,17 @@ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:BeatDetector" d:DataContext="{d:DesignInstance Type=local:MainWindow}" mc:Ignorable="d" - Title="MainWindow" Height="450" Width="800"> + Title="MainWindow" Height="450" Width="800" KeyUp="Window_KeyUp" ResizeMode="NoResize"> - + - + - + diff --git a/MainWindow.xaml.cs b/MainWindow.xaml.cs index ba4a344..59e4d57 100644 --- a/MainWindow.xaml.cs +++ b/MainWindow.xaml.cs @@ -1,8 +1,11 @@ using BeatDetector.AudioProcessing; +using NAudio.Wave; + using System; using System.Collections.ObjectModel; using System.ComponentModel; +using System.Linq; using System.Runtime.CompilerServices; using System.Windows; using System.Windows.Media; @@ -15,6 +18,19 @@ namespace BeatDetector; /// public partial class MainWindow : Window { + private readonly Brush[] brushes = new Brush[] + { + Brushes.Red, + Brushes.Orange, + Brushes.Plum, + Brushes.Purple, + Brushes.Blue, + Brushes.Green, + }; + + private readonly IWaveIn audioStream; + private int lightAmountFactor = 3; + private AudioBeatDetector? audioBeatDetector; public ObservableCollection BeatLights { get; } = new ObservableCollection(); public MainWindow() @@ -22,24 +38,64 @@ public partial class MainWindow : Window DataContext = this; InitializeComponent(); - for (int i = 0; i < 4; i++) - { - BeatLights.Add(new BeatLight()); - } + audioStream = new WasapiLoopbackCapture(); + audioStream.StartRecording(); - AudioBeatDetector audioBeatDetector = new AudioBeatDetector(new TimeSpan(0, 0, 0, 0, 100), 1024 * 2, BeatLights.Count); - audioBeatDetector.OnBeat += AudioBeatDetector_OnBeat; - audioBeatDetector.OnNoBeat += AudioBeatDetector_OnNoBeat; + SetupAudioBeatDetector(); } private void AudioBeatDetector_OnBeat(object? sender, BeatDetectorEventArgs e) { - Dispatcher.Invoke(() => BeatLights[e.ChunkIndex].Color = Brushes.Red); + Dispatcher.Invoke(() => + { + double index = (brushes.Length - 1) / (double)BeatLights.Count * BeatLights.Count(x => x.Color != Brushes.White); + BeatLights[e.ChunkIndex].Color = brushes[(int)index]; + }); } private void AudioBeatDetector_OnNoBeat(object? sender, BeatDetectorEventArgs e) { - Dispatcher.Invoke(() => BeatLights[e.ChunkIndex].Color = Brushes.White); + _ = Dispatcher.Invoke(() => BeatLights[e.ChunkIndex].Color = Brushes.White); + } + + private void Window_KeyUp(object sender, System.Windows.Input.KeyEventArgs e) + { + if (e.Key == System.Windows.Input.Key.Add && lightAmountFactor < 10) + { + lightAmountFactor++; + audioBeatDetector?.Dispose(); + SetupAudioBeatDetector(); + } + else if (e.Key == System.Windows.Input.Key.Subtract && lightAmountFactor > 1) + { + lightAmountFactor--; + audioBeatDetector?.Dispose(); + SetupAudioBeatDetector(); + } + } + + private void SetupAudioBeatDetector() + { + BeatLights.Clear(); + + for (int i = 0; i < Math.Pow(lightAmountFactor, 2); i++) + { + BeatLights.Add(new BeatLight()); + } + + if (audioBeatDetector is not null) + { + audioBeatDetector.OnBeat -= AudioBeatDetector_OnBeat; + audioBeatDetector.OnNoBeat -= AudioBeatDetector_OnNoBeat; + } + + audioBeatDetector = new AudioBeatDetector(audioStream, TimeSpan.FromMilliseconds(0), 1024 * 2, BeatLights.Count); + + audioBeatDetector.OnBeat += AudioBeatDetector_OnBeat; + audioBeatDetector.OnNoBeat += AudioBeatDetector_OnNoBeat; + + Width = (Math.Sqrt(BeatLights.Count) * 110) + 15; + Height = (Math.Sqrt(BeatLights.Count) * 110) + 40; } }