(module libflac-ffi racket/base (require ffi/unsafe ffi/unsafe/define "../utils/utils.rkt" ) (provide flac-ffi-decoder-handler _FLAC__StreamMetadata FLAC__StreamMetadata-type flac-ffi-meta flac-ffi-frame-header FLAC__uint32-pointer FLAC__int32** ) (define lib (get-lib '("libFLAC") '(#f))) (define-ffi-definer define-libflac lib) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Some FLAC Constants ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (define FLAC__MAX_CHANNELS 8) (define FLAC__MAX_FIXED_ORDER 4) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; FLAC Integer types ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (define _uint32_t _uint32) (define _uint64_t _uint64) (define FLAC__uint8 _uint8) (define FLAC__uint16 _uint16) (define FLAC__int64 _int64) (define FLAC__uint64 _uint64) (define FLAC__uint32 _uint32) (define FLAC__int32-pointer (_ptr i _int32)) (define FLAC__int32** (_ptr i (_ptr i _int32))) (define FLAC__uint32-pointer (_ptr i _uint32)) (define FLAC__int64-pointer (_ptr i _int64)) (define FLAC__uint64-pointer (_ptr i _uint64)) (define FLAC__bool _int) (define FLAC__byte _uint8) (define FLAC__byte-pointer (_ptr i FLAC__byte)) (define _uint32_1bit_t _uint32) (define _char _int8) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; FLAC enumerations ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (define _FLAC__ChannelAssignment (_enum '(independent = 0 left-side = 1 right-side = 2 mid-side = 3 ))) (define _FLAC__FrameNumberType (_enum '(frame-number sample-number ))) (define _FLAC__SubframeType (_enum '(constant = 0 verbatim = 1 fixed = 2 lpc = 3 ))) (define _FLAC__MetadataType (_enum '(streaminfo = 0 padding = 1 application = 2 seektable = 3 vorbis-comment = 4 cuesheet = 5 picture = 6 undefined = 7 max--metadata-type-code = 126 ))) (define _FLAC_StreamMetadata_Picture_Type (_enum '(other = 0 file-icon-standard = 1 file-icon = 2 front-cover = 3 back-cover = 4 leaflet-page = 5 media = 6 lead-artist = 7 artist = 8 conductor = 9 band = 10 composer = 11 lyricist = 12 recording-location = 13 during-recording = 14 during-performance = 15 video-screen-capture = 16 fish = 17 illustration = 18 band-logotype = 19 publisher-logotype = 20 undefined ))) ;typedef enum { ; FLAC__STREAM_DECODER_SEARCH_FOR_METADATA = 0, ; FLAC__STREAM_DECODER_READ_METADATA, ; FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC, ; FLAC__STREAM_DECODER_READ_FRAME, ; FLAC__STREAM_DECODER_END_OF_STREAM, ; FLAC__STREAM_DECODER_OGG_ERROR, ; FLAC__STREAM_DECODER_SEEK_ERROR, ; FLAC__STREAM_DECODER_ABORTED, ; FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR, ; FLAC__STREAM_DECODER_UNINITIALIZED, ; FLAC__STREAM_DECODER_END_OF_LINK ;} FLAC__StreamDecoderState; (define _FLAC_StreamDecoderState (_enum '(search-for-metadata = 0 read-metadata search-for-frame-sync read-frames end-of-stream ogg-error seek-error aborted memory-allocation-error uninitialized end-of-link ))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; FLAC Frames ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (define-cstruct _FLAC__FrameHeader ( [blocksize _uint32_t] [sample_rate _uint32_t] [channels _uint32_t] [channel_assignment _FLAC__ChannelAssignment] [bits_per_sample _uint32_t] [number_type _FLAC__FrameNumberType] [number (_union _uint32_t _uint64_t)] [crc FLAC__uint8] )) (define (flac-ffi-frame-header frame) (let* ((hdr (FLAC__Frame-header frame)) (h (make-hash))) (for-each (lambda (e) (hash-set! h (car e) (cdr e))) (list (cons 'blocksize (FLAC__FrameHeader-blocksize hdr)) (cons 'sample-rate (FLAC__FrameHeader-sample_rate hdr)) (cons 'channels (FLAC__FrameHeader-channels hdr)) (cons 'channel-assignment (FLAC__FrameHeader-channel_assignment hdr)) (cons 'bits-per-sample (FLAC__FrameHeader-bits_per_sample hdr)) (cons 'number-type (FLAC__FrameHeader-number_type hdr)) (cons 'number (if (eq? (FLAC__FrameHeader-number_type hdr) 'frame-number) (union-ref (FLAC__FrameHeader-number hdr) 0) (union-ref (FLAC__FrameHeader-number hdr) 1))) (cons 'crc (FLAC__FrameHeader-crc hdr))) ) h)) (define-cstruct _FLAC__FrameFooter ( [crc FLAC__uint16] )) (define-cstruct _FLAC__Subframe_Constant ( [value FLAC__int64] )) (define _FLAC__VerbatimSubframeDataType (_enum '(int32 int64))) (define-cstruct _FLAC__Subframe_Verbatim ( [data (_union FLAC__int32-pointer FLAC__int64-pointer )] [data_type _FLAC__VerbatimSubframeDataType] )) (define FLAC__EntropyCodingMethodType (_enum '(partitioned-rice = 0 partitioned-rice2 = 1))) (define-cstruct _FLAC__EntropyCodingMethod_PartitionedRiceContents ( [parameters FLAC__uint32-pointer] [raw_bits FLAC__uint32-pointer] [capacity_by_order FLAC__uint32-pointer] )) (define-cstruct _FLAC__EntropyCodingMethod_PartitionedRice ( [order _uint32_t] [contents _FLAC__EntropyCodingMethod_PartitionedRiceContents-pointer] )) (define-cstruct _FLAC__EntropyCodingMethod ( [type FLAC__EntropyCodingMethodType] [data (_union _FLAC__EntropyCodingMethod_PartitionedRice)] )) (define-cstruct _FLAC__Subframe_Fixed ( [entropy_coding_method _FLAC__EntropyCodingMethod] [order _uint32_t] [warmup (_array FLAC__int64 FLAC__MAX_FIXED_ORDER)] [residual FLAC__int32-pointer] )) (define-cstruct _FLAC__Subframe_LPC ( [jaja _int] )) (define-cstruct _FLAC__Subframe ( [type _FLAC__SubframeType] [data (_union _FLAC__Subframe_Constant _FLAC__Subframe_Fixed _FLAC__Subframe_LPC _FLAC__Subframe_Verbatim )] [wated_bits _uint32_t] )) (define-cstruct _FLAC__Frame ( [header _FLAC__FrameHeader] [subframes (_array _FLAC__Subframe FLAC__MAX_CHANNELS)] [footer _FLAC__FrameFooter] )) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; FLAC Metadata ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;typedef struct FLAC__StreamMetadata { ; FLAC__MetadataType type; ; FLAC__bool is_last; ; uint32_t length; ; union { ; FLAC__StreamMetadata_StreamInfo stream_info; ; FLAC__StreamMetadata_Padding padding; ; FLAC__StreamMetadata_Application application; ; FLAC__StreamMetadata_SeekTable seek_table; ; FLAC__StreamMetadata_VorbisComment vorbis_comment; ; FLAC__StreamMetadata_CueSheet cue_sheet; ; FLAC__StreamMetadata_Picture picture; ; FLAC__StreamMetadata_Unknown unknown; ; } data; ;} FLAC__StreamMetadata; (define-cstruct _FLAC__StreamMetadata_StreamInfo ( [min_blocksize _uint32_t] [max_blocksize _uint32_t] [min_framesize _uint32_t] [max_framesize _uint32_t] [sample_rate _uint32_t] [channels _uint32_t] [bits_per_sample _uint32_t] [total_samples FLAC__uint64] [md5sum (_array FLAC__byte 16)] )) (define (meta-stream-info si . hash) (let ((h (if (null? hash) (make-hash) (car hash)))) (hash-set! h 'min-blocksize (FLAC__StreamMetadata_StreamInfo-min_blocksize si)) (hash-set! h 'max-blocksize (FLAC__StreamMetadata_StreamInfo-max_blocksize si)) (hash-set! h 'min-framesize (FLAC__StreamMetadata_StreamInfo-min_framesize si)) (hash-set! h 'max-framesize (FLAC__StreamMetadata_StreamInfo-max_framesize si)) (hash-set! h 'sample-rate (FLAC__StreamMetadata_StreamInfo-sample_rate si)) (hash-set! h 'channels (FLAC__StreamMetadata_StreamInfo-channels si)) (hash-set! h 'bits-per-sample (FLAC__StreamMetadata_StreamInfo-bits_per_sample si)) (hash-set! h 'total-samples (FLAC__StreamMetadata_StreamInfo-total_samples si)) h)) (define-cstruct _FLAC__StreamMetadata_Padding ( [dummy _int] )) (define-cstruct _FLAC__StreamMetadata_Application ( [id (_array FLAC__byte 4)] [data FLAC__byte-pointer] )) (define-cstruct _FLAC__StreamMetadata_SeekPoint ( [sample_number FLAC__uint64] [stream_offset FLAC__uint64] [frame_samples _uint32_t] )) (define-cstruct _FLAC__StreamMetadata_SeekTable ( [num_points _uint32_t] [points _FLAC__StreamMetadata_SeekPoint-pointer] )) (define-cstruct _FLAC__StreamMetadata_VorbisComment_Entry ( [length FLAC__uint32] [entry FLAC__byte-pointer] )) (define-cstruct _FLAC__StreamMetadata_VorbisComment ( [vendor_string _FLAC__StreamMetadata_VorbisComment_Entry] [num_comments FLAC__uint32] [comments _FLAC__StreamMetadata_VorbisComment_Entry-pointer] )) (define-cstruct _FLAC__StreamMetadata_CueSheet_Index ( [offset FLAC__uint64] [number FLAC__byte] )) (define-cstruct _FLAC__StreamMetadata_CueSheet_Track ( [offset FLAC__uint64] [number FLAC__byte] [isrc (_array _char 13)] [type _uint32_1bit_t] [pre_emphasis _uint32_1bit_t] [num_indices FLAC__byte] [indices _FLAC__StreamMetadata_CueSheet_Index-pointer] )) (define-cstruct _FLAC__StreamMetadata_CueSheet ( [media_catalog_number (_array _char 129)] [lead_in FLAC__uint64] [is_cd FLAC__bool] [num_tracks _uint32_t] [tracks _FLAC__StreamMetadata_CueSheet_Track-pointer] )) (define-cstruct _FLAC__StreamMetadata_Picture ( [type _FLAC_StreamMetadata_Picture_Type] [mime_type _string/utf-8] [description FLAC__byte-pointer] [width FLAC__uint32] [height FLAC__uint32] [depth FLAC__uint32] [colors FLAC__uint32] [data_length FLAC__uint32] [date FLAC__byte-pointer] )) (define-cstruct _FLAC__StreamMetadata_Unknown ( [data FLAC__byte-pointer] )) (define-cstruct _FLAC__StreamMetadata ( [type _FLAC__MetadataType] [is_last FLAC__bool] [length _uint32_t] [data (_union _FLAC__StreamMetadata_StreamInfo _FLAC__StreamMetadata_Padding _FLAC__StreamMetadata_Application _FLAC__StreamMetadata_SeekTable _FLAC__StreamMetadata_VorbisComment _FLAC__StreamMetadata_CueSheet _FLAC__StreamMetadata_Picture _FLAC__StreamMetadata_Unknown )] )) (define (flac-ffi-meta meta) (let ((type (FLAC__StreamMetadata-type meta)) (h (make-hash))) (cond ([eq? type 'streaminfo] (meta-stream-info (union-ref (FLAC__StreamMetadata-data meta) 0) h)) (else (error (format "Cannot process metadata: ~a" type))) ) h)) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; FLAC Generic Pointer Definitions ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (define _FLAC__StreamDecoder-pointer (_cpointer 'flac-streamdecoder)) (define _FLAC__Data-pointer (_cpointer 'flac-client-data)) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; FLAC Callback function definitions ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;typedef FLAC__StreamDecoderWriteStatus(* FLAC__StreamDecoderWriteCallback) (const FLAC__StreamDecoder *decoder, const FLAC__Frame *frame, const FLAC__int32 *const buffer[], void *client_data) (define _FLAC__StreamDecoderWriteCallback (_fun _FLAC__StreamDecoder-pointer _FLAC__Frame-pointer FLAC__int32** _FLAC__Data-pointer -> _int)) ;typedef void(* FLAC__StreamDecoderMetadataCallback) (const FLAC__StreamDecoder *decoder, const FLAC__StreamMetadata *metadata, void *client_data) (define _FLAC__StreamDecoderMetadataCallback (_fun _FLAC__StreamDecoder-pointer _FLAC__StreamMetadata-pointer _FLAC__Data-pointer -> _void)) (define _FLAC__StreamDecoderErrorCallback (_fun _FLAC__StreamDecoder-pointer _int _FLAC__Data-pointer -> _void)) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Exported FLAC functions ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (define-libflac FLAC__stream_decoder_new (_fun -> _FLAC__StreamDecoder-pointer)) (define-libflac FLAC__stream_decoder_delete (_fun _FLAC__StreamDecoder-pointer -> _void)) (define-libflac FLAC__stream_decoder_get_state (_fun _FLAC__StreamDecoder-pointer -> _int)) (define states (make-hash)) (hash-set! states 0 'search-for-metadata) (hash-set! states 1 'read-metadata) (hash-set! states 2 'search-for-frame-sync) (hash-set! states 3 'read-frames) (hash-set! states 4 'end-of-stream) (hash-set! states 5 'ogg-error) (hash-set! states 6 'seek-error) (hash-set! states 7 'aborted) (hash-set! states 8 'memory-allocation-error) (hash-set! states 9 'uninitialized) (hash-set! states 10 'end-of-link) (define (decoder-state int-st) (hash-ref states int-st #f)) (define-libflac FLAC__stream_decoder_init_file (_fun _FLAC__StreamDecoder-pointer _string/utf-8 _FLAC__StreamDecoderWriteCallback _FLAC__StreamDecoderMetadataCallback _FLAC__StreamDecoderErrorCallback -> _int)) (define-libflac FLAC__stream_decoder_process_single (_fun _FLAC__StreamDecoder-pointer -> _bool)) (define-libflac FLAC__stream_decoder_process_until_end_of_metadata (_fun _FLAC__StreamDecoder-pointer -> _bool)) (define-libflac FLAC__stream_decoder_seek_absolute (_fun _FLAC__StreamDecoder-pointer FLAC__uint64 -> _bool)) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Our interface for decoding to racket ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (define (flac-ffi-decoder-handler) (define write-data '()) (define meta-data '()) (define error-no -1) (define fl #f) (define flac-file #f) (define (write-callback fl frame buffer data) (set! write-data (append write-data (list (cons frame buffer)))) 0) (define (meta-callback fl meta data) (set! meta-data (append meta-data (list meta)))) (define (error-callback fl errno 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))) (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 cb 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