javaswingjavasoundjava-module

ClassCastException thrown when creating a javax.sound.sampled.Clip


I want to play sound in a Java Swing application. I referred to https://www.baeldung.com/java-play-sound and modify like this:

module-info.java

module sound {
    requires java.desktop;
}

The source files are in the same folder [mydomain]/desktopproject/sound. There is a Swing JFrame with a JButton, when I press the button the following snippet runs until the last line throws a ClassCastException:

audioStream = AudioSystem.getAudioInputStream(new BufferedInputStream(
    new FileInputStream(new File("/path-to-my.wav"))
));
AudioFormat audioFormat = audioStream.getFormat();
DataLine.Info info = new DataLine.Info(SourceDataLine.class, audioFormat);
Clip audioClip = (Clip) AudioSystem.getLine(info);

The exception message is like this:

Exception in thread "AWT-EventQueue-0"
java.lang.ClassCastException: class com.sun.media.sound.DirectAudioDevice$DirectSDL
   cannot be cast to class javax.sound.sampled.Clip
   (com.sun.media.sound.DirectAudioDevice$DirectSDL
   and javax.sound.sampled.Clip are in module java.desktop
   of loader 'bootstrap')

I am not sure where the problem is. I use openjdk version "11.0.20" 2023-07-18 and when I ran java --list-module, I got:

java.desktop@11.0.20

Solution

  • According to the documentation (here), the first argument of the DataLine.Info constructor you are using is:

    the class of the data line described by the info object

    Then, according to the documentation of AudioSystem's getLine method (here):

    Obtains a line that matches the description in the specified Line.Info object.

    Also, DataLine.Info is a Line.Info, hence I assumed that the documentation of getLine applies to DataLine.Info as well. My assumption results mainly from OOP inheritance and trust in the implementation.

    Finally, the error is a ClassCastException, which indicates (and states) that the returned object by getLine was not of type Clip.

    From the docs and the observation of the error and if the assumption is true and after searching similar usages to get inspired, one can maybe conclude that the first argument of DataLine.Info constructor should be the type of object returned by getLine. This is why I suggested in the comments of the question to try the following solution to this:

    Change the following line of code:

    DataLine.Info info = new DataLine.Info(SourceDataLine.class, audioFormat);
    

    to:

    DataLine.Info info = new DataLine.Info(Clip.class, audioFormat);
    

    in order to obtain a instance of Clip (which is a DataLine, which is a Line).

    I didn't try it and I am not really familiar with javax.sound.sampled package (hence a comment initially), but since you reported it worked, I figured it is an answer and wrote it here.