2

I'm trying to use javafx.scene.media.AudioClip to play audio included with .jar file, but I'm constantly getting hit with this error:

Exception in thread "main" java.lang.UnsupportedOperationException: Unsupported protocol "file"
        at com.sun.media.jfxmedia.locator.Locator.<init>(Locator.java:241)
        at com.sun.media.jfxmediaimpl.NativeMediaAudioClip.<init>(NativeMediaAudioClip.java:53)
        at com.sun.media.jfxmediaimpl.NativeMediaAudioClip.load(NativeMediaAudioClip.java:63)
        at com.sun.media.jfxmediaimpl.AudioClipProvider.load(AudioClipProvider.java:66)
        at com.sun.media.jfxmedia.AudioClip.load(AudioClip.java:135)
        at javafx.scene.media.AudioClip.<init>(AudioClip.java:83)
        at main.Main.main(Main.java:77)

I simplified my code as much as possible, omitting abstractions and additional methods to make sure that the error is actually in the AudioClip:

package main;

import javafx.scene.media.AudioClip;

public class Main {
    public static void main(String[] args) {
        AudioClip clip = new AudioClip(Main.class.getResource("/sound/ambience/testSound.mp3").toExternalForm());
        clip.play();
    }
}

(I also tried using .toString() instead of toExternalForm())

I made sure that the sound file is actually included in .jar in the correct path. For example this way of playing audio via JavaX AudioInputStream and Clip works just fine:

    public static void main(String[] args) throws LineUnavailableException, IOException, UnsupportedAudioFileException, InterruptedException {
        AudioInputStream ais = AudioSystem.getAudioInputStream(Main.class.getResource("/sound/ambience/randomScare.wav"));
        Clip clip = AudioSystem.getClip();
        clip.open(ais);
        ais.close();

        clip.start();
        Thread.sleep()
    }

(But I can't use this in final product, as it supports only uncompressed .wav format)

I have no idea what could be causing that problem, as even official javadoc states:

source - URL string from which to load the audio clip. This can be an HTTP, file or jar source.

Some more information that possibly can help with finding solution: I'm using org.openjfx:javafx-media, version 21-ea+24 from maven central repository (and I also tried switching back to version 20.0.1):

        <dependency>
            <groupId>org.openjfx</groupId>
            <artifactId>javafx-media</artifactId>
            <version>21-ea+24</version>
        </dependency>

Maven project is setup with java version 1.17:

    <properties>
        <maven.compiler.source>1.17</maven.compiler.source>
        <maven.compiler.target>1.17</maven.compiler.target>
        <exec.mainClass>main.Main</exec.mainClass>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
    </properties>

And I run it using Zulu 18 (Zulu18.32+13-CA (build 18.0.2.1+1)) on Windows 10 (Build 19044)

I will appreciate any help (and hope that I've given all needed information).

Update: I tried running the .jar build with github workspaces (that .jar worked just fine for my friend), and got different error:

Exception in thread "AWT-EventQueue-0" java.lang.UnsatisfiedLinkError: no glib-lite in java.library.path: C:\Program Files\Zulu\zulu-18\bin;C:\Windows\Sun\Java\bin;C:\Windows\system32;C:\Windows;C:\Program Files\Zulu\zulu-18\bin\;C:\Program Files\Zulu\zulu-19\bin\;C:\ProgramData\Oracle\Java\javapath;C:\Program Files\Common Files\Oracle\Java\javapath;C:\ProgramData\Oracle\Java\jdk-16.0.1;C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Windows\System32\WindowsPowerShell\v1.0\;C:\Windows\System32\OpenSSH\;C:\Program Files (x86)\NVIDIA Corporation\PhysX\Common;C:\Program Files\Common Files\Autodesk Shared\;C:\Program Files\dotnet\;C:\Program Files\spwn\;C:\Program Files\Kotlin\kotlinc_1_6_10;C:\Program Files\Kotlin\kotlinc_1_6_10\bin;C:\Program Files\Kotlin\konaco_1_6_10;C:\Program Files\Kotlin\konaco_1_6_10\bin;C:\Program Files\Microsoft SQL Server\130\Tools\Binn\;C:\Program Files\Microsoft SQL Server\Client SDK\ODBC\170\Tools\Binn\;C:\Program Files\ffmpeg\bin;C:\Program Files\Git\cmd;C:\Program Files\PuTTY\;C:\Program Files\NVIDIA Corporation\NVIDIA NvDLISR;C:\Users\Kofiy\AppData\Local\Programs\Python\Python310\Scripts\;C:\Users\Kofiy\AppData\Local\Programs\Python\Python310\;C:\Users\Kofiy\AppData\Local\Microsoft\WindowsApps;C:\jdk-16.0.1\bin;C:\jdk-16.0.1\bin\;C:\jdk-16.0.1\bin\javac.exe;C:\Program Files\JetBrains\IntelliJ IDEA Community Edition 2021.1.2\bin;C:\Users\Kofiy\AppData\Local\GitHubDesktop\bin;C:\Users\Kofiy\AppData\Local\Programs\Microsoft VS Code\bin;C:\Program Files\JetBrains\PyCharm Community Edition 2021.3.2\bin;C:\Users\Kofiy\AppData\Local\Programs\Python\Python310;C:\Users\Kofiy\AppData\Local\Programs\Python\Python310\Scripts;C:\Users\Kofiy\.dotnet\tools;.
        at java.base/java.lang.ClassLoader.loadLibrary(ClassLoader.java:2434)
        at java.base/java.lang.Runtime.loadLibrary0(Runtime.java:848)
        at java.base/java.lang.System.loadLibrary(System.java:2015)
        at com.sun.glass.utils.NativeLibLoader.loadLibraryInternal(NativeLibLoader.java:168)
        at com.sun.glass.utils.NativeLibLoader.loadLibrary(NativeLibLoader.java:56)
        at com.sun.media.jfxmediaimpl.NativeMediaManager.lambda$new$0(NativeMediaManager.java:111)
        at java.base/java.security.AccessController.doPrivileged(AccessController.java:569)
        at com.sun.media.jfxmediaimpl.NativeMediaManager.<init>(NativeMediaManager.java:108)
        at com.sun.media.jfxmediaimpl.NativeMediaManager$NativeMediaManagerInitializer.<clinit>(NativeMediaManager.java:78)
        at com.sun.media.jfxmediaimpl.NativeMediaManager.getDefaultInstance(NativeMediaManager.java:90)
        at com.sun.media.jfxmedia.MediaManager.canPlayProtocol(MediaManager.java:78)
        at com.sun.media.jfxmedia.locator.Locator.<init>(Locator.java:240)
        at com.sun.media.jfxmediaimpl.NativeMediaAudioClip.<init>(NativeMediaAudioClip.java:53)
        at com.sun.media.jfxmediaimpl.NativeMediaAudioClip.load(NativeMediaAudioClip.java:63)
        at com.sun.media.jfxmediaimpl.AudioClipProvider.load(AudioClipProvider.java:66)
        at com.sun.media.jfxmedia.AudioClip.load(AudioClip.java:135)
        at javafx.scene.media.AudioClip.<init>(AudioClip.java:83)
        at main.Sound.playMP3(Sound.java:222)
        at main.GamePanel.startGame(GamePanel.java:1624)
        at main.KeyHandler.keyPressed(KeyHandler.java:126)
        at java.desktop/java.awt.Component.processKeyEvent(Component.java:6574)
        at java.desktop/javax.swing.JComponent.processKeyEvent(JComponent.java:2905)
        at java.desktop/java.awt.Component.processEvent(Component.java:6393)
        at java.desktop/java.awt.Container.processEvent(Container.java:2266)
        at java.desktop/java.awt.Component.dispatchEventImpl(Component.java:4991)
        at java.desktop/java.awt.Container.dispatchEventImpl(Container.java:2324)
        at java.desktop/java.awt.Component.dispatchEvent(Component.java:4823)
        at java.desktop/java.awt.KeyboardFocusManager.redispatchEvent(KeyboardFocusManager.java:1952)
        at java.desktop/java.awt.DefaultKeyboardFocusManager.dispatchKeyEvent(DefaultKeyboardFocusManager.java:883)
        at java.desktop/java.awt.DefaultKeyboardFocusManager.preDispatchKeyEvent(DefaultKeyboardFocusManager.java:1146)
        at java.desktop/java.awt.DefaultKeyboardFocusManager.typeAheadAssertions(DefaultKeyboardFocusManager.java:1020)
        at java.desktop/java.awt.DefaultKeyboardFocusManager.dispatchEvent(DefaultKeyboardFocusManager.java:848)
        at java.desktop/java.awt.Component.dispatchEventImpl(Component.java:4872)
        at java.desktop/java.awt.Container.dispatchEventImpl(Container.java:2324)
        at java.desktop/java.awt.Window.dispatchEventImpl(Window.java:2780)
        at java.desktop/java.awt.Component.dispatchEvent(Component.java:4823)
        at java.desktop/java.awt.EventQueue.dispatchEventImpl(EventQueue.java:775)
        at java.desktop/java.awt.EventQueue$4.run(EventQueue.java:720)
        at java.desktop/java.awt.EventQueue$4.run(EventQueue.java:714)
        at java.base/java.security.AccessController.doPrivileged(AccessController.java:399)
        at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:86)
        at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:97)
        at java.desktop/java.awt.EventQueue$5.run(EventQueue.java:747)
        at java.desktop/java.awt.EventQueue$5.run(EventQueue.java:745)
        at java.base/java.security.AccessController.doPrivileged(AccessController.java:399)
        at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:86)
        at java.desktop/java.awt.EventQueue.dispatchEvent(EventQueue.java:744)
        at java.desktop/java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:203)
        at java.desktop/java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:124)
        at java.desktop/java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:113)
        at java.desktop/java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:109)
        at java.desktop/java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:101)
        at java.desktop/java.awt.EventDispatchThread.run(EventDispatchThread.java:90)

(((The error in now in AWT-EventQueue-0 because it's full version of the project and not mine simplified code that I wrote earlier)))

And I also tried running .jar with doubleclicking on it, and both .jar built on my system via IntelJ IDEA and .jar built on GitHub workspaces showed this error: Error: A JNI error has occurred, please check your installation and try again

PS: This JNI visual error actually appears whenever I double click on any .jar, what's also very strange PS 2: The JNI visual error is because I messed up and had Java 1.8 as default JRE

So it does in fact look like the problem with my installation, so I will just try reinstalling JDK, verifying system files etc.

kofiy
  • 31
  • 4
  • Audio plays just fine for me on Windows 10 build 19045 using Zulu 20.0.2—with and without JavaFX included—and either JavaFX 20.0.2 or JavaFX 21-ea+24. [Zulu 18 doesn't seem to be available any more](https://www.azul.com/downloads/#zulu). – Slaw Jul 20 '23 at 23:18
  • There appears to be something strange in your setup, or at least something I don't understand in the description and error message. You mention that you *"made sure that the sound file is actually included in .jar in the correct path"*. But the error message is `Unsupported protocol "file"`. Resources in a jar file are accessed via the `jar` protocol, not the `file` protocol. I don't know what the cause is. – jewelsea Jul 20 '23 at 23:41
  • 2
    You example app is playing the audio app, without either starting the JavaFX runtime explicitly or launching a JavaFX application, which will implicitly start the JavaFX runtime. I don't know if playing audio with JavaFX media is supported if the JavaFX runtime is not running (I wouldn't try it). – jewelsea Jul 20 '23 at 23:44
  • Use a debugger with a breakpoint on [`NativeMediaManager.canPlayProtocol`](https://github.com/openjdk/jfx/blob/600cee70a2f6f907a53f7183315e6d7f79b291d6/modules/javafx.media/src/main/java/com/sun/media/jfxmediaimpl/NativeMediaManager.java#L264) and see which protocols it can actually play on your system. – jewelsea Jul 21 '23 at 04:06
  • 2
    @jewelsea "_I don't know if playing audio with JavaFX media is supported if the JavaFX runtime is not running_" -- It worked for me, but yes, I would not rely on that. – Slaw Jul 21 '23 at 04:24
  • 2
    It's definitely getting confused about the type of resource but as @jewelsea says, you are initializing the FX environment in the wrong way so that won't help. If it's any use I can post a link to a working version. I say *working* but actually the main thread doesn't complete for me, which suggests that *I* have something slightly wrong – g00se Jul 21 '23 at 07:46
  • @jewelsea It's actually `jar:file:/` – kofiy Jul 21 '23 at 08:12
  • The error message I quoted does not say `jar:file:`, but maybe that doesn’t matter. What protocols were reported as supported when performed the requested debugging? Calling JavaFX methods from the awt thread and without starting the runtime is inadvisable IMO. – jewelsea Jul 21 '23 at 09:03
  • 1
    @g00se Interesting. Playing the `AudioClip` the same way as the OP, I had to add a call to `Thread.sleep` to _stop_ the main thread from exiting too early (which led to the JVM exiting too early). Apparently, trying to use an `AudioClip` outside a JavaFX application leads to unexpected behavior, which is honestly not surprising. – Slaw Jul 21 '23 at 09:05
  • @Slaw My the original project is basically separate application so there was no need to Thread.sleep(), and I still get the UnsuportedProtocol "file"/UnsatisfiedLinkError: no glib-lite (based on if the .jar is built on my windows machine or on linux github workspace) – kofiy Jul 21 '23 at 09:11
  • Even though I reinstalled jdk – kofiy Jul 21 '23 at 09:12
  • JavaFX jars from Maven central include native components for the detected OS, CPU and architecture, including the streaming components. When you init the runtime, it extracts these from the jar to a cache directory it places in the native library load path. If you don’t init it, it won’t be done. The JavaFX SDK jars are different and include the native components as separate files shipped in the sdk lib dir. jmods and images built by jlink from jmods include the native libs in the mod or image files, which, unlike jars have a built-in mechanism for native code resolution. – jewelsea Jul 21 '23 at 09:13
  • 2
    @kofiy That stack trace and the presence of the `AWT-EventQueue-0` thread indicate you're using AWT or Swing as the UI framework, but then trying to use JavaFX to play audio. Mixing UI frameworks like this is not recommended. But if you must, then you have to manage which UI thread is executing certain code appropriately. First, initialize the JavaFX platform (there are a few ways to do this). Then make sure any interaction with the `AudioClip` occurs on the _JavaFX Application Thread_, not the EDT. – Slaw Jul 21 '23 at 09:14
  • 1
    Your issue is no native streaming library on the Java native library path (because you did not correctly package the app and start the JavaFX runtime). You use maven JavaFX dependencies and don’t init the runtime, so native libraries are not extracted from the JavaFX jars. When the native libraries can’t be found, the Java code can’t play media usng any protocol, hence the unsupported protocol exception, which is a symptom and not a cause of your problem. – jewelsea Jul 21 '23 at 09:20
  • @jewelsea The supported protocols are: `file`, `http`, `https`, `jrt`, `resource` (sorry for the wait) – kofiy Jul 21 '23 at 09:25
  • @jewelsea Oh thanks, got it. (Still kind off weird that it works for other people, and that error is different based on if the .jar was build on windows or linux) – kofiy Jul 21 '23 at 09:27
  • 1
    Probably the issue wasn’t replicable by others because they had a different configuration whch placed JavaFX native libraries on the Java native library path. For example, they had initialized the JavaFX environment for another project sometime in the past, which created a cached JavaFX native library directory on their system that was reused when they tried to replicate your issue. – jewelsea Jul 21 '23 at 09:30
  • 1
    Yes, the JavaFX, is misused. My FX knowledge is almost non-existent, but fwiw I'll append the code I'm using to my 'answer'. It plays but misbehaves in that the app doesn't exit properly. It could be that it's being inited as per gui but *without* a gui that's the problem – g00se Jul 21 '23 at 09:31
  • [JavaFX creates an application thread for running the application start method, processing input events, and running animation timelines.](https://docs.oracle.com/javase/8/javafx/api/javafx/application/Application.html). Not quite sure if the same thread that runs the start method runs the audio clip or not ... – g00se Jul 21 '23 at 09:35
  • The maven jars are implicitly classified dependencies. By default maven will only require the jar with native code matching the build system. The jars are not portable from windows to Linux. If you want jars as dependencies for different platforms you need to include them as classified dependencies for all target platforms, [like this](https://stackoverflow.com/questions/52653836/maven-shade-javafx-runtime-components-are-missing). Though linking jmods into a target image for a given platform using jlink is probably preferred. – jewelsea Jul 21 '23 at 09:38
  • @g00se media and JavaFX threads are different. See the [Threads section of the JavaFX architecture documentation](https://docs.oracle.com/javase/8/javafx/get-started-tutorial/jfx-architecture.htm#A1107438). – jewelsea Jul 21 '23 at 09:42
  • @g00se The JavaFX runtime has a lifecycle for startup and shutdown documented in the `Application` javadoc. When the JavaFX runtime is shutdown (`Platform.exit()` is one way), it will call stop on the application to release resources, then it will stop the JavaFX application, render and media threads. The code in the question does not do this, hence your shutdown issues. – jewelsea Jul 21 '23 at 09:58
  • 1
    Summary, problems so far: Ran JavaFX from classpath not module path, called JavaFX code from AWT thread, did not initialize the JavaFX runtime, did not shutdown the JavaFX runtime. did not include native components for required target platforms; native components were not placed on the Java library path due to incorrectly packaging and using the JavaFX system, tried to execute jar by double click using obsolete and incompatible Java 8 runtime, used jar built in git workspace that won’t match the target runtime architecture: – jewelsea Jul 21 '23 at 10:09
  • *The code in the question does not do this, hence your shutdown issues.* @jewelsea If I call `Platform.exit()` the app stops without playing the file, which is why it's commented out – g00se Jul 21 '23 at 10:29

2 Answers2

3

This is not an answer but I'm putting this out there to show you what happens for me. In both cases the sound plays and I use your toExternalForm to print to stdout. When running it from the jar:

goose@t410:/tmp/sound$ java -cp /tmp/sound/target/sound-1.0-SNAPSHOT.jar fx.sound.App
jar:file:/tmp/sound/target/sound-1.0-SNAPSHOT.jar!/sound/ambience/testSound.mp3

then with Maven:

goose@t410:/tmp/sound$ mvnt javafx:run
[INFO] Scanning for projects...
[INFO] 
[INFO] -----------------------< com.technojeeves:sound >-----------------------
[INFO] Building sound 1.0-SNAPSHOT
[INFO] --------------------------------[ jar ]---------------------------------
[INFO] 
[INFO] >>> javafx-maven-plugin:0.0.6:run (default-cli) > process-classes @ sound >>>
[INFO] 
[INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ sound ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] Copying 1 resource
[INFO] 
[INFO] --- maven-compiler-plugin:3.8.0:compile (default-compile) @ sound ---
[INFO] Nothing to compile - all classes are up to date
[INFO] 
[INFO] <<< javafx-maven-plugin:0.0.6:run (default-cli) < process-classes @ sound <<<
[INFO] 
[INFO] 
[INFO] --- javafx-maven-plugin:0.0.6:run (default-cli) @ sound ---
[INFO] Toolchain in javafx-maven-plugin null
[WARNING] Module name not found in <mainClass>. Module name will be assumed from module-info.java
file:/tmp/sound/target/classes/sound/ambience/testSound.mp3
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  7.320 s
[INFO] Finished at: 2023-07-20T23:13:32+01:00
[INFO] ------------------------------------------------------------------------
goose@t410:/tmp/sound$ 

Code used to run the clip:

package main;

import javafx.application.Application;
import javafx.application.Platform;
import javafx.stage.Stage;
import javafx.scene.media.AudioClip;

public class Main extends Application {
    @Override
    public void start(Stage stage) {
        AudioClip clip = new AudioClip(Main.class.getResource("/sound/ambience/testSound.mp3").toExternalForm());
        System.out.println(Main.class.getResource("/sound/ambience/testSound.mp3").toExternalForm());
        clip.play();
        //Platform.exit();
    }


    public static void main(String[] args) {
        launch();
    }

}
g00se
  • 3,207
  • 2
  • 5
  • 9
  • Interestingly, after `jlink`, the scheme is `jrt:/…` – trashgod Jul 21 '23 at 01:27
  • 2
    @trashgod [`jrt:/`](https://openjdk.org/jeps/220) scheme is discussed in the section "New URI scheme for naming stored modules, classes, and resources" under "JEP 220: Modular Run-Time Images". – jewelsea Jul 21 '23 at 03:56
  • I get `jar:file:/C:/Users/Kofiy/test.jar!/sound/ambience/testSound.mp3`, what looks pretty much the same except for differences between Windows and Linux. – kofiy Jul 21 '23 at 07:53
  • 2
    That's what you want if running from jar – g00se Jul 21 '23 at 07:56
1

In the end issue was in fact with JavaFX not being initialized.

As a simple test workaround I created very simple JavaFX app that just starts the actual JavaX application.

(Although in the future I will probably rewrite a big piece of the code to avoid this VERY BAD workaround. (But at least it works for now...))

kofiy
  • 31
  • 4