2011-01-31 16 views
19

Piszę edytor fal dźwiękowych w kakao z szerokim zakresem opcji zoomu. W najszerszym, pokazuje przebieg dla całej piosenki (~ 10 milionów próbek w widoku). W najwęższym miejscu pokazuje on dokładną reprezentację piksela fali dźwiękowej (~ 1 tys. Próbek w widoku). Chcę móc płynnie przechodzić między tymi poziomami powiększenia. Niektóre komercyjne edytory, takie jak Ableton Live, wydają się robić to w bardzo niedrogi sposób.Wydajna metoda narysowania linii z milionami punktów

Moja obecna implementacja spełnia żądany zakres powiększenia, ale jest nieefektywna i nierówna. Konstrukcja jest w dużej mierze zainspirowany tym znakomitym artykule na rysowanie przebiegów z kwarcu:

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

utworzyć wiele CGMutablePathRef dla pliku audio na różnych poziomach redukcji. Kiedy jestem przybliżany do końca, używam ścieżki zredukowanej do jednego punktu na x-tysiąc próbek. Kiedy jestem powiększony do końca, używam tej ścieżki, która zawiera punkt dla każdej próbki. Skaluję ścieżkę poziomo, gdy znajduję się pomiędzy poziomami redukcji. To sprawia, że ​​jest funkcjonalny, ale wciąż jest dość drogi, a artefakty pojawiają się podczas przechodzenia między poziomami redukcji.

Jedna myśl o tym, jak mogę ją obniżyć, to wyjąć antyaliasing. Przebieg w moim edytorze jest wygładzony, podczas gdy w Abletonie nie jest (patrz porównanie poniżej). enter image description here enter image description here

nie widzę sposobu, aby wyłączyć wygładzanie dla CGMutablePathRef jest. Czy istnieje nie-wygładzana alternatywa dla CGMutablePathRef w świecie kakao? Jeśli nie, czy ktoś wie o niektórych klasach OpenGL lub przykładowym kodzie, który może ustawić mnie na kurs, aby bardziej efektywnie rysować moją wielką linię?

Aktualizacja 21.01.2014: Jest teraz wielka biblioteka, która robi dokładnie to, czego szukałem: https://github.com/syedhali/EZAudio

Odpowiedz

6

używam CGContextMoveToPoint + CGContextAddLineToPoint + CGContextStrokePath w mojej aplikacji. jeden punkt za punkt na ekranie, aby narysować za pomocą wstępnie obliczonego bufora pomocniczego do przeglądu. bufor zawiera dokładne punkty do rysowania i wykorzystuje interpolowaną reprezentację sygnału (w oparciu o zoom/skalę). chociaż może być szybszy i wygląda lepiej, gdy wyrenderowałem do bufora obrazu, nigdy nie miałem skargi. możesz go skalować i wyrenderować z dodatkowego wątku, jeśli ustawisz go poprawnie.

Antyaliasing dotyczy kontekstu graficznego.

CGFloat (natywne wejście dla CGPaths) jest przesadą dla ogólnego przeglądu, jako reprezentacja pośrednia i do obliczenia przebiegu falowego. 16 bitów powinno być wystarczające. oczywiście podczas konwersji do wywołań CG będziesz musiał przekonwertować CGFloat.

Musisz profil, aby dowiedzieć się, gdzie spędzasz swój czas - skup się na częściach, które zajmują najwięcej czasu. również, upewnij się, że narysujesz tylko to, co musisz, kiedy musisz i unikaj nakładek/animacji, gdzie to możliwe. jeśli potrzebujesz nakładek, lepiej jest renderować obraz/bufor i aktualizować go w razie potrzeby. czasami pomaga rozbić wyświetlacz na wiele powierzchni rysunkowych, gdy powierzchnia jest duża.

semi-OT: użycie przez ableton wartości s + h może być nieco szybsze, ale ... wolę je jako opcję. jeśli twoja implementacja używa interpolacji liniowej (która może, w zależności od jej wyglądu), rozważ bardziej intuicyjne podejście. Interpolacja liniowa jest trochę oszustwem i naprawdę nie jest to, czego użytkownik by się spodziewał, jeśli tworzysz aplikację pro.

+0

Dzięki za szczegółową odpowiedź! Brzmi, jakbyś też tu był. Rzeczywiście rysuję kształt fali częściej niż być może potrzebuję. Spróbuję zastąpić pełne ponowne rysowanie za pomocą nakładki. Co się dzieje z Abletonem przy użyciu wartości s + h? Masz rację zakładając, że moja implementacja korzysta z interpolacji liniowej. Wygląda na to, że kwadraty linii Abletona mogą być bardziej wydajne do rysowania, ale nie jestem pewna, jak to zrobię. – tassock

+0

@tassock nie ma za co. przez "s + h" rozumiem wartości "próbki i wstrzymania" sygnału. to po prostu rysuje kroki fali dokładnie tak, jak są one reprezentowane w dziedzinie czasu. lepiej (imo) interpolować i skonstruować reprezentację sygnału. s + h jest w porządku, ale powinna być opcją, z interpolowaną reprezentacją jako domyślną. kiedy mówię interpolację, mam na myśli podejście, które jest bardziej reprezentatywne dla sygnału analogowego niż s + h lub interpolacja liniowa. w tym przypadku prawdopodobnie będzie wymagało zrównoważenia wydajności z dokładnością. sinc byłoby dobre, (cd) – justin

+0

, ale prawdopodobnie można uciec spline wyższego rzędu (jako jeden przykład). interpolacja/rekonstrukcja przebiegu poprawnie może dodać dużo procesora do twojej obecnej implementacji. – justin

2

W odniesieniu do konkretnej kwestii antyaliasingu. W Quartz antyaliasing jest stosowany do kontekstu w momencie rysowania. CGPathRef jest agnostyczny względem kontekstu rysowania. Tak więc ten sam CGPathRef może zostać przekształcony w kontekst antyaliasingowy lub w kontekst inny niż antyaliasing. Na przykład, aby wyłączyć antyaliasing podczas animacji:

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); 
+0

Ah, dziękuję za wskazanie kontekstowego ustawienia wygładzania. Niestety, wydaje się, że nie doprowadziło to do znacznego wzrostu wydajności. Najwyraźniej mam zbyt wiele innych nieefektywnych procesów w mojej obecnej implementacji. – tassock

Powiązane problemy