The question arrived in plain words: does this Apple amp have a profile for the HD 800 S? The headphones were in the 3.5 mm socket of an M4 MacBook Air, nothing else in the chain. The answer has two halves. macOS keeps no headphone profiles, no equaliser curves and no table of models. It does measure the load on every plug-in, and for a 300 ohm Sennheiser it had already switched the jack into its high-voltage mode before anyone asked. Finding that out took two commands. Making it repeatable took a Ruby script. The rest of the evening went on the harder question, which is how good this built-in output is next to the things people buy to replace it.
The short versionThe jack is driven by a Cirrus Logic CS42L84, an Apple-only codec. On plug-in it runs a DC load measurement and picks a drive mode: 1.25 V for loads under 150 ohm, 3 V between 150 ohm and 1 kilohm, 1 V for line-level inputs. The chosen mode is published in the I/O Registry as DCIDMode, next to a history of every probe. For the HD 800 S it reads High Impedance, which gives about 111 dB SPL at full scale and 29 mW into 300 ohm. Distortion and noise measure around 98 dB SINAD, the same class as Apple's USB-C dongle and well past audibility. A desktop amp adds ten times the power and better channel separation, not better sound at normal levels.
A profile for the HD 800 S?
The HD 800 S is the archetype of a headphone that "needs an amp". Sennheiser rates it at 300 ohm and 102 dB SPL for 1 V. A phone jack from the 2010s put out under 1 V and ran out of volume on it; the folklore followed. Apple's jacks changed in 2021, when the 14 and 16 inch MacBook Pro gained what Apple calls support for high-impedance headphones, and every Mac introduced since then with a 3.5 mm socket has the same circuit. The support note promises impedance detection and adaptive voltage output, with no detail about how.
The idea of a "profile" comes from somewhere else. Phone makers ship EQ presets per headphone model, and a few DAC apps do the same. It was worth asking whether Apple did too, and worth answering with the machine's own records rather than a forum thread.
What macOS admits to
The first stop is the audio device list. It confirms the headphones are on the built-in jack, not on some USB device, and that the output is running at 48 kHz:
$ system_profiler SPAudioDataType
External Headphones:
Default Output Device: Yes
Default System Output Device: Yes
Manufacturer: Apple Inc.
Output Channels: 2
Current SampleRate: 48000
Transport: Built-in
Output Source: External Headphones
That says nothing about impedance. The I/O Registry does. One grep across the whole tree for the word impedance finds a single property, on a node named after the codec chip:
$ ioreg -l -w0 | grep -i impedance
"DCIDMode" = "High Impedance"
$ ioreg -r -l -w0 -k DCIDMode -d 1 | head -3
+-o AppleCS42L84Audio <class AppleCS42L84Audio, id 0x100000711, ...>
{
"IOClass" = "AppleCS42L84Audio"
So the jack had measured the HD 800 S and chosen its high-impedance mode. Two commands, one answer. The node around that property turned out to hold a good deal more.
A codec with no datasheet
The CS42L84 is a Cirrus Logic part that Cirrus does not list. It first appeared in the 2021 MacBook Pro, and the only public description of it comes from the Asahi Linux developers, who wrote a driver for it and described it in the kernel's device-tree binding as an Apple part that is, to a degree, similar to the public CS42L42. That public cousin is a low-power codec with a stereo headphone driver rated at 114 dB dynamic range, a mic input and a headset-button detector. The 84 keeps the family resemblance and adds the thing Apple advertised: a load-measuring output stage.
In the registry the codec hangs off an I2C device, which is the control path. Audio reaches it over a separate serial interface from the SoC. The driver, com.apple.driver.AppleCS42L84Audio, publishes a small set of properties, and the interesting ones are these:
| Property | Value on this machine | Meaning |
|---|---|---|
Supports DCID | Yes | The codec can run DC load identification on the jack. |
DCIDMode | High Impedance | The drive mode chosen after the last measurement. |
HPDetect | Yes | A plug is present. |
MicDetect | No | No microphone contact on the plug. |
ButtonDetect | No | No inline remote. |
HSSwitchDetect | (3) | State of the headset switch matrix; an internal code. |
DetectConfidence | NoneNoNotify | The detector is settled and has nothing to report. |
DetectHistory | 5 entries | One record per probe since the plug went in. |
The driver registers an IOReport legend too, which is Apple's framework for kernel counters. Its channels are named Codec Output.LowImpedance, Codec Output.HighImpedance, Codec Output.HiCapacitance and Codec Output.1VRMS. Those four names are the complete list of modes the output stage has, straight from the driver, which is more than the support note gives.
Four drive modes and one measurement
DCID is the driver's name for what Apple's note calls DC load detection. On plug-in the codec most likely applies a small direct current to the load, reads the voltage, and sorts the result into a bracket. Apple's support note gives the voltages for the brackets that matter to headphones, and an independent measurement of a 2024 MacBook Air confirmed them at the socket: 1.25 V into 30 ohm, 2.97 V into 300 ohm, 1.00 V into 4.7 kilohm.
The fourth channel, HiCapacitance, is not in Apple's note. The name points at a load that looks like a capacitor to the DC probe, which is what a line input behind a coupling capacitor or a long cable presents. It is a separate case from the plain 1 V line mode, and nothing in the registry says what voltage it uses.
The path from a playing app to the headphones, as the registry describes it, is short:
+----------------------------------------+
| CoreAudio device: External Headphones, |
| 48 kHz |
+----------------------------------------+
|
v
+----------------------------------------+
| AppleCS42L84Audio driver, control over |
| I2C |
+----------------------------------------+
|
v
+----------------------------------------+
| CS42L84 codec: DAC and headphone |
| driver |
+----------------------------------------+
|
v
+----------------------------------------+
| DC load detection on plug-in |
+----------------------------------------+
|
v
+----------------------------------------+
| DCIDMode: LowImpedance, HighImpedance, |
| HiCapacitance or 1VRMS |
+----------------------------------------+
|
v
+----------------------------------------+
| 3.5 mm jack: HD 800 S, 300 ohm |
+----------------------------------------+
Nothing in that chain looks at what model of headphone is attached. The codec sees a resistance and acts on it. A 250 ohm Beyerdynamic, a 300 ohm Sennheiser and a 600 ohm AKG all land in the same bracket and get the same 3 V. That is the whole "profile".
Five probes in five seconds
The DetectHistory array is the part worth reading twice. It keeps one record per probe, and for a single plug-in of the HD 800 S there were five:
t HP Mic Button Confidence headset info
443 yes yes yes NoneWillNotify button=true microphone=true
2146 yes no no NoneNoNotify button=false microphone=false
3292 yes no no NoneNoNotify button=false microphone=false
4437 yes no no NoneNoNotify button=false microphone=false
5145 yes no no NoneNoNotify button=false microphone=false
The first record claims a microphone and a button. The HD 800 S cable has neither; its plug is a three-contact TRS. The likely reason: a four-contact headset plug has an extra ring where the TRS plug has a long sleeve, and in the moment the plug slides home the sleeve bridges the mic contact and looks, for an instant, like a headset. The codec reports what it sees, marks the result as one it will notify about, and probes again. Four more probes agree on plain stereo headphones and the detector settles. The time column is a driver counter; its unit is not documented, though the spacing fits milliseconds.
The headset half of the chip stays armed regardless. A second registry node, AppleCS42L84Mikey, presents a HID device named "Headset" on the consumer-control usage page, polled every 8 ms. It exists so that play, pause and volume presses on an Apple-style inline remote reach the event system. With a TRS plug it will never report anything, and it is still there.
A status script in plain Ruby
Two commands and some squinting at ioreg output answer the question once. A script answers it every time, and makes the history readable. The constraints were the usual ones for a tool that lives in a dotfiles folder: standard library only, no sudo, and a --json switch so the output can feed something else.
The data comes from ioreg -a, which prints the matched node as an XML property list. Apple's plutil refuses to convert this particular plist to JSON, since the node carries binary blobs with no JSON form. REXML, which ships with Ruby as a bundled gem, reads it fine, and a 20 line walker turns the dict, array, string, integer and true elements into Ruby values. The CoreAudio device list arrives as JSON from system_profiler, and the volume comes from one AppleScript call. The whole thing:
#!/usr/bin/env ruby
# frozen_string_literal: true
# jack_status.rb — report everything macOS exposes about the built-in 3.5 mm headphone jack.
#
# Sources (all read-only, no sudo):
# ioreg AppleCS42L84Audio codec node (plug detection, drive mode, history)
# ioreg AppleCS42L84Mikey HID node (headset buttons)
# system_profiler CoreAudio device list (default output, sample rate)
# osascript system volume settings
#
# Usage: ruby jack_status.rb [--json]
require "json"
require "rexml/document"
require "open3"
module Plist
# Minimal Apple XML plist reader. Enough for ioreg -a output.
module_function
def parse(xml)
doc = REXML::Document.new(xml)
node(doc.root.elements[1])
end
def node(el)
case el.name
when "dict"
el.elements.each_slice(2).to_h { |k, v| [k.text, node(v)] }
when "array" then el.elements.map { |e| node(e) }
when "string" then el.text.to_s
when "integer" then Integer(el.text)
when "real" then Float(el.text)
when "true" then true
when "false" then false
when "date" then el.text
when "data" then "<#{el.text.to_s.gsub(/\s+/, "")[0, 24]}…>"
else el.text
end
end
end
module Shell
module_function
def run(*cmd)
out, _err, status = Open3.capture3(*cmd)
status.success? ? out : ""
end
end
class JackStatus
CODEC_CLASS = "AppleCS42L84Audio"
HID_CLASS = "AppleCS42L84Mikey"
DCID_MODES = {
"Low Impedance" => "standard drive for ≤ ~150 Ω headphones and IEMs",
"High Impedance" => "boosted voltage swing for high-impedance headphones (e.g. 250–600 Ω)",
"HiCapacitance" => "line-level / capacitive loads such as powered speakers or a DAC input",
"1VRMS" => "1 Vrms line-out drive",
}.freeze
def initialize
@codec = ioreg_node("-k", "DCIDMode")
@hid = ioreg_node("-n", HID_CLASS)
@audio = coreaudio_devices
@volume = volume_settings
@model = hardware
end
def to_h
{
hardware: @model,
codec: codec_summary,
jack: jack_summary,
detect_history: history,
headset_hid: hid_summary,
coreaudio: @audio,
volume: @volume,
}
end
def print_report
h = to_h
section "Hardware" do
row "Model", h[:hardware][:model]
row "Chip", h[:hardware][:chip]
end
section "Headphone codec" do
c = h[:codec]
row "Chip", c[:chip]
row "Driver", c[:driver]
row "Device-tree match", c[:name_match]
row "Power state", c[:power_state]
row "Supports DCID", yes_no(c[:supports_dcid])
row "DCID report channels", c[:report_channels].join(", ")
end
section "Jack right now" do
j = h[:jack]
row "Headphones plugged in", yes_no(j[:headphones_detected])
row "Drive mode (DCIDMode)", j[:dcid_mode]
row " meaning", j[:dcid_meaning]
row "Microphone on plug", yes_no(j[:mic_detected])
row "Inline button", yes_no(j[:button_detected])
row "Headset switch state", j[:hs_switch].inspect
row "Detect confidence", j[:detect_confidence]
row "Detect time (ms since probe)", j[:detect_time]
end
section "Plug-detect history (oldest first)" do
puts format(" %-10s %-5s %-5s %-7s %-12s %s", "t(ms)", "HP", "Mic", "Button", "Confidence", "headset info")
h[:detect_history].each do |e|
puts format(" %-10s %-5s %-5s %-7s %-12s %s",
e[:time], yes_no(e[:hp]), yes_no(e[:mic]), yes_no(e[:button]),
e[:confidence], e[:headset_info].map { |k, v| "#{k}=#{v}" }.join(" "))
end
end
section "Headset HID (inline remote)" do
d = h[:headset_hid]
row "Product", d[:product]
row "Type hint", d[:type_hint]
row "Transport", d[:transport]
row "Opened by event system", yes_no(d[:opened])
row "Report interval (µs)", d[:report_interval]
end
section "CoreAudio devices" do
h[:coreaudio].each do |d|
flags = []
flags << "DEFAULT OUTPUT" if d[:default_output]
flags << "DEFAULT INPUT" if d[:default_input]
flags << "SYSTEM OUTPUT" if d[:default_system]
puts format(" %-26s %-9s out:%-2s in:%-2s %6s Hz %s",
d[:name], d[:transport], d[:outputs] || "-", d[:inputs] || "-", d[:sample_rate], flags.join(", "))
end
end
section "Volume" do
v = h[:volume]
row "Output", "#{v[:output]}%#{v[:muted] ? " (muted)" : ""}"
row "Input", "#{v[:input]}%"
row "Alert", "#{v[:alert]}%"
end
end
private
# ---------- sources ----------
def ioreg_node(flag, value)
xml = Shell.run("ioreg", "-r", "-a", "-w0", "-d", "1", flag, value)
return {} if xml.empty?
Plist.parse(xml).first || {}
rescue StandardError
{}
end
def coreaudio_devices
raw = Shell.run("system_profiler", "SPAudioDataType", "-json")
items = JSON.parse(raw).dig("SPAudioDataType", 0, "_items") || []
items.map do |d|
{
name: d["_name"],
transport: d["coreaudio_device_transport"].to_s.sub("coreaudio_device_type_", ""),
outputs: d["coreaudio_device_output"],
inputs: d["coreaudio_device_input"],
sample_rate: d["coreaudio_device_srate"],
default_output: d["coreaudio_default_audio_output_device"] == "spaudio_yes",
default_input: d["coreaudio_default_audio_input_device"] == "spaudio_yes",
default_system: d["coreaudio_default_audio_system_device"] == "spaudio_yes",
source: d["coreaudio_output_source"] || d["coreaudio_input_source"],
}
end
rescue StandardError
[]
end
def volume_settings
out = Shell.run("osascript", "-e", "get volume settings")
kv = out.scan(/(\w[\w ]*?):(\w+)/).to_h
{
output: kv["output volume"].to_i,
input: kv["input volume"].to_i,
alert: kv["alert volume"].to_i,
muted: kv["output muted"] == "true",
}
end
def hardware
hw = Shell.run("system_profiler", "SPHardwareDataType", "-json")
item = JSON.parse(hw).dig("SPHardwareDataType", 0) || {}
{
model: "#{item["machine_name"]} (#{item["machine_model"]})",
chip: item["chip_type"],
}
rescue StandardError
{ model: Shell.run("sysctl", "-n", "hw.model").strip, chip: nil }
end
# ---------- shaping ----------
def codec_summary
channels = Array(@codec["IOReportLegend"]).flat_map { |l| Array(l["IOReportChannels"]).map { |c| c[2] } }
{
chip: @codec["IONameMatched"].to_s.sub("audio-control,", "").upcase.then { |n| n.empty? ? nil : "Cirrus Logic #{n}" },
driver: @codec["CFBundleIdentifier"],
name_match: @codec["IONameMatched"],
power_state: @codec.dig("IOPowerManagement", "CurrentPowerState"),
supports_dcid: @codec["Supports DCID"],
report_channels: channels.map { |c| c.to_s.sub("Codec Output.", "") },
}
end
def jack_summary
mode = @codec["DCIDMode"]
{
headphones_detected: @codec["HPDetect"],
dcid_mode: mode,
dcid_meaning: DCID_MODES[mode] || "unknown mode",
mic_detected: @codec["MicDetect"],
button_detected: @codec["ButtonDetect"],
hs_switch: @codec["HSSwitchDetect"],
detect_confidence: @codec["DetectConfidence"],
detect_time: @codec["DetectTime"],
}
end
def history
Array(@codec["DetectHistory"]).map do |e|
{
time: e["DetectTime"],
hp: e["HPDetect"],
mic: e["MicDetect"],
button: e["ButtonDetect"],
confidence: e["DetectConfidence"],
headset_info: e["headset info"] || {},
}
end
end
def hid_summary
{
product: @hid["Product"],
type_hint: @hid["DeviceTypeHint"],
transport: @hid["Transport"],
opened: @hid["DeviceOpenedByEventSystem"],
report_interval: @hid["ReportInterval"],
}
end
# ---------- output helpers ----------
def section(title)
puts "\n#{title}"
puts "-" * title.length
yield
end
def row(label, value)
puts format(" %-30s %s", label, value.nil? ? "n/a" : value)
end
def yes_no(v)
case v
when true then "yes"
when false then "no"
else "n/a"
end
end
end
status = JackStatus.new
if ARGV.include?("--json")
puts JSON.pretty_generate(status.to_h)
else
status.print_report
end
Run on the Air with the Sennheisers plugged in, it prints this. The --json form carries the same fields with their raw values.
Hardware
--------
Model MacBook Air (Mac16,12)
Chip Apple M4
Headphone codec
---------------
Chip Cirrus Logic CS42L84
Driver com.apple.driver.AppleCS42L84Audio
Device-tree match audio-control,cs42l84
Power state 1
Supports DCID yes
DCID report channels 1VRMS, HiCapacitance, LowImpedance, HighImpedance
Jack right now
--------------
Headphones plugged in no
Drive mode (DCIDMode) Unplugged
meaning unknown mode
Microphone on plug no
Inline button no
Headset switch state nil
Detect confidence CompleteFinal
Detect time (ms since probe) n/a
Plug-detect history (oldest first)
----------------------------------
t(ms) HP Mic Button Confidence headset info
Headset HID (inline remote)
---------------------------
Product Headset
Type hint Headset
Transport Audio
Opened by event system yes
Report interval (µs) 8000
CoreAudio devices
-----------------
iPhone 15 Microphone unknown out:- in:1 48000 Hz
MacBook Air Microphone builtin out:- in:1 48000 Hz DEFAULT INPUT
MacBook Air Speakers builtin out:2 in:- 48000 Hz DEFAULT OUTPUT, SYSTEM OUTPUT
Volume
------
Output 100%
Input 71%
Alert 100%
The one-line meanings next to each DCID mode are an interpretation, not Apple text, and the script says so in its source. The four mode names come from the driver's own report legend and are the authoritative part.
How good it is, in numbers
Apple publishes voltages and a sample rate, 96 kHz, and nothing about distortion or noise. Audio Science Review measured a 2024 MacBook Air, a model Apple lists alongside the M4 Air for high-impedance support, and the figures below are from that review. SINAD is signal to noise and distortion, in decibels, with the signal at full scale; higher is cleaner.
| Measurement | Result |
|---|---|
| SINAD into 30 ohm, 0 dBFS | 97.9 dB |
| SINAD into 30 ohm, -6 dBFS | 94.8 dB |
| Output into 300 ohm | 2.97 V, 29 mW |
| Output into 30 ohm | 1.25 V, 48 mW |
| Output into 15 ohm | 85 mW |
| Output into 4.7 kilohm | 1.00 V (line mode) |
| Frequency response at 48 kHz | flat to 16 kHz, 6 dB down at 20 kHz |
| Crosstalk | about 50 dB |
A SINAD of 98 dB means the distortion and noise together sit at 0.0013% of the signal. For scale: Apple's nine-dollar USB-C dongle measures the same 98 dB on the same site. A current Topping or SMSL desktop DAC measures 110 to 120 dB. The gap between 98 and 120 is real on an analyser and absent in a listening room; the threshold where trained listeners begin to notice distortion in music sits far below either. The reviewer's verdict was five out of five, with the note that an external amp on a Mac made since 2021 earns its place only with demanding headphones.
Two numbers in the table deserve a second look. The response falls 6 dB by 20 kHz when the output runs at 48 kHz, which is a steep reconstruction filter starting early; setting the device to 96 kHz in Audio MIDI Setup moves that corner up and out of the audible band. And 50 dB of crosstalk is ordinary for a shared-ground 3.5 mm output and poor next to a desktop amp's 80 dB or better. Whether that is audible is a long argument; it is the one measurement here where the Mac is plainly behind.
Enough for 300 ohm
The arithmetic for the HD 800 S is short. Sennheiser's 102 dB SPL at 1 V, with 2.97 V available, gives a gain of 20 times the base-10 logarithm of 2.97, which is 9.5 dB, for a ceiling of about 111 dB SPL at full scale. Loud listening averages 80 to 85 dB with 15 to 20 dB of peaks on well-recorded music, so the peaks reach 100 to 105 dB and leave 6 to 10 dB of margin. At 102 dB for 1 V, reaching 100 dB takes about 2 mW; the 29 mW the jack delivers at 300 ohm is 14 times that, or 11 dB of headroom above it.
Where the margin goes is EQ. A 6 dB bass shelf, which is a common correction for the HD 800 S, takes 6 dB off the headroom before any music plays. That lands the loudest peaks at the ceiling. Listeners who equalise and listen loud are the "demanding" case the reviewer meant, and for them the jack is enough on most days and short on some.
None of this needed a setting. The mode switched on plug-in, the registry showed it, and the headphones played at the volume the folklore says they cannot. The volume slider sat at 31 percent.
What a desktop amp still buys
A JDS Atom or a Schiit Magni delivers 250 to 330 mW into 300 ohm, around 9 V, which is ten times the Mac's power and 10 dB more ceiling. Channel separation improves by 30 dB or more. Output impedance drops to a fraction of an ohm, though at 300 ohm load that stops mattering. Noise floors get lower, which is audible on sensitive in-ears and not on a 102 dB per volt open-back.
What a desktop amp does not buy is a different frequency response or a lower distortion that anyone can hear from this starting point. People who prefer the HD 800 S on a tube amp or a high output-impedance design are describing a tuning they like, a mild bass lift from the interaction of output impedance with the driver's resonance, and that is a legitimate reason to own one. It is not a deficiency of the jack.
The honest ranking, then: the Mac's jack is a good DAC and a modest amp. For a 300 ohm headphone at sane levels it is complete. For EQ with headroom, for very loud listening, or for the flavour of a particular amplifier, it is the thing you add to, and the registry will tell you which mode it was in when you did.
What to keep in mind
- The SINAD figures were taken into 30 ohm. No published measurement shows SINAD into 300 ohm for this jack. The reviewer's distortion plots at 300 ohm look as clean as the 30 ohm ones, which is reassuring and not the same as a number.
- The mode is chosen at plug-in. A plug seated slowly or a dirty socket can settle in the wrong bracket; the history will show it. Reseating the plug forces a new measurement.
- The registry property names belong to Apple's driver and can change with any macOS release. The script reads what is there and prints
n/afor what is not. - The I/O Registry describes the codec, not the sound. A codec in the right mode with a bad cable, a dirty jack or a wrong sample rate is still a codec in the right mode.
- The crosstalk and filter figures depend on the output sample rate and the analyser's load. Treat them as the shape of the thing, not as a spec sheet.
The question had a one-word answer and a worthwhile detour. There is no profile. There is a chip that measures, decides and remembers, and that is the better design: it works for headphones Apple has never heard of.
Sources
- Apple Support, Use high-impedance headphones with your Mac
- Audio Science Review, Review and measurements of MacBook Air (2024) headphone output
- Linux kernel, device-tree binding for the Cirrus Logic CS42L84 codec
- Cirrus Logic, CS42L42 datasheet (the public cousin of the CS42L84)
- Sennheiser, HD 800 S product page and specifications
- MacRumors, Apple provides details on using high-impedance headphones with new MacBook Pros (2021)
- Headphonesty, MacBook Air headphone jack tests, a summary of the ASR measurements
- StereoNET forum, entry-level headphone amplifier spec comparison (JDS Atom, Schiit Magni output power)
- Frieve audio review index, Apple USB-C to 3.5 mm adapter measurements summary
- Apple Developer, IOKit documentation (I/O Registry)
- REXML, the XML toolkit bundled with Ruby