how to show the music's spectrum in .net maui?

mc 6,596 Reputation points
2026-01-05T13:24:03.6933333+00:00

I am using .net MAUI 9.0 and I want to get this in page:

User's image

when I play music in the mobile I can get the rhythm how to do it?

Developer technologies | .NET | .NET MAUI
0 comments No comments
{count} votes

Answer accepted by question author
  1. Jack Dang (WICLOUD CORPORATION) 8,755 Reputation points Microsoft External Staff Moderator
    2026-01-06T08:27:01.6966667+00:00

    Hi @mc ,

    Thanks for reaching out.

    I’ve created a sample .NET MAUI project that demonstrates a music spectrum visualizer with mirrored bars, peak markers, and play/stop functionality. The spectrum responds to audio playback on Windows (tested in VS 2022).

    image

    Currently, this project works with Windows only, but you can use it as a base to extend for Android or iOS later. The spectrum is generated from audio samples and provides a nice visualization of frequency amplitudes.

    Make sure to install the following via NuGet:

    • Plugin.Maui.Audio – for audio playback
    • SkiaSharp.Views.Maui – for drawing the spectrum
    • MathNet.Numerics – for FFT calculation
    • NAudio – for decoding audio files on Windows

    MainPage.xaml

    <?xml version="1.0" encoding="utf-8" ?>
    <ContentPage 
        xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
        xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
        xmlns:skia="clr-namespace:SkiaSharp.Views.Maui.Controls;assembly=SkiaSharp.Views.Maui.Controls"
        x:Class="MauiMusicSpectrum.MainPage">
        <VerticalStackLayout Padding="20" Spacing="20">
            <HorizontalStackLayout Spacing="10">
                <Button Text="Pick Music File" Clicked="OnPickMusic" />
                <Button Text="Play" Clicked="OnPlayMusic" />
                <Button Text="Stop" Clicked="OnStopMusic" />
            </HorizontalStackLayout>
            <skia:SKCanvasView x:Name="SpectrumCanvas"
                               HeightRequest="200"
                               PaintSurface="OnPaintSurface" />
        </VerticalStackLayout>
    </ContentPage>
    

    MainPage.xaml.cs

    using Plugin.Maui.Audio;
    using SkiaSharp;
    using SkiaSharp.Views.Maui;
    using MathNet.Numerics.IntegralTransforms;
    using System.Numerics;
    
    namespace MauiMusicSpectrum;
    
    public partial class MainPage : ContentPage
    {
        IAudioPlayer player;
        float[] spectrum = new float[32];
        float[] spectrumPeak = new float[32]; // for peak decay
        float[] samples;
        bool isPlaying = false;
        bool spectrumLoopRunning = false;
    
        public MainPage()
        {
            InitializeComponent();
        }
    
        // -------------------------
    
        // File Picker
    
        // -------------------------
    
        async void OnPickMusic(object sender, EventArgs e)
        {
            var file = await FilePicker.PickAsync();
            if (file == null) return;
            player?.Stop();
            player?.Dispose();
            await LoadAudioSamples(file);
            var stream = await file.OpenReadAsync();
            player = AudioManager.Current.CreatePlayer(stream);
            player.Volume = 1.0;
            // Start playing
            player.Play();
            isPlaying = true;
            if (samples != null && !spectrumLoopRunning)
                StartAudioSpectrum();
        }
    
        void OnPlayMusic(object sender, EventArgs e)
        {
            if (player != null && !isPlaying)
            {
                player.Play();
                isPlaying = true;
            }
        }
    
        void OnStopMusic(object sender, EventArgs e)
        {
            if (player != null && isPlaying)
            {
                player.Pause();
                isPlaying = false;
            }
        }
    
        // -------------------------
    
        // Load audio samples (Windows only)
    
        // -------------------------
    
        async Task LoadAudioSamples(FileResult file)
        {
    #if WINDOWS
            var tempPath = Path.Combine(Path.GetTempPath(), file.FileName);
            using (var fs = File.Create(tempPath))
            using (var src = await file.OpenReadAsync())
                await src.CopyToAsync(fs);
            samples = DecodePCMWindows(tempPath);
    #else
            samples = null;
    #endif
        }
        void StartAudioSpectrum()
        {
            spectrumLoopRunning = true;
            int fftSize = 1024;
            Task.Run(async () =>
            {
                int pos = 0;
                while (true)
                {
                    if (!isPlaying || samples == null || pos + fftSize >= samples.Length)
                    {
                        await Task.Delay(30);
                        continue;
                    }
                    var buffer = new Complex[fftSize];
                    for (int i = 0; i < fftSize; i++)
                        buffer[i] = new Complex(samples[pos + i], 0);
                    Fourier.Forward(buffer, FourierOptions.Matlab);
                    int binsPerBar = fftSize / 2 / spectrum.Length;
                    for (int i = 0; i < spectrum.Length; i++)
                    {
                        double sum = 0;
                        for (int j = 0; j < binsPerBar; j++)
                            sum += buffer[i * binsPerBar + j].Magnitude;
                        float value = (float)(Math.Log10(sum + 1) * 20);
                        spectrum[i] = value;
                        if (value > spectrumPeak[i])
                            spectrumPeak[i] = value;
                        else
                            spectrumPeak[i] *= 0.95f;
                    }
                    pos += fftSize / 4;
                    MainThread.BeginInvokeOnMainThread(() => SpectrumCanvas.InvalidateSurface());
                    await Task.Delay(30);
                }
            });
        }
    
        void OnPaintSurface(object sender, SKPaintSurfaceEventArgs e)
        {
            var canvas = e.Surface.Canvas;
            canvas.Clear(SKColors.Black);
            int barCount = spectrum.Length;
            float barWidth = e.Info.Width / (barCount * 3f);
            float centerX = e.Info.Width / 2f;
            float centerY = e.Info.Height / 2f;
            for (int i = 0; i < barCount; i++)
            {
                float hue = (i * 360f / barCount + DateTime.Now.Millisecond / 10f) % 360f;
                var baseColor = SKColor.FromHsl(hue, 100, 30);
                float barHeight = spectrum[i] * 2.5f;
                if (barHeight > centerY) barHeight = centerY;
                var paint = new SKPaint { Color = baseColor, IsAntialias = true };
                canvas.DrawRect(centerX + i * barWidth, centerY - barHeight, barWidth - 1, barHeight, paint);
                canvas.DrawRect(centerX - (i + 1) * barWidth, centerY - barHeight, barWidth - 1, barHeight, paint);
                canvas.DrawRect(centerX + i * barWidth, centerY, barWidth - 1, barHeight, paint);
                canvas.DrawRect(centerX - (i + 1) * barWidth, centerY, barWidth - 1, barHeight, paint);
                var peakPaint = new SKPaint { Color = SKColors.White.WithAlpha(180), IsAntialias = true };
                float peakYTop = centerY - spectrumPeak[i] * 2.5f - 2;
                float peakYBottom = centerY + spectrumPeak[i] * 2.5f;
                if (peakYTop < 0) peakYTop = 0;
                if (peakYBottom > e.Info.Height) peakYBottom = e.Info.Height;
                canvas.DrawRect(centerX + i * barWidth, peakYTop, barWidth - 1, 2, peakPaint);
                canvas.DrawRect(centerX - (i + 1) * barWidth, peakYTop, barWidth - 1, 2, peakPaint);
                canvas.DrawRect(centerX + i * barWidth, peakYBottom, barWidth - 1, 2, peakPaint);
                canvas.DrawRect(centerX - (i + 1) * barWidth, peakYBottom, barWidth - 1, 2, peakPaint);
            }
        }
    #if WINDOWS
        float[] DecodePCMWindows(string path)
        {
            using var reader = new NAudio.Wave.AudioFileReader(path);
            var list = new List<float>();
            float[] buffer = new float[1024];
            int read;
            while ((read = reader.Read(buffer, 0, buffer.Length)) > 0)
                list.AddRange(buffer.Take(read));
            return list.ToArray();
        }
    #endif
    }
    

    Limitations / Notes

    1. Platform Support:
      • This sample is fully tested only on Windows.
      • Android / iOS decoding is not included.
    2. Spectrum Behavior:
      • The spectrum reflects audio samples, but in Windows it may appear somewhat “random” due to FFT simplification.
    3. Extensibility:
      • You can expand this project with Android/iOS decoding, smoother animations, color themes, volume control, etc.

    Hope this helps! If my answer was helpful - kindly follow the instructions here so others with the same problem can benefit as well.

    0 comments No comments

1 additional answer

Sort by: Most helpful
  1. Bruce (SqlWork.com) 82,326 Reputation points Volunteer Moderator
    2026-01-05T17:54:17.0133333+00:00

    It is a 3 step process. First you need a Maui plugin that gives access to audio stream, say

    https://github.com/jfversluis/Plugin.Maui.Audio

    second you will need a library package that can provide graphic visualizer of the stream (which just a transformation)

    third you need a graphics package to display the visualization.

    Google for c# samples.

    0 comments No comments

Your answer

Answers can be marked as 'Accepted' by the question author and 'Recommended' by moderators, which helps users know the answer solved the author's problem.