This uses the Pset function so it doesn’t display lines. But it is extremely fast. If anyone can tell me how to make a better spectrum analyzer without losing speed I would be very interested.
Just put it in a timer with interval of 1. Static Spectrum(128) As Single Call GetSpectrum(Spectrum) Dim Y As Integer PicSpectrum.Cls For Y = 0 To 128 PicSpectrum.PSet (Y, PicSpectrum.ScaleHeight - Spectrum(Y) * 100) Next Y
- Anonymous asked 15 years ago
I changed the variable name to X, because it is the X-axis you are looping over.
Peaks can easily be done with a second array,
For X = 0 to 270
‘Fade the peaks (change 0.99 to another value for slower or faster fallback)
Spectrum_Peaks(x) = Spectrum_Peaks(x) * 0.99
‘Make the peaks the highest points
If Spectrum(x) > Spectrum_Peaks(x) Then Spectrum_Peaks(x) = Spectrum(x)
‘Drawing code here
Yeah, but for some reason if I move the invalidaterect call after the drawing procedure then it doesnt work at all, and if I completely remove the call then it still doesnt work. The only way that I got it to work is to put it before the drawing code. Any suggestions?
For drawing the peaks you could just use the SetPixelV API to draw a single dot above each line, just a thought. It’s much faster than PSET.
Also, the thing about the spectrum part of this is that it isn’t actually plotting 270 lines, so it’s wasting time by calculating all those extra lines without actually plotting them because they are all 5 pixels wide or so. Maybe I’m wrong, but I think that it is only plotting maybe 55 lines
Maybe you also have to add a DoEvents after InvalidateRect?
I have replaced all Refresh calls in my program to InvalidateRect, and it seems to work, as long as you make sure windows gets time to update it now and then, which can be done by adding a DoEvents.
I changed 270 to 45 because, drawing 270 lines would be useless, 45 is a nice small number.
Private Declare Function SetPixelV Lib "gdi32" (ByVal hdc As Long, ByVal X As Long, ByVal Y As Long, ByVal crColor As Long) As Long
Static Spectrum(45) as single
Static Spectrum_Peaks(45) as single
For Y = 0 To 45
Spectrum_Peaks(Y) = Spectrum_Peaks(Y) * 0.9
If Spectrum(Y) > Spectrum_Peaks(Y) Then Spectrum_Peaks(Y) = Spectrum(Y)
rct.Left = Y * 5
rct.Top = rct.Bottom – Spectrum(Y) * rct.Bottom
rct.Right = Y * 5 + 4
FillRect hdc, rct, hBr
For X = 1 To 4
SetPixelV hdc, (Y * 5 + X) – 1, (rct.Bottom – Spectrum_Peaks(Y) * rct.Bottom), &HFFD8B0
I modified my post to show how to create full lined background peaks instead of just dots.
You might also try looking at the InvalidateRect api call to help improve refresh rates, and precalculate your rects, so you don’t have to build them each time. Furthermore your brushes and pens could be created at load and then deleted at unload time, however remember each time you leave a function to select the saved pen/brush (unless using fillRect). Remeber the more calculations and commands that you can remove from loops the better the preformance.
InvalidateRect must have the rect defined correctly if you use it with the wrong hwnd or the rect incorrectly defined then it will fail.
Keep in mind that a picturebox is more or less another window and you might need to calculate your rects based on your forms rect def, and then find the childwindow of the picturebox. (if this makes since). Try using the api to attain the rect of the child window of a form. These 2 api functions should help alleviate your pain.
Public Declare Function GetClientRect Lib "user32" (ByVal hwnd As Long, lpRect As RECT) As Long
Public Declare Function GetWindowRect Lib "user32" (ByVal hwnd As Long, lpRect As RECT) As Long
Yes this seems like alot more work, yet it is blazingly faster. One other suggestion I might make is that you get rid of VB timers they are the most inaccurate piece of junk ever written by M$ If you use the api timers you can collect the hwnd of the owner, from it attain the DC draw to it and whats REALLY nice is not have to refresh.
I think not using Picture1.Cls, but instead using DrawRectangle
hBr = CreateSolidBrush(Colour)
FillRect hdc, rct, hBr
(rct is a RECT, the declarations for these functions and type can be found using the win32 api viewer, or the win32.tlb type library)
If you also use this to draw lines instead of pixels with PSet it might look a bit better withouth losing too much speed.
And to make sure it looks good, enable the picture’s AutoRedraw property, and add
at the end of the code.
I moved the CreateSolidBrush calls to Form Load, and them moved the DeleteObject calls into Form Unload. I placed the InvalidateRect call into the timer, although I’m not sure that I did it correctly. The refresh rates are much better now, there is absolutely no pauses or skipping in the spectrum now. Thanks for your tips.
I have always used a rectangle with .Right and .Bottom set to pic.Width and pic.Height, and that works fine.
I also cache hwnd, hdc and this rectangle in memory so you don’t have to access object properties each time.
I haven’t used timers (windows or api), I just use a Do … Loop with a doevents and some waiting code with a SleepEx at the end to get a constant framerate.
The only thing that would be nice is a function like SleepEx, but much more accurate (when you call sleepex with 1 ms it might take 3 to 5 ms).
I’ve ever seen an example that seemed to be using a function ‘NanoSleep’, but I haven’t found that function yet.
I have made a spectrum analysis that is fast enough, using windows API for the lines and stuff.
Private Declare Function CreateSolidBrush Lib “gdi32” (ByVal crColor As Long) As Long
Private Declare Function FillRect Lib “user32” (ByVal hdc As Long, lpRect As Rect, ByVal hBrush As Long) As Long
Private Declare Function DeleteObject Lib “gdi32” (ByVal hObject As Long) As Long
Private Declare Function LineTo Lib “gdi32” (ByVal hdc As Long, ByVal X As Long, ByVal Y As Long) As Long
Private Declare Function MoveToEx Lib “gdi32” (ByVal hdc As Long, ByVal X As Long, ByVal Y As Long, lpPoint As Any) As Long
‘Just put this in a timer
'This updates the Spectrum and the VU Dim rct As Rect 'For clearing Dim hBr As Long Dim hdc As Long 'The spectrum is only set for 270 'floats because that is how big my 'picturebox is, and most of all the 'data after 128 floats is boring anyways. Static Spectrum(270) As Single Dim Y As Integer 'First set the rectangle for clearing rct.Bottom = picSpectrum.Height rct.Left = 0 rct.Right = picSpectrum.Width rct.Top = 0 'Get the spectrum from the FFT Call GetSpectrum(Spectrum) 'Set the HDC of the picturebox hdc = picSpectrum.hdc 'Create the rectangle hBr = CreateSolidBrush(&H804000) FillRect hdc, rct, hBr DeleteObject hBr 'Loop through the Spectrum and plot 'each line For Y = 0 To 270 'Change the current position MoveToEx hdc, Y, 0, ByVal 0& 'Create the vertical line LineTo hdc, Y, (picSpectrum.ScaleHeight - Spectrum(Y) * picSpectrum.ScaleHeight) Next Y 'Refresh the picturebox picSpectrum.Refresh
This works pretty good, but what I’m trying to do is make the lines wider so that each line is maybe 10 pixels wide. Currently each spectrum line is 1 pixel wide.
InvalidateRect and similar other API calls are similiar to doing a pic.refresh but much much more faster, and because u supply the acutal rect area where the refresh is done, its even faster because it only updates the area where u worked (if u define your rect correctly)
Shoudl be able to find more detailed information on them by searching the M$ web site and reading the GDI stuff, its chalk full of some very interesting calls.
I did look at your example, and found it to be a decent, however fairly complex to follow bit of code, however the code does preform nicely.
P.S…InvalidateRect is really only useful when u need to do refreshes, acutal drawing code like lines, rectfills, ect do not need to be refreshed, unless you are working with a pic that does not have autorefresh on or something similar to that.
So how would I go about using Do Loop??? The way I have it now is in a timer using For Next. It loops through the width of picturebox and plots each line, and then refreshes itself with the timer. So if you use Do Loop how would you refresh it. How do you use SleepEx?? Thanks.
Please login first to submit.