Live Microphone amplitude measurement in c#

2019-06-15 18:40发布

I am looking for simple solution which will return integer value of microphone input in c#. I was already checking available samples on net, but none of them worked in a x64 environment. (VS2008 + W7 x64).

Is there any simple solution that will return value of amplitude (or frequency) of microphone input in c#?

I tried NAudio without results and this: http://www.codeproject.com/KB/audio-video/cswavrec.aspx?msg=2155497 without luck.

2条回答
劳资没心,怎么记你
2楼-- · 2019-06-15 19:31

I reckon the easiest route to go is to use the old Windows multimedia API because it's really straight forward.

Here's the link to MSDN: http://msdn.microsoft.com/en-us/library/dd743586(v=VS.85).aspx

What you do is that you use the waveInOpen function to get an input device. To figure out what device to use you don't enumerate all the devices but you can query each and one of them. The number of installed devices is returned by calling waveInGetNumDevs. You can then call waveInGetDevCaps for each device and inspect those properties.

When you have your device handle you then repeatedly call waveInAddBuffer to get small chunks of data. Depending on the format you specified during waveInOpen the bytes represent the raw audio data. Amplitude in 8 or 16-bit signed or unisnged sampled at some frequency.

You could then apply a rolling average to smooth the signal and just print that.

C# doesn't have a sound API that I know of, so what you do is that you use P/Invoke to get at the Win32 API functions. This is fairly straight forward, you only need to port small versions of the Win32 headers to be able to call them from C# directly.

If you're more hardcore you could write a wrapper library in C++/CLI. That's not that bad of an idea because it let's you use existing Windows C/C++ header files and mix C++ and managed code in intresting ways. Just be careful with the unmanaged resources and you'll have a very powerful intropability library in no time.

But there's also more advanced audio APIs starting with Windows Vista, the Windows Core Audio components which could be more interesting further along the line. But for basic I/O operation the Windows multimedia functions will get you there faster.

I've used these functions on a number of occasions when building simple software synthesizers. Sadly that code is long gone.

查看更多
迷人小祖宗
3楼-- · 2019-06-15 19:38

I recommend SlimDX since it should work on just about any version of windows (x86 or x64) and provides the most features and flexibility. However, it is a pain to get up and running since there are no good complete code samples. I wrote a wrapper class to simplify its usage though so it can be called like this (I tested this code on Win7 x64):

    public void CaptureAudio()
    {
        using (var source = new SoundCardSource())
        {
            source.SampleRateKHz = 44.1;
            source.SampleDataReady += this.OnSampleDataReady;
            source.Start();

            // Capture 5 seconds of audio...
            Thread.Sleep(5000);

            source.Stop();
        }
    }

    private void OnSampleDataReady(object sender, SampleDataEventArgs e)
    {
        // Do something with e.Data short array on separate thread...
    }

Here is the source for the SlimDX wrapper class:

using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using SlimDX.DirectSound;
using SlimDX.Multimedia;

public class SampleDataEventArgs : EventArgs
{
    public SampleDataEventArgs(short[] data)
    {
        this.Data = data;
    }

    public short[] Data { get; private set; }
}

public class SoundCardSource : IDisposable
{
    private volatile bool running;
    private int bufferSize;
    private CaptureBuffer buffer;
    private CaptureBufferDescription bufferDescription;
    private DirectSoundCapture captureDevice;
    private WaveFormat waveFormat;
    private Thread captureThread;
    private List<NotificationPosition> notifications;
    private int bufferPortionCount;
    private int bufferPortionSize;
    private WaitHandle[] waitHandles;
    private double sampleRate;

    public SoundCardSource()
    {
        this.waveFormat = new WaveFormat();
        this.SampleRateKHz = 44.1;
        this.bufferSize = 2048;
    }

    public event EventHandler<SampleDataEventArgs> SampleDataReady = delegate { };

    public double SampleRateKHz
    {
        get 
        { 
            return this.sampleRate; 
        }

        set
        {
            this.sampleRate = value;

            if (this.running)
            {
                this.Restart();
            }
        }
    }

    public void Start()
    {
        if (this.running)
        {
            throw new InvalidOperationException();
        }

        if (this.captureDevice == null)
        {
            this.captureDevice = new DirectSoundCapture();
        }

        this.waveFormat.FormatTag = WaveFormatTag.Pcm; // Change to WaveFormatTag.IeeeFloat for float
        this.waveFormat.BitsPerSample = 16; // Set this to 32 for float
        this.waveFormat.BlockAlignment = (short)(waveFormat.BitsPerSample / 8);
        this.waveFormat.Channels = 1;
        this.waveFormat.SamplesPerSecond = (int)(this.SampleRateKHz * 1000D);
        this.waveFormat.AverageBytesPerSecond =
            this.waveFormat.SamplesPerSecond *
            this.waveFormat.BlockAlignment *
            this.waveFormat.Channels;

        this.bufferPortionCount = 2;

        this.bufferDescription.BufferBytes = this.bufferSize * sizeof(short) * bufferPortionCount;
        this.bufferDescription.Format = this.waveFormat;
        this.bufferDescription.WaveMapped = false;

        this.buffer = new CaptureBuffer(this.captureDevice, this.bufferDescription);

        this.bufferPortionSize = this.buffer.SizeInBytes / this.bufferPortionCount;
        this.notifications = new List<NotificationPosition>();

        for (int i = 0; i < this.bufferPortionCount; i++)
        {
            NotificationPosition notification = new NotificationPosition();
            notification.Offset = this.bufferPortionCount - 1 + (bufferPortionSize * i);
            notification.Event = new AutoResetEvent(false);
            this.notifications.Add(notification);
        }

        this.buffer.SetNotificationPositions(this.notifications.ToArray());
        this.waitHandles = new WaitHandle[this.notifications.Count];

        for (int i = 0; i < this.notifications.Count; i++)
        {
            this.waitHandles[i] = this.notifications[i].Event;
        }

        this.captureThread = new Thread(new ThreadStart(this.CaptureThread));
        this.captureThread.IsBackground = true;

        this.running = true;
        this.captureThread.Start();
    }

    public void Stop()
    {
        this.running = false;

        if (this.captureThread != null)
        {
            this.captureThread.Join();
            this.captureThread = null;
        }

        if (this.buffer != null)
        {
            this.buffer.Dispose();
            this.buffer = null;
        }

        if (this.notifications != null)
        {
            for (int i = 0; i < this.notifications.Count; i++)
            {
                this.notifications[i].Event.Close();
            }

            this.notifications.Clear();
            this.notifications = null;
        }
    }

    public void Restart()
    {
        this.Stop();
        this.Start();
    }

    private void CaptureThread()
    {
        int bufferPortionSamples = this.bufferPortionSize / sizeof(float);

        // Buffer type must match this.waveFormat.FormatTag and this.waveFormat.BitsPerSample
        short[] bufferPortion = new short[bufferPortionSamples];
        int bufferPortionIndex;

        this.buffer.Start(true);

        while (this.running)
        {
            bufferPortionIndex = WaitHandle.WaitAny(this.waitHandles);

            this.buffer.Read(
                bufferPortion,
                0,
                bufferPortionSamples,
                bufferPortionSize * Math.Abs((bufferPortionIndex - 1) % bufferPortionCount));

            this.SampleDataReady(this, new SampleDataEventArgs(bufferPortion));
        }

        this.buffer.Stop();
    }

    public void Dispose()
    {
        this.Dispose(true);
        GC.SuppressFinalize(this);
    }

    protected virtual void Dispose(bool disposing)
    {
        if (disposing)
        {
            this.Stop();

            if (this.captureDevice != null)
            {
                this.captureDevice.Dispose();
                this.captureDevice = null;
            }
        }
    }
}

It's fully multi-threaded to minimize latency. I originally wrote it for a real-time signal processing analysis tool and used float output instead of short but I modified the code sample to match your requested usage. If you need frequency data I would use http://www.mathdotnet.com/Neodym.aspx or http://www.exocortex.org/dsp/ for a good C# FFT library.

查看更多
登录 后发表回答