Files
rktplayer/player.rkt
T
2026-05-18 10:35:14 +02:00

201 lines
5.9 KiB
Racket

#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 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)
(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))))
(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")
)
)
)