Music Understanding is a new on-device framework that analyzes audio files to extract musical intelligence including beat/rhythm, key, structure (sections/segments/phrases), pace, instrument activity, and loudness — all without requiring any ML or signal processing expertise.
• Enables rich music-reactive features (beat-synced visuals, auto video editing, DJ tools) with a single high-level API — no CoreML or DSP knowledge required
• All analysis runs entirely on-device, preserving user privacy and enabling offline use
• Pre-computable, Codable results mean you can bundle analysis data at build time and animate your game or app UI without runtime cost
Analyzes a local audio file using MusicUnderstandingSession and displays the detected musical key and BPM, then lists the timestamp of every detected beat.
import SwiftUI
import MusicUnderstanding
import AVFoundation
struct BeatKeyAnalyzerView: View {
@State private var bpm: Double? = nil
@State private var keyLabel: String = "—"
@State private var beatTimes: [Double] = []
@State private var isAnalyzing = false
@State private var showFilePicker = false
@State private var fileURL: URL? = nil
var body: some View {
NavigationStack {
VStack(spacing: 20) {
Button("Select Audio File") { showFilePicker = true }
.buttonStyle(.borderedProminent)
if isAnalyzing {
ProgressView("Analyzing…")
} else {
GroupBox("Key") {
Text(keyLabel)
.font(.title)
.bold()
}
GroupBox("Tempo") {
Text(bpm.map { String(format: "%.1f BPM", $0) } ?? "—")
.font(.title)
.bold()
}
GroupBox("Beats (\(beatTimes.count))") {
ScrollView {
LazyVStack(alignment: .leading) {
ForEach(beatTimes.indices, id: \.self) { i in
Text(String(format: "Beat %d: %.3fs", i + 1, beatTimes[i]))
.font(.caption.monospaced())
}
}
}
.frame(maxHeight: 200)
}
}
}
.padding()
.navigationTitle("Music Understanding")
}
.fileImporter(isPresented: $showFilePicker, allowedContentTypes: [.audio]) { result in
if case .success(let url) = result {
fileURL = url
Task { await analyze(url: url) }
}
}
}
@MainActor
func analyze(url: URL) async {
isAnalyzing = true
defer { isAnalyzing = false }
// Ensure access to the security-scoped resource
guard url.startAccessingSecurityScopedResource() else { return }
defer { url.stopAccessingSecurityScopedResource() }
let asset = AVURLAsset(url: url, options: [
AVURLAssetPreferPreciseDurationAndTimingKey: true
])
do {
let session = MusicUnderstandingSession(asset: asset)
// Only request rhythm and key to avoid unnecessary computation
let sessionResult = try await session.analyze(for: [.rhythm, .key])
// Extract BPM
bpm = sessionResult.rhythm?.beatsPerMinute
// Extract beat timestamps (CMTime → seconds)
beatTimes = (sessionResult.rhythm?.beats ?? []).map { $0.seconds }
// Extract key signature (first range if available)
if let firstKey = sessionResult.key?.ranges.first?.value {
let tonicName = firstKey.tonic.description
let modeName = firstKey.mode == .major ? "Major" : "Minor"
keyLabel = "\(tonicName) \(modeName)"
} else {
keyLabel = "Unknown"
}
} catch {
keyLabel = "Error: \(error.localizedDescription)"
}
}
}
#Preview {
BeatKeyAnalyzerView()
}Set AVURLAssetPreferPreciseDurationAndTimingKey to true for most accurate results; beatsPerMinute is optional and will be nil if fewer than two beats are detected; the targeted analyze(for:) API only populates the requested result fields — all others are nil
Runs on-device; no specific Apple Intelligence hardware required, but performance will vary by device generation
More iOS 27 APIs land every week.
Get notified when new capabilities are published — no noise, just signal.