diff --git a/gui.rkt b/gui.rkt index fe66b93..f820642 100644 --- a/gui.rkt +++ b/gui.rkt @@ -184,7 +184,7 @@ ((eq? st 'stopped) (set-play-button "buttons/play.svg") (send el set-innerHTML! '(span (tr "stopped")))) - ((eq? st 'pauzed) + ((eq? st 'paused) (set-play-button "buttons/play.svg") (send el set-innerHTML! '(span ((class "blink")) (tr "paused")))) (else @@ -308,7 +308,7 @@ (send playlist add-tab!) (send this update-tabs)) - (define (update-audio-info samples rate channels bits audio-format) + (define (update-audio-info rate channels bits audio-format) (send el-bits set-innerHTML! (format "~a ~a" bits (tr "bits"))) (send el-channels set-innerHTML! (format "~a ~a" channels (tr "channels"))) (send el-rate set-innerHTML! (format "~a Hz" rate)) @@ -579,7 +579,7 @@ (cond ((eq? state 'playing) (send player pause!)) - ((eq? state 'pauzed) + ((eq? state 'paused) (send player play!)) (else (play-track 0)) diff --git a/music-library.rkt b/music-library.rkt index e6b6553..03af7b5 100644 --- a/music-library.rkt +++ b/music-library.rkt @@ -16,7 +16,7 @@ (not (string-prefix? name "."))) (if (eq? type 'file) (let* ((fn (string-downcase (format "~a" f))) - (exts (audio-supported-extensions))) + (exts (audio-known-exts?))) (let ((l (filter (λ (e) (string-suffix? fn (string-append "." e))) exts))) (not (null? l)))) #f)))) diff --git a/player.rkt b/player.rkt index f741d55..b700a3f 100644 --- a/player.rkt +++ b/player.rkt @@ -3,12 +3,11 @@ (require racket/class racket-audio "utils.rkt" + lru-cache ) (provide player%) -(define orig-current-seconds current-seconds) - (define player% (class object% (init-field [settings #f] @@ -20,395 +19,182 @@ [buffer-max-seconds 10] [buffer-min-seconds 4] ) - (define use-ao #t) + + (define player #f) + (define playlist #f) + (define state 'stopped) + (define repeat 'no-repeat) + + (define full-state (make-hash)) + (define music-id -1) + (define track-cache (make-lru 10 + #:cmp (λ (a b) (= (car a) (car b))))) + + + (define (music-id->track-nr id) + (let ((item (lru-use track-cache (list music-id) #f))) + (if (eq? item #f) + #f + (cadr item)))) + + (define (register-music-id&track-nr id track-nr) + (lru-add! track-cache (list id track-nr))) + + (define (clear-music-ids!) + (lru-clear track-cache)) + - (define pl #f) - (define state 'stopped) - (define track -1) - (define current-track -1) - (define ct-data #f) - (define closing #f) - (define pause #f) - (define stop-in-pause 'nil) - (define repeat-state 'no-repeat) - (define volume (send settings get 'volume 100.0)) - - (define ao-handle #f) - (define audio-handle #f) - - (define current-music-id -1) - (define current-track-id -1) - - (define current-rate 0) - (define current-bits 0) - (define current-channels 0) - (define current-audio-format 'none) - - (define current-length 0) - (define current-seconds 0) - - (define repeat 'no-repeat) ;; no-repeat, repeat-1, repeat-all - - (define play-time-updater-state 'stopped) - - (define (set-state! st) - (set! state st) - (state-updater st)) - - (define (check-ao-handle) - (when (eq? ao-handle #f) - (dbg-rktplayer "ao-handle equals #f") - (unless (or (= current-rate 0) (= current-bits 0) (= current-channels 0)) - (dbg-rktplayer "current-rate = ~a, current-bits = ~a, current-channels = ~a, ao-handle = ~a" - current-rate current-bits current-channels ao-handle) - (dbg-rktplayer "Opening ao-handle") - (when use-ao - (set! ao-handle (ao-open-live current-bits current-rate current-channels 'native-endian)) - (start-play-time-updater) - ) + (define (audio-state-cb handle st*) + (set! full-state st*) + (let ((st (audio-state player))) + (when (or (eq? st 'paused) (eq? st 'playing)) + (time-updater (audio-at-second player) + (audio-duration player)) + (when (not (= music-id (audio-music-id player))) + (set! music-id (audio-music-id player)) + (let ((track-nr (music-id->track-nr music-id))) + (if (eq? track-nr #f) + (error "Unexpected: no track-nr for given music-id") + (track-nr-updater track-nr)))) ) - ) - (when (not (= (ao-volume ao-handle) volume)) - (ao-set-volume! ao-handle volume)) - ) - - (define (start-play-time-updater) - (when (eq? play-time-updater-state 'stopped) - (set! play-time-updater-state 'updating) - (dbg-rktplayer "Starting play-time-updater") - (thread (λ () - (define (updater) - (if (or (eq? ao-handle #f) closing) - (begin - (set! play-time-updater-state 'stopped) - (dbg-rktplayer "Terminating play-time-updater") - 'done) - (let ((seconds (ao-at-second ao-handle)) - (duration (ao-music-duration ao-handle)) - (music-id (ao-at-music-id ao-handle)) - ) - (set! current-seconds seconds) - (time-updater current-seconds duration) - (unless (= music-id current-music-id) - (dbg-rktplayer "a ~a ~a ~a" music-id current-track-id seconds) - (set! current-music-id music-id) - (track-nr-updater track)) - (sleep 0.2) - (updater)))) - (updater) - ) - ) + (state-updater st) + (repeat-updater repeat) + (audio-info-cb (audio-rate player) (audio-channels player) + (audio-bits player) (audio-decoder player)) ) ) + (define (on-eof-stream-cb handle) + (let ((track-nr (music-id->track-nr music-id))) + (send this next))) - (define (stream-equal? rate bits channels) - (and (= current-rate rate) - (= current-bits bits) - (= current-channels channels))) - - (define (audio-play type ao-type handle buf-info buffer buf-len) - (unless (eq? state 'quitted) - (let* ((sample (hash-ref buf-info 'sample)) - (rate (hash-ref buf-info 'sample-rate)) - (second (/ (* sample 1.0) (* rate 1.0))) - (bits-per-sample (hash-ref buf-info 'bits-per-sample)) - (bytes-per-sample (/ bits-per-sample 8)) - (channels (hash-ref buf-info 'channels)) - (bytes-per-sample-all-channels (* channels bytes-per-sample)) - (duration (hash-ref buf-info 'duration)) - ) - - (unless (stream-equal? rate bits-per-sample channels) - (dbg-rktplayer "Stream has changed to ~a ~a ~a" rate bits-per-sample channels) - (unless (eq? ao-handle #f) - (dbg-rktplayer "Waiting for play buffer to reach empty state, buf-size = ~a" (ao-bufsize-async ao-handle)) - (while (> (ao-bufsize-async ao-handle) 0) - (dbg-rktplayer "buffer size: ~a" (ao-bufsize-async ao-handle)) - (sleep 1.0) ;0.25) - ) - (dbg-rktplayer "buffer size: ~a" (ao-bufsize-async ao-handle)) - (dbg-rktplayer "Empty state reached") - (dbg-rktplayer "Closing ao-handle") - (ao-close ao-handle) - (set! ao-handle #f)) - ) - - (set! current-rate rate) - (set! current-bits bits-per-sample) - (set! current-channels channels) - (set! current-length duration) - - (when (eq? ao-handle #f) - (audio-info-cb sample current-rate current-channels current-bits current-audio-format) - ) - - (check-ao-handle) - (when (not (eq? ao-handle #f)) - (let ((buf-seconds-left (λ () (exact->inexact - (/ (ao-bufsize-async ao-handle) - bytes-per-sample-all-channels - rate))))) - (when (> (buf-seconds-left) buffer-max-seconds) - (dbg-rktplayer "waiting for buffer to get below ~as" buffer-min-seconds) - (while (and (not (eq? ao-handle #f)) - (not closing) - (not pause) - (> (buf-seconds-left) buffer-min-seconds)) - (sleep 0.25)) - (dbg-rktplayer "under ~a seconds" buffer-min-seconds))) - - (when (not (eq? ao-handle #f)) - (ao-play ao-handle current-track-id second duration buffer buf-len ao-type) - ) - ) - - (when pause - (dbg-rktplayer "Pausing now...") - (set-state! 'pauzed) - (ao-pause ao-handle #t) - (while (and (not (eq? ao-handle #f)) - (not (eq? stop-in-pause 'stop-request)) - (not closing) - pause) - (sleep 0.5)) - (if (eq? stop-in-pause 'stop-request) - (set! stop-in-pause 'stop-ack) - (begin - (ao-pause ao-handle #f) - (dbg-rktplayer "Playing on...") - (set-state! 'playing)) - ) - ) - ) - ) - ) - - (define (audio-meta type ao-type handle meta) - (set! current-audio-format type) - (dbg-rktplayer "type: ~a" type) - (dbg-rktplayer "ao-type: ~a" ao-type) - (dbg-rktplayer "meta: ~a" meta)) - - (define (play-track-worker) - (thread - (λ () - (if (eq? ct-data #f) - 'no-track-data - (let ((file (send ct-data get-file))) - (dbg-rktplayer "opening audios handle for file: ~a" file) - (set! audio-handle (audio-open file audio-meta audio-play)) - (set! current-track-id (send ct-data get-id)) - (dbg-rktplayer "Starting audio-read") - (audio-read audio-handle) - (unless (eq? state 'stopped) - (set-state! 'track-feeded) - (dbg-rktplayer "Audio read done") - ) - 'worker-done - ) - ) - ) - ) - (set-state! 'playing) - 'playing - ) - - (define (close-player*) - (dbg-rktplayer "Closing audio handle") - - (set! closing #t) - - (unless (eq? audio-handle #f) - (audio-stop audio-handle) - (set! audio-handle #f)) - - (set! current-rate 0) - (set! current-channels 0) - (set! current-bits 0) - (set! ct-data #f) - - (unless (eq? ao-handle #f) - (let ((h ao-handle)) - (dbg-rktplayer "closing ao-handle") - (set! ao-handle #f) - (dbg-rktplayer "ao-handle = ~a" h) - (ao-close h) - )) - (dbg-rktplayer "close-player*: ao-handle = ~a" ao-handle) - (dbg-rktplayer "Waiting for updater to stop") - (while (eq? play-time-updater-state 'updating) - (dbg-rktplayer "close-player*: ao-handle = ~a" ao-handle) - (sleep 0.1)) - (dbg-rktplayer "resetting tracks") - (set! track -1) - (set! current-track -1) - - (set! closing #f) - (dbg-rktplayer "done close-player*") - ) - - (define (quit-player) - (close-player*) - (set-state! 'quitted) - ) - - (define (stop-and-clear) - (info-rktplayer "STOP AND CLEAR") - (when pause - (set! stop-in-pause 'stop-request) - (set! pause #f) - (while (not (eq? stop-in-pause 'stop-ack)) - (sleep 0.2))) - (set-state! 'stopped) - (info-rktplayer "calling close-player*") - (close-player*) - ) - - (define/public (next-track) - (unless (eq? repeat-state 'repeat-one) - (set! track (+ track 1))) - - (when (eq? repeat-state 'repeat-all) - (when (>= track (send pl length)) - (set! track 0))) - - (if (>= track (send pl length)) - (begin - (set-state! 'stopped) - (track-nr-updater #f)) - (begin - (set! ct-data (send pl track track)) - (set-state! 'play) - ;(track-nr-updater track) - ) - ) - ) - - (define/public (play-track i) - (unless (= (send pl length) 0) - (dbg-rktplayer "play-track ~a" i) - (set! state 'stopped) - (close-player*) - (dbg-rktplayer "Player closed") - (set! track i) - (set! ct-data (send pl track i)) - (set-state! 'play) - (dbg-rktplayer "Set state to 'play, updating to track ~a" track) - (track-nr-updater track) - (dbg-rktplayer "track-nr-updater called") - ) - ) - - (define/public (stop) - (stop-and-clear) - ) - - (define/public (set-volume! percentage) - (set! volume percentage) - (send settings set! 'volume percentage) - (unless (eq? ao-handle #f) - (ao-set-volume! ao-handle volume)) - ) + (define (check-player) + (when (eq? player #f) + (set! player (make-audio-player audio-state-cb on-eof-stream-cb)))) (define/public (get-volume) - volume) + (check-player) + (audio-volume player)) + + (define/public (set-volume! percentage) + (check-player) + (audio-volume! player percentage)) + + (define/public (set-list! playlist*) + ;; if the player exists and is playing, stop it. + (unless (eq? player #f) + (audio-stop! player)) + ;; Set the playlist to the new one. + (set! playlist playlist*) + ;; reset music-id to -1, because the playlist has been reset. + (set! music-id -1) + ;; clear lru cache, because the playlist has been reset. + (clear-music-ids!) + ) + + (define/public (play playlist*) + (check-player) + (set-list! playlist*) + (send this play-track 0)) + + (define/public (play-track nr) + (check-player) + (when (and (>= nr 0) (< nr (send playlist length))) + (let ((track (send playlist track nr))) + (let ((id (audio-play! player (send track get-file)))) + (register-music-id&track-nr id nr))))) (define/public (next) - (if (= (send pl length) 0) - #f - (let ((idx track)) - (set! idx (+ idx 1)) - (when (>= idx (send pl length)) - (set! idx 0)) - (send this play-track idx)) - )) + (check-player) + (if (= music-id -1) + (warn-rktplayer "No music-id set (yet), so can't play anything next") + (let ((track-nr (music-id->track-nr music-id))) + (if (eq? track-nr #f) + (error "Unexpected: no track-nr for given music-id") + (begin + (cond + ((eq? repeat 'repeat-one) (play-track track-nr)) + ((eq? repeat 'repeat-all) + (set! track-nr (+ track-nr 1)) + (when (>= track-nr (send playlist length)) + (set! track-nr 0)) + (play-track track-nr)) + (else + (set! track-nr (+ track-nr 1)) + (if (>= track-nr (send playlist length)) + (stop) + (play-track track-nr))) + ) + ) + ) + ) + ) + ) (define/public (previous) - (if (= (send pl length) 0) - #f - (let ((idx track)) - (set! idx (- idx 1)) - (when (< idx 0) - (set! idx (- (send pl length) 1))) - (send this play-track idx) - ))) - - (define/public (pause-unpause) - (set! pause (not pause)) - (dbg-rktplayer "pauzed: ~a" pause) + (check-player) + (if (= music-id -1) + (warn-rktplayer "No music-id set (yet), so can't play anything previous") + (let ((track-nr (music-id->track-nr music-id))) + (if (eq? track-nr #f) + (error "Unexpected: no track-nr for given music-id") + (begin + (cond + ((eq? repeat 'repeat-one) (play-track track-nr)) + ((eq? repeat 'repeat-all) + (set! track-nr (- track-nr 1)) + (when (< track-nr 0) + (set! track-nr (- (send playlist length) 1))) + (play-track track-nr)) + (else + (set! track-nr (- track-nr 1)) + (when (< track-nr 0) (set! track-nr 0)) + (play-track track-nr)) + ) + ) + ) + ) + ) ) (define/public (pause!) - (set! pause #t)) + (check-player) + (audio-pause! player #t)) (define/public (play!) - (set! pause #f)) + (check-player) + (audio-pause! player #f)) - (define/public (get-repeat) - repeat-state) - - (define/public (repeat! state) ; no-repeat, repeat-all, repeat-one - (set! repeat-state state) - (repeat-updater state) - ) + (define/public (pause-unpause) + (check-player) + (if (audio-paused? player) + (send this pause!) + (send this play!))) + + (define/public (stop) + (check-player) + (audio-stop! player)) (define/public (seek percentage) - (ao-clear-async ao-handle) - (audio-seek audio-handle percentage)) + (check-player) + (audio-seek! player percentage)) + + (define/public (get-repeat) + (check-player) + repeat) + + (define/public (repeat! r) + (check-player) + (set! repeat r)) - (define (state-machine) - (let ((st (orig-current-seconds)) - (s (orig-current-seconds))) - (define (worker) - (if (eq? state 'quit) - (begin - (quit-player) - 'done) - (begin - (cond - ((eq? state 'stopped) - (sleep 0.1)) - ((eq? state 'play) - (if (eq? pl #f) - (set-state! 'stoppped) - (play-track-worker))) - ((eq? state 'playing) - (sleep 0.1)) - ((eq? state 'track-feeded) - (send this next-track)) - (else - (sleep 0.1)) - ) - ;(let ((ns (orig-current-seconds))) - ; (when (> (- ns 5) s) - ; (displayln (format "state-machine: ~a" (- ns st))) - ; (set! s ns))) - (worker) - ) - )) - (worker))) - - (define/public (set-list! playlist) - (stop-and-clear) - (set! pl playlist) - ) - - (define/public (play playlist) - (send this set-list! playlist) - (send this play-track 0) - ) - (define/public (quit) - (set-state! 'quit) - (while (not (eq? state 'quitted)) - (sleep 0.1)) - ) - + (unless (eq? player #f) + (audio-quit! player))) + (super-new) (begin - (thread (λ () (state-machine))) + (dbg-rktplayer "player% initialized") ) ) ) diff --git a/rktplayer.rkt b/rktplayer.rkt index 50961f1..3d5bfe5 100644 --- a/rktplayer.rkt +++ b/rktplayer.rkt @@ -59,6 +59,38 @@ ) (set! rktplayer-window window) (set! rktplayer-tray tray) + (thread (λ () + (sleep 5) + (let ((prg (string-append "let f_evt_info = window.rkt_event_info;\n" + "window.rkt_event_info = function(e, id, evt) {\n" + " if (evt.dataTransfer) {\n" + " for(const item of evt.dataTransfer.items) {\n" + " if (item.kind == 'file') {\n" + " console.log(item.getAsFile());\n" + " }\n" + " }\n" + " }\n" + " return f_evt_info(e, id, evt);\n" + "}; return 42;"))) + + + #|(js (let* ((f_evt_info window.rkt_event_info)) + (send console log "Setting new window.rkt_event_info") + (set! window.rkt_event_info + (λ (e id evt) + (if evt.dataTransfer + (let* ((items evt.dataTransfer.items) + (fitems (send items filter (λ (item) + (return (== item.kind "file"))))) + ) + (send fitems forEach (λ (item) + (console.log item))) + ) + 42) + (return (f_evt_info e id evt)))) + (return 42)))))|# + (displayln prg) + (displayln (send window call-js prg))))) (when (or (null? no-exit) (not (eq? (car no-exit) #t))) (webview-wait-for-quit)