Conversation
The released macOS binary inherited Homebrew's absolute install name for libportaudio.2.dylib and aborted under dyld on any Mac without it. CI could never see it: the runner brewed portaudio, then smoke-tested the .pkg on that same runner. CoreAudio and AudioToolbox ship with macOS, so the binary now links only what every Mac already has. PortAudio stays in the tree, built OFF on the macOS release leg alone. Also add the guard that would have caught this: a macOS CI step failing on any load command outside /usr/lib and /System.
A lost device does not stop the render callback -- the HAL keeps pulling until the unit does. So clear() asking unit_alive_() took the ring_.drop() branch while the callback was inside read(), writing both positions from the producer side and leaving the next read a whole ring of stale audio to play. clear() now asks callback_running_(), which is liveness alone. configure() and write() keep unit_alive_(), which is the question they actually have.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The released macOS binary aborts at launch on any Mac without Homebrew's PortAudio:
CI brewed
portaudioon themacos-14runner and then smoke-tested the.pkgon thatsame runner, so the one host that could have caught it was the one host guaranteed not to.
The fix is not to bundle the dylib but to stop needing it. CoreAudio and AudioToolbox ship
with macOS, so the binary now links only what every Mac already has. PortAudio stays in the
tree and stays
ONeverywhere but the macOS release leg.The sink
src/coreaudio_sink.{h,cpp}, anAudioSinkover AUHAL, modelled onportaudio_sink.{h,cpp}and reusing
PcmRingBuffer,pcm_volumeandSinkRecovery/OutageGapHandoffrather thanreimplementing them. The four invariants
portaudio_sink.hwrites down come from theAudioSinkcontract, not from PortAudio, so they carry across unchanged.AudioTimeStamp, converted throughmach_timebase_info(). That timestamp is when the HAL hands the buffer over rather thanwhen it reaches the DAC, so the device's latency, safety offset, stream latency and the
unit's converter latency go on top — unlike PortAudio, where
outputBufferDacTimealreadycarries them.
letting the AU convert.
capabilities()probes the three ladders by setting each candidateformat on a scratch unit — the same question a stream asks.
-o coreaudio[:<device>], with PortAudio's device grammar exactly, re-resolved per stream.AudioDeviceIDis stable andAudioObjectAddPropertyListenerreportskAudioDevicePropertyDeviceIsAlivedirectly, sothere is no
Pa_Terminate()/Pa_Initialize()and no index renumbering.SinkRecovery's budget: a move isan ordinary event, and spending the reopen on it would leave a real outage in the same
track with nothing left.
CI
with_portaudiojoins the matrix on every leg (a key missing from one reads as false),OFFonmacos-arm64only. That leg'sbrew install portaudio pkgconfis gone — nothingleft on it needs pkg-config — its
expect_backendsisnull, stdout, coreaudio, and itsruntime_packagesis the fact that there are none.And the guard that would have caught this: a macOS step running
otool -Lover the stagedbinary and failing on any load command outside
/usr/liband/System. Worth havingwhatever the backend is. Proven by hand against both a clean listing and a Homebrew-tainted
one; no deliberately broken commit is left behind.
What is and is not proven
Linux is unchanged and green: 437/439 (the two failures are a local path-length quirk of the
task worktree, not a regression — they resolve a relative socket path against a cwd that
overflows
sun_path, and pass from a short cwd).scripts/smoke_test.sh,scripts/format.sh --checkandshellcheck scripts/*.share clean, andtests/device_spec_test.cppcovers the-o coreaudio[:...]forms and the Linuxreserved-backend refusal on every leg.
This was written on Linux, where it is not built, so CI's
macos-arm64leg was the firstcompiler to see it — and it compiles clean there under
-Werror. That leg'sotool -Lguardreports only CoreAudio, AudioToolbox, CoreFoundation,
libc++andlibSystem: no/opt/homebrewpath, no bundled dylib. 425/425 ctest on macOS, and nobrewstep left.Draft until the hardware pass, which no runner can do — every
smoke_test.shinvocationis
-o null, so nothing in CI opens a device. Owed on a Mac with real output: clean tone at48 kHz/16-bit, 44.1 kHz/24-bit and 44.1 kHz/32-bit; exact
on_frames_playedaccountingagainst the wall clock; a DAC offset plausible against the device's reported latency; a
mid-stream format change and recovery from a refused one; volume, mute, the ramp across a
change and the shutdown latch; a default-output move and an unplug while playing; and a real
Sendspin server driving it end to end. Whatever the pass finds goes into roadmap item 27 the
way item 3's did.