Created
July 11, 2010 22:42
-
-
Save budu/471910 to your computer and use it in GitHub Desktop.
A simple (headless) MP3 player written in Clojure using the clj-audio library.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(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