我正在寻找简单的解决方案,它将在 c# 中返回麦克风输入的整数值。我已经在网上检查了可用的样本,但它们都没有在 x64 环境中工作。(VS2008 + W7 x64)。
是否有任何简单的解决方案可以在 c# 中返回麦克风输入的幅度(或频率)值?
我试过 NAudio 没有结果,这个:http ://www.codeproject.com/KB/audio-video/cswavrec.aspx?msg=2155497没有运气。
我认为最简单的方法是使用旧的 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 多媒体功能会让您更快地到达那里。
在构建简单的软件合成器时,我曾多次使用这些功能。可悲的是,该代码早已不复存在。
我推荐 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.aspx或http://www.exocortex.org/dsp/来获得一个好的 C# FFT 库。