数百万点绘制直线的高效方法
我正在 Cocoa 中编写一个音频波形编辑器,具有多种缩放选项。在最宽处,它显示整首歌曲的波形(视图中约 1000 万个样本)。在最窄处,它显示了声波的像素精确表示(视图中约 1000 个样本)。我希望能够在这些缩放级别之间平滑过渡。一些商业编辑器(例如 Ableton Live)似乎以非常便宜的方式做到这一点。
我当前的实现满足了我想要的变焦范围,但效率低下且不稳定。该设计很大程度上受到这篇关于用石英绘制波形的优秀文章的启发:
http: //supermegaulttragroovy.com/blog/2009/10/06/drawing-waveforms/
我为音频文件创建了多个 CGMutablePathRef 在不同的缩减级别。当我一直缩小时,我使用的路径已减少到每 x 千个样本一个点。当我完全放大时,我使用包含每个样本的点的路径。当我处于缩小级别之间时,我会水平缩放路径。这使它可以正常工作,但仍然相当昂贵,并且在缩减级别之间转换时会出现伪影。
关于如何降低成本的一个想法是取消抗锯齿功能。我的编辑器中的波形是抗锯齿的,而 Ableton 中的波形则不是(参见下面的比较)。
我没有找到关闭 CGMutablePathRef 抗锯齿功能的方法。在 Cocoa 的世界里有没有 CGMutablePathRef 的非抗锯齿替代品?如果没有,有谁知道一些 OpenGL 类或示例代码可能会让我更有效地绘制我的巨线?
更新 1-21-2014: 现在有一个很棒的库,它完全可以满足我的需求:https://github.com/syedhali/EZAudio
I'm writing an audio waveform editor in Cocoa with a wide range of zoom options. At its widest, it shows a waveform for an entire song (~10 million samples in view). At its narrowest, it shows a pixel accurate representation of the sound wave (~1 thousand samples in a view). I want to be able to smoothly transition between these zoom levels. Some commercial editors like Ableton Live seem to do this in a very inexpensive fashion.
My current implementation satisfies my desired zoom range, but is inefficient and choppy. The design is largely inspired by this excellent article on drawing waveforms with quartz:
http://supermegaultragroovy.com/blog/2009/10/06/drawing-waveforms/
I create multiple CGMutablePathRef's for the audio file at various levels of reduction. When I'm zoomed all the way out, I use the path that's been reduced to one point per x-thousand samples. When I'm zoomed in all the way in, I use that path that contains a point for every sample. I scale a path horizontally when I'm in between reduction levels. This gets it functional, but is still pretty expensive and artifacts appear when transitioning between reduction levels.
One thought on how I might make this less expensive is to take out anti-aliasing. The waveform in my editor is anti-aliased while the one in Ableton is not (see comparison below).
I don't see a way to turn off anti-aliasing for CGMutablePathRef's. Is there a non-anti-aliased alternative to CGMutablePathRef in the world of Cocoa? If not, does anyone know of some OpenGL classes or sample code that might set me on course to drawing my huge line more efficiently?
Update 1-21-2014: There's now a great library that does exactly what I was looking for: https://github.com/syedhali/EZAudio
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
我在我的应用程序中使用 CGContextMoveToPoint+CGContextAddLineToPoint+CGContextStrokePath 。使用预先计算的后备缓冲区为屏幕上的每个点绘制一个点以进行概览。缓冲区包含要绘制的精确点,并使用信号的插值表示(基于缩放/比例)。虽然如果我渲染到图像缓冲区,它可能会更快并且看起来更好,但我从未有过抱怨。如果设置正确,您可以从辅助线程计算并渲染所有这些。
抗锯齿与图形上下文有关。
CGFloat(CGPaths 的本机输入)对于概述、作为中间表示以及计算波形概述来说是多余的。 16 位应该足够了。当然,在传递给 CG 调用时,您必须转换为 CGFloat。
您需要进行分析以找出您的时间花在哪里 - 专注于花费最多时间的部分。另外,请确保只在必要时绘制必须绘制的内容,并尽可能避免覆盖/动画。如果您需要覆盖,最好渲染到图像/缓冲区并根据需要进行更新。有时,当表面很大时,将显示分解为多个绘图表面会有所帮助。
半OT:ableton 使用 s+h 值,这可能会稍微快一些,但是......我更喜欢它作为一个选项。如果您的实现使用线性插值(根据其外观,可能会使用线性插值),请考虑更直观的方法。线性插值有点作弊,如果您正在开发专业应用程序,那么它确实不是用户所期望的。
i use CGContextMoveToPoint+CGContextAddLineToPoint+CGContextStrokePath in my app. one point per onscreen point to draw using a pre-calculated backing buffer for the overview. the buffer contains the exact points to draw, and uses an interpolated representation of the signal (based on the zoom/scale). although it could be faster and look better if i rendered to an image buffer, i've never had a complaint. you can calc and render all of this from a secondary thread, if you set it up correctly.
anti-aliasing pertains to the graphics context.
CGFloat (the native input for CGPaths) is overkill for an overview, as an intermediate representation, and for calculating the waveform overview. 16 bits should be adequate. of course, you'll have to convert to CGFloat when passing to CG calls.
you need to profile to find out where your time is spent -- focus on the parts that take the most time. also, make you sure you only draw what you must, when you must and avoid overlays/animations where possible. if you need overlays, it's better to render to an image/buffer and update that as needed. sometimes it helps to break up the display into multiple drawing surfaces when the surface is large.
semi-OT: ableton's using s+h values this can be slightly faster but... i much prefer it as an option. if your implementation uses linear interpolation (which it may, based on its appearance), consider a more intuitive approach. linear interpolation is a bit of a cheat, and really not what the user would expect if you're developing a pro app.
关于抗锯齿的特定问题。在 Quartz 中,抗锯齿在绘图时应用于上下文。 CGPathRef 与绘图上下文无关。因此,相同的 CGPathRef 可以渲染到抗锯齿上下文或非抗锯齿上下文中。例如,要在动画期间禁用抗锯齿功能:
In relation to the particular question of anti-aliasing. In Quartz the anti-aliasing is applied to the context at the moment of drawing. The CGPathRef is agnostic to the drawing context. Thus, the same CGPathRef can be rendered into an antialiased context or to a non-antialiased context. For example, to disable antialiasing during animations: