How can I draw the waveform of a wav sample

738 views
Skip to first unread message

Ghassan Thabit

unread,
Apr 17, 2013, 5:25:16 AM4/17/13
to beadsp...@googlegroups.com
Hi,

I need to draw the waveform of short samples (~4 sec). I've dug into the svn repo and found the class SampleView but it doesn't contain much comments. I've used it as the following:

SampleView viewer = new SampleView(player.sample);
viewer.bindToSamplePlayer(player);
viewer.redraw();
viewer.getComponent() 
and added the component to my frame

It produced very tiny funny shape.

I understand one way of drawing the waveform; get all samples from wav file, divide them into chunks- possibly by the division of #samples/#pixels, find the max and min values in each chunk, draw a vertical line between each max and min, but I don't know how can I do it using beads.
getFrames() is not working for me, getFrame() does.

Any hints on how to use SampleView class, or what's the best way to do it would be much appreciated.

Cheers,
Ghassan

Oliver Bown

unread,
Apr 17, 2013, 11:05:44 PM4/17/13
to beadsp...@googlegroups.com
Hi Ghassan,

the processing examples show how to do simple drawing of waveforms, but your description of what to do is perfectly fine and would work for a preloaded sample. From a Sample object you can call getFrame() which gives you the sample(s) (multichannel) for the given sample index. So you have the sample data and you can choose how you want to draw it (literally sample by sample, or by getting the RMS power or something). Note to access the sample data use the Sample object not the SamplePlayer object.

SampleView is part of the GUI / play stuff which is not documented and isn't really developed enough to include in beads officially. They're just crude GUI experiments. But it does totally work.

O

--
You received this message because you are subscribed to the Google Groups "beadsproject" group.
To unsubscribe from this group and stop receiving emails from it, send an email to beadsproject...@googlegroups.com.
To post to this group, send email to beadsp...@googlegroups.com.
Visit this group at http://groups.google.com/group/beadsproject?hl=en.
For more options, visit https://groups.google.com/groups/opt_out.
 
 

Ghassan Thabit

unread,
Apr 28, 2013, 6:39:46 AM4/28/13
to beadsp...@googlegroups.com
Hi Ollie.

I managed to achieve what I want using SampleView with very little modifications to improve the output.

Cheers,

Daniel Stroud

unread,
Apr 29, 2013, 5:28:11 AM4/29/13
to beadsp...@googlegroups.com
Hi Ghassan,

I've just started playing with Beads last night, it kicks minims ar*e.
I had no idea that there was such a class, this sounds like an ideal way of drawing a waveform so well done for finding it! would you mind posting some example code showing how you got it to display a waveform?

Also what other hidden classes did you find?

Regards

Dan

Daniel Stroud

unread,
Apr 29, 2013, 6:18:12 AM4/29/13
to beadsp...@googlegroups.com
Hi Ollie,

Thanks for being involved in such an excellent library!

So I see you are a dev on the Beads project, perhaps you could shed some light on the hidden/undocumented classes?

Ghassan Thabit

unread,
Apr 29, 2013, 7:39:53 AM4/29/13
to beadsp...@googlegroups.com
Hi Dan,

It's fairly straightforward, you need to create an instance of BeadsPlayer, create an instance of SampleView, bind the sampleview to the player, get a component using SampleView.getComponent and add it to JPanel. Here's an example:

// add player
BeadsPlayer player = new BeadsPlayer("sample.wav");

// add SampleView component
SampleView viewer = new SampleView(player.sample);
viewer.bindToSamplePlayer(player);
viewer.redraw();

JPanel panel = new JPanel();
panel.add(viewer.getComponent());

I've omitted the layout manager code for simplicity. 

The core of SampleView is in calculateOverview() and recalculateBackgroundImage() methods. 
My understanding is that calculateOverview() is finding the average for chunks of the samples and stores them in the int array "view[]". recalculateBackgroundImage() then uses these values to draw the waveform.

Instead of getting the average I thought it would be better to get the maximum and minimum values for each chunk so I created 2 int arrays max[] and min[] and stored the maximums and minimums in them for recalculateBackgroundImage() to use.

Here's the modified code (these modifications are optional, the class does draw the waveform without them)

private void calculateOverview() {
if(sample != null) {
float[] frame = new float[sample.getNumChannels()];
if(sample != null) {
double hop = (double)sample.getNumFrames() / width;
for(int i = 0; i < width; i++) {
int index = (int)(i * hop);
float maxValue = 0;
float minValue = 0;
int maxJ = Math.min(chunkSize, (int)sample.getNumFrames() - index);
for(int j = 0; j < maxJ; j++) {
sample.getFrame(index + j, frame);
maxValue = (maxValue < frame[0]) ? frame[0] : maxValue;   // <----- MY MODIFICATION
minValue = (minValue > frame[0]) ? frame[0] : minValue;      // <----- MY MODIFICATION
}

max[i] = (int)((-1*maxValue + 1f) * (float)height / 2f);                     // <----- MY MODIFICATION
min[i] = (int)((-1*minValue + 1f) * (float)height / 2f);                       // <----- MY MODIFICATION
}
}
}
waveForm = null;
}

private void recalculateBackgroundImage() {
//now draw to the buffered image
waveForm = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
Graphics g = waveForm.getGraphics();
((Graphics2D)g).setStroke(lightStroke);
g.setColor(Color.lightGray);
g.fillRect(0, 0, width, height);
g.setColor(Color.gray);
g.setColor(new Color(255, 20, 0));
//wave
if(max != null && min != null) {
for(int i = 1; i < view.length; i++) {
g.drawLine(i - 1, max[i], i - 1, min[i]);                                             // <----- MY MODIFICATION
}
}

if(component != null) {
component.getTopLevelAncestor().repaint();
}
}

I've removed some code from recalculateBackgroundImage() cause I don't need it in my application.

I've attached a screenshot for the waveform. I hope that helps.

Cheers,

Ghassan


--
You received this message because you are subscribed to a topic in the Google Groups "beadsproject" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/beadsproject/kp17EyqB7lY/unsubscribe?hl=en.
To unsubscribe from this group and all its topics, send an email to beadsproject...@googlegroups.com.
Capture.PNG

Daniel Stroud

unread,
Apr 29, 2013, 8:18:47 AM4/29/13
to beadsp...@googlegroups.com
Thanks Ghassan,

you explained it perfectly :)

David Johnson

unread,
Nov 8, 2015, 4:22:14 PM11/8/15
to beadsproject
I'm trying to follow along this example, as I'm trying to get the same idea going, drawing the waveform for the whole song/sample.

I'm stuck in a few spots.

1) "reate an instance of BeadsPlayer".  There doesn't appear to be a "BeadsPlayer".  Was this something in an older version?  I'm trying it with SamplePlayer, but that also is not working.  When I use this line:

  viewer = new SampleView( s );

I get this error:
"The constructor SampleView( Sample) is undefined"
(One thing I'm doing differently is setting up a "Sample" from the "SamplePlayer".  I've also tried just like recommended:

  viewer = new SampleView( player.sample );

But that results in this error:
"The field SamplePlayer.sample is not visible"

The only form of this I can get working is:

  viewer = new SampleView();

But then it's not referencing the sample in any way.

2) "viewer.bindToSamplePlayer( player );"  Isn't working

I get this error:
"the function bindToSamplePlayer( SamplePlayer ) does not exist.

3) I'm looking through the source code, and I'm finding no "SampleViewer" file.  Since I can use "viewer = new SampleView();" just fine... It seems that there is code behind it, but I can't seem to see where that is coming from.  It's not in core and it's not in analysis.

Here's the code I've got now, which is functioning (with a line commented out).  Not sure how to proceed.

"
// SampleView.pde

import beads.*;
import org.jaudiolibs.beads.*;

AudioContext ac;
Gain g;
SamplePlayer player;
SampleView viewer;
Sample s;

void setup()
{
  size( 800, 600 );
  
  // Beads basics setup
  ac = new AudioContext();
  g = new Gain( ac, 2, 0.75 );
  ac.out.addInput( g );
  
  // Load the sample
  try
  {
    player = new SamplePlayer( ac, new Sample( sketchPath( "" ) + "248 - Sugar Hill Gang - Rapper's Delight (Original).wav" ) );

    g.addInput( player );
  }
  catch( Exception e )
  {
    e.printStackTrace();
  }
 
 s = player.getSample();
    
 // Add Sample Veiwer component
  viewer = new SampleView();
  //viewer.bindToSamplePlayer( player );
  
  ac.start();

Oliver Bown

unread,
Nov 8, 2015, 4:28:22 PM11/8/15
to beadsp...@googlegroups.com
Hi David,

these classes are part of a set of GUI objects (built on top of Java’s Swing GUI system). They used to be part of Beads but some time ago I separated them out - mostly because they were crap, but also conceptually I wanted to keep Beads simple and focused on audio functionality. At present they are not available via any public library. I can send you the src code for them if you like (let me know in a direct message). I still use them but they are pretty poor and if you have the stomach to grab the sample content and draw it yourself then you have most of what you need. Just drawing the sample data is not very hard.

Ollie

David Johnson

unread,
Nov 16, 2015, 12:40:26 PM11/16/15
to beadsproject
Thanks for the heads up Ollie!  If this is a poor way to go, I'll avoid it, and just work on getting it drawing with current Beads classes.

Here's another thread that gets into attempting this with getFrame() and getFrames(), maybe you could follow up over there:

Thanks!!

Really enjoying playing with beads.  It's pretty awesome to read a book on the topic (Sonifying Processing), then having the creator of the API helping unstick you.  Very cool!

Thanks much for this library!!

dj
Reply all
Reply to author
Forward
0 new messages