Fun with NSImage...not

• Chris Liscio


Last night I managed to round my time off to 40 hours. The majority of my time was spent trying to figure out how to speed up my NSView. Since the NSBezierPath was simply following my FFT's window size, it would really bog down around the 8192 point mark (well, duh!). I jumped the gun on a future optimization I was planning, which was caching the NSView's contents in an NSImage. Apparently it's not as simple as it seems…</p><p>During last night's session, I have not moved any closer to having my NSView draw quickly. In fact, it's not even drawing anymore now! I know there must be something wrong with the way I'm using it, so I'll have to leave that optimization aside for now. I think I would have been better off spending my time trying to reduce the data used to draw the NSBezierPath in the first place. :P</p><p>Anyway, I hacked this little routine together in about 5 minutes this morning to reduce the point count by quite a bit.</p><p><pre>
# logRangeFilter.py
#
# Counts only "significant" points in a logarithmic plot for an optimized
# display of data.
#
# Original Problem: When plotting a graph with a large number of points on a
# logarithmic scale, the number of points per physical pixel is far higher on
# the right side of the graph.
#
# With FFT sizes going as high as 16384 (in the initial version of
# SpeaKonstruct), there is an issue with the time taken to draw the resulting
# NSView. This is caused by the graph's NSBezierPath being clogged with a
# large number of points in the path. To alleviate this problem, more points
# must be discarded from the graph, while still retaining maximum detail on the
# left side of the graph which has data points spread further out.
#
# Moreover, it is important to retain detail up to a certain point in the
# graph, since complex impedance charts for loudspeakers can show a peak in
# impedance within a wide range of frequencies (woofers in the tens of hertz,
# tweeters in the thousands of hertz). As a result, the threshold may need
# to be tweaked (programmatically or otherwise) to balance between detail and
# drawing speed.
</pre>
<pre>
import math
</pre>
<pre>
fftWindowSize = 16384
nyquist = 22050.0
last = 0.0
threshold = 0.01
pointCount = 0
</pre>
<pre>
for i in range(1,fftWindowSize) :
num = math.log10( (i / float(fftWindowSize)) * nyquist)
</pre>
<pre>
# Only display the items in a certain threshold range
if ( (num - last) >= threshold ) :
print "Diff - ", (num - last), " at pt ", num, " idx ", i
pointCount+=1
last = num
</pre>
<pre>
# Want the last point included as well
if ( i == (fftWindowSize-1) ) :
print "Final pt - ", num
pointCount+=1
</pre>
<pre>
print "Threshold of ", threshold, "yields a total of ", pointCount, <br /> " points out of ", fftWindowSize, " used."
</pre></p><p>With a threshold of 0.01, That drops the number of points used down to about 250, plus or minus depending on the FFT window size. I am sure there will possibly be drawing errors introduced, but I plan to average the values in the threshold blocks in case data gets lost in the display.</p><p>We'll see how it goes.
</p>