2010-12-27 3 views
10

나는 C#에서 마이크 입력의 정수 값을 반환하는 간단한 솔루션을 찾고 있습니다. 이미 인터넷에서 사용 가능한 샘플을 확인했지만, 그 중 어느 것도 x64 환경에서 작업하지 않았습니다. (VS2008 + W7 x64).라이브 마이크 진폭 측정 C#

C#에서 마이크 입력의 진폭 (또는 주파수) 값을 반환하는 간단한 해결책이 있습니까?

결과가없고 NAudio를 사용해 보았습니다. 행운을 제외하면 http://www.codeproject.com/KB/audio-video/cswavrec.aspx?msg=2155497입니다.

+0

DirectX DirectSound를 사용해 보셨습니까? – JYelton

+0

프로그램을 "Any CPU"에서 "32 bit only"로 설정해 보았습니까? 대부분의 프로그램은 64 비트 모드에서 많은 이익을 얻지 못합니다. – CodesInChaos

+0

나는 그것을 벌써 시도했다. 그러나 지금까지 어떤 운도 가지지 않고있다. 어떤 간단한 directSound 예제도 찾지 못했습니다. 나는 또한 SlimDX를 시도했지만, 모든 예제를 통해 항상 문제가 있음을 알 수 있습니다. 필자의 경우에는 동적 업데이트 (초당 몇 번 샘플링)로 정수 값이 필요합니다. 누구나 그런 경험이 있습니까? 어떤 도움을 주셔서 감사합니다. – Marian

답변

2

내가보기에 가장 쉬운 방법은 이전 Windows 멀티미디어 API를 사용하는 것입니다. http://msdn.microsoft.com/en-us/library/dd743586(v=VS.85).aspx

당신이 할 것은 당신이 입력 장치를 얻을 수있는 waveInOpen 기능을 사용한다는 것입니다 :

다음은 MSDN에 대한 링크입니다. 어떤 장치를 사용할 지 파악하려면 모든 장치를 열거하지 말고 각 장치를 하나씩 쿼리 할 수 ​​있습니다. 설치된 장치의 수는 waveInGetNumDevs을 호출하여 반환됩니다. 그런 다음 각 장치에 대해 waveInGetDevCaps으로 전화하여 해당 속성을 검사 할 수 있습니다.

장치를 처리 할 때 작은 숫자의 데이터를 얻기 위해 반복적으로 waveInAddBuffer을 호출하십시오. waveInOpen 동안 지정한 형식에 따라 바이트는 원시 오디오 데이터를 나타냅니다. 일부 주파수에서 샘플링 된 8 비트 또는 16 비트 진폭의 부호가 있거나없는 신호입니다.

그런 다음 평균을 적용하여 신호를 부드럽게하고 인쇄 할 수 있습니다.

C#에는 내가 아는 사운드 API가 없으므로 P/Invoke를 사용하여 Win32 API 기능을 사용하는 것이 좋습니다. 이것은 매우 간단합니다. C#에서 직접 호출 할 수 있도록 작은 버전의 Win32 헤더를 포트하기 만하면됩니다.

더 많은 하드 코어를 사용한다면 C++/CLI에서 래퍼 라이브러리를 작성할 수 있습니다. 기존 Windows C/C++ 헤더 파일을 사용하고 C++ 및 관리 코드를 intresting 방식으로 사용하기 때문에 그렇게 나쁘지 않습니다. 관리되지 않는 리소스에주의를 기울이면 시간이별로 지나지 않는 강력한 라이브러리가 제공됩니다.

하지만 윈도우 비스타에서 시작하는 고급 오디오 API도 있습니다. Windows Core Audio 구성 요소가 더 흥미 진진 할 수 있습니다. 그러나 기본 I/O 작업의 경우 Windows 멀티미디어 기능을 통해 더 빠르게 사용자를 얻을 수 있습니다.

간단한 소프트웨어 신디사이저를 구축 할 때 이러한 기능을 여러 번 사용했습니다. 슬프게도 그 코드는 오래 전에 사라졌습니다.

1

SlimDX는 Windows의 모든 버전 (x86 또는 x64)에서 작동해야하며 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; 
      } 
     } 
    } 
} 
: 여기
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 래퍼 클래스의 소스입니다 : 나는 그것과 같이 호출 할 수 있도록 불구하고 그것의 사용을 단순화하는 래퍼 클래스를 썼다 (나는 Win7에 64에이 코드를 테스트)

대기 시간을 최소화하기 위해 완전히 멀티 스레드입니다. 원래는 실시간 신호 처리 분석 도구로 작성했으며 짧은 대신 float 출력을 사용했지만 요청한 용도에 맞게 코드 샘플을 수정했습니다.빈도 데이터가 필요한 경우 좋은 C# FFT 라이브러리에 http://www.mathdotnet.com/Neodym.aspx 또는 http://www.exocortex.org/dsp/을 사용합니다.