#lang racket (require racket/class racket-audio "utils.rkt" lru-cache ) (provide player%) (define player% (class object% (init-field [settings #f] [time-updater (λ (time-s length-s) #t)] [track-nr-updater (λ (nr) #t)] [state-updater (λ (state) #t)] [repeat-updater (λ (state) #t)] [audio-info-cb (λ (current-sample rate channels bits kind) #t)] [buffer-max-seconds 10] [buffer-min-seconds 4] ) (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 (audio-state-cb handle player-state 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)))) ) (state-updater st) (repeat-updater repeat) (if (or (eq? player-state 'quit) (eq? player-state 'stopped)) (audio-info-cb 0 0 0 'none) (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 (check-player) (when (eq? player #f) (set! player (make-audio-player audio-state-cb on-eof-stream-cb)) (audio-ao-buf-ms! player 500) (audio-buf-seconds! player buffer-min-seconds buffer-max-seconds) )) (define/public (get-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) (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) (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!) (check-player) (audio-pause! player #t)) (define/public (play!) (check-player) (audio-pause! player #f)) (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) (check-player) (audio-seek! player percentage)) (define/public (get-repeat) (check-player) repeat) (define/public (repeat! r) (check-player) (set! repeat r)) (define/public (quit) (unless (eq? player #f) (audio-quit! player))) (super-new) (begin (dbg-rktplayer "player% initialized") ) ) )