diff --git a/libmpg123-ffi.rkt b/libmpg123-ffi.rkt new file mode 100644 index 0000000..b31fccb --- /dev/null +++ b/libmpg123-ffi.rkt @@ -0,0 +1,333 @@ +(module libflac-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. */ + ) + ) + ) + + (define _mpg123_handle _pointer) + + + ; MPG123_EXPORT int mpg123_init (void) + (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)) + + +#| +#include +#include + +#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 (mpg123-ffi-decoder-handler) + #t +#| (define write-data '()) + (define meta-data '()) + (define error-no -1) + (define fl #f) + (define flac-file #f) + (define client-data #f) + + (define (write-callback fl frame buffer client-data) + (set! write-data (append write-data (list (cons frame buffer)))) + 0) + + (define (meta-callback fl meta client-data) + (let ((meta-clone (FLAC__metadata_object_clone meta))) + (unless (eq? meta-clone #f) + (set! meta-data (append meta-data (list meta-clone)))))) + + (define (error-callback fl errno client-data) + (set! error-no errno) + ) + + (define (new) + (set! fl (FLAC__stream_decoder_new)) + fl) + + (define (init file) + (let ((r (FLAC__stream_decoder_init_file + fl + file + write-callback + meta-callback + error-callback + client-data))) + (set! flac-file file) + r)) + + (define (process-single) + (FLAC__stream_decoder_process_single fl)) + + (define (int-state) + (FLAC__stream_decoder_get_state fl)) + + (define (state) + (decoder-state (int-state))) + + (define (process-meta-data cb) + (for-each (λ (meta-entry) + (cb meta-entry) + (FLAC__metadata_object_delete meta-entry)) + meta-data) + (set! meta-data '())) + + (define (process-write-data cb) + (for-each (lambda (d) + (cb (car d) (cdr d))) + write-data) + (set! write-data '())) + + (define (buffer->vectorlist buffer channels size) + (letrec ((for-channels + (lambda (channel) + (if (< channel channels) + (letrec ((v (make-vector size 0)) + (p (ptr-ref buffer FLAC__int32-pointer channel)) + (to-vec (lambda (i) + (when (< i size) + (vector-set! v i (ptr-ref p _int32 i)) + (to-vec (+ i 1))))) + ) + (to-vec 0) + (cons v (for-channels (+ channel 1)))) + '()))) + ) + (for-channels 0))) + + (define (seek-to-sample sample) + (FLAC__stream_decoder_seek_absolute fl sample)) + + (lambda (cmd . args) + (cond + [(eq? cmd 'write-data) write-data] + [(eq? cmd 'meta-data) meta-data] + + [(eq? cmd 'new) (new)] + [(eq? cmd 'init) (init (car args))] + [(eq? cmd 'process-single) (process-single)] + [(eq? cmd 'get-buffers) (buffer->vectorlist (car args) (cadr args) (caddr args))] + + [(eq? cmd 'int-state) (int-state)] + [(eq? cmd 'state) (state)] + + [(eq? cmd 'has-write-data?) (not (null? write-data))] + [(eq? cmd 'has-meta-data?) (not (null? meta-data))] + [(eq? cmd 'has-errno?) (not (= error-no -1))] + + [(eq? cmd 'process-meta-data) (process-meta-data (car args))] + [(eq? cmd 'process-write-data) (process-write-data (car args))] + [(eq? cmd 'errno) error-no] + + [(eq? cmd 'seek-to-sample) (seek-to-sample (car args))] + [(eq? cmd 'file) flac-file] + + [else (error (format "unknown command ~a" cmd))] + )) + |# + ) + +); end of module \ No newline at end of file