Stopping a threaded sound loop in Clojure

299 views Asked by At

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)))
1

There are 1 answers

13
Ankur On BEST ANSWER

You can try something like this:

(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)
          stop (fn [] (.stop clip))]
      (.open clip sound-in)
      (.loop clip javax.sound.sampled.Clip/LOOP_CONTINUOUSLY)
       stop)))

(def stop-loop @f)
(stop-loop)