3

plese look at my code snippets , wha is wrong with it , it frrezes GUI when the Swing timer stats which is repeteadly paints on the jpnael ??

class WaveformPanel extends JPanel {

        Timer graphTimer = null;
        AudioInfo helper = null;

        WaveformPanel() {
            setPreferredSize(new Dimension(200, 80));
            setBorder(BorderFactory.createLineBorder(Color.BLACK));
            graphTimer = new Timer(15, new TimerDrawing());
        }

        /**
         * 
         */
        private static final long serialVersionUID = 969991141812736791L;
        protected final Color BACKGROUND_COLOR = Color.white;
        protected final Color REFERENCE_LINE_COLOR = Color.black;
        protected final Color WAVEFORM_COLOR = Color.red;

        protected void paintComponent(Graphics g) {

            super.paintComponent(g);

            int lineHeight = getHeight() / 2;
            g.setColor(REFERENCE_LINE_COLOR);
            g.drawLine(0, lineHeight, (int) getWidth(), lineHeight);

            if (helper == null) {
                return;
            }

            drawWaveform(g, helper.getAudio(0));

        }

        protected void drawWaveform(Graphics g, int[] samples) {

            if (samples == null) {
                return;
            }

            int oldX = 0;
            int oldY = (int) (getHeight() / 2);
            int xIndex = 0;

            int increment = helper.getIncrement(helper
                    .getXScaleFactor(getWidth()));
            g.setColor(WAVEFORM_COLOR);

            int t = 0;

            for (t = 0; t < increment; t += increment) {
                g.drawLine(oldX, oldY, xIndex, oldY);
                xIndex++;
                oldX = xIndex;
            }

            for (; t < samples.length; t += increment) {
                double scaleFactor = helper.getYScaleFactor(getHeight());
                double scaledSample = samples[t] * scaleFactor;
                int y = (int) ((getHeight() / 2) - (scaledSample));
                g.drawLine(oldX, oldY, xIndex, y);

                xIndex++;
                oldX = xIndex;
                oldY = y;
            }
        }

        public void setAnimation(boolean turnon) {
            if (turnon) {
                graphTimer.start();
            } else {
                graphTimer.stop();
            }
        }

        class TimerDrawing implements ActionListener {

            @Override
            public void actionPerformed(ActionEvent e) {

                byte[] bytes = captureThread.getTempBuffer();

                if (helper != null) {
                    helper.setBytes(bytes);
                } else {
                    helper = new AudioInfo(bytes);
                }
                repaint();
            }
        }

    }

I am calling setAnimation of WaveFormPanel from its parent class.when animation starts it does not draw anything but freezes. please , give me solution.

Thank You Mihir Parekh

mKorbel
  • 109,525
  • 20
  • 134
  • 319
Mihir
  • 2,480
  • 7
  • 38
  • 57

2 Answers2

4

The java.swingx.Timer calls the ActionPerformed within the EDT. The question then is, what's taking the time to render. It could be the call to captureThread.getTempBuffer it could be the construction of the help, but I suspect it's just the sheer amount of data you are trying to paint.

Having played with this recently, it takes quite a bit of time to process the waveform.

One suggestion might be to reduce the number of samples that you paint. Rather then painting each one, maybe paint every second or forth sample point depending on the width of the component. You should still get the same jist but without all the work...

UPDATED

All samples, 2.18 seconds

AllSamples

Every 4th sample, 0.711 seconds

enter image description here

Every 8th sample, 0.450 seconds

enter image description here

Rather then paint in response to the timer, maybe you need to paint in response to batches of data.

As your loader thread has a "chunk" of data, may be paint it then.

As HoverCraftFullOfEels suggested, you could paint this to a BufferedImage first and then paint that to the screen...

SwingWorker might be able to achieve this for you

UPDATED

This is the code I use to paint the above samples.

// Samples is a 2D int array (int[][]), where the first index is the channel, the second is the sample for that channel
if (samples != null) {

    Graphics2D g2d = (Graphics2D) g;

    int length = samples[0].length;

    int width = getWidth() - 1;
    int height = getHeight() - 1;

    int oldX = 0;
    int oldY = height / 2;
    int frame = 0;

    // min, max is the min/max range of the samples, ie the highest and lowest samples
    int range = max + (min * -2);
    float scale = (float) height / (float) range;

    int minY = Math.round(((height / 2) + (min * scale)));
    int maxY = Math.round(((height / 2) + (max * scale)));

    LinearGradientPaint lgp = new LinearGradientPaint(
            new Point2D.Float(0, minY),
            new Point2D.Float(0, maxY),
            new float[]{0f, 0.5f, 1f},
            new Color[]{Color.BLUE, Color.RED, Color.BLUE});
    g2d.setPaint(lgp);
    for (int sample : samples[0]) {

        if (sample % 64 == 0) {

            int x = Math.round(((float) frame / (float) length) * width);
            int y = Math.round((height / 2) + (sample * scale));

            g2d.drawLine(oldX, oldY, x, y);

            oldX = x;
            oldY = y;

        }

        frame++;

    }

}

I use an AudioStream stream to load a Wav file an produce the 2D samples.

MadProgrammer
  • 343,457
  • 22
  • 230
  • 366
  • Hello Mr. Mad Programmer i tried to paint it each every seconds but it freezes.i tried to paint also every 8th sample rather your suggestion four but no luck. – Mihir Aug 22 '12 at 05:41
  • Painting every second probably isn't the best solution, given that it can more then a second to paint (my initial test was 2.18 seconds) – MadProgrammer Aug 22 '12 at 05:44
  • so , please give me the solution or it is best if you can give me any working example link , you r talking about. – Mihir Aug 22 '12 at 05:45
  • even if comment repaint line in the TimerDrawing class it still freezes the GUI and my whole application. – Mihir Aug 22 '12 at 05:50
  • The I'd suggest that's not the problem area then. – MadProgrammer Aug 22 '12 at 06:09
  • hello Mr. wise Programmer can you post here the code from which you generated this output ?? so i can debug . i think now i am very near to my solution . – Mihir Aug 22 '12 at 06:19
  • I don't know how useful it will be, but it's there – MadProgrammer Aug 22 '12 at 06:40
  • thanks Mr. wise programmer , after slight modification of your code and some hard work i have achieved what i want exactly. thanks again. – Mihir Aug 23 '12 at 07:48
  • I'm thinking an `AffineTransform` would (also) be of use here. – Andrew Thompson Oct 14 '12 at 06:49
3

I'm guessing that your wave drawing code, which is being called from within a paintComponent(...) method is taking longer than you think and is tying up both Swing painting and the EDT.

If this were my code, I'd consider drawing my waves to BufferedImages once, making ImageIcons from these images and then simply swapping icons in my Swing Timer.

Hovercraft Full Of Eels
  • 283,665
  • 25
  • 256
  • 373
  • Hello Mr. Hovercraft Full Of Eels thanks for your advice i am trying it now and let you know. – Mihir Aug 22 '12 at 05:37
  • Yes i know it Mr. Hovercraft , i wrote my first comment to Mr. Mad programmer. – Mihir Aug 22 '12 at 05:38
  • Mr. HoverCraft can you give me a link of any working example you have suggested me in the comment. – Mihir Aug 22 '12 at 05:40
  • @Mihir: nope I can't, but it shouldn't be hard to try to do. – Hovercraft Full Of Eels Aug 22 '12 at 05:41
  • I did the removal of comment , i posted it by mistake on your answer. – Mihir Aug 22 '12 at 05:42
  • actually the byte i am getting is from TragetDataLine's buffer which is the numeric values of samples of recorded voice and i am trying to draw that bytes on the panel so i can construct a wave form in real time using those values.everything works fine but failed in painting on the panel. – Mihir Aug 22 '12 at 05:44
  • Ah, perhaps what you want to do is to only draw that which is new, and leave the old graphics data unchanged. You could still use a BufferedImage for this I think. – Hovercraft Full Of Eels Aug 22 '12 at 05:46
  • even if comment repaint line in the TimerDrawing class it still freezes the GUI and my whole application. – Mihir Aug 22 '12 at 05:49
  • Then it's your other methods that are causing your problems as MadProgrammer suggests. Time to look into using a little background threading. 1+ to him. – Hovercraft Full Of Eels Aug 22 '12 at 05:55