1
0

Hello, as told in a recent post and thanks to Andrew, I give you now a part of my code. It shows how to draw the waveform of any files open with FSOUND.
In this code, I suppose:
– you have a device context in hDC
– the name of the file is in “char* wavfile”
– “unsigned int y” is the top height coord of the rectangle where you want to draw
– “unsigned int Height” is the total height of the rectangle where you want to draw
– “RECT rect” is the client area of the window corresponding to hDC
– MARGEG is the left margin (I’m french :) ) you want to keep on the client area and MARGED the right one


[code:2igmkklu]
FSOUND_SAMPLE* pfsam;
// open the sample
pfsam=FSOUND_Sample_Load(FSOUND_FREE,wavfile,FSOUND_NORMAL,0,0);
// we want to know if it is stereo/mono and 16bits/8bits
unsigned int fsmode=FSOUND_Sample_GetMode(pfsam);
unsigned int fsmul=1;
if (fsmode & FSOUND_16BITS) fsmul=2;
if (fsmode & FSOUND_STEREO) fsmul
=2;
SelectObject(hDC,GetStockObject(DKGRAY_BRUSH));
// we draw a "zero value" line
// if mono, we draw one line in the center of the rectangle
if (fsmode & FSOUND_MONO)
{
MoveToEx(hDC,rect.left+MARGEG,y+Height/2,NULL);
LineTo(hDC,rect.right-MARGED,y+Height/2);
}
// if stereo, we draw 2 lines one at the quarter of the rectangle and
// another one at the third quarter of the rectangle
else
{
MoveToEx(hDC,rect.left+MARGEG,y+Height/4,NULL);
LineTo(hDC,rect.right-MARGED,y+Height/4);
MoveToEx(hDC,rect.left+MARGEG,y+3Height/4,NULL);
LineTo(hDC,rect.right-MARGED,y+3
Height/4);
}
// we get the length of the sample in SAMPLES
unsigned int fsplen=FSOUND_Sample_GetLength(pfsam);
// we get the frequency of the sample
int fsfreq;
FSOUND_Sample_GetDefaults(pfsam,&fsfreq,0,0,0);
// we create the lock vars: shorts for 16 bits and chars for 8 bits
signed short locksptr,locksptr2;
signed char lockcptr,lockcptr2;
unsigned int longlue,longlue2;
// case 16bits
if (fsmode & FSOUND_16BITS)
{
// case 16bits stereo
if (fsmode & FSOUND_STEREO)
{
// we lock the memory to get access
FSOUND_Sample_Lock(pfsam,0,fsplen2,(void)&locksptr,(void)&locksptr2,&longlue,&longlue2);
// we move to the beginning of the left waveform
// (centerline at the first quarter)
MoveToEx(hDC,rect.left+MARGEG,y+Height/4+
(signed int)((float)Height
(float)locksptr[0]/(4.0f32768.0f)),NULL);
// we draw the waveforme here
for (signed int ti=0;ti<=rect.right-MARGED-(rect.left+MARGEG+1);ti++)
{
// xtab = pos in the locked memory
// of the value we draw
int xtab=(int)((float)ti
(float)fsplen2.0f/
(float)(rect.right-MARGED-(rect.left+MARGEG+1)));
// ycor = value adjusted to be drawn
signed int ycor=y+Height/4+
(signed int)((float)Height
(float)locksptr[xtab]/(4.0f32768.0f));
LineTo(hDC,ti+rect.left+MARGEG+1,ycor);
}
// we do the same for the right waveform
// (centerline at the third quarter)
MoveToEx(hDC,rect.left+MARGEG,y+3
Height/4+
(signed int)((float)Height(float)locksptr[1]/(4.0f32768.0f)),NULL);
for (signed int ti=0;ti<=rect.right-MARGED-(rect.left+MARGEG+1);ti++)
{
int xtab=(int)((float)ti(float)fsplen2.0f/
(float)(rect.right-MARGED-(rect.left+MARGEG+1)));
signed int ycor=y+3Height/4+
(signed int)((float)Height
(float)locksptr[xtab+1]/(4.0f32768.0f));
LineTo(hDC,ti+rect.left+MARGEG+1,ycor);
}
}
// case 16bits mono
else
{
FSOUND_Sample_Lock(pfsam,0,fsplen,(void)&locksptr,(void)&locksptr2,&longlue,&longlue2);
MoveToEx(hDC,rect.left+MARGEG,y+Height/2+
(signed int)((float)Height
(float)locksptr[0]/(2.0f32768.0f)),NULL);
for (signed int ti=0;ti<=rect.right-MARGED-(rect.left+MARGEG+1);ti++)
{
int xtab=(int)((float)ti
(float)fsplen/
(float)(rect.right-MARGED-(rect.left+MARGEG+1)));
signed int ycor=y+Height/2+
(signed int)((float)Height(float)locksptr[xtab]/(2.0f32768.0f));
LineTo(hDC,ti+rect.left+MARGEG+1,ycor);
}
}
// never forget to unlock when completed
FSOUND_Sample_Unlock(pfsam,(void)locksptr,(void)locksptr2,longlue,longlue2);
}
// case 8bits
else
{
// case 8bits stereo
if (fsmode & FSOUND_STEREO)
{
FSOUND_Sample_Lock(pfsam,0,fsplen2,(void)&lockcptr,(void)&lockcptr2,&longlue,&longlue2);
// left channel
MoveToEx(hDC,rect.left+MARGEG,y+Height/4+
(signed int)((float)Height
(float)lockcptr[0]/(4.0f128.0f)),NULL);
for (signed int ti=0;ti<=rect.right-MARGED-(rect.left+MARGEG+1);ti++)
{
int xtab=(int)((float)ti
(float)fsplen2.0f/
(float)(rect.right-MARGED-(rect.left+MARGEG+1)));
signed int ycor=y+Height/4+
(signed int)((float)Height
(float)lockcptr[xtab]/(4.0f128.0f));
LineTo(hDC,ti+rect.left+MARGEG+1,ycor);
}
// right channel
MoveToEx(hDC,rect.left+MARGEG,y+3
Height/4+
(signed int)((float)Height(float)lockcptr[1]/(4.0f128.0f)),NULL);
for (signed int ti=0;ti<=rect.right-MARGED-(rect.left+MARGEG+1);ti++)
{
int xtab=(int)((float)ti(float)fsplen2.0f/
(float)(rect.right-MARGED-(rect.left+MARGEG+1)));
signed int ycor=y+3Height/4+
(signed int)((float)Height
(float)lockcptr[xtab+1]/(4.0f128.0f));
LineTo(hDC,ti+rect.left+MARGEG+1,ycor);
}
}
// case 8bits mono
else
{
FSOUND_Sample_Lock(pfsam,0,fsplen,(void)&lockcptr,(void)&lockcptr2,&longlue,&longlue2);
MoveToEx(hDC,rect.left+MARGEG,y+Height/2+
(signed int)((float)Height
(float)lockcptr[0]/(2.0f128.0f)),NULL);
for (signed int ti=0;ti<=rect.right-MARGED-(rect.left+MARGEG+1);ti++)
{
int xtab=(int)((float)ti
(float)fsplen/
(float)(rect.right-MARGED-(rect.left+MARGEG+1)));
signed int ycor=y+Height/2+
(signed int)((float)Height(float)lockcptr[xtab]/(2.0f128.0f));
LineTo(hDC,ti+rect.left+MARGEG+1,ycor);
}
}
// never forget to unlock when completed
FSOUND_Sample_Unlock(pfsam,(void)lockcptr,(void)lockcptr2,longlue,longlue2);
}
// and close the sample
FSOUND_Sample_Free(pfsam);

[/code:2igmkklu]

I guess you can make it shorter to avoid managing 16bits/stereo then 16bits/mono then 8bits/stereo then 8bits/mono, but then you have the whole thing.

One just problem remaining is that the result is looking this way:

[img:2igmkklu]http://www.chez.com/sdbf/mixwin.jpg[/img:2igmkklu]

when for a pro software like cool edit, it is looking like that (for the same sample):

[img:2igmkklu]http://www.chez.com/sdbf/cooledit.jpg[/img:2igmkklu]

Even if my version is correct, the cool edit one is easier to use (to see where the sound is getting low, etc…). Do any of you know what is the way to process the datas to get such waveform?

Hope it will help anybody ๐Ÿ˜€
David

  • You must to post comments
1
0

So here it is, I just post the code for the 16bits stereo, adjust it for the other cases:
Let me first explain what I do. In the previous code, I just draw a sequence of lines that passes through the read value of the sample corresponding to the position. But:
1/ when you look to the way it is drawn in cool edit, you see that there are 2 lines: one following the negative values and one following the positive values and the middle is filled.
2/ in a standard window, the size of the width is around 1000 pixels. a standard song is about 3 minutes with in general 44100hz, it makes 7,938,000 samples, so if you only pick 1000 values out of it, the final drawing has no relation with the original sample. that’s why I thought to use a mean of all the values (not all in fact you’ll see later that I drop some values to speed up the calculation)

So that’s exactly what I do, I draw 2 lines:
one for the negative values where in the y axis, it is the mean of all (…not all…) the negative values between the actual position and the previous one
one for the positive values where in the y axis, it is the mean of all (…not all…) the positive values between the actual position and the previous one
then I fill (in fact I don’t fill the center, I fill the up and down part).

for the calculation, I have the following function:

  • tabs is the pointer to the sample (that’s the 16bits proc so it’s short*)
  • idxi is the index in this sample calculated in the previews loop
  • idxf is the index in this sample calculated in this loop
  • step is the step in the indexes, it is used to choose between stereo (step=2) and mono (step=1)
  • valpos is true if we look for the positive values false if we look for the negative values

[code:2ptpfukr]
signed short ValMoyS(signed short *tabs,int idxi,int idxf,int step,bool valpos)
{
// initial sum is 0
signed int ValMoy=0;
// the number of values found
int nval=0;
// we take the values between idxi and idxf
for (int ti=idxi;ti<=idxf;ti+=step)
{
// if valpos, we only look for positive values
if (valpos)
{
if (tabs[ti]>=0)
{
// we got a positive value, we add it to the sum and increase nval
ValMoy+=(signed int)tabs[ti];
nval++;
}
}
// idem if !valpos, we look for negative values
else
{
if (tabs[ti]<=0)
{
ValMoy+=(signed int)tabs[ti];
nval++;
}
}
// this is the position where we increase the ti index to drop some values and speed up the calculation
// it must be a multiple of 2 to avoid changing channel in case of stereo
ti+=80;
}
if (nval==0) return 0; // to avoid division by 0
ValMoy/=nval;
return (signed short)ValMoy;
}
[/code:2ptpfukr]

So now we have minor changes to do to the original code (remember that I only show the 16bits/stereo code):

[code:2ptpfukr]
FSOUND_Sample_Lock(pfsam,0,fsplen,(void)&locksptr,(void)&locksptr2,&longlue,&longlue2);

// we begin with the positive values of the left channel

// lstIdx is the var to keep the old index
unsigned int lstIdx=0;
// I changed the 32768.0f by 15000.0f, because 32768 makes it too flat
// we should check that it doesn’t get out of the rectangle, but in all the files I tried it seemed ok (I let you add the little code to check that)
MoveToEx(hDC,rect.left+MARGEG,y+Height/2+(signed int)((float)Height(float)locksptr[0]/(2.0f15000.0f)),NULL);
for (signed int ti=0;ti<=rect.right-MARGED-(rect.left+MARGEG+1);ti++)
{
unsigned int xtab=(int)((float)ti(float)fsplen/(float)(rect.right-MARGED-(rect.left+MARGEG+1)));
// I forgot this in the previous code, as we use float for the calculation of the index, we can switch the channel, with this we are sure we are on a left channel
xtab=xtab&0xFFFFFFFE;
// we use our function with valpos=true (positive values) first and step=2 (stereo)
signed int ycor=y+Height/2+(signed int)((float)Height
(float)ValMoyS(locksptr,lstIdx,xtab,2,true)/(2.0f*15000.0f));
LineTo(hDC,ti+rect.left+MARGEG+1,ycor);
lstIdx=xtab;
}

// then the negative values of the left channel

lstIdx=0;
MoveToEx(hDC,rect.left+MARGEG,y+Height/2+(signed int)((float)Height(float)locksptr[0]/(2.0f15000.0f)),NULL);
for (signed int ti=0;ti<=rect.right-MARGED-(rect.left+MARGEG+1);ti++)
{
unsigned int xtab=(int)((float)ti(float)fsplen/(float)(rect.right-MARGED-(rect.left+MARGEG+1)));
xtab=xtab&0xFFFFFFFE;
// we now use our function with valpos=false (negative values) and step=2 (stereo)
signed int ycor=y+Height/2+(signed int)((float)Height
(float)ValMoyS(locksptr,lstIdx,xtab,2,false)/(2.0f*15000.0f));
LineTo(hDC,ti+rect.left+MARGEG+1,ycor);
lstIdx=xtab;
}

// then the positive values of the right channel

unsigned int lstIdx=1;
MoveToEx(hDC,rect.left+MARGEG,y+Height/2+(signed int)((float)Height(float)locksptr[1]/(2.0f15000.0f)),NULL);
for (signed int ti=0;ti<=rect.right-MARGED-(rect.left+MARGEG+1);ti++)
{
unsigned int xtab=(int)((float)ti(float)fsplen/(float)(rect.right-MARGED-(rect.left+MARGEG+1)));
// now we align on right channel
xtab=xtab&0xFFFFFFFE;
xtab+=1;
// we use our function with valpos=true (positive values) first and step=2 (stereo)
signed int ycor=y+Height/2+(signed int)((float)Height
(float)ValMoyS(locksptr,lstIdx,xtab,2,true)/(2.0f*15000.0f));
LineTo(hDC,ti+rect.left+MARGEG+1,ycor);
lstIdx=xtab;
}

// then the negative values of the right channel

lstIdx=1;
MoveToEx(hDC,rect.left+MARGEG,y+Height/2+(signed int)((float)Height(float)locksptr[0]/(2.0f15000.0f)),NULL);
for (signed int ti=0;ti<=rect.right-MARGED-(rect.left+MARGEG+1);ti++)
{
unsigned int xtab=(int)((float)ti(float)fsplen/(float)(rect.right-MARGED-(rect.left+MARGEG+1)));
xtab=xtab&0xFFFFFFFE;
xtab+=1;
// we now use our function with valpos=false (negative values) and step=2 (stereo)
signed int ycor=y+Height/2+(signed int)((float)Height
(float)ValMoyS(locksptr,lstIdx,xtab,2,false)/(2.0f*15000.0f));
LineTo(hDC,ti+rect.left+MARGEG+1,ycor);
lstIdx=xtab;
}
// now we fill the exterior part of the drawn sample
// the exterior of the rectangle is black and the lines are drawn in black too to comply with the RGB(0,0,0) of the FloodFill lines
SelectObject(hDC,CreateSolidBrush(RGB(0,0,255)));
FloodFill(hDC,rect.left+MARGEG+1,y+1,RGB(0,0,0));
FloodFill(hDC,rect.left+MARGEG+1,y+hautpiste/2,RGB(0,0,0));
FloodFill(hDC,rect.left+MARGEG+1,y+hautpiste-2,RGB(0,0,0));
[/code:2ptpfukr]

I hope I was clear and I put again the link on the definitive waveform:

[i:2ptpfukr][img:2ptpfukr]http://www.chez.com/sdbf/mixwin2.jpg[/img:2ptpfukr][/i:2ptpfukr]

If you post any question in the next days, I’ll try to answer…

David

  • You must to post comments
0
0

๐Ÿ˜ณ Oops, I’ve just seen that the TABS are erased in the posts, so I would have better done to put some SPACES to make the code clearer.

  • You must to post comments
0
0

No problem for the debug part of my code, I’ll post a fixed code soon, as I tried something and it worked.
For my pictures, they are not missing, try to put those in the adress bar of your explorer. I don’t understand why it doesn’t display in the forum???
my program initial one:
http://www.chez.com/sdbf/mixwin.jpg
the same sample with Cool Edit
http://www.chez.com/sdbf/cooledit.jpg
the new (and fine for me) one:
http://www.chez.com/sdbf/mixwin2.jpg
Better, isn’t it?
I’ll post this evening the fixed version of the data computation

  • You must to post comments
0
0

The links to your images are gone. I’m curious what they look like. Can you re-post?

THX

  • You must to post comments
0
0

๐Ÿ˜• Sorry I don’t understand why it doesn’t work, the images are still there… If you copy and paste (DON’T CLICK ON THE LINKS, paste or type it in) these addresses in the address bar of your explorer, they work well… I hope. Tell me if it does or not, please.
David

Cool Edit pic:
http://www.chez.com/sdbf/cooledit.jpg
Old code pic:
http://www.chez.com/sdbf/mixwin.jpg
New code pic:
http://www.chez.com/sdbf/mixwin2.jpg

  • You must to post comments
Showing 5 results
Your Answer

Please first to submit.