用数百万点绘制线的有效方法

时间:2011-01-31 09:01:45

标签: cocoa audio quartz-2d waveform cgpath

我正在Cocoa中编写一个带有各种缩放选项的音频波形编辑器。在最宽的时候,它显示了整首歌曲的波形(视野中约1000万个样本)。在最窄处,它显示了声波的像素精确表示(视图中约1千个样本)。我希望能够在这些缩放级别之间平滑过渡。像Ableton Live这样的商业编辑似乎以非常便宜的方式做到这一点。

我目前的实现方式满足我所需的缩放范围,但效率低且不连贯。该设计很大程度上受到这篇关于使用石英绘制波形的优秀文章的启发:

http://supermegaultragroovy.com/blog/2009/10/06/drawing-waveforms/

我为各种缩小级别的音频文件创建了多个CGMutablePathRef。当我完全缩小时,我使用的路径已经减少到每x千个样本一个点。当我一直放大时,我会使用包含每个样本点的路径。当我处于缩小级别之间时,我会水平缩放路径。这使它具有功能性,但仍然相当昂贵,并且在减少级别之间转换时会出现伪像。

关于如何降低价格的一个想法是取消抗锯齿。我的编辑器中的波形是消除锯齿的,而Ableton中的波形则没有(参见下面的比较)。 enter image description here enter image description here

我没有看到关闭CGMutablePathRef的抗锯齿的方法。在Cocoa世界中,CGMutablePathRef是否有一个非抗锯齿的替代品?如果没有,有没有人知道一些OpenGL类或示例代码可能会让我更有效地绘制我的大线?

更新1-21-2014:现在有一个很棒的图书馆可以完全满足我的需求:https://github.com/syedhali/EZAudio

2 个答案:

答案 0 :(得分:6)

我在我的应用程序中使用CGContextMoveToPoint + CGContextAddLineToPoint + CGContextStrokePath。每个屏幕上的一个点使用预先计算的后备缓冲区进行绘制。缓冲区包含要绘制的精确点,并使用信号的插值表示(基于缩放/比例)。虽然它可以更快,看起来更好,如果我渲染到图像缓冲区,我从来没有抱怨过。如果你正确设置它,你可以从辅助线程计算和渲染所有这些。

抗锯齿属于图形上下文。

CGFloat(CGPaths的原生输入)对于概述,作为中间表示以及计算波形概述来说是过度的。 16位应该足够了。当然,在转到CG电话时你必须转换成CGFloat。

您需要进行分析以找出您的时间花在哪里 - 专注于花费最多时间的部分。此外,确保您只在必要时绘制必要的内容,并尽可能避免使用叠加/动画。如果你需要叠加,最好渲染到图像/缓冲区并根据需要更新。有时,当表面很大时,有助于将显示分解成多个绘图表面。

半OT:能够使用s + h值,这可能会稍微快一点但是......我更喜欢它作为选项。如果您的实现使用线性插值(可能基于其外观),请考虑更直观的方法。线性插值有点作弊,如果你正在开发一个专业应用程序,那么用户就不会想到它。

答案 1 :(得分:2)

关于抗锯齿的特定问题。在Quartz中,在绘制时将抗锯齿应用于上下文。 CGPathRef与绘图上下文无关。因此,可以将相同的CGPathRef呈现为抗锯齿上下文或非抗锯齿上下文。例如,要在动画期间禁用抗锯齿:

CGContextRef context = UIGraphicsGetCurrentContext();
GMutablePathRef fill_path = CGPathCreateMutable();
// Fill the path with the wave
...

CGContextAddPath(context, fill_path);
if ([self animating])
    CGContextSetAllowsAntialiasing(context, NO);
else
    CGContextSetAllowsAntialiasing(context, YES);
// Do the drawing
CGContextDrawPath(context, kCGPathStroke);