x
• Status: Solved
• Priority: Medium
• Security: Public
• Views: 1464

# Help need to draw graph of a wav file

Hi,

I'm writing a simple wave file editor for a college project in MS Visual C++.  At the moment I'm trying to draw a graph of a wave file.  I have achieved this to some extent but when I try to graph very large wave files the graph does not give an accurate representation of the waveform.  I know that this is to do with the fact that there are far more samples than pixels on the screen but I have seen editor's e.g. CoolEdit and GoldWave where this is done.  Does anyone have any suggestions as to how I might go about this?  Also I want to be able to draw 'rulers' on the display to represent time and amplitude.

J:o)
0
2HD
• 7
• 5
• 5
• +1
1 Solution

Commented:
>> It would help if we coudl see the code you were using to do the drawing.

There are four options I can think of for handling this:

The first is sampling, like if there are 500 data points and you have a 100 pixel wide area to draw in, you draw ever 5th pixel.  This method is fast, but if the samle rate becomes too low, like if you were drawing every 100th pixel, the graph may appear chaotic.

The second is averaging.   Again if there are 500 data points and you have 100 pixel wide area to draw in, you would average groups of 5 pixels and draw the averages.  This method is a little slower, but it does not become usually "chaotic" for large "compressions"  However, it may begin to become 0 for large compressions.  i.e you might see that the graph tends to just become 0, or near 0 much of the time.

continues

0

Commented:
A third option has no name I am aware of  (although I'm sure it has a name (or two), these are "recognized" approaches).  This approach is to draw every data point, even though some points may be in the same column of pixels.  Again for the 500 and 100 example, you would draw the first 5 points (1 to 5) in the left-most column of pixels, then the next set of points (6 to 10) ont he 2nd column of pixels.  This gives a very good picture of the data even when it becomes very compressed.  (Altough for very high compressions, the graph may tend to just become a single high value.  (probably not, though, this tends to work well for this type of data.))   Now a dissadvantage of this is that it is time consuming, because you may have a large number of points to plot.  You can make this a little faster by simply recording the low and high values in an "interval (group of data points that appear in a single pixel column) and plot these two values.  (If this is for a scatter plot, you would just plot these two as points.  if it is for a like plot (which is more likely for your case), you would draw a line that connects them.)

A fourth option, similar to the 3rd, is to find the value with the largest absolute value in the interval and plot this value.  This is a litle faster than the third option, but like the first option it may yield chaotic results for high compressions (less than the first option, though).

Of all the options, the 3rd tends to be the best, but it it the hardest to program efficiently.
0

Commented:
>> also I want to be able to draw 'rulers' on
>> the display to represent time and amplitude
0

Commented:
I have some code what displays waveform data which contains a lot of data (more than 100000 data points).
The idea is the following: if you have to display 5000 points in a area of 500 pixels, it's clear that for each X-pixel there will colapse 10 points. In this case, just draw a line connecting the maximum intensity with the minimum intensity.
Another hint is that you should use PolyLine because is much faster than LineTo and MoveTo.

I have the source code at home, are you interested?

0

Commented:
>> In this case, just draw a line connecting the
>> maximum intensity
>> with the minimum intensity
Herein officially known a option 3.  :-)
0

Commented:
yes, you're right, actually what I did was just to send my comment without reading yours.
Sorry
0

Commented:
>>  I did was just to send my comment without reading yours.
Write thou shall not.... 1000 times.  :-)

Its not a good idea.  At best it makes you look foolish, at worst it can cause conflicts.  Besides you may miss important details from the client, intespaces between expert's comments.
0

Commented:
A 'scope like & Sound Input Class
http://www.codeguru.com/multimedia/scope_input_sound_class.shtml

Wave File Editor Control
http://www.codeguru.com/controls/WaveEdit.shtml

Also have a look at the PaintAudio function in the aviedit.c of the following sample.

AVIedit: Editing APIs in AVIfile
http://msdn.microsoft.com/isapi/msdnlib.idc?theURL=/library/devprods/vs6/visualc/vcsample/vcsmpaviedit.htm
0

Author Commented:
Thanks guys that was a great help.  Here's the code I'm using at the moment to draw my 8-bit samples...

nNextSample = lpSound->nTotalSamples / (long)m_nGraphWidth ;
nAmpFactor = 4 * floor((255L / (long)m_nGraphHeight));

for ( int i = 0; i <  m_nGraphWidth; ++i)
{

nAmp = (unsigned int)max(labs(((long)p[0] - 128L) / afactor), 1L);

dc->MoveTo(x + i, max(y + (m_nGraphHeight / 2) - nAmp, y));
dc->LineTo(x + i, min(y + (m_nGraphHeight / 2) + nAmp, y + m_nGraphHeight));

p += nNextSample;

}

Qocarlos I would be interested in seeing that code you have for drawing waveform data.

About the rulers thing (I should have made it more clear) what I'm trying to do is draw an area on my graph window, similar to the rulers in MS Word, where I can display values, like time instead of inches or whatever, under the graph without the graph itself drawing over them.

Again many thanks for your help.

J:o)

0

Commented:
Do you get C/C++ User's Journal.  there was an article in the last month or two (probably March) that gives some good algorithms for drawing scales for graphs.   That is esentually what you need to do.

Do you know how to change your drawing code?  Do you know which algorithm you are going to choose?

In the interests of effciency,you may want to allocate an array that is the size of the graph width (or the data sample, whichever is smaller) and use one of the algorithms I suggested to fill this array in with the "compressed" data.  Then draw the graph from this array.  The advantage is that the potentually slow "compression" algorithm only has to be done the first time the scale is changed or the window is resized. Then you can draw the information repeatidely, without doing the calculations again and agan.
0

Author Commented:
I don't subscribe to the C/C++ User's Journal.  Is it an electronic publication or a paper one? I've actually never heard of it so I don't even know if you can get it here in Ireland??

I have no idea how I'm going to change my drawing code or which algorithm I will use.  Again, any help would be much appreciated.

Cheers.

J:o)
0

Commented:
Its a paper publication--and very very worth subscribing to.   They may have back articles and code at www.cuj.com or at least might have the ability to order an old article.   You probably get copies from a good library.  (Like a university library.)   I don't have the copy in question in front of me, but you are probalby much better of with the original, rather than my ramblings.
0

Commented:
Hi,
Sorry but I did not find my code. Anyway, I have written the following routine that it should work in your case.
I've used the algorithm suggested by nietod (and later by me).

//'fData' is the array of data points to draw
//'from' is the first point of the data points to draw
//'to' is the last point to draw
//'left' and 'right' are the left and right screen coordinates (i.e. the plot starts at left and finish at right)
//'yPos' is the vertical offset of the plot
//'max' is the maximum amplitude of the data points
//'factor' is a scaling factor

void Draw(CDC* pDC, float* fData, int from, int to, int left, int right, int yPos,float max, float factor)
{
CPoint p;
p.x=left;
p.y=(int)(yPos-fData[from]*factor/max);
float min,max;
min=max=(float)p.y;
int oldP=p.x;
pDC->MoveTo(p);
int y;
for (register int i=from;i<to;i++)
{
p.x=MulDiv(right-left,i-from,to-from)+left;
p.y=(int)(baseline-fData[i]*factor/max);
if ((p.x-oldP)!=0)//New pixel X
{

y=min;
pDC->LineTo(oldP,y);
y=max;
pDC->LineTo(oldP,y);
oldP=p.x;
max=min=(float)p.y;

}
else
{
if (p.y>max) max=(float)p.y;
if (p.y<min) min=(float)p.y;
}
}
}

For instance, if you have 10000 data points in an array
float* data;
and you want to draw all the data points and the plot should be contained between 200-600 pixels (X-coordinates) using a vertical offset of 400 pixels with a scale factor of 80 pixels you should call the above functions as follows:

Draw(pDC, data, 0, 10000, 200, 600, 400, max /*you must compute this*/,80)

If you have any question, please let me know.
Note that the above code is not tested/optimized. Also remember that you should use PolyLine rather than MoveTo/LineTo.

HTH
0

Author Commented:
Many thanks, guys...
0

Author Commented:
Hi guys,

I don't know if you remember? but a few weeks back I requested some help on how to draw a graph of a waveform.  I have since implemented the algorithm as suggested by qocarlos and it works perfectly except for the fact that; if I try and draw a large waveform (anything longer that about a minute) the redraw takes ages and is even worse if I try to draw a stereo waveform!  Also how would I got about zooming the waveform and selecting portions for to apply effects to?  I know I'm asking a lot but any help would be very much appreciated as the project counts towards my degree.

J:o)
0

Commented:
Sorry for the delay in replying.  I have been away from work for a couple of
weeks.
I don't know if you have solved your problem. What I can tell you is that I can draw large waveforms (more than 1000000 points) in one second or even less.

How many points are you working with?

Carlos
0

Author Commented:
Hi,

Thanks for the reply. I haven't solved the problem.  I want to try and be able to graph about 3mins of cd quality audio so I would be talking about > 1000000 I'd say about 8000000 !

J:o)
0

Commented:
Hi,
You've got a lot of points! Anyway, I think that 1 minute is too long for that amount of points. Show me your code and I see what can I do.
Just a couple of hints:
* Try to use Polyline
* Draw first in a memory DC and then copy it to the screen DC (you could use CMemDC class by Keith Rule)

As for zooming, that should be very easy. The code I provided you (see above) should do the job. Just feed the function with the corresponding left and right limits of the array.

You can write me at qocarlos@usc.es
0
Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.