352 lines
11 KiB
Racket
352 lines
11 KiB
Racket
(module utils racket/base
|
|
|
|
(require racket/path
|
|
racket/file
|
|
racket/runtime-path
|
|
racket/system
|
|
ffi/unsafe
|
|
setup/dirs
|
|
"downloader.rkt"
|
|
simple-log
|
|
)
|
|
|
|
(provide while
|
|
until
|
|
build-lib-path
|
|
get-lib
|
|
linux-lib-versions
|
|
ffmpeg-lib-versions
|
|
do-for
|
|
dbg-sound
|
|
info-sound
|
|
err-sound
|
|
warn-sound
|
|
fatal-sound
|
|
sync-log-sound
|
|
racket-sound-cache-directory
|
|
racket-sound-log-file
|
|
open-racket-sound-log-file
|
|
integer->int-bytes
|
|
int-bytes->integer
|
|
valid-ffmpeg-versions
|
|
make-mutex
|
|
mutex-lock
|
|
mutex-unlock
|
|
with-mutex
|
|
)
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
;; Create log definitions
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
|
|
(sl-def-log racket-sound sound)
|
|
|
|
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
;; Standard cache/log paths
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
|
|
(define (racket-sound-cache-directory)
|
|
(build-path (find-system-path 'cache-dir) "racket-audio"))
|
|
|
|
(define (racket-sound-log-file name)
|
|
(define filename
|
|
(cond [(symbol? name) (format "~a.log" name)]
|
|
[(string? name) name]
|
|
[else (raise-argument-error 'racket-sound-log-file "(or/c symbol? string?)" name)]))
|
|
(build-path (racket-sound-cache-directory) filename))
|
|
|
|
(define (open-racket-sound-log-file path #:exists [exists 'append])
|
|
(define parent (path-only (path->complete-path path)))
|
|
(when parent (make-directory* parent))
|
|
(open-output-file path #:exists exists))
|
|
|
|
|
|
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
;; Mutex definitions
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
|
|
(define-struct mutex
|
|
(thread count mut own) #:mutable)
|
|
|
|
(define make-mutex-struct make-mutex)
|
|
|
|
(set! make-mutex (λ ()
|
|
(make-mutex-struct #f 0 (make-semaphore 1) (make-semaphore 1))))
|
|
|
|
(define (mutex-lock m)
|
|
(semaphore-wait (mutex-own m))
|
|
(if (eq? (mutex-thread m) (current-thread))
|
|
(begin
|
|
(set-mutex-count! m (+ (mutex-count m) 1))
|
|
(semaphore-post (mutex-own m))
|
|
)
|
|
(begin
|
|
(semaphore-post (mutex-own m))
|
|
(semaphore-wait (mutex-mut m))
|
|
(set-mutex-count! m 1)
|
|
(set-mutex-thread! m (current-thread)))
|
|
)
|
|
)
|
|
|
|
(define (mutex-unlock m)
|
|
(semaphore-wait (mutex-own m))
|
|
(let ((count (mutex-count m)))
|
|
(set! count (- count 1))
|
|
(set-mutex-count! m count)
|
|
(if (= count 0)
|
|
(begin
|
|
(set-mutex-thread! m #f)
|
|
(semaphore-post (mutex-own m))
|
|
(semaphore-post (mutex-mut m)))
|
|
(semaphore-post (mutex-own m)))
|
|
)
|
|
)
|
|
|
|
(define-syntax with-mutex
|
|
(syntax-rules ()
|
|
((_ m b1 ...)
|
|
(begin
|
|
(dynamic-wind
|
|
(λ () (mutex-lock m))
|
|
(λ () b1 ...)
|
|
(λ () (mutex-unlock m)))))))
|
|
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
;; Provide some loop constructions
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
|
|
(define-syntax while
|
|
(syntax-rules ()
|
|
((_ cond body ...)
|
|
(letrec ((while-f (lambda (last-result)
|
|
(if cond
|
|
(let ((last-result (begin
|
|
body
|
|
...)))
|
|
(while-f last-result))
|
|
last-result))))
|
|
(while-f #f))
|
|
)
|
|
))
|
|
|
|
(define-syntax until
|
|
(syntax-rules ()
|
|
((_ cond body ...)
|
|
(letrec ((until-f (lambda (last-result)
|
|
(if cond
|
|
last-result
|
|
(let ((last-reult (begin
|
|
body
|
|
...)))
|
|
(until-f last-result))))))
|
|
(until-f #f)))))
|
|
|
|
(define-syntax do-for
|
|
(syntax-rules ()
|
|
((_ (init cond next) body ...)
|
|
(begin
|
|
init
|
|
(letrec ((do-for-f (lamba ()
|
|
(if cond
|
|
(begin
|
|
(begin
|
|
body
|
|
...)
|
|
next
|
|
(do-for-f))))))
|
|
(do-for-f))))))
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
;; Loading libraries
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
|
|
(define valid-ffmpeg-versions
|
|
(make-hash (list (list 'avutil 58 60 "libavutil")
|
|
(list 'avcodec 60 62 "libavcodec")
|
|
(list 'avformat 60 62 "libavformat")
|
|
(list 'swresample 4 6 "libswresample"))))
|
|
|
|
(define (versions-high-to-low from until)
|
|
(let loop ((n from) (acc '(#f)))
|
|
(if (> n until)
|
|
acc
|
|
(loop (+ n 1) (cons (number->string n) acc)))))
|
|
|
|
(define (ffmpeg-lib-versions kind)
|
|
(case (system-type 'os)
|
|
[(linux)
|
|
(let ((v (hash-ref valid-ffmpeg-versions kind)))
|
|
(versions-high-to-low (car v) (cadr v)))]
|
|
[else '(#f)]))
|
|
|
|
(define (linux-lib-versions versions [default '(#f)])
|
|
(case (system-type 'os)
|
|
[(linux) versions]
|
|
[else default]))
|
|
|
|
(define (version-str kind)
|
|
(let ((v (hash-ref valid-ffmpeg-versions kind)))
|
|
(format " - ~a.so.~a - ~a.so.~a\n" (caddr v) (car v) (caddr v) (cadr v))
|
|
)
|
|
)
|
|
|
|
(define (lib-not-found-message orig-libs libs-path)
|
|
(displayln (format "Warning: Cannot find library, tried ~a in ~a" orig-libs libs-path))
|
|
(let ((st (system-type 'os*)))
|
|
(cond ((eq? st 'windows)
|
|
(displayln
|
|
(format
|
|
"\nLibraries for Windows should have been downloaded to\n\n - ~a\n"
|
|
(soundlibs-directory))))
|
|
((eq? st 'linux)
|
|
(displayln
|
|
(string-append
|
|
"Make sure you have installed the following libraries,\n"
|
|
"e.g. on a debian based system with apt:\n"
|
|
"\n"
|
|
" sudo apt install libflac12 libmpg123-0 libao4 \
|
|
"
|
|
" libavcodec60 libavutil58 libswresample4 libavformat60 \
|
|
"
|
|
" libogg0 libopus0 libopusenc0 libopusfile0 libtag1v5
|
|
"
|
|
"
|
|
"
|
|
"For development from source or local FFI rebuilding, install the matching -dev packages,
|
|
"
|
|
"for example libflac-dev, libmpg123-dev, libao-dev, libavcodec-dev,
|
|
"
|
|
"libavutil-dev, libswresample-dev, libavformat-dev, libogg-dev,
|
|
"
|
|
"libopus-dev, libopusenc-dev, libopusfile-dev and libtag1-dev.
|
|
"
|
|
"
|
|
"
|
|
)))
|
|
((eq? st 'macosx)
|
|
(displayln
|
|
(string-append
|
|
"Make sure you have the right libraries installed, using 'homebrew', see https://brew.sh/\n"
|
|
"\n"
|
|
" brew install ffmpeg
|
|
"
|
|
" brew install libao
|
|
"
|
|
" brew install mpg123
|
|
"
|
|
" brew install flac
|
|
"
|
|
" brew install opus
|
|
"
|
|
" brew install libopusenc
|
|
"
|
|
" brew install taglib
|
|
"
|
|
"
|
|
"
|
|
"If your local setup uses ffmpeg-full instead of ffmpeg, install that variant instead.
|
|
"
|
|
"
|
|
"
|
|
)))
|
|
(else
|
|
(displayln
|
|
(string-append
|
|
"Make sure you have the right libraries installed on your system and reachable by racket\n"
|
|
"\n"
|
|
"You need following libraries:\n"
|
|
"\n"
|
|
"- xiph libao (https://xiph.org).\n"
|
|
"- xiph libFLAC (https://xiph.org).\n"
|
|
"- ffmpeg of the right version (https://ffmpeg.org).\n"
|
|
"- libmpg123 (https://mpg123.org).\n"
|
|
"\n")
|
|
))
|
|
)
|
|
(displayln
|
|
(string-append "NB. currently supported major versions for the ffmpeg libraries are:\n"
|
|
"\n"
|
|
(version-str 'avcodec)
|
|
(version-str 'avutil)
|
|
(version-str 'swresample)
|
|
(version-str 'avformat)
|
|
"\n"
|
|
))
|
|
)
|
|
)
|
|
|
|
(define (build-lib-path p)
|
|
(if (eq? (system-type 'os) 'macosx)
|
|
(let ((brew-lib "/opt/homebrew/lib"))
|
|
(cons (soundlibs-directory) (cons brew-lib p)))
|
|
(cons (soundlibs-directory) p)))
|
|
|
|
(define (get-lib* libs-to-try orig-libs versions)
|
|
|
|
(unless (soundlibs-available?)
|
|
(download-soundlibs))
|
|
|
|
(let ((libs-path (build-lib-path (get-lib-search-dirs))))
|
|
(if (null? libs-to-try)
|
|
(begin
|
|
(lib-not-found-message orig-libs libs-path)
|
|
#f)
|
|
(ffi-lib (car libs-to-try) versions
|
|
#:get-lib-dirs (λ () libs-path)
|
|
#:fail (λ ()
|
|
(ffi-lib (car libs-to-try) versions
|
|
#:fail (λ ()
|
|
(get-lib* (cdr libs-to-try) orig-libs versions))))
|
|
)
|
|
)
|
|
)
|
|
)
|
|
|
|
(define (get-lib libs-to-try versions)
|
|
(get-lib* libs-to-try libs-to-try versions))
|
|
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
;; integer->int-bytes and vise versa.
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
|
|
(define-syntax-rule (integer->int-bytes v size signed? big? bs pos)
|
|
(if (= size 3)
|
|
(if big?
|
|
(begin
|
|
(bytes-set! bs pos (bitwise-and (arithmetic-shift v -16) #xff))
|
|
(bytes-set! bs (+ pos 1) (bitwise-and (arithmetic-shift v -8) #xff))
|
|
(bytes-set! bs (+ pos 2) (bitwise-and v #xff)))
|
|
(begin
|
|
(bytes-set! bs pos (bitwise-and v #xff))
|
|
(bytes-set! bs (+ pos 1) (bitwise-and (arithmetic-shift v -8) #xff))
|
|
(bytes-set! bs (+ pos 2) (bitwise-and (arithmetic-shift v -16) #xff))))
|
|
(integer->integer-bytes v size signed? big? bs pos)))
|
|
|
|
(define-syntax-rule (int-bytes->integer bs signed? big? start end)
|
|
(let ([size (- end start)])
|
|
(if (= size 3)
|
|
(let* ([b0 (bytes-ref bs start)]
|
|
[b1 (bytes-ref bs (+ start 1))]
|
|
[b2 (bytes-ref bs (+ start 2))]
|
|
[u (if big?
|
|
(bitwise-ior (arithmetic-shift b0 16)
|
|
(arithmetic-shift b1 8)
|
|
b2)
|
|
(bitwise-ior b0
|
|
(arithmetic-shift b1 8)
|
|
(arithmetic-shift b2 16)))])
|
|
(if (and signed? (not (zero? (bitwise-and u #x800000))))
|
|
(- u #x1000000)
|
|
u))
|
|
(integer-bytes->integer bs signed? big? start end))))
|
|
|
|
|
|
|
|
) ; end of module
|