Измерение амплитуды живого микрофона в c # - PullRequest
10 голосов
/ 27 декабря 2010

Я ищу простое решение, которое будет возвращать целочисленное значение входа микрофона в c #. Я уже проверял доступные образцы в сети, но ни один из них не работал в среде x64. (VS2008 + W7 x64).

Есть ли какое-нибудь простое решение, которое вернет значение амплитуды (или частоты) микрофонного входа в c #?

Я попробовал NAudio без результатов, и это: http://www.codeproject.com/KB/audio-video/cswavrec.aspx?msg=2155497 без удачи.

Ответы [ 2 ]

2 голосов
/ 09 марта 2011

Я считаю, что самый простой путь - использовать старый мультимедийный API Windows, потому что он действительно прост.

Вот ссылка на 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, которые могут быть еще интереснеевдоль линии.Но для базовой операции ввода-вывода мультимедийные функции Windows помогут вам быстрее.

Я использовал эти функции несколько раз при создании простых программных синтезаторов.К сожалению, этот код давно ушел.

1 голос
/ 19 декабря 2011

Я рекомендую 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.

...