2011-01-31 3 views
19

J'écris un éditeur de forme d'onde audio dans Cocoa avec un large éventail d'options de zoom. Au plus large, il montre une forme d'onde pour une chanson entière (~ 10 millions d'échantillons en vue). Au plus étroit, il montre une représentation précise de l'onde sonore (environ 1 000 échantillons dans une vue). Je veux être en mesure de passer en douceur entre ces niveaux de zoom. Certains éditeurs commerciaux comme Ableton Live semblent le faire d'une manière très bon marché.Méthode efficace pour tracer une ligne avec des millions de points

Mon implémentation actuelle satisfait la plage de zoom souhaitée, mais elle est inefficace et instable. La conception est largement inspirée par cet excellent article sur le dessin des formes d'ondes avec du quartz:

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

Je crée plusieurs années CGMutablePathRef pour le fichier audio à différents niveaux de réduction. Quand je suis complètement zoomé, j'utilise le chemin qui a été réduit à un point par millier d'échantillons. Lorsque je suis zoomé au maximum, j'utilise ce chemin qui contient un point pour chaque échantillon. Je redimensionne un chemin horizontalement lorsque je suis entre deux niveaux de réduction. Cela devient fonctionnel, mais est encore assez cher et les artefacts apparaissent lors de la transition entre les niveaux de réduction. Une pensée sur la façon dont je pourrais rendre cela moins cher est de supprimer l'anti-aliasing. La forme d'onde de mon éditeur est anti-aliasée alors que celle d'Ableton ne l'est pas (voir la comparaison ci-dessous). enter image description here enter image description here

Je ne vois pas un moyen de désactiver l'anti-aliasing pour CGMutablePathRef de. Existe-t-il une alternative non-anti-alias à CGMutablePathRef dans le monde de Cocoa? Si ce n'est pas le cas, est-ce que quelqu'un connaît des classes OpenGL ou un exemple de code qui pourrait m'aider à dessiner mon énorme ligne plus efficacement?

Mise à jour 21/01/2014: Il y a maintenant une grande bibliothèque qui fait exactement ce que je cherchais: https://github.com/syedhali/EZAudio

Répondre

6

i utiliser CGContextMoveToPoint + CGContextAddLineToPoint + CGContextStrokePath dans mon application. un point par point à l'écran à dessiner à l'aide d'un tampon de sauvegarde pré-calculé pour la vue d'ensemble. le tampon contient les points exacts à dessiner, et utilise une représentation interpolée du signal (basée sur le zoom/échelle). Bien que cela puisse être plus rapide et mieux si j'ai rendu à un tampon d'image, je n'ai jamais eu de plainte. vous pouvez calculer et rendre tout cela à partir d'un thread secondaire, si vous l'avez configuré correctement.

L'anti-aliasing concerne le contexte graphique.

CGFloat (l'entrée native de CGPaths) est trop grande pour une vue d'ensemble, une représentation intermédiaire et pour le calcul de la vue d'ensemble de la forme d'onde. 16 bits devraient être adéquats. bien sûr, vous devrez convertir en CGFloat en passant aux appels CG.

Vous devez effectuer un profil pour savoir où votre temps est passé - concentrez-vous sur les parties qui prennent le plus de temps. Aussi, assurez-vous que vous ne dessinez que ce que vous devez, quand vous le devez et évitez les superpositions/animations si possible. Si vous avez besoin d'incrustations, il est préférable de les restituer à une image/un tampon et de les mettre à jour si nécessaire. parfois, il est utile de diviser l'affichage en plusieurs surfaces de dessin lorsque la surface est grande.

semi-OT: ableton utilisant les valeurs s + h cela peut être légèrement plus rapide mais ... je le préfère de beaucoup en option. Si votre implémentation utilise une interpolation linéaire (qu'elle peut, en fonction de son apparence), envisager une approche plus intuitive. interpolation linéaire est un peu une triche, et vraiment pas ce que l'utilisateur attendrait si vous développez une application pro.

+0

Merci pour la réponse détaillée! On dirait que vous avez été ici aussi. En effet, je dessine la forme d'onde plus souvent que nécessaire. Je chercherai à remplacer une reprise complète par une superposition. En ce qui concerne Ableton en utilisant les valeurs s + h, quelles sont ces valeurs? Vous avez raison de supposer que ma mise en œuvre utilise une interpolation linéaire. On dirait que les lignes au carré d'Ableton pourraient être plus efficaces à dessiner, mais je ne sais pas comment je ferais ça. – tassock

+0

@tassock vous êtes les bienvenus. par 's + h', je voulais dire les valeurs 'sample and hold' du signal. ceci dessine juste les étapes de forme d'onde exactement comme elles sont représentées dans le domaine temporel. il est préférable (imo) d'interpoler et de construire la représentation du signal. s + h est ok, mais cela devrait être une option, avec une représentation interpolée par défaut. quand je dis interpolation, je parle d'une approche plus représentative d'un signal analogique que s + h ou d'une interpolation linéaire. pour cela, il faudra probablement que vous équilibrez la performance avec précision. un sinc serait bon, (suite) – justin

+0

mais vous pouvez probablement vous en sortir avec une spline d'ordre supérieur (par exemple). interpoler/reconstruire correctement la forme d'onde peut ajouter beaucoup de cpu à votre implémentation actuelle. – justin

2

En ce qui concerne la question particulière de l'anti-aliasing. Dans Quartz, l'anti-aliasing est appliqué au contexte au moment du dessin. Le CGPathRef est agnostique au contexte de dessin. Ainsi, le même CGPathRef peut être rendu dans un contexte anti-aliasé ou dans un contexte non anti-aliasé. Par exemple, pour désactiver l'anticrénelage lors des animations:

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, merci d'avoir souligné le réglage d'anti-aliasing du contexte. Malheureusement, cela ne semble pas avoir entraîné une augmentation significative des performances. Apparemment, j'ai trop d'autres processus inefficaces dans ma mise en œuvre actuelle. – tassock

Questions connexes