C#中的实时麦克风振幅测量

10

我正在寻找一个简单的解决方案,能够在C#中返回麦克风输入的整数值。我已经检查了网上可用的示例,但是没有一个在x64环境下工作(VS2008 + W7 x64)。

是否有任何简单的解决方案能够在C#中返回麦克风输入的振幅(或频率)值?

我尝试过NAudio,但没有结果。同时,这个http://www.codeproject.com/KB/audio-video/cswavrec.aspx?msg=2155497 也没有成功。


你尝试过DirectX DirectSound吗? - JYelton
你尝试过将程序从“任何 CPU”设置为“仅 32 位”吗?大多数程序在 64 位模式下运行并没有太多好处。 - CodesInChaos
我已经尝试过了,但到目前为止没有运气。也没有找到任何简单的directSound示例。我还尝试了SlimDX,但看起来所有这些示例都有一些问题。此外,在我的情况下,我需要整数值进行动态更新(每秒采样几次)。有人有相关经验吗?感谢任何帮助。 - Marian
这里有一个非常方便的示例,展示如何使用naudio录制麦克风输入,也许可以帮到你:http://opensebj.blogspot.com/2009/04/naudio-tutorial-5-recording-audio.html - MUG4N
显示剩余2条评论
2个回答

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++和托管代码。只要小心处理非托管资源,你就可以很快拥有一个非常强大的互操作性库。
但是,从Windows Vista开始还有更高级的音频API,即Windows核心音频组件,这可能更加有趣。但对于基本的I/O操作,Windows多媒体函数会更快地帮助你实现目标。
我曾经在构建简单的软件合成器时使用过这些函数。遗憾的是,那段代码已经消失了。

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库。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接