c#频率检索

本文关键字:检索 频率 | 更新日期: 2023-09-27 18:08:04

我需要做的是计算麦克风输入的频率。我用IWaveProvider来实现它的Read()。缓冲区总是有8820个元素的大小,从字节数组到浮点数组的转换似乎也出了问题(FloatBuffer属性部分)。

这里是一些重要的比特…

这是我开始录音的地方:

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();
}

当调用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);
}

最后,这是"真正"重要的位:

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;
}

奇怪的是,当它进入构造函数时,waveBuffer包含一些数据(255,1,0等),但是当我检查Read()的"buffer"参数时,它完全是0。每一个元素。

出于好奇,为什么Read()有一个缓冲区参数,但实际上并没有在方法中使用(我从你的一篇文章中得到了那段代码)?

任何帮助解决这个问题将非常感谢!我研究这个问题已经有一段时间了,但还是没有任何意义。

谢谢,阿兰

c#频率检索

不清楚你指的是什么文章,我不熟悉这个库。然而,Read方法显然是在读取您的"时间序列"/或其他数据。由此可见,您所说的buffer参数很可能是您希望放置在数据集两端的填充长度。

这种填充被称为"零填充",它用零填充您记录的信号(在信号的两端放置n个零,其中n根据所使用的基数设置)。这允许使用更长的FFT,这将产生更长的FFT结果向量。

较长的FFT结果具有更多频率间隔更紧密的频率箱。但它们基本上将提供与原始数据的较短的非零填充FFT的高质量Sinc插值相同的结果。

这可能会导致在绘制时没有进一步的插值更平滑的频谱。

更多信息见

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

这不是你问题的答案,但我写了一个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;
    }
}

显然,你可以用

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