简体   繁体   English

C#频率检索

[英]C# Frequency Retrieval

What I need to do is calculate the frequency of the microphone input. 我需要做的是计算麦克风输入的频率。 I'm using IWaveProvider for this and its implemented Read() . 我为此使用IWaveProvider及其实现的Read() The buffer always has a size of 8820 elements and something seems to be going wrong with the conversion from byte array to float array as well (the FloatBuffer property part). 缓冲区的大小始终为8820个元素,并且从字节数组到float数组的转换似乎也出了问题( FloatBuffer属性部分)。

Here are some of the important bits... 以下是一些重要的方面...

This is where I start my recording: 这是我开始录制的地方:

private void InitializeSoundRecording()
{
    WaveIn waveIn = new WaveIn();
    waveIn.DeviceNumber = 0;   
    waveIn.DataAvailable += (s, e) => this.waveIn_DataAvailable(s, e); 
    waveIn.RecordingStopped += (s, e) => this.waveIn_RecordingStopped(s, e);
    waveIn.WaveFormat = new WaveFormat(44100, 1);
    waveIn.StartRecording();
}

When the DataAvailable event handler is called, the following is executed: 调用DataAvailable事件处理程序时,将执行以下操作:

private void waveIn_DataAvailable(object sender, WaveInEventArgs e)
{
    WaveBuffer wb = new WaveBuffer(e.Buffer.Length);

    IWaveProvider iWaveProvider = new PitchDetector(new WaveInProvider(sender as WaveIn), new WaveBuffer(e.Buffer));
    iWaveProvider.Read(wb, 0, e.Buffer.Length);

    PitchDetector pd = iWaveProvider as PitchDetector;

    this.ShowPitch(pd.Pitch);
}

And lastly, this is the "actual" important bit: 最后,这是“实际”重要的一点:

private const int FLOAT_BUFFER_SIZE = 8820;
private IWaveProvider source;
private WaveBuffer waveBuffer;
private int sampleRate;
private float[] fftBuffer;
private float[] prevBuffer;
public float Pitch { get; private set; }

public WaveFormat WaveFormat { get { return this.source.WaveFormat; } }

internal PitchDetector(IWaveProvider waveProvider, WaveBuffer waveBuffer = null)
{
    this.source = waveProvider;
    this.sampleRate = waveProvider.WaveFormat.SampleRate;
    this.waveBuffer = waveBuffer;
}

/// <summary>
/// UNSAFE METHOD! 
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
private unsafe float[] ByteArrayToFloatArray(byte[] input)
{
    float[] fb = new float[FLOAT_BUFFER_SIZE];
    unsafe
    {
        fixed (byte* ptrBuffer = input)
        {
            float* ptrFloatBuffer = (float*)ptrBuffer;
            for (int i = 0; i < FLOAT_BUFFER_SIZE; i++)
            {
                fb[i] = *ptrFloatBuffer;
                ptrFloatBuffer++;
            }
        }
    }
    return fb;
}

public int Read(byte[] buffer, int offset = 0, int count = 0)
{
    if (this.waveBuffer == null || this.waveBuffer.MaxSize < count)
        this.waveBuffer = new WaveBuffer(count);

    int readBytes = this.source.Read(this.waveBuffer, 0, count);

    if (readBytes > 0) readBytes = count;

    int frames = readBytes / sizeof(float);

    this.Pitch = this.DeterminePitch(this.waveBuffer.FloatBuffer, frames);

    return frames * 4;
}

Strangely enough, when it enters the constructor, waveBuffer contains some data (255, 1, 0, etc.), but when I check the "buffer" parameter of Read() , it's entirely 0. Every element. 奇怪的是,当它进入构造函数时,waveBuffer包含一些数据(255、1、0等),但是当我检查Read()的“ buffer”参数时,它完全为0。每个元素。

Out of curiosity also, why does Read() have a buffer parameter, but isn't actually used in the method at all (I got that piece of code from one of your articles)? 同样出于好奇,为什么Read()有一个buffer参数,但实际上根本没有在该方法中使用(我从您的一篇文章中得到了这段代码)?

Any help to resolve this issue would be greatly appreciated! 解决此问题的任何帮助将不胜感激! I've been at this for quite a while already, but can make no sense out of it. 我已经花了很长时间了,但是这毫无意义。

Thanks, Alain 谢谢阿兰

It is not clear what article you are referring to and I am not familiar with this library. 您所指的是什么文章尚不清楚,并且我对该库不熟悉。 However, the Read method is clearly reading in your 'time-series'/or other data. 但是, Read方法显然是在读取“时间序列” /或其他数据。 From this, the buffer parameter you speak of is likely to be the padding length that you want to place on either end of your data set. 由此看来,您所说的buffer参数可能是您要放置在数据集任一端的填充长度。

This padding is known as 'Zero Padding' and it pads your recorded signal with zeros (places n zeros on either end of the signal, where n is set according to the radix used). 这种填充称为“零填充”,它用零填充您记录的信号(在信号的两端放置n个零,其中n根据所用的基数设置)。 This allows one to use a longer FFT, which will produce a longer FFT resulting vector. 这允许使用更长的FFT,这将产生更长的FFT结果向量。

A longer FFT result has more frequency bins that are more closely spaced in frequency. 较长的FFT结果具有更多的频率段,这些频率段的频率间隔更近。 But they will be essentially providing the same result as a high quality Sinc interpolation of a shorter non-zero-padded FFT of the original data. 但是,它们实际上将提供与原始数据较短的非零填充FFT的高质量Sinc插值相同的结果。

This might result in a smoother looking spectrum when plotted without further interpolation. 在不进行进一步插值的情况下绘制时,这可能会导致频谱看起来更平滑。

For further in formation see 如需进一步了解,请参阅

https://dsp.stackexchange.com/questions/741/why-should-i-zero-pad-a-signal-before-taking-the-fourier-transform https://dsp.stackexchange.com/questions/741/why-should-i-zero-pad-a-signal-before-take-the-fourier-transform

I hope this helps. 我希望这有帮助。

This is not really an answer to your question but I wrote a safe generic alternative to your array conversion function. 这并不是您问题的真正答案,但我为数组转换函数写了一个safe通用替代方案。

using System;
using System.Runtime.InteropServices;

public static class Extensions
{
    public staitc TDestination[] Transform<TSource, TDestination>(
        this TSource[] source)
        where TSource : struct
        where TDestination : struct
    {
        if (source.Length == 0)
        {
            return new TDestination[0];
        }

        var sourceSize = Marshal.SizeOf(typeof(TSource));
        var destinationSize = Marshal.SizeOf(typeof(TDestination));

        var byteLength = source.Length * sourceSize;

        int remainder;
        var destinationLength = Math.DivRem(
            byteLength,
            destinationSize,
            out remainder);
        if (remainder > 0)
        {
            destinationLength++;
        }

        var destination = new TDestination[destinationLength];
        Buffer.BlockCopy(source, 0, destination, 0, byteLength);
        return destination;
    }
}

Which obviously, you could use like 显然,您可以使用

var bytes = new byte[] { 1, 1, 2, 3, 5, 8, 13, 21 };
var floats = bytes.Transform<byte, float>();

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM