333 lines
12 KiB
Racket
333 lines
12 KiB
Racket
(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 <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 (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 |