Эффективный метод построения линии с миллионами точек


Я пишу редактор звуковых сигналов в Cocoa с широким диапазоном параметров масштабирования. В самом широком месте он показывает форму сигнала для всей песни (~10 миллионов сэмплов в поле зрения). В самом узком месте он показывает пиксельное точное представление звуковой волны (~1 тыс. образцов в виде). Я хочу иметь возможность плавно переходить между этими уровнями масштабирования. Некоторые коммерческие редакторы, такие как Ableton Live, похоже, делают это очень недорого.

Моя текущая реализация удовлетворяет моим желаемый диапазон масштабирования, но неэффективен и неустойчив. Дизайн в значительной степени вдохновлен этой превосходной статьей о рисовании осциллограмм с кварцем:

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

Я создаю несколько CGMutablePathRef для аудиофайла на различных уровнях сокращения. Когда я увеличен до конца, я использую путь, который был уменьшен до одной точки На x-тысячу образцов. Когда я увеличен до конца, я использую тот путь, который содержит очко за каждый образец. Я масштабирую путь горизонтально, когда нахожусь между уровнями сокращения. Это делает его функциональным, но все еще довольно дорогим, и артефакты появляются при переходе между уровнями сокращения.

Одна мысль о том, как я мог бы сделать это менее дорогостоящим,-это убрать сглаживание. Форма сигнала в моем редакторе сглажена, а в Ableton-нет (см. сравнение ниже). Введите описание изображения здесьВведите описание изображения здесь

Я не вижу способа отключить сглаживание для CGMutablePathRef. Существует ли в мире какао-бобов альтернатива Cgmutable Pathref без сглаживания псевдонимов? Если нет, то знает ли кто-нибудь о некоторых классах OpenGL или образцах кода, которые могли бы заставить меня более эффективно рисовать свою огромную линию?

Обновление 1-21-2014: теперь есть отличная библиотека, которая делает именно то, что я искал: https://github.com/syedhali/EZAudio

2 19

2 ответа:

Я использую CGContextMoveToPoint+CGContextAddLineToPoint+CGContextStrokePath в моем приложении. одна точка на экранную точку для рисования с использованием предварительно рассчитанного резервного буфера для обзора. буфер содержит точные точки для рисования и использует интерполированное представление сигнала (на основе масштабирования/масштаба). хотя это могло бы быть быстрее и выглядеть лучше, если бы я визуализировал в буфер изображений, у меня никогда не было жалоб. вы можете вычислить и отрисовать все это из вторичного потока, если настроите его правильно.

Сглаживание относится к графическому контексту.

CGFloat (собственный вход для CGPaths) является избыточным для обзора, как промежуточного представления, так и для вычисления обзора формы волны. 16 бит должно быть достаточно. конечно, вам придется конвертировать в CGFloat при переходе к вызовам CG.

Вам нужно составить профиль, чтобы узнать, где тратится ваше время-сосредоточьтесь на тех частях, которые занимают больше всего времени. кроме того, убедитесь, что вы рисуете только то, что должны, когда вы должны и избегать наложений / анимаций, где это возможно. если вам нужны оверлеи, лучше отрисовать изображение / буфер и обновить его по мере необходимости. иногда это помогает разбить дисплей на несколько поверхностей рисования, когда поверхность большая.

Semi-OT: ableton использует значения s+h это может быть немного быстрее, но... я предпочитаю его как вариант. если ваша реализация использует линейную интерполяцию (которая может быть основана на ее внешнем виде), рассмотрите более интуитивный подход. линейный интерполяция - это немного чит, и на самом деле это не то, что ожидал бы пользователь, если бы вы разрабатывали профессиональное приложение.

В связи с конкретным вопросом сглаживания. В 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);