From eb6b7e9c10fbb7d448f5c4fc49d45b407adcdbec Mon Sep 17 00:00:00 2001 From: Steve Ayerhart Date: Sun, 6 Jun 2021 12:53:59 -0400 Subject: [PATCH] add ffi and metadata --- flac/ffi/ffi.factor | 147 ++++++++++++++++++++++++++++++++++ flac/metadata/metadata.factor | 140 ++++++++++++++++++++++++++++++++ 2 files changed, 287 insertions(+) create mode 100644 flac/ffi/ffi.factor create mode 100644 flac/metadata/metadata.factor diff --git a/flac/ffi/ffi.factor b/flac/ffi/ffi.factor new file mode 100644 index 0000000..61a668a --- /dev/null +++ b/flac/ffi/ffi.factor @@ -0,0 +1,147 @@ +! Copyright (C) 2020 . +! See http://factorcode.org/license.txt for BSD license. + +USING: system combinators alien alien.libraries alien.syntax alien.c-types classes.struct ; +IN: flac.ffi + +<< +"avcodec" { + { [ os unix? ] [ "libFLAC.so" ] } +} cond cdecl add-library +>> + +TYPEDEF: int FLAC__bool +TYPEDEF: int16_t FLAC__int16 +TYPEDEF: int32_t FLAC__int32 +TYPEDEF: int64_t FLAC__int64 +TYPEDEF: uint FLAC__uint8 +TYPEDEF: uint16_t FLAC__uint16 +TYPEDEF: uint32_t FLAC__uint32 +TYPEDEF: uint64_t FLAC__uint64 +TYPEDEF: uint unsigned +TYPEDEF: uint8_t FLAC__byte + +ENUM: FLAC__MetadataType + FLAC__METADATA_TYPE_STREAMINFO + FLAC__METADATA_TYPE_PADDING + FLAC__METADATA_TYPE_APPLICATION + FLAC__METADATA_TYPE_SEEKTABLE + FLAC__METADATA_TYPE_VORBIS_COMMENT + FLAC__METADATA_TYPE_CUESHEET + FLAC__METADATA_TYPE_PICTURE + FLAC__METADATA_TYPE_UNDEFINED + FLAC__METADATA_TYPE_TYPE ; + +STRUCT: FLAC__StreamMetadata_StreamInfo + { min_blocksize unsigned } + { max_blocksize unsigned } + { min_framesize unsigned } + { max_framesize unsigned } + { sample_rate unsigned } + { channels unsigned } + { bits_per_sample unsigned } + { total_samples FLAC__uint64 } + { md5sum FLAC__byte[16] } ; + +STRUCT: FLAC__StreamMetadata_Padding + { dummy int } ; + +STRUCT: FLAC__StreamMetadata_Application + { id FLAC__byte[4] } + { data FLAC__byte* } ; + +STRUCT: FLAC__StreamMetadata_SeekPoint + { sample_number FLAC__uint64 } + { stream_offset FLAC__uint64 } + { frame_samples unsigned } ; + +STRUCT: FLAC__StreamMetadata_SeekTable + { num_points unsigned } + { points FLAC__StreamMetadata_SeekPoint* } ; + +STRUCT: FLAC__StreamMetadata_VorbisComment_Entry + { length FLAC__uint32 } + { entry FLAC__byte* } ; + +STRUCT: FLAC__StreamMetadata_VorbisComment + { vendor_string FLAC__StreamMetadata_VorbisComment_Entry } + { num_comments FLAC__uint32 } + { comments FLAC__StreamMetadata_VorbisComment_Entry* } ; + +STRUCT: FLAC__StreamMetadata_CueSheet_Index + { offset FLAC__uint64 } + { number FLAC__byte } ; + +STRUCT: FLAC__StreamMetadata_CueSheet_Track + { offset FLAC__uint64 } + { number FLAC__byte } + { isrc char[13] } + { type unsigned bits: 1 } + { pre_emphasis unsigned bits: 1 } + { num_indices FLAC__byte } + { indices FLAC__StreamMetadata_CueSheet_Index* } ; + +STRUCT: FLAC__StreamMetadata_CueSheet + { media_catalog_number char[129] } + { lead_in FLAC__uint64 } + { is_cd FLAC__bool } + { num_tracks unsigned } + { tracks FLAC__StreamMetadata_CueSheet_Track* } ; + +ENUM: FLAC__StreamMetadata_Picture_Type + FLAC__STREAM_METADATA_PICTURE_TYPE_OTHER + FLAC__STREAM_METADATA_PICTURE_TYPE_FILE_ICON_STANDARD + FLAC__STREAM_METADATA_PICTURE_TYPE_FILE_ICON + FLAC__STREAM_METADATA_PICTURE_TYPE_FRONT_COVER + FLAC__STREAM_METADATA_PICTURE_TYPE_BACK_COVER + FLAC__STREAM_METADATA_PICTURE_TYPE_LEAFLET_PAGE + FLAC__STREAM_METADATA_PICTURE_TYPE_MEDIA + FLAC__STREAM_METADATA_PICTURE_TYPE_LEAD_ARTIST + FLAC__STREAM_METADATA_PICTURE_TYPE_ARTIST + FLAC__STREAM_METADATA_PICTURE_TYPE_CONDUCTOR + FLAC__STREAM_METADATA_PICTURE_TYPE_BAND + FLAC__STREAM_METADATA_PICTURE_TYPE_COMPOSER + FLAC__STREAM_METADATA_PICTURE_TYPE_LYRICIST + FLAC__STREAM_METADATA_PICTURE_TYPE_RECORDING_LOCATION + FLAC__STREAM_METADATA_PICTURE_TYPE_DURING_RECORDING + FLAC__STREAM_METADATA_PICTURE_TYPE_DURING_PERFORMANCE + FLAC__STREAM_METADATA_PICTURE_TYPE_VIDEO_SCREEN_CAPTURE + FLAC__STREAM_METADATA_PICTURE_TYPE_FISH + FLAC__STREAM_METADATA_PICTURE_TYPE_ILLUSTRATION + FLAC__STREAM_METADATA_PICTURE_TYPE_BAND_LOGOTYPE + FLAC__STREAM_METADATA_PICTURE_TYPE_PUBLISHER_LOGOTYPE ; + +STRUCT: FLAC__StreamMetadata_Picture + { type FLAC__StreamMetadata_Picture_Type } + { mime_type char* } + { description FLAC__byte* } + { width FLAC__uint32 } + { height FLAC__uint32 } + { depth FLAC__uint32 } + { colors FLAC__uint32 } + { data_length FLAC__uint32 } + { data FLAC__byte* } ; + +STRUCT: FLAC__StreamMetadata_Unknown + { data FLAC__byte* } ; + +UNION-STRUCT: metadata-union + { stream_info FLAC__StreamMetadata_StreamInfo } + { padding FLAC__StreamMetadata_Padding } + { application FLAC__StreamMetadata_Application } + { seek_table FLAC__StreamMetadata_SeekTable } + { vorbis_comment FLAC__StreamMetadata_VorbisComment } + { cue_sheet FLAC__StreamMetadata_CueSheet } + { picture FLAC__StreamMetadata_Picture } + { unknown FLAC__StreamMetadata_Unknown } ; + +STRUCT: FLAC__StreamMetadata + { type FLAC__MetadataType } + { is_last FLAC__bool } + { length unsigned } + { data metadata-union } ; + +FUNCTION: FLAC__bool FLAC__metadata_get_streaminfo ( c-string filename, FLAC__StreamMetadata* streaminfo ) +FUNCTION: FLAC__bool FLAC__metadata_get_tags ( c-string filename, FLAC__StreamMetadata** streaminfo ) + + diff --git a/flac/metadata/metadata.factor b/flac/metadata/metadata.factor new file mode 100644 index 0000000..66dfd08 --- /dev/null +++ b/flac/metadata/metadata.factor @@ -0,0 +1,140 @@ +! Copyright (C) 2020 . +! See http://factorcode.org/license.txt for BSD license. +USING: endian sequences kernel classes.struct io.binary io.files io.encodings io.encodings.string io.encodings.utf8 io.encodings.binary alien.c-types alien.endian math locals accessors prettyprint combinators math.parser strings arrays io.streams.byte-array sequences.generalizations assocs splitting byte-arrays alien.syntax alien.enums io.encodings.ascii pack ; +USING: flac.bitstream flac.format ; + +QUALIFIED: bitstreams +QUALIFIED: io + +IN: flac.metadata + +ERROR: cuesheet-index-reserved-must-be-zero ; + +: read-metadata-block-header ( -- header ) + 1 flac-read-uint 1 = + 7 flac-read-uint + 24 flac-read-uint + metadata-block-header boa ; + +: read-metadata-block-stream-info ( -- stream-info ) + 16 flac-read-uint + 16 flac-read-uint + 24 flac-read-uint + 24 flac-read-uint + 20 flac-read-uint + 3 flac-read-uint 1 + + 5 flac-read-uint 1 + + 36 flac-read-uint + 128 flac-read-uint 16 >be bytes>hex-string + stream-info boa ; + +: read-metadata-block-seek-table ( length -- seek-table ) + 18 / [ + drop + 64 flac-read-uint + 64 flac-read-uint + 16 flac-read-uint + seek-point boa + ] map + seek-table boa ; + + +: read-metadata-block-vorbis-comment ( length -- vorbis-comment ) + ! vorbis comments are in little endian... + drop + 32 flac-read-uint 4 >le be> dup 8 * flac-read-uint swap >n-byte-array utf8 decode + 32 flac-read-uint 4 >le be> + [ + drop + 32 flac-read-uint 4 >le be> dup 8 * flac-read-uint swap >n-byte-array utf8 decode + "=" split + ] map + >alist vorbis-comment boa ; + +: encode-vorbis-string ( str -- byte-array ) + dup binary [ length 4 >le io:write utf8 encode io:write ] with-byte-writer ; + +: encode-vorbis-comments ( assoc -- byte-array ) + dup binary [ + length 4 >le io:write + [ 2array "=" join encode-vorbis-string io:write ] assoc-each + ] with-byte-writer ; + +: encode-vorbis-comment ( vorbis-comment -- byte-array ) + binary [ + [ vendor-string>> encode-vorbis-string io:write ] + [ comments>> encode-vorbis-comments io:write ] bi + ] with-byte-writer ; + +: encode-padding ( padding -- byte-array ) + length>> ; + +: read-metadata-block-padding ( length -- padding ) + dup 8 * flac-read-uint drop flac-padding boa ; + +: read-metadata-block-application ( length -- application ) + 8 * flac-read-uint drop application new ; + +: read-metadata-block-cuesheet ( length -- cuesheet ) + dup [ 8 * flac-read-uint ] dip >be + binary + [ + 128 io:read ascii decode + 8 io:read be> + 259 io:read drop f + 1 io:read be> [ + drop + 8 io:read be> + 1 io:read be> + 12 io:read ascii decode + 21 io:read drop 0 t + 1 io:read [ + drop + 8 io:read be> + 1 io:read be> + 3 io:read be> = 0 [ cuesheet-index-reserved-must-be-zero ] unless + cuesheet-index boa + ] map + cuesheet-track boa + ] map + ] with-byte-reader cuesheet boa ; + +: read-metadata-block-picture ( length -- picture ) + drop + 32 flac-read-uint + 32 flac-read-uint dup 8 * flac-read-uint swap >n-byte-array utf8 decode + 32 flac-read-uint dup 8 * flac-read-uint swap >n-byte-array utf8 decode + 32 flac-read-uint + 32 flac-read-uint + 32 flac-read-uint + 32 flac-read-uint + 32 flac-read-uint dup 8 * flac-read-uint swap >n-byte-array + picture boa ; + +: read-metadata-block ( metadata length type -- metadata ) + [ + { + { metadata-stream-info [ drop read-metadata-block-stream-info >>stream-info ] } + { metadata-padding [ read-metadata-block-padding >>padding ] } + { metadata-application [ read-metadata-block-application >>application ] } + { metadata-seek-table [ read-metadata-block-seek-table >>seek-table ] } + { metadata-vorbis-comment [ read-metadata-block-vorbis-comment >>vorbis-comment ] } + { metadata-cuesheet [ read-metadata-block-cuesheet >>cuesheet ] } + { metadata-picture [ read-metadata-block-picture >>picture ] } + } case + ] with-big-endian ; + +: read/assert-flac-magic ( -- ) + 32 flac-read-uint FLAC-MAGIC = [ not-a-flac-file ] unless ; + +: read-flac-metadata ( -- metadata ) + read/assert-flac-magic + metadata new + [ + read-metadata-block-header + [ length>> ] [ type>> ] [ last?>> not ] tri + [ read-metadata-block ] dip + ] loop ; + +: ( filename -- metadata ) + [ read-flac-metadata ] with-flac-file-reader ;