Welcome! Please see the About page for a little more info on how this works.

0 votes
ago in Java Interop by

Hi, I've found some weird behaviour when attempting to play a sound using a javax.sound.sampled.SourceDataLine. The following code illustrates the problem:

(ns cljtest.core)
(import '[java.util Random]
        '[javax.sound.sampled AudioSystem AudioFormat])

; create an AudioFormat instance:
(def fmt (AudioFormat. 44100. 16 2 true false))

; create some data representing a second of white noise:
(def data (byte-array 176400))
(.nextBytes (Random.) data)

; define a function that creates a SourceDataLine and uses it to play a sound:
(defn play1 []
  (let [l (AudioSystem/getSourceDataLine fmt)]
    (.open l) (.start l) (.write l data 0 (count data))))

; define a function that creates a SourceDataLine and returns it:
(defn getline []
  (AudioSystem/getSourceDataLine fmt))

; define a function that uses the output of getline to play a sound:
(defn play2 []
  (let [l (getline)]
    (.open l) (.start l) (.write l data 0 (count data))))

Compiling this with Clojure 1.12.0 using Leiningen, play1 does what it's supposed to, but play2 gives me the following error:

Execution error (IllegalArgumentException) at cljtest.core/play2 (core.clj:24).
No matching method write found taking 3 args for class com.sun.media.sound.DirectAudioDevice$DirectSDL

I get this error using both OpenJDK 21.0.5.11-hotspot and Oracle's JDK 23. Also with Clojure 1.11.0 and 1.10.0, but 1.9.0 works fine. Anyone know why this is happening?

1 Answer

+1 vote
ago by
selected ago by
 
Best answer

I suspect the latter case is using reflection (because you've lost the interface return type of getSourceDataLine), and you are thus searching the concrete class for the method, which may not be accessible due to module visibility.

If so, you could try:

(defn getline ^javax.sound.sampled.SourceDataLine []
  (AudioSystem/getSourceDataLine fmt))

That flows the interface type into play2 where it should be able to make the same calculation as in play1.

BTW, you might find doto could simplify that code in the play methods.

ago by
That works, thanks!
...