ryanfrancesconi/spfk-taglib
TagLib packaged for Swift Package Manager.
Overview
spfk-taglib is an independent SPM repackaging of taglib/taglib, the C++ audio metadata library. The source tree mirrors upstream TagLib's directory layout — headers live alongside their .cpp files in format-specific subdirectories — making syncs with upstream a straightforward directory-level diff.
The current upstream base is TagLib 2.3.2 (tracked via SPFK_TAGLIB_UPSTREAM_VERSION in taglib_config.h).
Changes Not in Upstream
Sources/taglib/ is upstream plus the changes below, so this package may differ from taglib. Each is its own set of commits here and is offered to taglib/taglib as its own pull request; it leaves this list when a sync brings it back in from upstream, and stays if upstream declines it.
| Change | Commits (original; latest re-apply) | Files | Upstream | | --- | --- | --- | --- | | RIFF INFO: under an assumed UTF-8 (setAssumedCodePage(65001), no CSET), a field that isn't valid UTF-8 is read as ISO 8859-1 instead of as empty; a declared code page is never second-guessed | c901643 | riff/wav/infotag.cpp, riff/wav/infotag.h | PR #1480 open | | RIFF: only the audio chunk (data, SSND) is clamped to the end of the file, and its size is repaired before a chunk is appended after it; any other chunk running past the end, such as a trailing APEv2 tag, ends the chunk walk instead of swallowing chunks a save appends | 93f90e1 | riff/rifffile.cpp | PR #1482 open (issue #733) | | ID3v2: a frame that cannot be read, such as one with an invalid ID, is skipped when another frame, padding or the end of the frames follows where its size ends, instead of ending the tag and losing every frame after it | 8c77c2a | mpeg/id3v2/id3v2tag.cpp | PR #1483 open (issue #437) | | ByteVector::fromBase64 skips ASCII whitespace, so a METADATA_BLOCK_PICTURE stored as line-wrapped (MIME-style) base64 is read and kept instead of dropped, and deleted by the next save; other invalid characters still fail | 0f39cc3 | toolkit/tbytevector.cpp, toolkit/tbytevector.h | PR #1484 open (issue #1461) | | Matroska: an Audio element without SamplingFrequency or Channels reads their defaults (8000 Hz, one channel) instead of 0, and its track is no longer skipped when BitDepth is absent too | dad64af | matroska/ebml/ebmlmktracks.cpp | PR #1485 open (issue #1358) | | MPEG: findID3v2, nextFrameOffset and previousFrameOffset read raw bytes in 64 KB blocks, with the sync check in an isFrameSync(b1, b2) overload, so junk before or after the audio no longer makes opening slow; results unchanged | eff0993 | mpeg/mpegfile.cpp, mpeg/mpegutils.h | PR #1486 open (issue #842) | | ID3v2 setProperties updates the one frame holding an edited key in place when its type can hold the new value, keeping a COMM/USLT language, a TXXX description's spelling, the frame's position and its stored encoding | f4644d0 | mpeg/id3v2/id3v2tag.cpp | PR #1481 open (issue #1475) | | FileRef: a file whose content is another format than its extension names is read as what it is — the MPEG, MPC, WavPack, APE, TTA, AIFF and WAV branches check the header before parsing (for MPEG, another container's signature after any ID3v2 tag; a WAVE holding MPEG audio stays MPEG) and try content detection first on a mismatch; parse() rewinds the stream before content detection. Approach from taglib PR #1305 (lepre-zhang), reworked to check before parsing | a40ba55 | fileref.cpp | header checks: not opened (#1305 open, issue #1308); rewind: PR #1487 open |
Structure
Public headers are colocated with their implementations (matching upstream) and exposed to SPM via symlinks in include/taglib/. A module.modulemap defines the taglib module. Run scripts/update-symlinks.sh after adding or removing public headers.
Syncing with Upstream
The directory layout mirrors upstream TagLib, so syncing is a bulk mirror of upstream's taglib/ directory into Sources/taglib/ via rsync --delete, preserving the SPM-specific paths (include/, utfcpp/, toolkit/taglib_config.h) and stripping build-system files (CMakeLists.txt, *.cmake, etc.).
The mirror overwrites every change in Changes Not in Upstream. Re-apply each one still listed after it, as its own commit, record that commit in the list, then run the tests.
For new format modules introduced upstream, add the corresponding headerSearchPath entries to Package.swift, add public headers to module.modulemap, and run scripts/update-symlinks.sh.
On tagged upstream releases, bump SPFK_TAGLIB_UPSTREAM_VERSION in Sources/taglib/toolkit/taglib_config.h and the version line in this README.
Versioning
This package uses its own semantic versioning starting at 1.0.0, independent of upstream TagLib version numbers. The upstream TagLib revision is tracked in taglib_config.h.
Installation
Add to your Package.swift:
dependencies: [
.package(url: "https://github.com/ryanfrancesconi/spfk-taglib", from: "1.0.0"),
],
targets: [
.target(
name: "YourTarget",
dependencies: [
.product(name: "taglib", package: "spfk-taglib"),
]
),
]Swift Bindings
Swift bindings and higher-level metadata functionality are provided by SPFKMetadata, which wraps spfk-taglib's C++ API in a native Swift interface.
License
About
Spongefork is the personal software projects of musician and developer Ryan Francesconi. Dedicated to creative sound manipulation, his first application, Spongefork, was released in 1999 for macOS 8. From 2026, Spongefork returns as his software container for more musical experimentation. In addition to software releases, open source components can be found on his GitHub page.
Package Metadata
Repository: ryanfrancesconi/spfk-taglib
Default branch: main
README: README.md