Skip to content

Instantly share code, notes, and snippets.

@budu

budu/player.clj

Created Jul 11, 2010
Embed
What would you like to do?
A simple (headless) MP3 player written in Clojure using the clj-audio library.
(ns player
(:use clj-audio.core
clj-audio.sampled)
(:import javax.sound.sampled.SourceDataLine
java.io.File))
;;;; playlist
(def music-file-extensions ["mp3"])
(defn extension [f]
(let [n (.getName f)
i (.lastIndexOf n ".")]
(.substring n (+ i 1))))
(defn music-file? [f]
(when (.isFile f)
(some #(= % (extension f))
music-file-extensions)))
(defn music-files [& paths]
(apply concat
(map #(vec (filter music-file?
(file-seq (File. %))))
paths)))
(defn playlist [songs current action]
(condp = action
:previous (when (> @current 0)
(swap! current dec)
(nth songs @current))
:next (when (< @current (dec (count songs)))
(swap! current inc)
(nth songs @current))
:random (do (swap! current (constantly (rand-int (count songs))))
(nth songs @current))
(nth songs @current)))
(defn make-playlist [paths]
(let [songs (apply music-files (if (coll? paths)
paths
[paths]))
current (atom 0)]
(fn [action] (playlist songs current action))))
;;;; player actions
(def *player-actions* (ref clojure.lang.PersistentQueue/EMPTY))
(defn clear-actions []
(dosync
(ref-set *player-actions*
clojure.lang.PersistentQueue/EMPTY)))
(defn send-action [action & actions]
(dosync
(alter *player-actions* #(apply conj % action actions))))
(defn pop-current-action []
(dosync
(let [action (peek @*player-actions*)]
(when action
(alter *player-actions* pop))
action)))
(defn wait? [action]
(or (= action :pause)
(= action :stop)))
;;;; player
(defn read-file [file]
(try (->stream file)
(catch Exception e
(println "Error reading " file "\n" e))))
(def default-sleep-duration 20)
(defmacro sleep-while [test]
`(while ~test (Thread/sleep default-sleep-duration)))
(defn play_ [source audio-stream mark]
(sleep-while (running? source))
(future (-> audio-stream
(skip @mark)
decode
(play-with source)))
(sleep-while (not (running? source))))
(defn action-loop [source audio-stream]
(loop [action (pop-current-action)]
(when (and action (running? source))
(stop))
(Thread/sleep default-sleep-duration)
(cond (finished? audio-stream) nil
(and action (not (wait? action))) action
:default (recur (pop-current-action)))))
(def default-action :random)
(defn player [playlist]
(println "Starting player...")
(let [source (make-line :source *default-format* (* 4 1024))
mark (ref 0)]
(loop [song (playlist :play)]
(when song
(println "Currently playing:" song)
(if-let [stream (read-file song)]
(let [length (.available stream)
_ (play_ source stream mark)
action (action-loop source stream)]
(dosync
(ref-set mark (if (and (= action :play)
(not (finished? stream)))
(- length (.available stream))
0)))
(when (not= action :close)
(recur (playlist (or action default-action)))))
(recur (playlist default-action)))))
(close source))
(println "Player closed, bye!")
nil)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.