【问题标题】:Live Microphone amplitude measurement in c#c#中的现场麦克风幅度测量
【发布时间】:2011-05-31 05:46:09
【问题描述】:

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

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

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

【问题讨论】:

  • 你试过 DirectX DirectSound 吗?
  • 您是否尝试将程序从“任何 CPU”设置为“仅 32 位”?大多数程序在 64 位模式下运行并没有太多好处。
  • 我已经尝试过了,但到目前为止还没有运气。也没有找到任何简单的 directSound 示例。我也尝试过 SlimDX,但看起来,所有这些示例总是存在一些问题。此外,在我的情况下,我需要具有动态更新的整数值(每秒采样几次)。有人有这方面的经验吗?感谢您的帮助。
  • 计算幅度的数学非常简单。您可以在一个时间间隔内平均输入的平方幅度。输入通常是一个字节数组,其中包含一系列带符号的短裤。
  • 我使用来自 codeproject.com/KB/cs/… 的代码,但我从未在 64 位操作系统上尝试过。

标签: c# .net input voice microphone


【解决方案1】:

我认为最简单的方法是使用旧的 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++ 和托管代码。只需小心非托管资源,您很快就会拥有一个非常强大的可溯源库。

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

在构建简单的软件合成器时,我曾多次使用这些功能。遗憾的是,该代码早已不复存在。

【讨论】:

    【解决方案2】:

    我推荐 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 库。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-01-11
      • 2015-11-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-09-05
      • 2013-09-18
      相关资源
      最近更新 更多