10

C# でマイク入力の整数値を返す簡単なソリューションを探しています。すでにネットで入手可能なサンプルをチェックしていましたが、x64 環境ではどれも機能しませんでした。(VS2008 + W7 x64)。

C#でマイク入力の振幅(または周波数)の値を返す簡単なソリューションはありますか?

私は結果なしでNAudioを試しました.

4

2 に答える 2

2

古い W​​indows マルチメディア 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 関数を取得します。これは非常に簡単です。C# から直接呼び出せるようにするには、Win32 ヘッダーの小さなバージョンを移植するだけで済みます。

もっと筋金入りなら、C++/CLI でラッパー ライブラリを作成できます。既存の Windows C/C++ ヘッダー ファイルを使用して、興味深い方法で C++ とマネージ コードを混在させることができるため、これはそれほど悪い考えではありません。管理されていないリソースに気を付ければ、すぐに非常に強力な非互換性ライブラリを作成できます。

しかし、Windows Vista から始まるより高度なオーディオ API もあり、Windows コア オーディオ コンポーネントは、今後さらに興味深いものになる可能性があります。ただし、基本的な I/O 操作については、Windows のマルチメディア機能を使用するとより高速に処理できます。

簡単なソフトウェア シンセサイザを作成する際に、これらの関数を何度も使用してきました。悲しいことに、そのコードはなくなってしまいました。

于 2011-03-09T16:30:24.243 に答える
1

ほぼすべてのバージョンの Windows (x86 または x64) で動作し、ほとんどの機能と柔軟性を提供するため、SlimDX をお勧めします。ただし、適切な完全なコード サンプルがないため、起動して実行するのは面倒です。ただし、このように呼び出すことができるように、その使用法を簡素化するためにラッパー クラスを作成しました (このコードは 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;
            }
        }
    }
}

レイテンシを最小限に抑えるために完全にマルチスレッド化されています。元々はリアルタイム信号処理解析ツール用に作成し、short ではなく float 出力を使用していましたが、ご要望の使用方法に合わせてコード サンプルを修正しました。周波数データが​​必要な場合は、優れた C# FFT ライブラリとしてhttp://www.mathdotnet.com/Neodym.aspxまたはhttp://www.exocortex.org/dsp/を使用します。

于 2011-12-19T00:33:40.353 に答える