small adjustments, many enhancements to rktplayer

This commit is contained in:
2026-04-16 22:22:25 +02:00
parent 82fa80746c
commit 4bef5cf94c
8 changed files with 492 additions and 49 deletions

218
gui.rkt
View File

@@ -4,6 +4,8 @@
racket/runtime-path racket/runtime-path
racket/gui racket/gui
racket-sprintf racket-sprintf
open-app
xml
"utils.rkt" "utils.rkt"
"music-library.rkt" "music-library.rkt"
"translate.rkt" "translate.rkt"
@@ -24,27 +26,37 @@
(wv-menu 'main-menu (wv-menu 'main-menu
(wv-menu-item 'm-file (tr "File") (wv-menu-item 'm-file (tr "File")
#:submenu #:submenu
(wv-menu (wv-menu-item 'm-select-library-dir (tr "Select Music Library Folder")) (wv-menu
(wv-menu-item 'm-set-lang (tr "Set language")) (wv-menu-item 'm-add-tab (tr "Add Playlist"))
(wv-menu-item 'm-quit (tr "Quit") #:separator #t))) (wv-menu-item 'm-select-library-dir (tr "Select Music Library Folder"))
(wv-menu-item 'm-set-lang (tr "Set language"))
(wv-menu-item 'm-quit (tr "Quit") #:separator #t)))
) )
)) ))
(define rktplayer% (define rktplayer%
(class wv-window% (class wv-window%
(inherit-field settings icon) (inherit-field settings icon)
(super-new (super-new
[html-path "rktplayer.html"] [html-path "rktplayer.html"]
[title "Racket Music Player"] [title "Racket Music Player"]
[icon (build-path rkt-gui-dir "rktplayer.png")] [icon (build-path rkt-gui-dir "rktplayer.png")]
) )
(define initialized (make-semaphore 0))
(define closed #f) (define closed #f)
(define el-seeker #f) (define el-seeker #f)
(define el-library #f) (define el-library #f)
(define el-playlist #f) (define el-playlist #f)
(define el-at #f) (define el-at #f)
(define el-length #f) (define el-length #f)
(define el-rate #f)
(define el-channels #f)
(define el-bits #f)
(define current-tab 0)
(define music-library (define music-library
(let ((path (format "~a" (send settings get 'music-library (find-system-path 'home-dir))))) (let ((path (format "~a" (send settings get 'music-library (find-system-path 'home-dir)))))
@@ -123,7 +135,7 @@
(dbg-rktplayer "Setting album art") (dbg-rktplayer "Setting album art")
(let ((el (send this element 'album-art))) (let ((el (send this element 'album-art)))
(let ((html (format "<img src=\"/get-image?~a&~a\" />" (let ((html (format "<img src=\"/get-image?~a&~a\" />"
(string-replace (format "~a" stored-file) "\\" "/") (format "~a" stored-file)
(current-milliseconds)))) (current-milliseconds))))
(dbg-rktplayer "Html = ~a" html) (dbg-rktplayer "Html = ~a" html)
(send el set-innerHTML! html) (send el set-innerHTML! html)
@@ -148,32 +160,163 @@
) )
(define (update-state st) (define (update-state st)
(dbg-rktplayer "state: ~a" st)
(unless (eq? st state) (unless (eq? st state)
(dbg-rktplayer "Changing to state ~a" st) (dbg-rktplayer "Changing to state ~a" st)
(unless (eq? state #f) ; Prevent setting src twice very fast (unless (eq? state #f) ; Prevent setting src twice very fast
(if (eq? st 'playing) (if (eq? st 'playing)
(set-play-button "buttons/stop.svg") (set-play-button "buttons/pause.svg")
(set-play-button "buttons/play.svg") (set-play-button "buttons/play.svg")
) )
) )
(set! state st) (set! state st)
) )
) )
(define/public (update-tabs)
(displayln (format "playlist = ~a" playlist))
(let* ((tabs (send playlist tab-count))
(html "")
(tab-el (send this element 'tabs))
(idx 0)
)
(while (< idx tabs)
(let ((tab-name (send playlist get-tab-name idx)))
(set! html (string-append
html
(xexpr->string
(list 'span (list (list 'id (format "tab~a" idx))
'(class "tab"))
tab-name))))
)
(set! idx (+ idx 1)))
(send tab-el set-innerHTML! html)
(send this bind! "#tabs > span" 'click
(λ (el evt data)
(let* ((tab-id (send el id))
(tab-idx (string->number (substring (format "~a" tab-id) 3)))
)
(send this set-tab! tab-idx))))
(send this bind! "#tabs > span" 'contextmenu
(λ (el evt data)
(let* ((tab-id (send el id))
(tab-idx (string->number (substring (format "~a" tab-id) 3)))
)
(send this tab-context data tab-id tab-idx))))
(let ((id (string->symbol (format "tab~a" current-tab))))
(let ((el (send this element id)))
(send el add-class! 'current))
)
)
)
(define/public (tab-context evt tab-id tab-idx)
(let ((items (list
(wv-menu-item 'm-tab-rename (tr "Rename playlist") #:callback (λ () (send this rename-tab! tab-id tab-idx)))
(wv-menu-item 'm-tab-drop (tr "Remove playlist") #:callback (λ () (send this drop-tab! tab-id tab-idx)))
(wv-menu-item 'm-tab-add (tr "Add playlist") #:callback (λ () (send this add-tab)))
)
)
)
(let* ((mnu (wv-menu 'tab-popup items))
(clientX (hash-ref evt 'clientX 60))
(clientY (hash-ref evt 'clientY 60))
)
(send this popup-menu! mnu clientX clientY)
)
)
)
(define/public (drop-tab! tab-id tab-idx)
(when (= current-tab tab-idx)
(send this stop))
(send playlist drop-tab! tab-idx)
(send this set-tab! 0)
)
(define/public (rename-tab! tab-id tab-idx)
(let* ((inp-id (string->symbol (format "tab-input~a" tab-idx)))
(tab-el-id (string->symbol (format "tab~a" tab-idx)))
(html (list 'input (list (list 'id (format "~a" inp-id))
(list 'name (format "~a" inp-id))
'(type "text")
(list 'value (send playlist get-tab-name tab-idx))
)))
(tab-el (send this element tab-el-id))
(unbind-events (λ ()
(send this unbind! inp-id 'change)
(send this unbind! inp-id 'blur)))
)
(send tab-el set-innerHTML! html)
(send this unbind! tab-el-id '(click contextmenu))
(send this bind! inp-id 'change
(λ (el evt data)
(let ((tab-name (hash-ref data 'value (send playlist get-tab-name tab-idx))))
(send playlist set-tab-name! tab-idx tab-name)
(unbind-events)
(send this update-tabs))))
(send this bind! inp-id 'blur
(λ (el evt data)
(unbind-events)
(send this update-tabs)))
(let ((inp-el (send this element inp-id)))
(send inp-el focus!))
)
)
(define/public (set-tab! tab-idx)
(send this stop)
(set! current-tab tab-idx)
(send playlist load-tab tab-idx)
(send this update-tabs)
(send this update-playlist)
)
(define/public (add-tab)
(send playlist add-tab!)
(send this update-tabs))
(define (update-audio-info samples rate channels bits)
(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))
)
(define (update-repeat state)
(let ((img (if (eq? state 'no-repeat)
"buttons/repeat-off.svg"
(if (eq? state 'repeat-one)
"buttons/repeat-one.svg"
"buttons/repeat.svg"))))
(let ((el (send this element 'repeat-img)))
(send el set-attr! (list 'src img)))
)
)
(define player (new player% (define player (new player%
[time-updater update-time] [time-updater update-time]
[track-nr-updater update-track-nr] [track-nr-updater update-track-nr]
[state-updater update-state] [state-updater update-state]
[repeat-updater update-repeat]
[audio-info-cb update-audio-info]
[settings settings] [settings settings]
)) ))
(define inner-html-handlers (make-hash)) (define inner-html-handlers (make-hash))
(define/override (page-loaded oke) (define/override (page-loaded oke)
(semaphore-wait initialized)
(semaphore-post initialized)
(super page-loaded oke) (super page-loaded oke)
(ww-connect 'play play-or-stop) (ww-connect 'play play-or-pause)
(ww-connect 'pause pause) (ww-connect 'stop stop)
(ww-connect 'prev previous-track) (ww-connect 'prev previous-track)
(ww-connect 'next next-track) (ww-connect 'next next-track)
(ww-connect 'repeat repeat) (ww-connect 'repeat repeat)
@@ -190,11 +333,19 @@
(set! el-at (send this element 'time)) (set! el-at (send this element 'time))
(set! el-length (send this element 'totaltime)) (set! el-length (send this element 'totaltime))
(set! el-rate (send this element 'rate))
(set! el-bits (send this element 'bits))
(set! el-channels (send this element 'channels))
(send this set-menu! (player-menu)) (send this set-menu! (player-menu))
(send this connect-menu! 'm-quit (λ () (send this quit))) (send this connect-menu! 'm-quit (λ () (send this quit)))
(send this connect-menu! 'm-select-library-dir (λ () (send this select-library))) (send this connect-menu! 'm-select-library-dir (λ () (send this select-library)))
(send this connect-menu! 'm-add-tab (λ () (send this add-tab)))
(displayln (format "page-loaded, playlist = ~a" playlist))
(send this update-tabs)
(send this update-library) (send this update-library)
(send this update-playlist)
) )
@@ -284,9 +435,14 @@
(set! items (append items (set! items (append items
(list (list
(wv-menu-item 'm-add-this (tr "Add this") #:callback (λ () (send this add-path path))))))) (wv-menu-item 'm-add-this (tr "Add this") #:callback (λ () (send this add-path path)))))))
(when (file-exists? (build-path path "booklet.pdf"))
(set! items (append items
(list
(wv-menu-item 'm-booklet (tr "Open booklet") #:callback (λ () (send this open-booklet path))) ;; todo check if pdf file exists
))))
(set! items (append items (set! items (append items
(list (list
(wv-menu-item 'm-booklet (tr "Open booklet") #:callback (λ () (send this open-booklet path))) ;; todo check if pdf file exists
(wv-menu-item 'm-folder (tr "Open containing folder") #:callback (λ () (send this open-folder path))) (wv-menu-item 'm-folder (tr "Open containing folder") #:callback (λ () (send this open-folder path)))
))) )))
(let* ((mnu (wv-menu 'library-popup items)) (let* ((mnu (wv-menu 'library-popup items))
@@ -300,7 +456,7 @@
(define/public (play-path path) (define/public (play-path path)
(dbg-rktplayer "Playing ~a" path) (dbg-rktplayer "Playing ~a" path)
(let ((pl (new playlist% [start-map path]))) (let ((pl (new playlist% [start-map path] [settings (send settings clone 'playlists)] [id current-tab])))
(set! current-track-nr #f) (set! current-track-nr #f)
(send pl read-tracks) (send pl read-tracks)
(set! playlist pl) (set! playlist pl)
@@ -316,21 +472,32 @@
) )
(define/public (open-booklet path) (define/public (open-booklet path)
(dbg-rktplayer "Open booklet ~a" path)) (let ((booklet (build-path path "booklet.pdf")))
(dbg-rktplayer "Open booklet ~a" path)
(open-app booklet)))
(define/public (open-folder path) (define/public (open-folder path)
(dbg-rktplayer "path: ~a" path) (dbg-rktplayer "path: ~a" path)
(let ((folder (if (file-exists? path) (path-only path) path))) (open-file-manager path))
(open-file-manager folder))) ;(let ((folder (if (file-exists? path) (path-only path) path)))
; (open-file-manager folder)))
(define/public (play-or-stop) (define/public (play-or-pause)
(if (eq? state 'playing) (cond
(begin ((eq? state 'playing)
(send player stop) (send player pause!))
(update-time 0.0 0.0)) ((eq? state 'pauzed)
(send player play-track current-track-nr)) (send player play!))
(else
(play-track 0))
)
) )
(define/public (stop)
(dbg-rktplayer "Stop")
(send player stop)
(update-track-nr #f))
(define/public (play-track idx) (define/public (play-track idx)
(send player play-track idx)) (send player play-track idx))
@@ -346,7 +513,14 @@
) )
(define/public (repeat) (define/public (repeat)
(dbg-rktplayer "Repeat") (let ((r (send player get-repeat)))
(let ((nr (cond
((eq? r 'no-repeat) 'repeat-all)
((eq? r 'repeat-all) 'repeat-one)
(else 'no-repeat))))
(send player repeat! nr)
)
)
) )
(define/public (volume) (define/public (volume)
@@ -355,6 +529,7 @@
(define/public (seek-to percentage) (define/public (seek-to percentage)
(dbg-rktplayer "Seeking to percentage: ~a" percentage) (dbg-rktplayer "Seeking to percentage: ~a" percentage)
(send player seek percentage)
) )
(define/public (quit) (define/public (quit)
@@ -381,9 +556,14 @@
) )
(begin (begin
(displayln "Initalizing gui")
(dbg-rktplayer "ICON: ~a" (get-field icon this)) (dbg-rktplayer "ICON: ~a" (get-field icon this))
(let ((lang (send settings get 'lang 'en))) (let ((lang (send settings get 'lang 'en)))
(dbg-rktplayer "RktPlayer started, current language: ~a" lang)) (dbg-rktplayer "RktPlayer started, current language: ~a" lang))
(set! playlist (new playlist% [settings (send settings clone 'playlists)]))
(send player set-list! playlist)
(displayln (format "playlist = ~a" playlist))
(semaphore-post initialized)
) )
) )
) )

View File

@@ -11,8 +11,8 @@
<div class="pane"> <div class="pane">
<div class="buttons"> <div class="buttons">
<button id="prev" class="command"><img id="previous-img" src="buttons/previous.svg" /></button> <button id="prev" class="command"><img id="previous-img" src="buttons/previous.svg" /></button>
<button id="pause" class="command"><img id="pause-img" src="buttons/pause.svg" /></button>
<button id="play" class="command"><img id="play-img" src="buttons/play.svg" /></button> <button id="play" class="command"><img id="play-img" src="buttons/play.svg" /></button>
<button id="stop" class="command"><img id="stop-img" src="buttons/stop.svg" /></button>
<button id="next" class="command"><img id="next-img" src="buttons/next.svg" /></button> <button id="next" class="command"><img id="next-img" src="buttons/next.svg" /></button>
<input type="range" min="0" max="100" value="0" class="h-slider" id="seek" step="0.01" /> <input type="range" min="0" max="100" value="0" class="h-slider" id="seek" step="0.01" />
<div class="text-block"><span id="time" class="time">00:00:00</span></div> <div class="text-block"><span id="time" class="time">00:00:00</span></div>
@@ -34,11 +34,17 @@
</div> </div>
</div> </div>
<div class="music-playing"> <div class="music-playing">
<div id="tabs" class="tabs">
<span id="tab1" class="tab">Default</span>
</div>
<div id="tracks" class="content scrolly"> <div id="tracks" class="content scrolly">
<!-- Music playing --> <!-- Music playing -->
</div> </div>
</div> </div>
</div> </div>
<div class="status">
<span class="info" id="bits"></span><span class="info" id="rate"></span><span class="info" id="channels"></span>
</div>
</div> </div>
</body> </body>
</html> </html>

View File

@@ -6,7 +6,7 @@ body {
} }
.pane { .pane {
height: calc(100vh - 40px - 2em - 10px); height: calc(100vh - 2em - 20px);
width: calc(100% - 10px); width: calc(100% - 10px);
display: flex; display: flex;
flex-direction: column; flex-direction: column;
@@ -31,6 +31,7 @@ button {
button:hover { button:hover {
background: #909090; background: #909090;
transition: all 0.5s ease-in;
} }
@@ -69,7 +70,7 @@ input.h-slider {
} }
.hpane { .hpane {
height: 100%; height: calc(100% - 40px - 2.5em);
width: 100%; width: 100%;
display: flex; display: flex;
} }
@@ -99,12 +100,45 @@ input.h-slider {
height: 100%; height: 100%;
} }
.tabs {
width: 100%;
height: 1.5em;
border-bottom: 1px solid #505050;
}
.tabs span.tab {
width: 50px;
overflow: hide;
text-overflow: hide;
white-space: nowrap;
border: 1px solid #505050;
cursor: default;
padding-left: 5px;
padding-right: 5px;
border-bottom: none;
}
.tabs span.tab.current {
font-weight: bold;
color: #f3961e;
}
.tabs span.tab:hover {
background: #e0e0e0;
color: black;
transition: all 0.5s ease-in;
}
.content { .content {
width: 100%; width: 100%;
height: 100%; height: 100%;
overflow: hidden; overflow: hidden;
} }
.music-playing .content {
height: calc(100% - 1.5em);
}
.scrolly { .scrolly {
overflow-y: auto; overflow-y: auto;
} }
@@ -127,6 +161,7 @@ table.music-library tr td {
table.music-library tr td:hover { table.music-library tr td:hover {
background: #e0e0e0; background: #e0e0e0;
color: black; color: black;
transition: all 0.5s ease-in;
} }
.popup-menu, .popup-submenu { .popup-menu, .popup-submenu {
@@ -195,6 +230,7 @@ table.tracks tr, table.tracks td {
table.tracks tr:hover { table.tracks tr:hover {
background: #e0e0e0; background: #e0e0e0;
color: black; color: black;
transition: all 0.5s ease-in;
} }
table.tracks tr:hover.current { table.tracks tr:hover.current {
@@ -218,4 +254,18 @@ table.tracks tr.current {
justify-content: center; justify-content: center;
} }
div.status {
width: 100%;
height: 2em;
padding-top: 0.25em;
border-left: 1px solid #505050;
}
div.status span.info {
padding-left: 5px;
padding-right: 5px;
border-right: 1px solid #505050;
color: #d0d0d0;
}

View File

@@ -15,6 +15,8 @@
[time-updater (λ (time-s length-s) #t)] [time-updater (λ (time-s length-s) #t)]
[track-nr-updater (λ (nr) #t)] [track-nr-updater (λ (nr) #t)]
[state-updater (λ (state) #t)] [state-updater (λ (state) #t)]
[repeat-updater (λ (state) #t)]
[audio-info-cb (λ (current-sample rate channels bits) #t)]
[buffer-max-seconds 10] [buffer-max-seconds 10]
[buffer-min-seconds 4] [buffer-min-seconds 4]
) )
@@ -27,9 +29,13 @@
(define ct-data #f) (define ct-data #f)
(define closing #f) (define closing #f)
(define pause #f) (define pause #f)
(define repeat-state 'no-repeat)
(define ao-handle #f) (define ao-handle #f)
(define flac-handle #f) (define flac-handle #f)
(define current-music-id -1)
(define current-track-id -1)
(define current-rate 0) (define current-rate 0)
(define current-bits 0) (define current-bits 0)
@@ -53,10 +59,9 @@
current-rate current-bits current-channels ao-handle) current-rate current-bits current-channels ao-handle)
(dbg-rktplayer "Opening ao-handle") (dbg-rktplayer "Opening ao-handle")
(when use-ao (when use-ao
(let ((fmt (ao-mk-format current-bits current-rate current-channels 'big-endian))) (set! ao-handle (ao-open-live current-bits current-rate current-channels 'big-endian))
(set! ao-handle (ao-open-live #f fmt)) (start-play-time-updater)
(start-play-time-updater) )
))
) )
) )
) )
@@ -74,10 +79,15 @@
'done) 'done)
(let ((seconds (ao-at-second ao-handle)) (let ((seconds (ao-at-second ao-handle))
(duration (ao-music-duration ao-handle)) (duration (ao-music-duration ao-handle))
(music-id (ao-at-music-id ao-handle))
) )
(set! current-seconds seconds) (set! current-seconds seconds)
(time-updater current-seconds duration) (time-updater current-seconds duration)
(sleep 0.1) (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))))
(updater) (updater)
) )
@@ -85,6 +95,12 @@
) )
) )
(define (stream-equal? rate bits channels)
(and (= current-rate rate)
(= current-bits bits)
(= current-channels channels)))
(define (flac-play frame buffer buf-len) (define (flac-play frame buffer buf-len)
(unless (eq? state 'quitted) (unless (eq? state 'quitted)
(let* ((sample (hash-ref frame 'number)) (let* ((sample (hash-ref frame 'number))
@@ -96,10 +112,27 @@
(bytes-per-sample-all-channels (* channels bytes-per-sample)) (bytes-per-sample-all-channels (* channels bytes-per-sample))
(duration (hash-ref frame 'duration)) (duration (hash-ref frame '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")
(while (> (ao-bufsize-async ao-handle) 0)
(sleep 0.25)
)
(dbg-rktplayer "Closing ao-handle")
(ao-close ao-handle)
(set! ao-handle #f))
)
(set! current-rate rate) (set! current-rate rate)
(set! current-bits bits-per-sample) (set! current-bits bits-per-sample)
(set! current-channels channels) (set! current-channels channels)
(set! current-length duration) (set! current-length duration)
(when (eq? ao-handle #f)
(audio-info-cb sample current-rate current-channels current-bits)
)
(check-ao-handle) (check-ao-handle)
(when (not (eq? ao-handle #f)) (when (not (eq? ao-handle #f))
@@ -115,19 +148,21 @@
(sleep 0.25)))) (sleep 0.25))))
(when (not (eq? ao-handle #f)) (when (not (eq? ao-handle #f))
(ao-play ao-handle second duration buffer buf-len 'flac) (ao-play ao-handle current-track-id second duration buffer buf-len 'flac)
) )
) )
(when pause (when pause
(dbg-rktplayer "Pauzing now...") (dbg-rktplayer "Pausing now...")
(set-state! 'pauzed)
(ao-pause ao-handle #t) (ao-pause ao-handle #t)
(while (and (not (eq? ao-handle #f)) (while (and (not (eq? ao-handle #f))
(not closing) (not closing)
pause) pause)
(sleep 0.25)) (sleep 0.5))
(ao-pause ao-handle #f) (ao-pause ao-handle #f)
(dbg-rktplayer "Playing on...") (dbg-rktplayer "Playing on...")
(set-state! 'playing)
) )
) )
) )
@@ -144,6 +179,7 @@
(let ((file (send ct-data get-file))) (let ((file (send ct-data get-file)))
(dbg-rktplayer "opening flac handle for file: ~a" file) (dbg-rktplayer "opening flac handle for file: ~a" file)
(set! flac-handle (flac-open file flac-meta flac-play)) (set! flac-handle (flac-open file flac-meta flac-play))
(set! current-track-id (send ct-data get-id))
(dbg-rktplayer "Starting flac-read") (dbg-rktplayer "Starting flac-read")
(let ((result (flac-read flac-handle))) (let ((result (flac-read flac-handle)))
(if (eq? result 'end-of-stream) (if (eq? result 'end-of-stream)
@@ -202,7 +238,13 @@
) )
(define/public (next-track) (define/public (next-track)
(set! track (+ track 1)) (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)) (if (>= track (send pl length))
(begin (begin
(set-state! 'stopped) (set-state! 'stopped)
@@ -210,7 +252,7 @@
(begin (begin
(set! ct-data (send pl track track)) (set! ct-data (send pl track track))
(set-state! 'play) (set-state! 'play)
(track-nr-updater track) ;(track-nr-updater track)
) )
) )
) )
@@ -256,6 +298,24 @@
(set! pause (not pause)) (set! pause (not pause))
(dbg-rktplayer "pauzed: ~a" pause) (dbg-rktplayer "pauzed: ~a" pause)
) )
(define/public (pause!)
(set! pause #t))
(define/public (play!)
(set! pause #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 (seek percentage)
(ao-clear-async ao-handle)
(flac-seek flac-handle percentage))
(define (state-machine) (define (state-machine)
(let ((st (orig-current-seconds)) (let ((st (orig-current-seconds))
@@ -268,15 +328,17 @@
(begin (begin
(cond (cond
((eq? state 'stopped) ((eq? state 'stopped)
(sleep 0.01)) (sleep 0.1))
((eq? state 'play) ((eq? state 'play)
(if (eq? pl #f) (if (eq? pl #f)
(set-state! 'stoppped) (set-state! 'stoppped)
(play-track-worker))) (play-track-worker)))
((eq? state 'playing) ((eq? state 'playing)
(sleep 0.01)) (sleep 0.1))
((eq? state 'track-feeded) ((eq? state 'track-feeded)
(send this next-track)) (send this next-track))
(else
(sleep 0.1))
) )
;(let ((ns (orig-current-seconds))) ;(let ((ns (orig-current-seconds)))
; (when (> (- ns 5) s) ; (when (> (- ns 5) s)
@@ -286,12 +348,14 @@
) )
)) ))
(worker))) (worker)))
(define/public (set-list! playlist)
(stop-and-clear)
(set! pl playlist)
)
(define/public (play playlist) (define/public (play playlist)
(stop-and-clear) (send this set-list! playlist)
;(unless (eq? pl #f) (send pl display-tracks))
(set! pl playlist)
;(unless (eq? pl #f) (send pl display-tracks))
(send this play-track 0) (send this play-track 0)
) )

View File

@@ -5,6 +5,7 @@
racket-sound racket-sound
"utils.rkt" "utils.rkt"
racket-sprintf racket-sprintf
"keystore.rkt"
) )
(provide track% (provide track%
@@ -12,6 +13,10 @@
) )
(define the-displayln displayln) (define the-displayln displayln)
(define list-for-each for-each)
(define list-length length)
(define next-track-id 0)
(define track% (define track%
(class object% (class object%
@@ -31,12 +36,19 @@
album album
length))) length)))
(define my-id (begin
(set! next-track-id (+ next-track-id 1))
(when (> next-track-id 10000000)
(set! next-track-id 1))
next-track-id))
(define/public (get-file) file) (define/public (get-file) file)
(define/public (get-title) title) (define/public (get-title) title)
(define/public (get-artist) artist) (define/public (get-artist) artist)
(define/public (get-album) album) (define/public (get-album) album)
(define/public (get-number) number) (define/public (get-number) number)
(define/public (get-length) length) (define/public (get-length) length)
(define/public (get-id) my-id)
(define (read-tags) (define (read-tags)
(let* ((f (if (path? file) (path->string file) file)) (let* ((f (if (path? file) (path->string file) file))
@@ -148,8 +160,12 @@
(init-field (init-field
[start-map #f] [start-map #f]
[max-tracks 100] [max-tracks 100]
[name "Default"]
[id #f]
[settings #f]
) )
(define store (new keystore% [filename "rktplayer.store"]))
(define tracks '()) (define tracks '())
(define (can-add? file) (define (can-add? file)
@@ -183,16 +199,125 @@
) )
) )
;(define/public (set-name! n)
; (set! name n))
;(define/public (set-id! id*)
; (set! id id*))
;(define/public (get-id)
; id)
;(define/public (get-name)
; name)
(define/public (tabs)
(map (λ (k)
(if (string? k)
(string->symbol k)
k))
(send store get 'tabs '(tabkey-default)))
)
(define/public (tab-count)
(list-length (tabs)))
(define/public (make-tab-key)
(string->symbol
(format "tabkey-~a-~a" (current-milliseconds) (random 10000))))
(define/public (get-tab-name idx)
(let* ((t (tabs))
(entry (list-ref t idx)))
(let ((v (send store get entry (list (format "Playlist-~a" idx) '()))))
(car v))))
(define/public (set-tab-name! idx name)
(let* ((t (tabs))
(entry (list-ref t idx))
(v (send store get entry (list (format "Playlist-~a" idx) '())))
)
(send store set! entry (list name (cadr v)))
)
)
(define/public (tab-id idx)
(let ((t (tabs)))
(list-ref t idx)))
(define/public (tab-index id)
(let ((t (tabs)))
(letrec ((f (λ (t idx)
(if (null? t)
#f
(if (eq? (car t) id)
idx
(f (cdr t) (+ idx 1)))))))
(f t 0))))
(define/public (drop-tab! idx)
(let* ((t (tabs))
(entry (list-ref t idx))
)
(send store set! 'tabs (list-drop! t idx))
(send store drop! entry)
))
(define/public (add-tab!)
(let* ((t (tabs))
(new-entry (send this make-tab-key)))
(send store set! 'tabs (append t (list new-entry)))
)
)
(define/public (save-tab!)
(let* ((entry id)
(idx (send this tab-index entry))
)
(displayln (format "entry id = ~a, ~a" entry idx))
(if (eq? idx #f)
(err-rktplayer "Cannot get tab for id ~a" entry)
(let ((value (list (send this get-tab-name idx)
(map (λ (track)
(format "~a" (send track get-file)))
tracks))))
(send store set! entry value)
)
)
)
)
(define/public (load-tab idx)
(let* ((t (tabs))
(entry (list-ref t idx))
)
(displayln (format "loading ~a" entry))
(set! id entry)
(set! tracks '())
(let ((value (send store get entry (list "Default" '()))))
(set! name (car value))
(list-for-each (λ (file)
(send this add-track file #f))
(cadr value))
)
)
#t
)
(define/public (read-tracks) (define/public (read-tracks)
(set! tracks '()) (set! tracks '())
(read-tracks-internal start-map) (read-tracks-internal start-map)
(send this save-tab!)
) )
(define/public (length) (define/public (length)
(list-len tracks)) (list-len tracks))
(define/public (add-track file) (define/public (add-track file . args)
(add-track* file)) (add-track* file)
(when (null? args)
(send this save-tab!))
)
(define/public (track i) (define/public (track i)
(list-ref tracks i)) (list-ref tracks i))
@@ -244,9 +369,11 @@
(mktable rows 'tracks formatter)))) (mktable rows 'tracks formatter))))
(super-new) (super-new)
(begin (begin
(when (eq? start-map #f) (if (eq? start-map #f)
(error "Initialize playlist% with a starting map")) (send this load-tab 0)
(set! id (send this tab-id id)))
) )
) )
) )

View File

@@ -44,7 +44,7 @@
)) ))
(window (new rktplayer% [wv-context context])) (window (new rktplayer% [wv-context context]))
) )
(send window devtools) ;(send window devtools)
window) window)
) )

View File

@@ -51,3 +51,5 @@
('nl "Kies de map met de Muziek Bibliotheek")) ('nl "Kies de map met de Muziek Bibliotheek"))
(add "Quit" (add "Quit"
('nl "Beëindigen")) ('nl "Beëindigen"))
(add "channels"
('nl "kanalen"))

View File

@@ -19,6 +19,7 @@
warn-rktplayer warn-rktplayer
fatal-rktplayer fatal-rktplayer
(all-from-out simple-log) (all-from-out simple-log)
list-drop!
) )
@@ -50,6 +51,13 @@
) )
) )
(define (list-drop! l idx)
(if (null? l)
l
(if (= idx 0)
(cdr l)
(cons (car l) (list-drop! (cdr l) (- idx 1))))))
(define (make-delayed-reactor seconds closure) (define (make-delayed-reactor seconds closure)
(let* ((last-val #f) (let* ((last-val #f)
(last-time -1) (last-time -1)
@@ -87,11 +95,17 @@
) )
(define (open-file-manager path) (define (open-file-manager path)
(let ((folder (if (path? path) (path->string path) path))) (let ((folder (if (path? path) (path->string path) path))
(case (system-type 'os) (do-open (λ (prg arg)
[(windows) (process (string-append "explorer.exe " folder))] (let ((exe (find-executable-path prg)))
[(macosx) (process (string-append "open " folder))] (dbg-rktplayer "(process* ~a ~a)" exe arg)
[else (process (string-append "xdg-open " folder))])) (process* exe arg))))
)
(dbg-rktplayer "open-file-manager ~a" folder)
(case (system-type 'os)
[(windows) (do-open "explorer.exe" folder)]
[(macosx) (do-open "open" folder)]
[else (do-open "xdg-open" folder)]))
) )
(define (basedir file) (define (basedir file)