I have a threaded looping sound clip:
(def f
(future
(let [sound-file (java.io.File. "/path/to/file.wav")
sound-in (javax.sound.sampled.AudioSystem/getAudioInputStream sound-file)
format (.getFormat sound-in)
info (javax.sound.sampled.DataLine$Info. javax.sound.sampled.Clip format)
clip (javax.sound.sampled.AudioSystem/getLine info)]
(.open clip sound-in)
(.loop clip javax.sound.sampled.Clip/LOOP_CONTINUOUSLY))))
The problem is that when I try to kill the thread:
(future-cancel f)
it doesn't stop the clip, which plays forever. I found that the only way to stop it was to call (.stop clip)
explicitly. My question: what would be the best/idiomatic way of doing this? I'm pretty new to Clojure, so I only experimented with future
so far, but maybe an agent
would be better suited in this context?
Update: given that the .loop
function is non-blocking (as was discussed below), I simplified my design by getting rid of the initial future
:
(defn play-loop [wav-fn]
(let [sound-file (java.io.File. wav-fn)
sound-in (javax.sound.sampled.AudioSystem/getAudioInputStream sound-file)
format (.getFormat sound-in)
info (javax.sound.sampled.DataLine$Info. javax.sound.sampled.Clip format)
clip (javax.sound.sampled.AudioSystem/getLine info)]
(.open clip sound-in)
(.loop clip javax.sound.sampled.Clip/LOOP_CONTINUOUSLY)
clip))
along with a controlling atom
:
(def ^:dynamic *clip* (atom nil))
with which I start the loop:
(when (nil? @*clip*)
(reset! *clip* (play-loop "/path/to/file.wav")))
and stop it:
(when @*clip*
(future (.stop @*clip*) ; to avoid a slight delay caused by .stop
(reset! *clip* nil)))
You can try something like this: