JDK-6196668 : Pausing/resuming playback adds overlapped audio to output
  • Type: Bug
  • Component: client-libs
  • Sub-Component: javax.sound
  • Affected Version: 5.0
  • Priority: P4
  • Status: Closed
  • Resolution: Duplicate
  • OS: windows_xp
  • CPU: x86
  • Submitted: 2004-11-17
  • Updated: 2011-01-19
  • Resolved: 2005-07-08
Related Reports
Duplicate :  
Description
FULL PRODUCT VERSION :
java version "1.5.0"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.5.0-b64)
Java HotSpot(TM) Client VM (build 1.5.0-b64, mixed mode)

ADDITIONAL OS VERSION INFORMATION :
Microsoft Windows XP [Version 5.1.2600]

EXTRA RELEVANT SYSTEM CONFIGURATION :
Realtek AC97 Audio on motherboard

A DESCRIPTION OF THE PROBLEM :
(This is being resubmitted since I didn't get an auto-generated email the first time it was submitted.)

Audio playback within a java.sound.sampled.Clip is behaving strangely.  While playing a wav (haven't tried anything else) file, if playback is paused and resumed, the audio output is repeating audio data.  It kind of sounds like a skip and then it continues (eg. "Welcome <pause><resume> to Java...to Java sound").  When it get's to the end of the file the microsecond position indicates a position longer than the actual length.

Playback can be paused and resumed multiple times for a more pronounced effect.

Debugger shows that the actual Clip class is com.sun.media.sound.DirectAudioDevice$DirectClip.

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
1.  Run the test app provided.
2. Open a wav file.
3. Click Play.
4. Click Pause, then Play again to resume.

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
Clip is played, paused and resumed without a problem.
ACTUAL -
No error, just a problem with the audio playback.  A portion of the audio is overlapped with previously played audio.

REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------
import javax.sound.sampled.*;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.File;
import java.io.IOException;
import java.text.DecimalFormat;
import java.util.Vector;


public class VoicePromptPlayer extends JFrame implements ActionListener
{
	Player player = new Player();

	AudioInputStream audioInputStream;

	JButton buttonPlayPause = new JButton();
    JButton buttonStop = new JButton();
    JButton buttonClose = new JButton();
    JLabel lblPosition = new JLabel();
    JLabel lblLength = new JLabel();
    JLabel lblPositionSeconds = new JLabel();
    JLabel lblLengthSeconds = new JLabel();
    JLabel lblSecond1 = new JLabel();
    JLabel lblSecond2 = new JLabel();


    JPanel topPanel = new JPanel(new BorderLayout());
    JPanel positionPanel = new JPanel();
    JPanel lengthPanel = new JPanel();
    JPanel bottomPanel = new JPanel(new BorderLayout());
    JPanel controlButtonPanel = new JPanel(new FlowLayout(FlowLayout.LEFT, 5, 5));
    JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT, 5, 5));

	int lengthInMilliseconds, seconds;
	Vector lines = new Vector();


	public VoicePromptPlayer()
    {
        super("Player");

		this.getContentPane().setLayout(new BorderLayout());

        setupTopPanel();

        buttonPlayPause.setText("Play");
        buttonPlayPause.setActionCommand("Play");
        buttonPlayPause.addActionListener(this);
        buttonPlayPause.setEnabled(false);
        buttonPlayPause.setFocusPainted(false);
        buttonStop.setText("Stop");
        buttonStop.setActionCommand("Stop");
        buttonStop.addActionListener(this);
        buttonStop.setEnabled(false);
        buttonStop.setFocusPainted(false);
        buttonClose.setText("Close");
        buttonClose.addActionListener(this);

        buttonPanel.add(buttonClose);
        controlButtonPanel.add(buttonPlayPause);
        controlButtonPanel.add(buttonStop);
        controlButtonPanel.add(buttonPanel);
        bottomPanel.add(controlButtonPanel, BorderLayout.WEST);
        bottomPanel.add(buttonPanel, BorderLayout.EAST);

        this.getContentPane().add(topPanel, BorderLayout.NORTH);
        this.getContentPane().add(bottomPanel, BorderLayout.SOUTH);

        createAudioInputStream();
        lblPosition.setText("Position: ");
        lblPositionSeconds.setText(formatMilliseconds(0));
        lblLength.setText("Length: ");
        lblLengthSeconds.setText(formatMilliseconds((long)lengthInMilliseconds));
        lblSecond1.setText(" sec.");
        lblSecond2.setText(" sec.");

        if (audioInputStream != null)
        {
            buttonPlayPause.setEnabled(true);
        }
        this.setResizable(false);
        this.addWindowListener(new MyWindowAdapter());
        this.pack();
        this.setLocationRelativeTo(null);
	}

    private void setupTopPanel()
    {
        GridBagLayout gbLayout = new GridBagLayout();
        GridBagConstraints gbc = new GridBagConstraints();

        positionPanel.setLayout(gbLayout);

        gbc.weightx      = 0.0;
        gbc.weighty      = 0.0;
        gbc.gridheight   = 1;
        gbc.gridwidth    = 1;
        gbc.insets       = new Insets(4, 4, 0, 0);
        gbc.ipadx        = 0;
        gbc.ipady        = 0;
        gbc.gridx        = 0;
        gbc.gridy        = 0;
        gbc.fill         = GridBagConstraints.NONE;
        gbc.anchor       = GridBagConstraints.WEST;

        gbLayout.setConstraints(lblPosition, gbc);
        positionPanel.add(lblPosition);

		gbc.insets       = new Insets(4, 0, 0, 0);
        gbc.gridx        = 1;
        positionPanel.add(lblPositionSeconds, gbc);

        gbc.weightx      = 1.0;
        gbc.gridx        = 2;
        positionPanel.add(lblSecond1, gbc);
        topPanel.add(positionPanel, BorderLayout.WEST);

        gbLayout = new GridBagLayout();
        lengthPanel.setLayout(gbLayout);

        gbc.gridx        = 0;
        gbc.anchor       = GridBagConstraints.EAST;
        lengthPanel.add(lblLength, gbc);
        gbc.weightx      = 0.0;
        gbc.gridx        = 1;
        lengthPanel.add(lblLengthSeconds, gbc);
		gbc.insets       = new Insets(4, 0, 0, 4);
        gbc.gridx        = 2;
        lengthPanel.add(lblSecond2, gbc);
        topPanel.add(lengthPanel, BorderLayout.EAST);
    }



    public static void main(String args[])
    {
        VoicePromptPlayer player = new VoicePromptPlayer();
        player.setVisible(true);
    }


	public String toString()
	{
		return ("Player");
	}


	public void actionPerformed(ActionEvent e)
    {
		Object obj = e.getSource();
		if (obj.equals(buttonPlayPause))
        {
			if (buttonPlayPause.getActionCommand().equals("Play"))
            {
                // Need to start play or resume
                if (player.isPaused())
    				player.resume();
                else
                    player.play();

                // Either way modify the button
                setPauseButton();
                buttonStop.setEnabled(true);
			}
            else
            {
				player.pause();
                setPlayButton();
			}
            pack();
		}
        else if (obj.equals(buttonStop))
        {
            player.stop();
            buttonStop.setEnabled(false);
            if (buttonPlayPause.getActionCommand().equals("Pause"))
                setPlayButton();
		}
        else if (obj.equals(buttonClose))
        {
            player.stop();
            this.dispose();
		}
    }


    private String formatMilliseconds(long milliseconds)
    {
        DecimalFormat format = new DecimalFormat("0.00");
        double seconds = milliseconds / 1000.0;
        String formattedOutput = format.format(seconds);
        return formattedOutput;
    }

    private void setPlayButton()
    {
        buttonPlayPause.setText("Play");
        buttonPlayPause.setActionCommand("Play");
    }

    private void setPauseButton()
    {
        buttonPlayPause.setText("Pause");
        buttonPlayPause.setActionCommand("Pause");
    }

	public void createAudioInputStream()
    {
        JFileChooser chooser = new JFileChooser();
        chooser.setFileSelectionMode(ListSelectionModel.SINGLE_SELECTION);
        chooser.showOpenDialog(this);

        File file = chooser.getSelectedFile();

        try {
            audioInputStream = AudioSystem.getAudioInputStream(file);
        } catch (Exception e) {}

        if (audioInputStream != null)
        {
            player.initialize();
            lengthInMilliseconds = (int)((audioInputStream.getFrameLength() * 1000) / audioInputStream.getFormat().getFrameRate());
        }

	}


	/**
	 * Voice prompt player.
	 */
	public class Player implements ActionListener
    {
        Timer timer;
        Clip clip;
        boolean paused = false;

        public Player()
        {
            timer = new Timer(100, this);
            timer.setRepeats(true);
        }

        public void actionPerformed(ActionEvent e)
        {
            long usec = clip.getMicrosecondPosition();

            System.out.println("Clip position: " + usec + " usec, isRunning: " + clip.isRunning());
            System.out.println("Timer time: " + System.currentTimeMillis() + " msec");
            if ( (clip.isRunning() == false))
            {
               // At end of playback
                if (buttonStop.isEnabled())
                    buttonStop.doClick();
            }

            lblPositionSeconds.setText(formatMilliseconds((long) usec/1000));
            lblPosition.repaint();
        }

        public boolean isPaused()
        {
            return paused;
        }

		public void play()
        {
            if(clip == null)
            {
              JOptionPane.showMessageDialog(null, "There is no loaded audio to playback.",
                                            "Play Problem",JOptionPane.WARNING_MESSAGE);
              return;
            }
            System.out.println("\nClip starting");
            System.out.println("Clip position: " + clip.getMicrosecondPosition() + " usec, isRunning: " + clip.isRunning());
            clip.start();
            System.out.println("Clip started");
            System.out.println("Clip position: " + clip.getMicrosecondPosition() + " usec, isRunning: " + clip.isRunning());
            System.out.println("Start time: " + System.currentTimeMillis() + " msec");

            timer.start();
		}

        public void pause()
        {
            if (timer != null)
                timer.stop();
            if (clip != null)
                clip.stop();
            paused = true;
        }

        public void resume()
        {
            if (clip != null)
                clip.start();
            if (timer != null)
               timer.restart();
            paused = false;
        }

		public void stop()
        {
            if (timer != null)
                timer.stop();

            if (clip != null)
            {
                clip.stop();
                clip.setFramePosition(0);
            }
            paused = false;
		}

		private void shutDown(String message)
        {
			if (message != null)
            {
				System.err.println(message);
			}
                setPlayButton();
                buttonStop.setEnabled(false);
		}

        public void setPosition(long microseconds)
        {
            clip.setMicrosecondPosition(microseconds);
        }

		public void initialize()
        {
			// make sure we have something to play
			if (audioInputStream == null) {
				shutDown("There is no loaded audio to playback.");
				return;
			}

			// define the required attributes for our line,
			// and make sure a compatible line is supported.

            AudioFormat format = audioInputStream.getFormat();
            DataLine.Info clipInfo = new DataLine.Info(Clip.class, format);
			if (!AudioSystem.isLineSupported(clipInfo))
            {
				shutDown("A line matching desired info not supported" + ": " + clipInfo);
				return;
			}

			// get and open the source data line for playback.

			try
            {
                clip = (Clip)AudioSystem.getLine(clipInfo);   // Create the clip
                clip.open(audioInputStream);
                clip.setFramePosition(0);
			}
            catch (IOException e)
            {
                shutDown("Unable to open clip" + ": " + e);
                return;
            }
            catch (LineUnavailableException ex)
            {
				shutDown("Unable to open line" + ": " + ex);
				return;
			}


		}
	} // End class Player


    class MyWindowAdapter extends WindowAdapter
    {
        public void windowClosing(WindowEvent e)
        {
            player.stop();
        }
    }
}


---------- END SOURCE ----------
###@###.### 2004-11-17 17:32:17 GMT