problem with painting background in paintComponent.
Hi everyone,
What I'm trying to do is draw a waveform on a Jpanel. I created an inner class that extends JPanel. The inner class has a method called setData that accepts an array of bytes. This method converts the byte array to a set of x values and a set of y values. Then the repaint() method is called for each point. The problem is that it erases all the previous points. How do I prevent it doing that?
This is the code - might make it clearer.
publicvoid setData(byte []audioData)
{
yValues =newint[audioData.length];
xValues =newint[audioData.length];
started =true;
for(int i =0;i<audioData.length;i++)
{
yValues[i] = (int) audioData[i];
xValues[i]+=20;
this.repaint();
}
}
publicvoid paintComponent(Graphics g)
{
super.paintComponent(g);//paint background
this.setBackground(Color.green);
this.setForeground(Color.blue);
Graphics2D g2 = (Graphics2D)g;
if(started ==true)
{
int i =0;
i++;
x+=20;
g2.fillOval(x,yValues[i]+200, 5,5);
}
}
>
[1908 byte] By [
CurtinR] at [2007-9-27 22:48:16]

There are 2 options:1. draw all the points every time in the paintComponent(..)2. create an image the first time the paintComponent is called, draw the point to this image and draw the image to the panel.Noah
Thanks for that! I think I'll go with the second option - I can't use the first option because I don't know the coordinates of the points in advance.
How do I go about adding the other points to an image that I've already created? Do I need to use memoryImageSource?
This is my code so far - I'm not sure if its right cos I'm not too familiar with graphics in Java:
wave = createImage(this.getWidth(),this.getHeight());
g.drawImage(wave,0,0, wave.getWidth(this),wave.getHeight(this),Color.green,this);
I had a look at some tutorials but couldn't really find any examples that used images in this way - if you could suggest any I'd really appreciate it.
Thanks,
CurtinR.
You can use java.awt.image.BufferedImage for that.
BufferedImage bi = new BufferedImage(BufferedImage.TYPE_INT_ARGB, getSize().width, getSize().height);
Graphics2D g = bi.createGraphics();
double framesPerPixel = 5d; //The zoom
int nullLine = getSize().height, index;
int[] samples = getSamples(byteData);
for(int i=0; i<getSize().width-1; i++){
index = (int)(framesPerPixel * i);
if(index+1 > samples.length-1)
break;
g.drawLine(i, nullLine - samples[index],
i+1, nullLine - samples[index+1]);
}
But remember that you cannot simply cast the bytes to integers!
You would get wrong results using that method(except you have 8-bit data).
My code does not make a difference between mono and stereo, which is neccessary, too (except you're dealing only with mono)
Best regards,
Djamal
DOMO at 2007-7-7 13:52:30 >

Thank you so much for that Djamal! It works now! Just one question - why can't you cast bytes to integers?Again thanks a million for your help!Roseanne.
Of course you can, but there's no sense in doing that. ;)
Soundfiles are stored like this (16-bit, stereo, bigendian):
hl|hl hl|hl hl|hl hl|hl hl|hl hl|hl
where h is higher-byte and l lower-byte.
The | separates the channels left and right.
The spaces separate the frames.
Or (16-bit, stereo, littleendian:
lh|lh lh|lh lh|lh lh|lh lh|lh lh|lh
The two bytes are forming a sample. Many samples form the waveform. You have to make a difference between bytes and samples(except you're dealing with 8-bit only, because in that special case a byte is a sample)
If you have stereodata you have two samples in one frame.
In mono you have only one sample in a frame.
Bigendian is the order in which the bytes are interpreted as samples:
(I took only 4 bits, that's easier but the prinzip is exactly the same)
1 0 0 1 | 0 1 1 0 the bits in bigendian are:
7 6 5 4 | 3 2 1 0
1 0 0 1 | 0 1 1 0 the bits in littleendian are:
3 2 1 0 | 7 6 5 4
As you see the order in littleendian is reversed.
Now, if you cast the bytes to integers you just get not the right information, because you have to interprete the bytes. Your waveform will look like garbage, if you don't do that.
Bye,
Djamal
DOMO at 2007-7-7 13:52:30 >

