c#中的实时麦克风幅度测量

时间:2010-12-27 17:21:12

标签: c# .net input voice microphone

我正在寻找简单的解决方案,它将在c#中返回麦克风输入的整数值。我已经在网上检查可用的样本,但它们都没有在x64环境中工作。 (VS2008 + W7 x64)。

是否有任何简单的解决方案可以返回c#中麦克风输入的幅度(或频率)值?

我试过没有结果的NAudio而且:http://www.codeproject.com/KB/audio-video/cswavrec.aspx?msg=2155497没有运气。

2 个答案:

答案 0 :(得分:2)

我认为最简单的方法是使用旧的Windows多媒体API,因为它非常直接。

以下是MSDN的链接:http://msdn.microsoft.com/en-us/library/dd743586(v=VS.85).aspx

您所做的就是使用waveInOpen功能来获取输入设备。要确定要使用的设备,请不要枚举所有设备,但可以查询每个设备。通过调用waveInGetNumDevs返回已安装设备的数量。然后,您可以为每个设备调用waveInGetDevCaps并检查这些属性。

当您处理设备时,请重复调用waveInAddBuffer以获取小块数据。根据您在waveInOpen期间指定的格式,字节表示原始音频数据。以某个频率采样8或16位有符号或无符号采样的幅度。

然后,您可以应用滚动平均值来平滑信号并打印出来。

C#没有我所知道的声音API,所以你要做的是使用P / Invoke来获取Win32 API函数。这是相当简单的,您只需要移植Win32标头的小版本就可以直接从C#调用它们。

如果你是更硬核,你可以用C ++ / CLI编写一个包装库。这并不是一个想法,因为它让你使用现有的Windows C / C ++头文件,并以有趣的方式混合C ++和托管代码。只需要小心非托管资源,您就可以立即拥有一个非常强大的intropability库。

但是还有更高级的音频API,从Windows Vista开始,Windows Core Audio组件可能会更加有趣。但是对于基本的I / O操作,Windows多媒体功能可以让您更快地到达目的地。

在构建简单的软件合成器时,我已经在很多场合使用过这些函数。可悲的是,代码早已不复存在。

答案 1 :(得分:1)

我推荐SlimDX,因为它应该适用于任何版本的Windows(x86或x64),并提供最多的功能和灵活性。但是,由于没有良好的完整代码示例,因此启动和运行是一种痛苦。我写了一个包装类来简化它的用法,所以它可以这样调用(我在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...
    }

以下是SlimDX包装类的来源:

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;
            }
        }
    }
}

它是完全多线程的,可以最大限度地减少延迟。我最初是为实时信号处理分析工具编写的,并使用浮点输出而不是简短但我修改了代码示例以匹配您请求的用法。如果您需要频率数据,我会使用http://www.mathdotnet.com/Neodym.aspxhttp://www.exocortex.org/dsp/作为一个好的C#FFT库。