使用声音缓冲区转换的AccessViolationException
本文关键字:AccessViolationException 转换 缓冲区 用声音 | 更新日期: 2023-09-27 18:05:10
我使用Naudio AsioOut对象将数据从输入缓冲区传递到我的delayProc()
函数,然后输出缓冲区。
delayProc()
需要float[]
缓冲类型,使用e.GetAsInterleavedSamples()
可以实现。问题是我需要将其重新转换为多维IntPtr
,要做到这一点,我使用AsioSampleConvertor
类。
当我尝试应用效果时,它向我显示了一个错误:AccessViolationException对AsioSampleConvertor
类的代码。
所以我认为问题是由于从float[]
到IntPtr[]
的转换…
我给你一些代码:
OnAudioAvailable ()
floatIn = new float[e.SamplesPerBuffer * e.InputBuffers.Length];//*2
e.GetAsInterleavedSamples(floatIn);
floatOut = delayProc(floatIn, e.SamplesPerBuffer * e.InputBuffers.Length, 1.5f);
//conversione da float[] a IntPtr[L][R]
Outp = Marshal.AllocHGlobal(sizeof(float)*floatOut.Length);
Marshal.Copy(floatOut, 0, Outp, floatOut.Length);
NAudio.Wave.Asio.ASIOSampleConvertor.ConvertorFloatToInt2Channels(Outp, e.OutputBuffers, e.InputBuffers.Length, floatOut.Length);
delayProc ()
private float[] delayProc(float[] sourceBuffer, int sampleCount, float delay)
{
if (OldBuf == null)
{
OldBuf = new float[sampleCount];
}
float[] BufDly = new float[(int)(sampleCount * delay)];
int delayLength = (int)(BufDly.Length - (BufDly.Length / delay));
for (int j = sampleCount - delayLength; j < sampleCount; j++)
for (int i = 0; i < delayLength; i++)
BufDly[i] = OldBuf[j];
for (int j = 0; j < sampleCount; j++)
for (int i = delayLength; i < BufDly.Length; i++)
BufDly[i] = sourceBuffer[j];
for (int i = 0; i < sampleCount; i++)
OldBuf[i] = sourceBuffer[i];
return BufDly;
}
AsioSampleConvertor
public static void ConvertorFloatToInt2Channels(IntPtr inputInterleavedBuffer, IntPtr[] asioOutputBuffers, int nbChannels, int nbSamples)
{
unsafe
{
float* inputSamples = (float*)inputInterleavedBuffer;
int* leftSamples = (int*)asioOutputBuffers[0];
int* rightSamples = (int*)asioOutputBuffers[1];
for (int i = 0; i < nbSamples; i++)
{
*leftSamples++ = clampToInt(inputSamples[0]);
*rightSamples++ = clampToInt(inputSamples[1]);
inputSamples += 2;
}
}
}
ClampToInt ()
private static int clampToInt(double sampleValue)
{
sampleValue = (sampleValue < -1.0) ? -1.0 : (sampleValue > 1.0) ? 1.0 : sampleValue;
return (int)(sampleValue * 2147483647.0);
}
如果你需要其他代码,就问我。
当您调用ConvertorFloatToInt2Channels
时,您正在传递所有通道的样本总数,然后尝试读取许多对的样本。所以你要从输入缓冲区中读取的样本数是实际的两倍。使用不安全的代码,您试图解决远远超过分配块的结束,这将导致访问冲突。
将ConvertorFloatToInt2Channels
方法中的for
循环更改为:
for (int i = 0; i < nbSamples; i += 2)
这将阻止您的代码试图读取源内存块中实际存在的项数的两倍。
顺便说一句,为什么你在这里乱分配全局内存和使用不安全的代码?为什么不将它们作为托管数组处理呢?处理数据本身并不会慢很多,并且您可以节省在非托管内存中复制数据的所有开销。
试试这个:
public static void FloatMonoToIntStereo(float[] samples, float[] leftChannel, float[] rightChannel)
{
for (int i = 0, j = 0; i < samples.Length; i += 2, j++)
{
leftChannel[j] = (int)(samples[i] * Int32.MaxValue);
rightChannel[j] = (int)(samples[i + 1] * Int32.MaxValue);
}
}
在我的机器上,每秒处理大约1200万个样本,将样本转换为整数并分割通道。如果我为每组结果分配缓冲区,速度大约是原来的一半。当我使用不安全代码,AllocHGlobal
等时,大约是原来的一半。
永远不要认为不安全的代码更快。