Right-click lossless audio in Finder and convert it to AIFF — no quality loss, all artwork and metadata preserved, originals moved to the Trash. Works on a single file, a selection, or a whole folder (recursing into subfolders). Lossy and non-audio files are left untouched.
It also enriches the ID3 tags as it converts — backfilling a catalog number,
track/disc numbers, and folder artwork from the file/folder names, and
normalizing feat.→ft. — without ever overwriting tags the source already has.
Important
Originals are moved to the Trash (recoverable), never hard-deleted, and
only after the new .aiff is verified readable. A failed conversion never
loses the source. Still, try it on a copy first if you're cautious.
- macOS 13 (Ventura) or later — tested on Sequoia and Tahoe
- ffmpeg (installed automatically by Homebrew, below)
brew install jzstern/tap/toaiffThis installs the toaiff CLI and pulls in ffmpeg automatically.
Or install manually from source
git clone https://github.com/jzstern/toaiff.git
cd toaiff && ./install.shCopies the CLI to ~/.local/bin/toaiff. ffmpeg is installed via Homebrew on
first run if it isn't already present.
macOS won't let an installer register a Finder Quick Action — you add the bundled Shortcut yourself. It takes about a minute:
- Double-click
shortcut/→ aiff.shortcut→ Add Shortcut. It's pre-built to receive Files & Folders and run the CLI. - Enable it. macOS imports Shortcut Quick Actions disabled. Turn it on in System Settings ▸ Login Items & Extensions ▸ Finder → toggle → aiff on. (Easy to miss — without it the action won't appear in the menu.)
- Right-click any lossless files or a folder in Finder → Quick Actions ▸ → aiff.
- First run only: macOS asks to let the shortcut access the file(s) — click Always Allow. Silent thereafter.
It works in protected folders (Desktop / Documents / Downloads) with no Full Disk Access needed — see How it works.
toaiff ~/Music/Album # recurse a folder
toaiff track.flac other.wav # one or more files
toaiff --dry-run ~/Music/Album # preview tags/conversions, write nothing
toaiff --no-enrich ~/Music/Album # pure transcode, skip all tag enrichment
toaiff --versionConversion is decided by the actual audio codec, not the file extension, so
a .m4a holding ALAC is converted while a .m4a holding AAC is skipped.
| Input | Action |
|---|---|
| FLAC, ALAC, WavPack, Monkey's Audio (APE), TAK, TTA, MLP/TrueHD | convert → AIFF |
| WAV / raw PCM | convert → AIFF (uncompressed passthrough) |
| Already AIFF | skipped |
| MP3, AAC, Vorbis, Opus, AC3, WMA, … (lossy) | skipped |
DSD (.dsf/.dff) |
skipped (no clean lossless PCM mapping) |
| Non-audio files | ignored |
ffmpeg's AIFF encoder defaults to 16-bit, which would silently downsample
24-bit masters. toaiff reads each source's true bit depth and selects a PCM
target that is always ≥ the source depth:
| Source | AIFF target |
|---|---|
| 8-bit | pcm_s8 |
| 16-bit | pcm_s16be |
| 24-bit | pcm_s24be |
| 32-bit int | pcm_s32be |
| 32/64-bit float | pcm_f32be / pcm_f64be (AIFF-C) |
Sample rate and channel layout are never touched (no resampling). Tags and
embedded cover art are carried over via -map_metadata and an ID3v2 chunk; if
the AIFF container rejects an embedded image, the audio still converts and the
artwork is dropped with a logged note.
During conversion, toaiff derives tags that live in the file/folder names but
are missing from the audio tags, and applies one normalization. Everything is
fill-gaps-only — an existing tag is never overwritten — and enrichment is
on by default. Turn it off with --no-enrich (or TOAIFF_NO_ENRICH=1) for
a pure transcode.
| Enrichment | Source | Written to | Rule |
|---|---|---|---|
| Catalog number | album folder name, e.g. [SHA300], (snf137), {LLR004}, bare USB002 |
grouping (uppercased, e.g. SHA300) |
only if no grouping tag |
| Track / disc | leading filename token: 01 - …, 001 - …, (01 - 02) … |
track / disc |
leading number only (trailing numbers in a title ignored); only if absent |
feat.→ft. |
the title itself |
title |
word-anchored (FEISTY is safe); the one value that is edited, not gap-filled |
| Folder artwork | cover/folder/front.jpg/png beside the file |
embedded cover | only if the source has no embedded art |
Catalog detection rejects look-alikes in the same brackets — format/quality
words ([WEB FLAC], [FLAC 24]), years ((2026)), release types ([EP]), and
barcodes ({…, 5056818805226}). The heuristics are tuned to scene/label folder
naming; on a 7,988-folder test library it found ~5,500 real catalog numbers with
a single false positive. If your library is named differently and you don't want
the guesswork, run with --no-enrich.
Preview exactly what each file would get, without writing anything:
toaiff --dry-run ~/Music/Album
# toaiff: would convert 02 - Lion Soul (feat. X).flac (pcm_s24be) [grouping=ARTKL081 track=2 title=Lion Soul (ft. X) art=cover.png]ffmpeg writes the .aiff directly to its final path, and the original is
trashed only after the output is verified to be a readable AIFF. On any failure
the original is left exactly as it was.
Why it works in protected folders without Full Disk Access
A Finder Quick Action runs the script sandboxed under ShortcutsMacHelper. In
TCC-protected folders (Desktop / Documents / Downloads) that sandbox lets a
child process (ffmpeg) create files but won't let the shell rename or
delete a file ffmpeg made. So toaiff has ffmpeg write the .aiff directly
to its final name (no temp + rename) and trashes the original via
NSFileManager, which the Quick Action's scoped access to the selected file
permits. That's also why a true Quick Action here must be a Shortcut: a
hand-built Automator .workflow only ever registers as a Service on recent
macOS.
One central log at ~/Library/Logs/toaiff.log, appended to no matter where
the action runs. By default only errors are logged, so a clean run writes
nothing. For a full trace of every run / conversion / skip:
toaiff --debug ~/Music/Album # or set TOAIFF_DEBUG=1
tail -f ~/Library/Logs/toaiff.logTo debug the Finder Quick Action, add --debug to its Run Shell Script line
temporarily — change exec toaiff --notify "$@" to
exec toaiff --notify --debug "$@".
| Flag | Env | Effect |
|---|---|---|
--no-enrich |
TOAIFF_NO_ENRICH |
Pure transcode; skip all tag enrichment. |
--dry-run |
— | Preview the tag/conversion plan; write and trash nothing. |
--debug |
TOAIFF_DEBUG |
Log every run/conversion/skip, not just errors. |
| — | TOAIFF_KEEP_ORIGINALS |
Convert without trashing originals (cautious first pass). |
| — | TOAIFF_NO_NOTIFY |
Suppress the completion notification even with --notify. |
| — | TOAIFF_LOG |
Override the log file path (used by the test suite). |
No stray output files: as a Finder Quick Action the script runs with
--notifyand no terminal, so it writes nothing to stdout/stderr — which the Shortcuts "Run Shell Script" action would otherwise save as astdout.txt/stderr.txtbeside your files. Results come from the notification and the log.
brew uninstall toaiff # or: rm ~/.local/bin/toaiff
rm -f ~/Library/Logs/toaiff.logThen delete the → aiff shortcut in Shortcuts.app and toggle it off in System Settings ▸ Login Items & Extensions ▸ Finder.
Issues and PRs welcome. Run the test suite before submitting:
./tests/run.shIt generates fixtures with ffmpeg and exercises depth preservation, metadata and artwork retention, enrichment + decoy rejection, recursion, skipping, and logging — all non-destructively (no files are trashed). CI runs the same suite on every PR.