我需要做的是计算麦克风输入的频率。我正在使用IWaveProvider
进行此操作,并实施Read()
。缓冲区的大小始终为8820个元素,从字节数组到float数组的转换似乎也出现了问题(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()
有一个缓冲参数,但实际上并没有在方法中使用(我从你的一篇文章中得到了那段代码)?
我们将非常感谢您解决此问题的任何帮助!我已经在这已经做了很长一段时间,但是没有任何意义。
谢谢, 阿兰
答案 0 :(得分:0)
目前尚不清楚您所指的文章是什么,我对这个图书馆并不熟悉。但是,Read
方法显然正在读取您的“时间序列”/或其他数据。因此,您所说的buffer
参数很可能是您要放在数据集两端的填充长度。
此填充称为“零填充”,它用零填充您的记录信号(在信号的任一端放置n个零,其中n根据使用的基数设置)。这允许使用更长的FFT,这将产生更长的FFT结果矢量。
较长的FFT结果具有更多频率间隔更频繁的频率区间。但它们基本上可以提供与原始数据的较短非零填充FFT的高质量Sinc插值相同的结果。
在没有进一步插值的情况下绘制时,这可能会使光谱看起来更平滑。
有关进一步的信息,请参阅
我希望这会有所帮助。
答案 1 :(得分:0)
这不是您问题的真正答案,但我为您的数组转换函数编写了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>();