gui/gui-lib/mred/private/wx/win32/sound.rkt
Matthew Flatt edc56ee8de Windows: change play-sound to use MCI
Provided by Eli Barzilay.

This approach is better than a separate process, because creating
too many processes can overwhelm the OS. Also, MCI supports more
sound formats.
2016-01-04 17:41:53 -07:00

65 lines
1.9 KiB
Racket

#lang racket/base
(require ffi/unsafe
ffi/winapi
racket/class
"../../lock.rkt"
"utils.rkt"
"types.rkt"
"const.rkt")
(provide
(protect-out play-sound))
(define BUFFER-SIZE 512)
(define BUFFER-BYTES-SIZE (* 2 BUFFER-SIZE))
(define-winmm mciGetErrorStringW
(_fun _int
[buf : _pointer = (malloc BUFFER-BYTES-SIZE)]
[_int = BUFFER-SIZE]
-> [ret : _bool]
-> (and ret (cast buf _pointer _string/utf-16))))
(define-winmm mciSendStringW
(_fun _string/utf-16 [_pointer = #f] [_int = 0] [_pointer = #f]
-> [ret : _int]
-> (if (zero? ret)
(void)
(error 'mciSendStringW "~a" (mciGetErrorStringW ret)))))
(define (mci-send fmt . args)
(mciSendStringW (apply format fmt args)))
(define-winmm mciSendStringW*
(_fun _string/utf-16
[buf : _pointer = (malloc BUFFER-BYTES-SIZE)]
[_int = BUFFER-SIZE]
[_pointer = #f]
-> [ret : _int]
-> (if (zero? ret)
(cast buf _pointer _string/utf-16)
(error 'mciSendStringW* "~a" (mciGetErrorStringW ret))))
#:c-id mciSendStringW)
(define (mci-send* fmt . args)
(mciSendStringW* (apply format fmt args)))
(define (play-sound file async?)
;; Generated ID is unique enough, because we only
;; instantiate this library in one place:
(define id (gensym 'play))
(define (done msec)
(when msec (sleep (/ msec 1000)))
(mci-send "close ~a" id))
(with-handlers ([void (lambda (e) (done #f) (raise e))])
;; adding "type waveaudio" will make it work only for wavs (no mp3 etc)
(mci-send "open \"~a\" alias ~a" (simplify-path file) id)
(mci-send "set ~a time format milliseconds" id)
(define len (let ([s (mci-send* "status ~a length" id)])
(string->number s)))
(unless len (error 'play "mci did not return a numeric length"))
(mci-send "play ~a" id)
(if async? (thread (lambda () (done (+ len 5000)))) (done len)))
;; Report success, since otherwise we throw an error:
#t)