Skip to content

Instantly share code, notes, and snippets.

@htlin222
Created July 27, 2026 06:30
Show Gist options
  • Select an option

  • Save htlin222/ad1ae4ffa7074a726d54863136167b8b to your computer and use it in GitHub Desktop.

Select an option

Save htlin222/ad1ae4ffa7074a726d54863136167b8b to your computer and use it in GitHub Desktop.
iphone-ringtone-m4r — SKILL.md style reference: convert audio to iOS .m4r ringtone/text tone with ffmpeg
name iphone-ringtone-m4r
description Use when converting an audio file (mp3, wav, m4a, flac) into an iPhone ringtone or text tone, when a .m4r file is rejected by iOS, or when asked to trim/cut a song to a ringtone-length clip for AirDrop, Dropbox, or Finder sync.

iPhone Ringtone (.m4r) Conversion

Overview

iOS ringtones are plain AAC-in-MP4 files with the extension .m4r. There is no special codec — the only real constraints are the container, the extension, and the length limit. One ffmpeg command does the whole job.

Length limits (the thing people get wrong)

Use Max length Where it appears in iOS
Ringtone (incoming call) 40 s Settings → Sounds & Haptics → Ringtone
Text tone / alert 29 s Settings → Sounds & Haptics → Text Tone

A 29 s file shows up in both lists, so 29 s is the safer default when the user hasn't specified. Anything over the limit is silently rejected on import.

The command

ffmpeg -y -ss 50 -i input.mp3 -t 29 \
  -af "afade=t=in:st=0:d=0.3,afade=t=out:st=27.5:d=1.5" \
  -c:a aac -b:a 192k -ar 44100 -ac 2 \
  -f ipod -movflags +faststart output.m4r
Flag Why
-f ipod Required. ffmpeg can't infer a muxer from .m4r and errors out: Unable to choose an output format. ipod is the MP4/M4A muxer.
-ss before -i Fast input seek. Fade timestamps then count from the cut, not the original file.
-t 29 Clip length. Keep ≤ 29 (text tone) or ≤ 40 (ringtone).
afade out at len - 1.5 Avoids a hard cut at the end. Recompute when -t changes.
-c:a aac -b:a 192k AAC is what iOS expects. 192k stereo ≈ 700 KB for 29 s.
-movflags +faststart Moves the moov atom to the front so the file scrubs instantly.

Verify before handing it over:

ffprobe -v error -show_entries format=duration -show_entries stream=codec_name \
  -of default=noprint_wrappers=1 output.m4r
# expect: codec_name=aac, duration=29.000000

Multiple cuts from one source

Give each output a distinct filename — iOS lists ringtones by filename, so ring.m4r and ring-50s.m4r coexist, but re-importing the same name overwrites.

for spec in "0:ring-29s.m4r" "50:ring-50s-29s.m4r"; do
  ss="${spec%%:*}"; out="${spec##*:}"
  ffmpeg -y -v error -ss "$ss" -i ring.mp3 -t 29 \
    -af "afade=t=in:st=0:d=0.3,afade=t=out:st=27.5:d=1.5" \
    -c:a aac -b:a 192k -ar 44100 -ac 2 -f ipod -movflags +faststart "$out"
done

Getting it onto the phone

  1. AirDrop — fastest. Send the .m4r; the phone offers "Save to Ringtones".
  2. Dropbox / Files app — share → Save to Files, then tap the .m4r in Files.
  3. Finder sync — plug in, select the device in the Finder sidebar, drag the file in.

Then: Settings → Sounds & Haptics → Ringtone (or Text Tone).

Common mistakes

Mistake Symptom Fix
Omitting -f ipod Unable to choose an output format for 'x.m4r' Add -f ipod
Just renaming .mp3.m4r iOS won't list it Must be re-encoded to AAC/MP4
Clip longer than the limit File imports but never appears in the list Trim to ≤ 40 s / ≤ 29 s
-ss after -i with fade offsets from the original file Fades land in the wrong place Put -ss before -i; time fades from 0
Reusing one filename for several cuts Later import replaces the earlier tone One distinct filename per cut

Alternative: no ffmpeg

macOS ships afconvert, but it needs a pre-trimmed input and can't fade:

afconvert -f m4af -d aac -b 192000 trimmed.wav output.m4r

Prefer ffmpeg unless it isn't installed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment