Search code examples
javafxaudioaudio-playeraudioinputstream

Restarting and pausing and resuming clip hangs the gui of music player, while pressing pause and play resumes playing from stopping point


This program is a music player that allows user to pick a .wav file, play, pause, resume, and restart a the music file from a clip object and audioinput stream. The audio input stream loads a file that is determined by user via FileChooser. The program can play, pause, and resume by selecting a file, pressing play, pause, then play again, but does not play using the restart method or the resume method invoked via the respective buttons. Instead, the program hangs until the X button is clicked. I think it has something to do with the resetaudiostream method, but I am unsure what. Maybe something to do with ending the old clip and creating a new clip instance. Please review the logic and let me know what is making it hang and how that could be remedied.

package sample;

import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.VBox;
import javafx.stage.FileChooser;
import javafx.stage.Stage;

import javax.sound.sampled.*;
import java.io.File;
import java.io.IOException;


public class Main extends Application {
    static File musicfile;
    static Long currentFrame;
    static Clip clip;
    static String status = "play";
    static AudioInputStream audioInputStream;
    static String filePath;
    public void SimpleAudioPlayer()
            throws UnsupportedAudioFileException,
            IOException, LineUnavailableException
    {
        // create AudioInputStream object
        audioInputStream =
                AudioSystem.getAudioInputStream(new File(filePath).getAbsoluteFile());

        // create clip reference
        clip = AudioSystem.getClip();

        // open audioInputStream to the clip
        clip.open(audioInputStream);

        clip.loop(Clip.LOOP_CONTINUOUSLY);
    }
    @Override
    public void start(Stage primaryStage) throws Exception{
        Parent root = FXMLLoader.load(getClass().getResource("sample.fxml"));
        primaryStage.setTitle("Music Player");
        GridPane gp = new GridPane();

        Button selectFile = new Button("Select File");
        GridPane.setConstraints(selectFile, 0,0);
        selectFile.setOnAction(event->{
            FileChooser filechooser = new FileChooser();
            // create AudioInputStream object
            try {
                musicfile = filechooser.showOpenDialog(null);
                audioInputStream = AudioSystem.getAudioInputStream(musicfile);

                clip = AudioSystem.getClip();

                // open audioInputStream to the clip
                clip.open(audioInputStream);

            }catch(IOException | UnsupportedAudioFileException | LineUnavailableException e){
                e.printStackTrace();
            }
        });
        Button play = new Button("Play");
        GridPane.setConstraints(play, 1,0);
        play.setOnAction(event->{
            if(status == "play") {


                    clip.loop(Clip.LOOP_CONTINUOUSLY);

            }
            play();

        });
        Button pause = new Button("Pause");
        GridPane.setConstraints(pause, 2,0);
        pause.setOnAction(event -> pause());

        Button restart = new Button("Restart");
        GridPane.setConstraints(restart, 0,1);
        restart.setOnAction(event -> {
            try{
            restart();
        }
        catch(IOException | UnsupportedAudioFileException | LineUnavailableException e){
        e.printStackTrace();}
        });
        Button resume = new Button("Resume");
        GridPane.setConstraints(resume, 1,1);
        resume.setOnAction(event -> {
            try {
                resumeAudio();
            }catch(IOException | LineUnavailableException | UnsupportedAudioFileException e){
                e.printStackTrace();
        }
        });
        gp.getChildren().addAll(play,selectFile, pause, restart, resume);
        primaryStage.setScene(new Scene(gp, 300, 275));
        primaryStage.show();
    }

    public void play()
    {
        //start the clip
        clip.start();

        status = "play";
    }

    // Method to pause the audio
    public  void pause()
    {
        if (status.equals("paused"))
        {
            System.out.println("audio is already paused");
            return;
        }
        currentFrame =
                clip.getMicrosecondPosition();
        clip.stop();
        status = "paused";
    }

    // Method to resume the audio
    public void resumeAudio() throws UnsupportedAudioFileException,
            IOException, LineUnavailableException
    {
        if (status.equals("play"))
        {
            System.out.println("Audio is already "+
                    "being played");
            return;
        }
        clip.close();
        resetAudioStream();
        clip.setMicrosecondPosition(currentFrame);
        status = "play";
        play();
    }

    // Method to restart the audio
    public void restart() throws IOException, LineUnavailableException,
            UnsupportedAudioFileException
    {
        clip.stop();
        clip.close();
        resetAudioStream();
        currentFrame = 0L;
        clip.setMicrosecondPosition(0);
        status = "play";
        play();
    }

    // Method to stop the audio
    public void stop() throws UnsupportedAudioFileException,
            IOException, LineUnavailableException
    {
        currentFrame = 0L;
        clip.stop();
        clip.close();
    }

    // Method to jump over a specific part
    public void jump(long c) throws UnsupportedAudioFileException, IOException,
            LineUnavailableException
    {
        if (c > 0 && c < clip.getMicrosecondLength())
        {
            clip.stop();
            clip.close();
            resetAudioStream();
            currentFrame = c;
            clip.setMicrosecondPosition(c);
            this.play();
        }
    }

    // Method to reset audio stream
    public void resetAudioStream() throws UnsupportedAudioFileException, IOException,
            LineUnavailableException
    {
        audioInputStream = AudioSystem.getAudioInputStream(musicfile);
        clip = AudioSystem.getClip();
        clip.open(audioInputStream);
        clip.loop(Clip.LOOP_CONTINUOUSLY);
    }
    public static void main(String[] args) {
        launch(args);
    }
}

Solution

  • It is quiet simple to get the required functionality with a MediaPlayer:

    import java.net.URI;
    import javafx.application.Application;
    import javafx.scene.Scene;
    import javafx.scene.control.Button;
    import javafx.scene.control.Label;
    import javafx.scene.layout.GridPane;
    import javafx.scene.media.Media;
    import javafx.scene.media.MediaPlayer;
    import javafx.scene.media.MediaPlayer.Status;
    import javafx.stage.Stage;
    import javafx.util.Duration;
        
    /*
     * If you get "cannot access class com.sun.glass.utils.NativeLibLoader" exception you may need to
     * add a VM argument: --add-modules javafx.controls,javafx.media as explained here:
     * https://stackoverflow.com/questions/53237287/module-error-when-running-javafx-media-application
     */
    public class Main extends Application {
    
        private MediaPlayer player;
        private static final long JUMP_BY = 5000;//millis 
    
        @Override
        public void start(Stage primaryStage) throws Exception{
    
            URI uri = new URI("https://www.soundhelix.com/examples/mp3/SoundHelix-Song-5.mp3");
            Media media = new Media(uri.toString());
            //OR Media media = new Media("https://www.soundhelix.com/examples/mp3/SoundHelix-Song-5.mp3");
            player = new MediaPlayer(media);
            player.setOnError(() -> System.out.println(media.getError().toString()));
    
            GridPane gp = new GridPane();
            gp.setHgap(10);
    
            Button play = new Button("Play");
            GridPane.setConstraints(play, 0,0);
            play.setOnAction(event->  playAudio());
    
            Button pause = new Button("Pause");
            GridPane.setConstraints(pause, 1,0);
            pause.setOnAction(event -> pauseAudio());
    
            Button resume = new Button("Resume");
            GridPane.setConstraints(resume, 2,0);
            resume.setOnAction(event -> resumeAudio());
    
            Button stop = new Button("Stop");
            GridPane.setConstraints(stop, 3,0);
            stop.setOnAction(event ->  stopAudio());
    
            Button restart = new Button("Restart");
            GridPane.setConstraints(restart, 4,0);
            restart.setOnAction(event ->  restartAudio());
    
            Button jump = new Button("Jump >");
            GridPane.setConstraints(jump, 5,0);
            jump.setOnAction(event ->  jump(JUMP_BY));
    
            Label time = new Label();
            GridPane.setConstraints(time, 6,0);
            time.textProperty().bind( player.currentTimeProperty().asString("%.4s") );
    
            gp.getChildren().addAll(play, pause, resume, stop, restart, jump, time);
            primaryStage.setScene(new Scene(gp, 400, 45));
            primaryStage.show();
        }
    
        //play audio 
        public void playAudio()
        {
            player.play();
        }
    
        //pause audio
        public  void pauseAudio()
        {
            if (player.getStatus().equals(Status.PAUSED))
            {
                System.out.println("audio is already paused");
                return;
            }
            player.pause();
        }
    
        //resume audio
        public void resumeAudio()
        {
            if (player.getStatus().equals(Status.PLAYING))
            {
                System.out.println("Audio is already playing");
                return;
            }
            playAudio();
        }
    
        //restart audio
        public void restartAudio()
        {
            player.seek(Duration.ZERO);
            playAudio();
        }
    
        // stop audio
        public void stopAudio()
        {
           player.stop();
        }
    
        //jump by c millis 
        public void jump(long c)
        {
            player.seek(player.getCurrentTime().add(Duration.millis(c)));
        }
    
        public static void main(String[] args) {
            launch(args);
        }
    }