taglib documentation.
This commit is contained in:
@@ -26,7 +26,8 @@ For encoding, also install:
|
|||||||
|
|
||||||
The Opus encoder backend uses libopusenc directly. The FLAC encoder backend
|
The Opus encoder backend uses libopusenc directly. The FLAC encoder backend
|
||||||
uses libFLAC directly. FLAC sample-rate conversion uses the existing FFmpeg
|
uses libFLAC directly. FLAC sample-rate conversion uses the existing FFmpeg
|
||||||
swresample layer.
|
swresample layer. Metadata and cover-art copying use the TagLib wrapper; the
|
||||||
|
public `taglib.rkt` API also supports read-write tag editing.
|
||||||
|
|
||||||
## macOS
|
## macOS
|
||||||
|
|
||||||
|
|||||||
@@ -62,8 +62,8 @@ file extension. Pass @racket['opus] or @racket['flac] to force a backend.
|
|||||||
When @racket[copy-tags?] is true, common textual tags and an embedded picture
|
When @racket[copy-tags?] is true, common textual tags and an embedded picture
|
||||||
are copied from the source file to the destination file. Opus comments and
|
are copied from the source file to the destination file. Opus comments and
|
||||||
cover art are written before encoding starts through @tt{libopusenc}. FLAC
|
cover art are written before encoding starts through @tt{libopusenc}. FLAC
|
||||||
metadata is copied after the encoded file has been written, using the TagLib
|
metadata is copied after the encoded file has been written, using the
|
||||||
wrapper.
|
read-write API from @racketmodname[racket-audio/taglib].
|
||||||
|
|
||||||
When @racket[progress-callback] is a procedure, it is called with a progress
|
When @racket[progress-callback] is a procedure, it is called with a progress
|
||||||
hash during encoding. Progress is based on the number of input frames read from
|
hash during encoding. Progress is based on the number of input frames read from
|
||||||
|
|||||||
+199
-79
@@ -9,48 +9,72 @@
|
|||||||
@title{TagLib Metadata}
|
@title{TagLib Metadata}
|
||||||
@author[@author+email["Hans Dijkema" "hans@dijkewijk.nl"]]
|
@author[@author+email["Hans Dijkema" "hans@dijkewijk.nl"]]
|
||||||
|
|
||||||
|
|
||||||
@defmodule[racket-audio/taglib]
|
@defmodule[racket-audio/taglib]
|
||||||
|
|
||||||
The @racketmodname[racket-audio/taglib] module provides the high level metadata
|
The @racketmodname[racket-audio/taglib] module provides the high level metadata
|
||||||
reader used by the audio package. It wraps the lower level TagLib FFI module
|
API used by the audio package. It wraps the lower level TagLib C FFI module and
|
||||||
and presents a small, read-only Racket API for common tags, audio properties,
|
presents a Racket API for common tags, generic properties, audio properties, and
|
||||||
generic properties, and embedded cover art.
|
embedded cover art.
|
||||||
|
|
||||||
Calling @racket[id3-tags] opens the file through TagLib, copies the values that
|
The module can be used in two modes. The default mode is read-only and returns
|
||||||
are needed on the Racket side, reads the optional embedded picture, frees the
|
a snapshot of the metadata. A handle opened with @racket[#:mode 'read-write]
|
||||||
native TagLib objects, and returns an opaque tag handle. The handle is
|
keeps the native TagLib file open and can be modified with the setter
|
||||||
therefore a snapshot of the metadata at the time it was read. It does not keep
|
procedures documented below. Changes are written to the media file by calling
|
||||||
the media file or the native TagLib handle open.
|
@racket[tags-save!].
|
||||||
|
|
||||||
The name @racket[id3-tags] is historical. The module uses TagLib to open the
|
The name @racket[id3-tags] is historical. The implementation uses TagLib, so
|
||||||
file, so the usable file types are the file types supported by the TagLib
|
the usable file types are the file types supported by the TagLib library
|
||||||
library available at run time. This module is not a tag editor; it only reads
|
available at run time.
|
||||||
metadata.
|
|
||||||
|
|
||||||
@section{Reading metadata}
|
@section{Opening and closing tag handles}
|
||||||
|
|
||||||
@defproc[(id3-tags [file path-string?]) any/c]{
|
@defproc[(id3-tags [file path-string?]
|
||||||
Reads metadata from @racket[file] and returns an opaque tag handle. The
|
[#:mode mode (or/c 'read 'read-only 'read-write 'write) 'read])
|
||||||
argument may be a path or a string. On Windows, the implementation retries
|
any/c]{
|
||||||
with the wide-character TagLib open function when the normal open function does
|
Opens @racket[file] through TagLib and returns an opaque tag handle. In the
|
||||||
not produce a valid TagLib file.
|
default read-only mode, the module copies the values needed on the Racket side,
|
||||||
|
frees the native TagLib objects, and returns a snapshot handle.
|
||||||
|
|
||||||
The returned handle is passed to the other procedures in this module. If the
|
In read-write mode, the native TagLib file remains open. Setter procedures may
|
||||||
file cannot be opened, @racket[id3-tags] still returns a handle, but
|
then be used to modify fields, properties, and pictures. Call
|
||||||
@racket[tags-valid?] returns @racket[#f]. Other accessors then return their
|
@racket[tags-save!] to write changes and @racket[tags-close!] to close the
|
||||||
default values, such as @racket[""], @racket[-1], @racket['()], or
|
native handle.
|
||||||
@racket[#f].}
|
|
||||||
|
|
||||||
@defproc[(tags-valid? [tags any/c]) boolean?]{
|
On Windows, the implementation retries with the wide-character TagLib open
|
||||||
Returns @racket[#t] when @racket[id3-tags] successfully opened the file and
|
function when the normal open function does not produce a valid TagLib file.}
|
||||||
TagLib reported it as valid.}
|
|
||||||
|
@defproc[(call-with-id3-tags [file path-string?]
|
||||||
|
[proc procedure?]
|
||||||
|
[#:mode mode (or/c 'read 'read-only 'read-write 'write) 'read])
|
||||||
|
any/c]{
|
||||||
|
Opens @racket[file], calls @racket[proc] with the tag handle, and closes the
|
||||||
|
handle afterwards with @racket[tags-close!]. This is most useful for
|
||||||
|
read-write code because it avoids leaking the native TagLib file handle.}
|
||||||
|
|
||||||
|
@deftogether[
|
||||||
|
(@defproc[(tags-valid? [tags any/c]) boolean?]
|
||||||
|
@defproc[(tags-read-write? [tags any/c]) boolean?]
|
||||||
|
@defproc[(tags-closed? [tags any/c]) boolean?])]{
|
||||||
|
Return handle state. @racket[tags-valid?] reports whether TagLib opened the
|
||||||
|
file successfully. @racket[tags-read-write?] reports whether the handle was
|
||||||
|
opened in read-write mode. @racket[tags-closed?] reports whether the native
|
||||||
|
TagLib file handle has been closed.}
|
||||||
|
|
||||||
|
@deftogether[
|
||||||
|
(@defproc[(tags-save! [tags any/c]) boolean?]
|
||||||
|
@defproc[(tags-close! [tags any/c]) void?])]{
|
||||||
|
@racket[tags-save!] writes pending changes for a read-write handle to the media
|
||||||
|
file. @racket[tags-close!] closes the native TagLib file handle. Closing a
|
||||||
|
read-only snapshot is harmless.
|
||||||
|
}
|
||||||
|
|
||||||
@racketblock[
|
@racketblock[
|
||||||
(define tags (id3-tags "song.mp3"))
|
(call-with-id3-tags "track.flac"
|
||||||
|
(lambda (tags)
|
||||||
(when (tags-valid? tags)
|
(when (tags-valid? tags)
|
||||||
(printf "~a - ~a\n" (tags-artist tags) (tags-title tags)))]
|
(tags-title! tags "New title")
|
||||||
|
(tags-save! tags)))
|
||||||
|
#:mode 'read-write)]
|
||||||
|
|
||||||
@section{Common tag fields}
|
@section{Common tag fields}
|
||||||
|
|
||||||
@@ -59,32 +83,52 @@ TagLib reported it as valid.}
|
|||||||
@defproc[(tags-album [tags any/c]) string?]
|
@defproc[(tags-album [tags any/c]) string?]
|
||||||
@defproc[(tags-artist [tags any/c]) string?]
|
@defproc[(tags-artist [tags any/c]) string?]
|
||||||
@defproc[(tags-comment [tags any/c]) string?]
|
@defproc[(tags-comment [tags any/c]) string?]
|
||||||
@defproc[(tags-genre [tags any/c]) string?])]
|
@defproc[(tags-genre [tags any/c]) string?])]{
|
||||||
Return the common textual fields from the TagLib tag interface. Missing fields
|
Return the common textual fields from the TagLib tag interface. Missing fields
|
||||||
are returned as the empty string.
|
are returned as the empty string.}
|
||||||
|
|
||||||
@deftogether[
|
@deftogether[
|
||||||
(@defproc[(tags-year [tags any/c]) integer?]
|
(@defproc[(tags-year [tags any/c]) integer?]
|
||||||
@defproc[(tags-track [tags any/c]) integer?])]
|
@defproc[(tags-track [tags any/c]) integer?])]{
|
||||||
Return the year and track number from the common TagLib tag interface. Missing
|
Return the year and track number from the common TagLib tag interface. Missing
|
||||||
numeric values are returned as @racket[-1].
|
numeric values are returned as @racket[-1].}
|
||||||
|
|
||||||
@deftogether[
|
@deftogether[
|
||||||
(@defproc[(tags-composer [tags any/c])
|
(@defproc[(tags-title! [tags any/c] [value (or/c string? 'clear)]) void?]
|
||||||
(or/c string? (listof string?))]
|
@defproc[(tags-album! [tags any/c] [value (or/c string? 'clear)]) void?]
|
||||||
@defproc[(tags-album-artist [tags any/c])
|
@defproc[(tags-artist! [tags any/c] [value (or/c string? 'clear)]) void?]
|
||||||
(or/c string? (listof string?))]
|
@defproc[(tags-comment! [tags any/c] [value (or/c string? 'clear)]) void?]
|
||||||
@defproc[(tags-disc-number [tags any/c])
|
@defproc[(tags-genre! [tags any/c] [value (or/c string? 'clear)]) void?])]{
|
||||||
(or/c number? #f)])]
|
Set common textual fields on a read-write handle. Passing @racket['clear]
|
||||||
Return selected values from the generic TagLib property store. The composer is
|
clears the field. Call @racket[tags-save!] to persist the change.}
|
||||||
read from the lower-case @racket['composer] key, the album artist from
|
|
||||||
@racket['albumartist], and the disc number from @racket['discnumber].
|
|
||||||
|
|
||||||
Composer and album artist return a list of strings when the property is present
|
@deftogether[
|
||||||
and the empty string when it is missing. The disc number is parsed from the
|
(@defproc[(tags-year! [tags any/c] [value (or/c exact-nonnegative-integer? 'clear)]) void?]
|
||||||
first property value and defaults to @racket[-1]. If the stored value cannot be
|
@defproc[(tags-track! [tags any/c] [value (or/c exact-nonnegative-integer? 'clear)]) void?])]{
|
||||||
parsed as a number, the result may be @racket[#f]. Use @racket[tags-keys] and
|
Set numeric common fields on a read-write handle. Passing @racket['clear]
|
||||||
@racket[tags-ref] for direct access to the complete generic property store.
|
writes zero through the TagLib C API and updates the Racket-side cache to
|
||||||
|
@racket[-1].}
|
||||||
|
|
||||||
|
@section{Selected generic fields}
|
||||||
|
|
||||||
|
@deftogether[
|
||||||
|
(@defproc[(tags-composer [tags any/c]) (or/c string? (listof string?))]
|
||||||
|
@defproc[(tags-album-artist [tags any/c]) (or/c string? (listof string?))]
|
||||||
|
@defproc[(tags-disc-number [tags any/c]) (or/c number? #f)])]{
|
||||||
|
Return selected values from the generic TagLib property store. The composer is
|
||||||
|
read from the @racket['composer] key, the album artist from
|
||||||
|
@racket['albumartist], and the disc number from @racket['discnumber]. Use
|
||||||
|
@racket[tags-keys] and @racket[tags-ref] for direct access to the complete
|
||||||
|
generic property store.}
|
||||||
|
|
||||||
|
@deftogether[
|
||||||
|
(@defproc[(tags-composer! [tags any/c] [value (or/c string? 'clear)]) void?]
|
||||||
|
@defproc[(tags-album-artist! [tags any/c] [value (or/c string? 'clear)]) void?]
|
||||||
|
@defproc[(tags-disc-number! [tags any/c]
|
||||||
|
[value (or/c exact-nonnegative-integer? string? 'clear)])
|
||||||
|
void?])]{
|
||||||
|
Set selected generic properties on a read-write handle. The disc number may be
|
||||||
|
provided as a number or as the exact string that should be written.}
|
||||||
|
|
||||||
@section{Audio properties}
|
@section{Audio properties}
|
||||||
|
|
||||||
@@ -92,10 +136,11 @@ parsed as a number, the result may be @racket[#f]. Use @racket[tags-keys] and
|
|||||||
(@defproc[(tags-length [tags any/c]) integer?]
|
(@defproc[(tags-length [tags any/c]) integer?]
|
||||||
@defproc[(tags-sample-rate [tags any/c]) integer?]
|
@defproc[(tags-sample-rate [tags any/c]) integer?]
|
||||||
@defproc[(tags-bit-rate [tags any/c]) integer?]
|
@defproc[(tags-bit-rate [tags any/c]) integer?]
|
||||||
@defproc[(tags-channels [tags any/c]) integer?])]
|
@defproc[(tags-channels [tags any/c]) integer?])]{
|
||||||
Return audio properties reported by TagLib: length in seconds, sample rate in
|
Return audio properties reported by TagLib: length in seconds, sample rate in
|
||||||
Hz, bit rate in kbit/s, and number of channels. Missing values are returned as
|
Hz, bit rate in kbit/s, and number of channels. These values are read-only
|
||||||
@racket[-1].
|
properties of the media stream, not editable tags. Missing values are returned
|
||||||
|
as @racket[-1].}
|
||||||
|
|
||||||
@section{Generic properties}
|
@section{Generic properties}
|
||||||
|
|
||||||
@@ -109,9 +154,39 @@ Returns the list of values associated with @racket[key], or @racket[#f] when the
|
|||||||
property was not found. Use lower-case symbol keys, matching the values
|
property was not found. Use lower-case symbol keys, matching the values
|
||||||
returned by @racket[tags-keys].}
|
returned by @racket[tags-keys].}
|
||||||
|
|
||||||
|
@defproc[(tags-set! [tags any/c]
|
||||||
|
[key (or/c symbol? string?)]
|
||||||
|
[value (or/c string? 'clear)])
|
||||||
|
void?]{
|
||||||
|
Sets a generic property on a read-write handle. Symbol keys are converted to
|
||||||
|
upper-case TagLib property names; string keys are passed as supplied. Passing
|
||||||
|
@racket['clear] clears the property.}
|
||||||
|
|
||||||
|
@defproc[(tags-set-values! [tags any/c]
|
||||||
|
[key (or/c symbol? string?)]
|
||||||
|
[values (or/c (listof string?) 'clear)])
|
||||||
|
void?]{
|
||||||
|
Replaces a generic property with zero or more values. Passing @racket['clear]
|
||||||
|
removes the property.}
|
||||||
|
|
||||||
|
@defproc[(tags-append! [tags any/c]
|
||||||
|
[key (or/c symbol? string?)]
|
||||||
|
[value string?])
|
||||||
|
void?]{
|
||||||
|
Appends a value to a generic property on a read-write handle.}
|
||||||
|
|
||||||
|
@defproc[(tags-clear! [tags any/c]
|
||||||
|
[key (or/c symbol? string?)])
|
||||||
|
void?]{
|
||||||
|
Clears a generic property on a read-write handle.}
|
||||||
|
|
||||||
@racketblock[
|
@racketblock[
|
||||||
(for ([key (in-list (tags-keys tags))])
|
(call-with-id3-tags "track.flac"
|
||||||
(printf "~a: ~s\n" key (tags-ref tags key)))]
|
(lambda (tags)
|
||||||
|
(tags-set-values! tags 'composer '("Johann Sebastian Bach"))
|
||||||
|
(tags-set! tags 'discnumber "1")
|
||||||
|
(tags-save! tags))
|
||||||
|
#:mode 'read-write)]
|
||||||
|
|
||||||
Generic properties may contain multiple values for a single key. The API keeps
|
Generic properties may contain multiple values for a single key. The API keeps
|
||||||
those values as lists instead of joining them into one string.
|
those values as lists instead of joining them into one string.
|
||||||
@@ -120,8 +195,8 @@ those values as lists instead of joining them into one string.
|
|||||||
|
|
||||||
The module represents embedded artwork as an opaque @deftech{picture value}.
|
The module represents embedded artwork as an opaque @deftech{picture value}.
|
||||||
The picture value is returned by @racket[tags-picture] and can be inspected with
|
The picture value is returned by @racket[tags-picture] and can be inspected with
|
||||||
the picture procedures documented below. When no picture is available, the
|
the picture procedures documented below. It can also be written to another
|
||||||
picture-related procedures return @racket[#f].
|
file with @racket[tags-picture!] or @racket[tags-append-picture!].
|
||||||
|
|
||||||
@defproc[(tags-picture [tags any/c]) (or/c any/c #f)]{
|
@defproc[(tags-picture [tags any/c]) (or/c any/c #f)]{
|
||||||
Returns the embedded picture value, or @racket[#f] when the file has no picture
|
Returns the embedded picture value, or @racket[#f] when the file has no picture
|
||||||
@@ -130,14 +205,15 @@ that the underlying FFI layer could read.}
|
|||||||
@deftogether[
|
@deftogether[
|
||||||
(@defproc[(tags-picture->kind [tags any/c]) (or/c integer? #f)]
|
(@defproc[(tags-picture->kind [tags any/c]) (or/c integer? #f)]
|
||||||
@defproc[(tags-picture->mimetype [tags any/c]) (or/c string? #f)]
|
@defproc[(tags-picture->mimetype [tags any/c]) (or/c string? #f)]
|
||||||
|
@defproc[(tags-picture->description [tags any/c]) (or/c string? #f)]
|
||||||
@defproc[(tags-picture->size [tags any/c]) (or/c integer? #f)]
|
@defproc[(tags-picture->size [tags any/c]) (or/c integer? #f)]
|
||||||
@defproc[(tags-picture->ext [tags any/c]) (or/c symbol? #f)])]
|
@defproc[(tags-picture->ext [tags any/c]) (or/c symbol? #f)])]{
|
||||||
Return selected information about the embedded picture. The kind is the
|
Return selected information about the embedded picture. The kind is the
|
||||||
numeric picture type reported by the FFI layer. The MIME type is the stored
|
numeric picture type reported by the FFI layer. The MIME type is the stored
|
||||||
MIME type, such as @racket["image/jpeg"] or @racket["image/png"]. The size is
|
MIME type, such as @racket["image/jpeg"] or @racket["image/png"]. The size is
|
||||||
the number of bytes in the embedded image. The extension helper returns
|
the number of bytes in the embedded image. The extension helper returns
|
||||||
@racket['jpg], @racket['png], or @racket[#f] when the MIME type is not
|
@racket['jpg], @racket['png], or @racket[#f] when the MIME type is not
|
||||||
recognized.
|
recognized.}
|
||||||
|
|
||||||
@defproc[(tags-picture->bitmap [tags any/c])
|
@defproc[(tags-picture->bitmap [tags any/c])
|
||||||
(or/c (is-a?/c bitmap%) #f)]{
|
(or/c (is-a?/c bitmap%) #f)]{
|
||||||
@@ -150,41 +226,85 @@ Reads the embedded picture bytes with @racket[read-bitmap] and returns a
|
|||||||
boolean?]{
|
boolean?]{
|
||||||
Writes the embedded picture bytes to @racket[path] in binary mode, replacing an
|
Writes the embedded picture bytes to @racket[path] in binary mode, replacing an
|
||||||
existing file. The procedure returns @racket[#t] when a picture was written and
|
existing file. The procedure returns @racket[#t] when a picture was written and
|
||||||
@racket[#f] when the tag handle has no picture. The file name is not adjusted
|
@racket[#f] when the tag handle has no picture.}
|
||||||
automatically; use @racket[tags-picture->ext] when the caller wants to choose an
|
|
||||||
extension from the MIME type.}
|
@defproc[(make-tags-picture [mimetype string?]
|
||||||
|
[kind integer?]
|
||||||
|
[data (or/c bytes? (is-a?/c bitmap%))]
|
||||||
|
[#:description description string? ""])
|
||||||
|
id3-picture?]{
|
||||||
|
Creates a picture value from encoded image bytes or from a @racket[bitmap%].
|
||||||
|
The MIME type should normally be @racket["image/jpeg"] or @racket["image/png"].}
|
||||||
|
|
||||||
|
@defproc[(make-tags-picture-from-bitmap [bitmap (is-a?/c bitmap%)]
|
||||||
|
[kind integer?]
|
||||||
|
[#:mimetype mimetype string? "image/png"]
|
||||||
|
[#:description description string? ""])
|
||||||
|
id3-picture?]{
|
||||||
|
Creates a picture value by encoding @racket[bitmap] as PNG or JPEG.}
|
||||||
|
|
||||||
|
@deftogether[
|
||||||
|
(@defproc[(tags-picture! [tags any/c]
|
||||||
|
[picture (or/c id3-picture? 'clear)])
|
||||||
|
void?]
|
||||||
|
@defproc[(tags-append-picture! [tags any/c]
|
||||||
|
[picture id3-picture?])
|
||||||
|
void?]
|
||||||
|
@defproc[(tags-clear-picture! [tags any/c]) void?])]{
|
||||||
|
Set, append, or clear embedded artwork on a read-write handle. The procedures
|
||||||
|
use TagLib complex properties underneath. Call @racket[tags-save!] to persist
|
||||||
|
the change.}
|
||||||
|
|
||||||
@racketblock[
|
@racketblock[
|
||||||
(define ext (tags-picture->ext tags))
|
(define cover
|
||||||
|
(make-tags-picture "image/jpeg" 3 (file->bytes "cover.jpg")
|
||||||
|
#:description "Cover"))
|
||||||
|
|
||||||
(when ext
|
(call-with-id3-tags "track.flac"
|
||||||
(tags-picture->file tags
|
(lambda (tags)
|
||||||
(format "cover.~a" ext)))]
|
(tags-picture! tags cover)
|
||||||
|
(tags-save! tags))
|
||||||
|
#:mode 'read-write)]
|
||||||
|
|
||||||
@section{Picture values}
|
@section{Picture values}
|
||||||
|
|
||||||
@deftogether[
|
@deftogether[
|
||||||
(@defproc[(id3-picture-mimetype [picture any/c]) string?]
|
(@defproc[(id3-picture? [v any/c]) boolean?]
|
||||||
@defproc[(id3-picture-kind [picture any/c]) integer?]
|
@defproc[(id3-picture-mimetype [picture id3-picture?]) string?]
|
||||||
@defproc[(id3-picture-size [picture any/c]) integer?]
|
@defproc[(id3-picture-kind [picture id3-picture?]) integer?]
|
||||||
@defproc[(id3-picture-bytes [picture any/c]) bytes?])]
|
@defproc[(id3-picture-size [picture id3-picture?]) integer?]
|
||||||
Access the fields of a picture value returned by @racket[tags-picture]. These
|
@defproc[(id3-picture-bytes [picture id3-picture?]) bytes?]
|
||||||
procedures are useful when the caller wants to process the image bytes directly
|
@defproc[(id3-picture-description [picture id3-picture?]) string?])]{
|
||||||
instead of converting them to a bitmap or writing them to a file.
|
Access the fields of a picture value. These procedures are useful when the
|
||||||
|
caller wants to process the image bytes directly or pass a picture to another
|
||||||
|
component.}
|
||||||
|
|
||||||
@section{Converting to a hash}
|
@section{Converting to a hash}
|
||||||
|
|
||||||
@defproc[(tags->hash [tags any/c]) hash?]{
|
@defproc[(tags->hash [tags any/c]) hash?]{
|
||||||
Returns a mutable hash containing the core values copied from the tag handle.
|
Returns a mutable hash containing the core values copied from the tag handle.
|
||||||
The hash contains the keys @racket['valid?], @racket['title], @racket['album],
|
The hash contains the keys @racket['valid?], @racket['read-write?],
|
||||||
@racket['artist], @racket['comment], @racket['composer], @racket['genre],
|
@racket['closed?], @racket['title], @racket['album], @racket['artist],
|
||||||
@racket['year], @racket['track], @racket['length], @racket['sample-rate],
|
@racket['comment], @racket['composer], @racket['genre], @racket['year],
|
||||||
@racket['bit-rate], @racket['channels], @racket['picture], and @racket['keys].
|
@racket['track], @racket['length], @racket['sample-rate], @racket['bit-rate],
|
||||||
|
@racket['channels], @racket['picture], and @racket['keys].
|
||||||
|
|
||||||
The hash is intended as a convenient snapshot for application code. Generic
|
The hash is intended as a convenient snapshot for application code. Generic
|
||||||
property values are not expanded into the hash; use @racket[tags-ref] for those
|
property values are not expanded into the hash; use @racket[tags-ref] for those
|
||||||
values.}
|
values.}
|
||||||
|
|
||||||
|
@section{Copying tags and pictures}
|
||||||
|
|
||||||
|
The encoder pipeline uses this module for metadata transfer. For FLAC output,
|
||||||
|
@racket[audio-encode] first writes the audio stream and then opens the resulting
|
||||||
|
file with @racket[id3-tags] in read-write mode to copy tags and pictures through
|
||||||
|
TagLib. For Opus output, comments and pictures are supplied to
|
||||||
|
@tt{libopusenc} before encoding starts, because OpusTags are written at the
|
||||||
|
start of the Ogg Opus stream.
|
||||||
|
|
||||||
|
Applications that need explicit metadata editing should use the read-write API
|
||||||
|
directly, as in the examples above.
|
||||||
|
|
||||||
@section{Example}
|
@section{Example}
|
||||||
|
|
||||||
@racketblock[
|
@racketblock[
|
||||||
@@ -209,10 +329,10 @@ This chapter documents the public @racketmodname["taglib.rkt"] layer. The
|
|||||||
native TagLib calls are delegated to @racketmodname["taglib-ffi.rkt"], but
|
native TagLib calls are delegated to @racketmodname["taglib-ffi.rkt"], but
|
||||||
callers normally should not use that lower level module directly.
|
callers normally should not use that lower level module directly.
|
||||||
|
|
||||||
The tag handle is implemented as a small Racket object with a private dispatch
|
A read-only tag handle is a Racket-side snapshot. A read-write tag handle keeps
|
||||||
procedure. The native TagLib file is not stored in the handle. This keeps the
|
the native TagLib file open until @racket[tags-close!] is called. Setter
|
||||||
public API simple and prevents native resources from leaking into application
|
procedures update both the native file and the Racket-side cache; the changes
|
||||||
code.
|
are persisted only after @racket[tags-save!] succeeds.
|
||||||
|
|
||||||
The implementation normalizes generic property names by lower-casing TagLib
|
The implementation normalizes generic property names by lower-casing TagLib
|
||||||
property keys and converting them to symbols. Values remain lists of strings
|
property keys and converting them to symbols. Values remain lists of strings
|
||||||
|
|||||||
Reference in New Issue
Block a user