377 lines
13 KiB
Racket
377 lines
13 KiB
Racket
(module libmpg123-ffi racket/base
|
|
|
|
(require ffi/unsafe
|
|
ffi/unsafe/define
|
|
"private/utils.rkt"
|
|
)
|
|
|
|
(provide mpg123-ffi-decoder-handler
|
|
)
|
|
|
|
|
|
(define lib (get-lib '("libmpg123") '("0" #f)))
|
|
(define-ffi-definer define-libmpg123 lib
|
|
#:default-make-fail make-not-available)
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
;; Some MPG123 Constants
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
|
|
(define _size_t _size)
|
|
|
|
(define SEEK_SET 0)
|
|
(define SEEK_CUR 1)
|
|
(define SEEK_END 2)
|
|
|
|
(define _Seek_t
|
|
(_enum '(seek-set = 0
|
|
seek-cur = 1
|
|
seek-end = 2
|
|
)
|
|
)
|
|
)
|
|
|
|
|
|
(define _MPG123_Result
|
|
(_enum '(MPG123_DONE = -12 ;/**< Message: Track ended. Stop decoding. */
|
|
MPG123_NEW_FORMAT = -11 ;/**< Message: Output format will be different on next call.
|
|
; Note that some libmpg123 versions between 1.4.3 and 1.8.0
|
|
; insist on you calling mpg123_getformat() after getting this
|
|
; message code. Newer verisons behave like advertised:
|
|
; You have the chance to call mpg123_getformat(), but you can
|
|
; also just continue decoding and get your data. */
|
|
MPG123_NEED_MORE = -10 ;/**< Message: For feed reader: "Feed me more!" (call
|
|
; mpg123_feed() or mpg123_decode() with some new input data). */
|
|
MPG123_ERR = -1 ;/**< Generic Error */
|
|
MPG123_OK = 0 ;/**< Success */
|
|
MPG123_BAD_OUTFORMAT ;/**< Unable to set up output format! */
|
|
MPG123_BAD_CHANNEL ;/**< Invalid channel number specified. */
|
|
MPG123_BAD_RATE ;/**< Invalid sample rate specified. */
|
|
MPG123_ERR_16TO8TABLE ;/**< Unable to allocate memory for 16 to 8 converter table! */
|
|
MPG123_BAD_PARAM ;/**< Bad parameter id! */
|
|
MPG123_BAD_BUFFER ;/**< Bad buffer given -- invalid pointer or too small size. */
|
|
MPG123_OUT_OF_MEM ;/**< Out of memory -- some malloc() failed. */
|
|
MPG123_NOT_INITIALIZED ;/**< You didn't initialize the library! */
|
|
MPG123_BAD_DECODER ;/**< Invalid decoder choice. */
|
|
MPG123_BAD_HANDLE ;/**< Invalid mpg123 handle. */
|
|
MPG123_NO_BUFFERS ;/**< Unable to initialize frame buffers (out of memory?). */
|
|
MPG123_BAD_RVA ;/**< Invalid RVA mode. */
|
|
MPG123_NO_GAPLESS ;/**< This build doesn't support gapless decoding. */
|
|
MPG123_NO_SPACE ;/**< Not enough buffer space. */
|
|
MPG123_BAD_TYPES ;/**< Incompatible numeric data types. */
|
|
MPG123_BAD_BAND ;/**< Bad equalizer band. */
|
|
MPG123_ERR_NULL ;/**< Null pointer given where valid storage address needed. */
|
|
MPG123_ERR_READER ;/**< Error reading the stream. */
|
|
MPG123_NO_SEEK_FROM_END ;/**< Cannot seek from end (end is not known). */
|
|
MPG123_BAD_WHENCE ;/**< Invalid 'whence' for seek function.*/
|
|
MPG123_NO_TIMEOUT ;/**< Build does not support stream timeouts. */
|
|
MPG123_BAD_FILE ;/**< File access error. */
|
|
MPG123_NO_SEEK ;/**< Seek not supported by stream. */
|
|
MPG123_NO_READER ;/**< No stream opened or no reader callback setup. */
|
|
MPG123_BAD_PARS ;/**< Bad parameter handle. */
|
|
MPG123_BAD_INDEX_PAR ;/**< Bad parameters to MPG123_index() and MPG123_set_index() */
|
|
MPG123_OUT_OF_SYNC ;/**< Lost track in bytestream and did not try to resync. */
|
|
MPG123_RESYNC_FAIL ;/**< Resync failed to find valid MPEG data. */
|
|
MPG123_NO_8BIT ;/**< No 8bit encoding possible. */
|
|
MPG123_BAD_ALIGN ;/**< Stack aligmnent error */
|
|
MPG123_NULL_BUFFER ;/**< NULL input buffer with non-zero size... */
|
|
MPG123_NO_RELSEEK ;/**< Relative seek not possible (screwed up file offset) */
|
|
MPG123_NULL_POINTER ;/**< You gave a null pointer somewhere where you shouldn't have. */
|
|
MPG123_BAD_KEY ;/**< Bad key value given. */
|
|
MPG123_NO_INDEX ;/**< No frame index in this build. */
|
|
MPG123_INDEX_FAIL ;/**< Something with frame index went wrong. */
|
|
MPG123_BAD_DECODER_SETUP ;/**< Something prevents a proper decoder setup */
|
|
MPG123_MISSING_FEATURE ;/**< This feature has not been built into libmpg123. */
|
|
MPG123_BAD_VALUE ;/**< A bad value has been given somewhere. */
|
|
MPG123_LSEEK_FAILED ;/**< Low-level seek failed. */
|
|
MPG123_BAD_CUSTOM_IO ;/**< Custom I/O not prepared. */
|
|
MPG123_LFS_OVERFLOW ;/**< Offset value overflow during translation
|
|
; of large file API calls -- your client program
|
|
; cannot handle that large file. */
|
|
MPG123_INT_OVERFLOW ;/**< Some integer overflow. */
|
|
MPG123_BAD_FLOAT ;/**< Floating-point computations work not as expected. */
|
|
)
|
|
_int
|
|
)
|
|
)
|
|
|
|
(define _mpg123_handle _pointer)
|
|
|
|
|
|
; MPG123_EXPORT int mpg123_init (void)
|
|
; Not relevant anymore
|
|
(define-libmpg123 mpg123_init
|
|
(_fun -> _MPG123_Result))
|
|
|
|
; MPG123_EXPORT mpg123_handle *mpg123_new (const char *decoder, int *error)
|
|
(define-libmpg123 mpg123_new
|
|
(_fun _string/utf-8 (err : (_ptr o _int))
|
|
-> (h : _mpg123_handle)
|
|
-> (values h err)))
|
|
|
|
; MPG123_EXPORT size_t mpg123_outblock ( mpg123_handle * mh )
|
|
(define-libmpg123 mpg123_outblock
|
|
(_fun _mpg123_handle -> _size_t))
|
|
|
|
; MPG123_EXPORT int mpg123_open (mpg123_handle *mh, const char *path)
|
|
(define-libmpg123 mpg123_open
|
|
(_fun _mpg123_handle _string/utf-8 -> _MPG123_Result))
|
|
|
|
; MPG123_EXPORT int mpg123_close (mpg123_handle *mh)
|
|
(define-libmpg123 mpg123_close
|
|
(_fun _mpg123_handle -> _MPG123_Result))
|
|
|
|
; MPG123_EXPORT int mpg123_getformat (mpg123_handle *mh, long *rate, int *channels, int *encoding)
|
|
(define-libmpg123 mpg123_getformat
|
|
(_fun _mpg123_handle
|
|
(rate : (_ptr o _long))
|
|
(channels : (_ptr o _int))
|
|
(encoding : (_ptr o _int))
|
|
-> (r : _MPG123_Result)
|
|
-> (values r rate channels encoding)))
|
|
|
|
; MPG123_EXPORT int mpg123_encsize ( int encoding )
|
|
(define-libmpg123 mpg123_encsize
|
|
(_fun _int -> _int))
|
|
|
|
; MPG123_EXPORT int mpg123_read (mpg123_handle *mh, void *outmemory, size_t outmemsize, size_t *done)
|
|
(define-libmpg123 mpg123_read
|
|
(_fun _mpg123_handle
|
|
_pointer
|
|
_size_t
|
|
(done : (_ptr o _size_t))
|
|
-> (r : _MPG123_Result)
|
|
-> (values r done)))
|
|
|
|
; MPG123_EXPORT int64_t mpg123_tell64 ( mpg123_handle * mh )
|
|
(define-libmpg123 mpg123_tell64
|
|
(_fun _mpg123_handle -> _int64))
|
|
|
|
; MPG123_EXPORT int64_t mpg123_seek64(mpg123_handle * mh, int64_t sampleoff, int whence )
|
|
(define-libmpg123 mpg123_seek64
|
|
(_fun _mpg123_handle _int64 _Seek_t -> _int64))
|
|
|
|
; MPG123_EXPORT int mpg123_scan ( mpg123_handle * mh )
|
|
(define-libmpg123 mpg123_scan
|
|
(_fun _mpg123_handle -> _MPG123_Result))
|
|
|
|
; MPG123_EXPORT off_t mpg123_length ( mpg123_handle * mh )
|
|
(define-libmpg123 mpg123_length64
|
|
(_fun _mpg123_handle -> _int64))
|
|
|
|
; MPG123_EXPORT void mpg123_delete (mpg123_handle *mh)
|
|
(define-libmpg123 mpg123_delete
|
|
(_fun _mpg123_handle -> _void))
|
|
|
|
; MPG123_EXPORT void mpg123_exit (void)
|
|
; Not relevant anymore
|
|
(define-libmpg123 mpg123_exit
|
|
(_fun -> _void))
|
|
|
|
; MPG123_EXPORT const char* mpg123_plain_strerror ( int errcode )
|
|
(define-libmpg123 mpg123_plain_strerror
|
|
(_fun _MPG123_Result -> _string*/utf-8))
|
|
|
|
(define mpg123_int_strerror
|
|
(get-ffi-obj "mpg123_plain_strerror"
|
|
lib
|
|
(_fun _int -> _string*/utf-8)))
|
|
|
|
|
|
|
|
#|
|
|
#include <ao/ao.h>
|
|
#include <mpg123.h>
|
|
|
|
#define BITS 8
|
|
|
|
int main(int argc, char *argv[])
|
|
{
|
|
mpg123_handle *mh;
|
|
unsigned char *buffer;
|
|
size_t buffer_size;
|
|
size_t done;
|
|
int err;
|
|
|
|
int driver;
|
|
ao_device *dev;
|
|
|
|
ao_sample_format format;
|
|
int channels, encoding;
|
|
long rate;
|
|
|
|
if(argc < 2)
|
|
exit(0);
|
|
|
|
/* initializations */
|
|
ao_initialize();
|
|
driver = ao_default_driver_id();
|
|
mpg123_init();
|
|
mh = mpg123_new(NULL, &err);
|
|
buffer_size = mpg123_outblock(mh);
|
|
buffer = (unsigned char*) malloc(buffer_size * sizeof(unsigned char));
|
|
|
|
/* open the file and get the decoding format */
|
|
mpg123_open(mh, argv[1]);
|
|
mpg123_getformat(mh, &rate, &channels, &encoding);
|
|
|
|
/* set the output format and open the output device */
|
|
format.bits = mpg123_encsize(encoding) * BITS;
|
|
format.rate = rate;
|
|
format.channels = channels;
|
|
format.byte_format = AO_FMT_NATIVE;
|
|
format.matrix = 0;
|
|
dev = ao_open_live(driver, &format, NULL);
|
|
|
|
/* decode and play */
|
|
while (mpg123_read(mh, buffer, buffer_size, &done) == MPG123_OK)
|
|
ao_play(dev, buffer, done);
|
|
|
|
/* clean up */
|
|
free(buffer);
|
|
ao_close(dev);
|
|
mpg123_close(mh);
|
|
mpg123_delete(mh);
|
|
mpg123_exit();
|
|
ao_shutdown();
|
|
|
|
return 0;
|
|
}
|
|
|#
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
;; Our interface for decoding to racket
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
|
|
(define BITS 8)
|
|
|
|
(define (mpg123-ffi-decoder-handler)
|
|
|
|
(define mh #f)
|
|
|
|
(define buf-size -1)
|
|
(define buffer #f)
|
|
|
|
(define rate -1)
|
|
(define channels -1)
|
|
(define sample-bits -1)
|
|
(define sample-bytes -1)
|
|
(define pcm-length -1)
|
|
(define encoding -1)
|
|
(define mp3-file "")
|
|
|
|
(define (new)
|
|
(if (eq? mh #f)
|
|
(let-values ([(h err) (mpg123_new #f)])
|
|
(when (eq? h #f)
|
|
(error (format "mpg123_new: ~a" (mpg123_int_strerror err))))
|
|
(set! mh h)
|
|
(set! buf-size (mpg123_outblock mh))
|
|
(set! buffer (malloc buf-size 'atomic-interior ))
|
|
)
|
|
(error "mpg123 handle already initialized, delete it first"))
|
|
#t)
|
|
|
|
(define (delete)
|
|
(if (eq? mh #f)
|
|
(error "mpg123 has already been deleted")
|
|
(begin
|
|
(mpg123_delete mh)
|
|
(set! mh #f)
|
|
(set! buf-size -1)
|
|
(set! buffer #f)
|
|
))
|
|
#t)
|
|
|
|
(define (info)
|
|
(info-sound "file : ~a" mp3-file)
|
|
(info-sound "buf-size : ~a" buf-size)
|
|
(info-sound "channels : ~a" channels)
|
|
(info-sound "sample-bits: ~a" sample-bits)
|
|
(info-sound "rate : ~a" rate)
|
|
(info-sound "encoding : ~a" encoding)
|
|
(info-sound "pcm-length : ~a" pcm-length)
|
|
(info-sound "duration : ~a" (if (= rate -1)
|
|
0
|
|
(exact->inexact
|
|
(/ pcm-length rate))))
|
|
#t
|
|
)
|
|
|
|
(define (init file)
|
|
(let ((r (mpg123_open mh file)))
|
|
(unless (eq? r 'MPG123_OK)
|
|
(error (format "mpg123_open: ~a" (mpg123_plain_strerror r))))
|
|
)
|
|
(let-values ([(fr rate* channels* encoding*) (mpg123_getformat mh)])
|
|
(unless (eq? fr 'MPG123_OK)
|
|
(error (format "mpg123_format: ~a" (mpg123_plain_strerror fr))))
|
|
(set! rate rate*)
|
|
(set! channels channels*)
|
|
(set! encoding encoding*)
|
|
(dbg-sound "mpg123_format: ~a ~a ~a" rate channels encoding)
|
|
(set! sample-bits (* (mpg123_encsize encoding) BITS))
|
|
(set! sample-bytes (/ sample-bits 8)))
|
|
(let ((sr (mpg123_scan mh)))
|
|
(unless (eq? sr 'MPG123_OK)
|
|
(error (format "mpg123_scan: ~a" (mpg123_plain_strerror sr))))
|
|
(set! pcm-length (mpg123_length64 mh)))
|
|
(set! mp3-file (format "~a" file))
|
|
#t)
|
|
|
|
(define (mp3-format cb)
|
|
(cb rate channels sample-bits sample-bytes pcm-length))
|
|
|
|
(define (close)
|
|
(let ((r (mpg123_close mh)))
|
|
(unless (eq? r 'MPG123_OK)
|
|
(error (format "mpg123_close: ~a" (mpg123_plain_strerror r))))
|
|
(set! channels -1)
|
|
(set! pcm-length -1)
|
|
(set! rate -1)
|
|
(set! sample-bits -1)
|
|
(set! sample-bytes -1)
|
|
(set! encoding -1)
|
|
(set! mp3-file "")
|
|
#t))
|
|
|
|
(define (read cb)
|
|
(let-values ([(r done) (mpg123_read mh buffer buf-size)])
|
|
(if (eq? r 'MPG123_DONE)
|
|
(cb 'done -1 buffer done)
|
|
(if (eq? r 'MPG123_OK)
|
|
(let ((pcm-pos (mpg123_tell64 mh)))
|
|
(cb 'data pcm-pos buffer done))
|
|
(error (format "mpg123_read: ~a" (mpg123_plain_strerror r)))
|
|
)
|
|
)
|
|
)
|
|
#t)
|
|
|
|
(define (seek pcm-pos)
|
|
(let ((r (mpg123_seek64 mh pcm-pos 'seek-set)))
|
|
(unless (>= r 0)
|
|
(error (format "mpg123_seek64: ~a" (mpg123_int_strerror r))))
|
|
#t))
|
|
|
|
(define (tell)
|
|
(mpg123_tell64 mh))
|
|
|
|
(λ (cmd . args)
|
|
(cond
|
|
[(eq? cmd 'new) (new)]
|
|
[(eq? cmd 'delete) (delete)]
|
|
[(eq? cmd 'init) (init (car args))]
|
|
[(eq? cmd 'close) (close)]
|
|
[(eq? cmd 'format) (mp3-format (car args))]
|
|
[(eq? cmd 'info) (info)]
|
|
[(eq? cmd 'read) (read (car args))]
|
|
[(eq? cmd 'seek) (seek (car args))]
|
|
[(eq? cmd 'tell) (tell)]
|
|
[(eq? cmd 'file) mp3-file]
|
|
[else (error (format "Unknown command: ~a" cmd))]
|
|
)
|
|
)
|
|
)
|
|
|
|
); end of module |