2011-01-31 25 views
19

Sto scrivendo un editor di forme d'onda audio in Cocoa con una vasta gamma di opzioni di zoom. Nel suo punto più largo, mostra una forma d'onda per un'intera canzone (~ 10 milioni di campioni in vista). Al suo punto più stretto, mostra una rappresentazione accurata dei pixel dell'onda sonora (~ 1mila campioni in una vista). Voglio essere in grado di passare senza problemi tra questi livelli di zoom. Alcuni editori commerciali come Ableton Live sembrano farlo in un modo molto economico.Metodo efficiente per tracciare una linea con milioni di punti

La mia attuale implementazione soddisfa il mio intervallo di zoom desiderato, ma è inefficiente e mosso. Il design è in gran parte ispirato da questo ottimo articolo su disegno le forme d'onda con quarzo:

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

creo CGMutablePathRef multipli di per il file audio a vari livelli di riduzione. Quando ho eseguito lo zoom completo, utilizzo il percorso ridotto a un punto per x-mille campioni. Quando sono ingrandito completamente, utilizzo quel percorso che contiene un punto per ogni campione. Scala un tracciato orizzontale quando sono tra i livelli di riduzione. Ciò lo rende funzionale, ma è ancora piuttosto costoso e appaiono artefatti durante la transizione tra i livelli di riduzione.

Un pensiero su come rendere questo meno costoso è eliminare l'anti-aliasing. La forma d'onda nel mio editor è anti-alias mentre quella in Ableton non lo è (vedi confronto sotto). enter image description here enter image description here

Non vedo un modo per disattivare l'anti-aliasing per CGMutablePathRef di. Esiste un'alternativa non anti-alias a CGMutablePathRef nel mondo di Cocoa? In caso contrario, qualcuno sa di alcune classi OpenGL o codice di esempio che potrebbe mettermi sulla buona strada per disegnare la mia enorme linea in modo più efficiente?

Aggiornamento 2014/01/21: ora C'è una grande libreria che fa esattamente quello che stavo cercando: https://github.com/syedhali/EZAudio

risposta

6

i usano CGContextMoveToPoint + CGContextAddLineToPoint + CGContextStrokePath nella mia app. un punto per punto sullo schermo da disegnare utilizzando un buffer di supporto precalcolato per la panoramica. il buffer contiene i punti esatti da disegnare e utilizza una rappresentazione interpolata del segnale (in base allo zoom/scala). anche se potrebbe essere più veloce e avere un aspetto migliore se reso a un buffer di immagini, non ho mai avuto un reclamo. puoi calcolarlo e renderlo tutto da un filo secondario, se lo hai impostato correttamente.

l'anti-aliasing si riferisce al contesto grafico.

CGFloat (l'input nativo per CGPaths) è eccessivo per una panoramica, come una rappresentazione intermedia e per il calcolo della panoramica della forma d'onda. 16 bit dovrebbero essere adeguati. ovviamente, dovrai convertire in CGFloat quando passi alle chiamate CG.

è necessario profilo per scoprire dove il vostro tempo è trascorso - concentrarsi sulle parti che richiedono più tempo. inoltre, assicurati di tracciare solo ciò che devi, quando è necessario ed evitare sovrapposizioni/animazioni ove possibile. se hai bisogno di overlay, è meglio renderizzare su un'immagine/buffer e aggiornarlo secondo necessità. a volte aiuta a suddividere il display in più superfici di disegno quando la superficie è grande.

semi-OT: ableton usa i valori s + h questo può essere leggermente più veloce ma ... lo preferisco molto come opzione. se la tua implementazione utilizza l'interpolazione lineare (che può, in base al suo aspetto), considera un approccio più intuitivo. l'interpolazione lineare è un po 'un'impresa, e in realtà non è ciò che l'utente si aspetterebbe se sviluppassi un'applicazione professionale.

+0

Grazie per la risposta dettagliata! Sembra che tu sia stato anche qui. In effetti sto disegnando la forma d'onda più spesso di quanto potrebbe essere necessario. Vedrò di sostituire un completo re-draw con una sovrapposizione. Per quanto riguarda Ableton usando i valori s + h, quali sono quelli? Hai ragione di presumere che la mia implementazione utilizza l'interpolazione lineare. Sembra che le linee squadrate di Ableton potrebbero essere più efficaci da disegnare, ma non sono sicuro di come farei. – tassock

+0

@tassock prego. per 's + h', intendevo i valori 'sample and hold' del segnale. questo semplicemente disegna i passi della forma d'onda esattamente come sono rappresentati nel dominio del tempo. è meglio (imo) interpolare e costruire la rappresentazione del segnale. s + h è ok, ma dovrebbe essere un'opzione, con una rappresentazione interpolata come predefinita. quando dico interpolazione, mi riferisco a un approccio che è più rappresentativo di un segnale analogico rispetto a s + h o interpolazione lineare. per questo, probabilmente richiederà il bilanciamento delle prestazioni con precisione. un sinc sarebbe buono, (cont) – justin

+0

ma è probabile che si possa ottenere una spline più alta (come un esempio). interpolando/ricostruendo correttamente la forma d'onda può aggiungere molta cpu all'attuale implementazione. – justin

2

In relazione alla particolare questione di anti-aliasing. In Quartz l'anti-aliasing viene applicato al contesto al momento del disegno. Il CGPathRef è agnostico al contesto del disegno. Pertanto, lo stesso CGPathRef può essere reso in un contesto antialias o in un contesto non antialias. Ad esempio, per disabilitare l'antialiasing durante le animazioni:

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, grazie per aver sottolineato l'impostazione anti-alias del contesto. Sfortunatamente, non sembra aver portato ad un significativo aumento delle prestazioni. Apparentemente ho troppi altri processi inefficienti nella mia attuale implementazione. – tassock