5

I wanted a really simple way to play an mp3 file from a Java application. After some research I found that the newest versions of Java 7 SE are packaged with JavaFX, so I thought I would try. This question is NOT about playing mp3 files, but about getting JavaFX to work in a well-behaved way.

So, in my first experiment with JavaFX, I used some code suggested in a post to stackoverflow (see here) and created, essentially, the following test program:

import javafx.application.Application;
import javafx.stage.Stage;
import javafx.scene.media.Media;
import javafx.scene.media.MediaPlayer;

public class progtest extends Application
{

    /*---------------------------------------------------------*\
        FUNCTION:  main()
    \*---------------------------------------------------------*/
    public static void main(String args[])
    {

        launch(args);

    } /* END:  main() */

    /*---------------------------------------------------------*\
        FUNCTION:  start()
    \*---------------------------------------------------------*/
    @Override
    public void start(Stage stage)
    {

        Media medMsg 
            = 
            new Media(getClass().getResource("msg.mp3").toExternalForm());
        MediaPlayer medplMsg = new MediaPlayer(medMsg);
        medplMsg.play();

        System.out.println("Here.\n");

    } /* END:  start() */

}

(This is slightly more sophisticated than my original test program: this version came about after suggestions from jewelsea made in response to an earlier question I posted about getting the program running at all (see here).)

To compile this code I used:

javac -cp "c:\Program Files\Oracle\JavaFX 2.1 Runtime\lib\jfxrt.jar";..\bin -d ..\bin ..\src\progtest.java

And, to run the code I went into my ..\bin directory and used:

java -cp .;"c:\Program Files\Oracle\JavaFX 2.1 Runtime\lib\jfxrt.jar" progtest

This program runs and plays the clip plays. However, the program doesn't return to the command prompt unless I do a Ctrl-C.

That behavior seemed like some sort of thread issue. I have seen this sort of behavior many times working with Java threads (which, sadly, have real problems allowing for graceful exits from programs).

So, I thought, perhaps if I put the code in its own thread and then end the program's main thread when that thread ends, things would be okay. (I could see that this wasn't wholly a cogent thought, but anyway, I thought I would try.) So, I wrote the following second version of the code:

Here is the main thread:

import javafx.application.Application;
import javafx.stage.Stage;
import Msg.*;

public class progtest2 extends Application
{

    /*---------------------------------------------------------*\
        FUNCTION:  main()
    \*---------------------------------------------------------*/
    public static void main(String args[])
    {

        launch(args);

    } /* END:  main() */

    /*---------------------------------------------------------*\
        FUNCTION:  start()
    \*---------------------------------------------------------*/
    @Override
    public void start(Stage stage)
    {

        Msg msgTime = new Msg();
        msgTime.passClass(getClass());
        msgTime.start();

        try
        {
            msgTime.join();
        }
        catch (InterruptedException e)
        {
        }

        System.out.println("Here.\n");

    } /* END:  start() */

}

And here is the code for the Msg thread that should actually play the mp3 file:

package Msg;

import KeyIO.*;
import javafx.application.Application;
import javafx.scene.media.Media;
import javafx.scene.media.MediaPlayer;

public class Msg extends Thread
{

    private Class classRt = null;

    /*--------------------------------------------------------*\
        FUNCTION:  passClass()
    \*--------------------------------------------------------*/
    public void passClass(Class rt)
    {

        classRt = rt;

    } /* END:  passClass() */

    /*--------------------------------------------------------*\
        FUNCTION:  run()
    \*--------------------------------------------------------*/
    public void run()
    {

        Media medMsg
            = new Media(classRt.getResource("msg.mp3").toExternalForm());
        MediaPlayer medplMsg = new MediaPlayer(medMsg);
        medplMsg.play();

        System.out.println("Leaving Msg thread.\n");

    } /* END:  run() */

}

(I built and ran these files with the same command (mutatis mutandis re the filenames and classnames) as above.)

The program runs and plays the mp3 file. Before you hear the file, you see "Leaving Msg thread." and then "Here.", which as you can see come from the Msg thread and the main thread respectively. But, again, the program doesn't end. I have to hit Ctrl-C to return to the command prompt.

I put a System.exit() call right at the end of the main thread there just to see what the behavior would be. The behavior was that the clip was not heard. It seemed like the System.exit() call ended the process before the clip was played.

So, next I tried putting the following lines in between the System.out.println("Here.\n"); and the System.exit() call I made:

System.out.print("Press ENTER to end...");
KeyIO.ReadLine();

(KeyIO is a class I made just for doing keyboard input output simply. The details are not really relevant here.)

I figured that this would simply block the main thread from going forward until the clip played.

But, no. Instead, the clip didn't play. When I hit ENTER, the program exited without playing the clip.

Okay, so now I left that keyboard-input blocking code in, but I took out the System.exit() call. Now the audio clip played, but the program didn't end again.

Urgh!

Any ideas how to gracefully get this to work in the way that it is sort of obvious one would want it to work? I just want to make a call to play the clip, and then I want to be able to end the program.

Thank you in advance!

rgettman
  • 176,041
  • 30
  • 275
  • 357

2 Answers2

7

In the first progtest class, you do not create a window, so the implicitExit action on closing the last window is not invoked. This means the program will keep running until you call the application's exit method, which you don't do.

To get the program to exit after your mp3 has finished playing, call mediaPlayer.setOnEndOfMedia(runnable) and in the runnable callback invoke the Platform.exit() method.

medplMsg.setOnEndOfMedia(new Runnable() {
  @Override public void run() {
    Platform.exit();
  }
});

All the other threading stuff in your question is not required (and doesn't solve your issue anyway...). I wouldn't advise using custom threading unless you really need it (which you don't).

JavaFX 2.2 adds the following api:

Platform.setImplicitExit(boolean implicitExit)

Sets the implicitExit attribute to the specified value. If this attribute is true, the JavaFX runtime will implicitly shutdown when the last window is closed; the JavaFX launcher will call the Application.stop() method and terminate the JavaFX application thread. If this attribute is false, the application will continue to run normally even after the last window is closed, until the application calls exit(). The default value is true.

This should give you some clue as to the problems you are having.

jewelsea
  • 150,031
  • 14
  • 366
  • 406
  • Excellent! It works like a charm. I will post the entire code below. Thank you jewelsea!!! Very helpful. And it has given me a lot of insight about how I can figure out some of this stuff myself in the future. Thank you. –  Jun 05 '12 at 22:47
0

This answer to my own question is to give a definitive example of the code that ultimately solved the problem which comes from jewelsea's excellent responses in this thread and the other one I refer to in my original post.

Okay, so the following code worked (and jewelsea was right in commenting that the threading is unnecessary and beside any point here):

import javafx.application.Application;
import javafx.application.Platform;
import javafx.stage.Stage;
import javafx.scene.media.Media;
import javafx.scene.media.MediaPlayer;

public class progtest6 extends Application
{

    /*---------------------------------------------------------*\
        FUNCTION:  main()
    \*---------------------------------------------------------*/
    public static void main(String args[])
    {

        launch(args);

    } /* END:  main() */

    /*---------------------------------------------------------*\
        FUNCTION:  start()
    \*---------------------------------------------------------*/
    @Override
    public void start(Stage stage)
    {

        Media medMsg 
            = 
            new Media(getClass().getResource("msg.mp3").toExternalForm());
        MediaPlayer medplMsg = new MediaPlayer(medMsg);
        medplMsg.setOnEndOfMedia(new Runnable() {
            @Override
            public void run()
            {
                Platform.exit();
            }
        });
        medplMsg.play();

        System.out.println("Here.\n");

    } /* END:  start() */

}

Thank you jewelsea.