我能够显示波形,但我不知道如何在波形上实现放大。 有什么想法吗?
感谢piccolo
答案 0 :(得分:0)
通过缩放,我认为你的意思是水平缩放而不是垂直。音频编辑器这样做的方法是扫描波形,将其分解为时间窗口,其中X中的每个像素代表一定数量的样本。它可以是一个小数,但你可以通过dis-allow分数缩放比例而不会过多地扰乱用户。缩小一点后,最大值始终为正整数,最小值始终为负整数。
对于屏幕上的每个像素,您需要知道该像素的最小样本值和最大样本值。因此,您需要一个以块为单位扫描波形数据的函数,并跟踪该块的累计最大值和最小值。
这是一个缓慢的过程,因此专业音频编辑器会以某个固定的缩放比率保留预先计算的最小值和最大值表。它可能是512/1或1024/1。当您使用>的缩放比例绘图时1024个样本/像素,然后使用预先计算的表。如果您低于该比率,则直接从文件中获取数据。如果你不这样做,你会发现缩小时绘图代码会变得太慢。
在执行此扫描时编写处理文件所有通道的代码是值得的,这里的缓慢会使整个程序感觉迟钝,这是重要的磁盘IO,CPU没有问题保持如此简单的C ++代码可以用于构建最小/最大表,但是你不想多次浏览文件而你想要按顺序执行它。
一旦你有了最小/最大表,就把它们放在一边。您希望尽可能少地返回磁盘,并且想要重新绘制窗口的许多原因不需要您重新扫描最小/最大表。与首先构建它们的磁盘成本相比,保留它们的内存成本并不高。
然后通过在该像素表示的时间的最大值和最小值之间绘制一系列1像素宽的垂直线来绘制波形。如果您使用预先构建的最小/最大表格绘制,这应该非常快。
回答答案 1 :(得分:0)
现在正在处理这个问题,c#带有一点linq,但应该很容易阅读和理解。这里的想法是有一个从-1到1的浮点值数组,表示wav文件中每个样本的幅度。然后知道每秒有多少样本,然后我们需要一个缩放因子 - 每秒段数。此时,您只需缩小数据点并将其平滑。放大非常紧,每秒1000个样本,缩放方式可能是5-10。现在注意我只是做正常的平均值,需要更新它才能更有效率,并且可能使用RMS(均方根)平均值来使其完美。
private List<float> BuildAverageSegments(float[] aryRawValues, int iSamplesPerSecond, int iSegmentsPerSecond)
{
double nDurationInSeconds = aryRawValues.Length/(double) iSamplesPerSecond;
int iNumSegments = (int)Math.Round(iSegmentsPerSecond*nDurationInSeconds);
int iSamplesPerSegment = (int) Math.Round(aryRawValues.Length/(double) iNumSegments); // total number of samples divided by the total number of segments
List<float> colAvgSegVals = new List<float>();
for(int i=0; i<iNumSegments-1; i++)
{
int iStartIndex = i * iSamplesPerSegment;
int iEndIndex = (i + 1) * iSamplesPerSegment;
float fAverageSegVal = aryRawValues.Skip(iStartIndex).Take(iEndIndex - iStartIndex).Average();
colAvgSegVals.Add(fAverageSegVal);
}
return colAvgSegVals;
}
除此之外,您需要将音频转换为wav格式,您应该能够在任何地方找到源来读取该数据,然后使用类似的东西将原始字节数据转换为浮点数 - 再次这是非常粗糙的不尽如人意但很明确
public float[] GetFloatData()
{
//Scale Factor - SignificantBitsPerSample
if (Data != null && Data.Length > 0)
{
float nMaxValue = (float) Math.Pow((double) 2, SignificantBitsPerSample);
float[] aryFloats = new float[Data[0].Length];
for (int i = 0; i < Data[0].Length; i++ )
{
aryFloats[i] = Data[0][i]/nMaxValue;
}
return aryFloats;
}
else
{
return null;
}
}