AudioQueue Output fails playing audio almost immediately?

On macOS Sequoia, I'm having the hardest time getting this basic audio output to work correctly. I'm compiling in XCode using C99, and when I run this, I get audio for a split second, and then nothing, indefinitely.

Any ideas what could be going wrong?

Here's a minimum code example to demonstrate:

#include <AudioToolbox/AudioToolbox.h>
#include <stdint.h>

#define RENDER_BUFFER_COUNT 2
#define RENDER_FRAMES_PER_BUFFER 128
// mono linear PCM audio data at 48kHz
#define RENDER_SAMPLE_RATE 48000
#define RENDER_CHANNEL_COUNT 1
#define RENDER_BUFFER_BYTE_COUNT (RENDER_FRAMES_PER_BUFFER * RENDER_CHANNEL_COUNT * sizeof(f32))

void RenderAudioSaw(float* outBuffer, uint32_t frameCount, uint32_t channelCount)
{
    static bool isInverted = false;
    float scalar = isInverted ? -1.f : 1.f;
    for (uint32_t frame = 0; frame < frameCount; ++frame)
    {
        for (uint32_t channel = 0; channel < channelCount; ++channel)
        {
            // series of ramps, alternating up and down.
            outBuffer[frame * channelCount + channel] = 0.1f * scalar * ((float)frame / frameCount);
        }
    }
    
    isInverted = !isInverted;
}

AudioStreamBasicDescription coreAudioDesc = { 0 };
AudioQueueRef coreAudioQueue = NULL;
AudioQueueBufferRef coreAudioBuffers[RENDER_BUFFER_COUNT] = { NULL };

void coreAudioCallback(void* unused, AudioQueueRef queue, AudioQueueBufferRef buffer)
{
    // 0's here indicate no fancy packet magic
    AudioQueueEnqueueBuffer(queue, buffer, 0, 0);
}

int main(void)
{
    const UInt32 BytesPerSample = sizeof(float);
    
    coreAudioDesc.mSampleRate = RENDER_SAMPLE_RATE;
    coreAudioDesc.mFormatID = kAudioFormatLinearPCM;
    coreAudioDesc.mFormatFlags = kLinearPCMFormatFlagIsFloat | kLinearPCMFormatFlagIsPacked;
    coreAudioDesc.mBytesPerPacket = RENDER_CHANNEL_COUNT * BytesPerSample;
    coreAudioDesc.mFramesPerPacket = 1;
    coreAudioDesc.mBytesPerFrame = RENDER_CHANNEL_COUNT * BytesPerSample;
    coreAudioDesc.mChannelsPerFrame = RENDER_CHANNEL_COUNT;
    coreAudioDesc.mBitsPerChannel = BytesPerSample * 8;
    
    coreAudioQueue = NULL;
    
    OSStatus result;

    // most of the 0 and NULL params here are for compressed sound formats etc.
    result = AudioQueueNewOutput(&coreAudioDesc, &coreAudioCallback, NULL, 0, 0, 0, &coreAudioQueue);
    
    if (result != noErr)
    {
        assert(false == "AudioQueueNewOutput failed!");
        abort();
    }
    
    for (int i = 0; i < RENDER_BUFFER_COUNT; ++i)
    {
        uint32_t bufferSize = coreAudioDesc.mBytesPerFrame * RENDER_FRAMES_PER_BUFFER;
        result = AudioQueueAllocateBuffer(coreAudioQueue, bufferSize, &(coreAudioBuffers[i]));
        if (result != noErr)
        {
            assert(false == "AudioQueueAllocateBuffer failed!");
            abort();
        }
    }
    
    for (int i = 0; i < RENDER_BUFFER_COUNT; ++i)
    {
        RenderAudioSaw(coreAudioBuffers[i]->mAudioData, RENDER_FRAMES_PER_BUFFER, RENDER_CHANNEL_COUNT);
        coreAudioBuffers[i]->mAudioDataByteSize = coreAudioBuffers[i]->mAudioDataBytesCapacity;
        
        AudioQueueEnqueueBuffer(coreAudioQueue, coreAudioBuffers[i], 0, 0);
    }

    AudioQueueStart(coreAudioQueue, NULL);

    sleep(10); // some time to hear the audio
    
    AudioQueueStop(coreAudioQueue, true);
    AudioQueueDispose(coreAudioQueue, true);
    
    return 0;
}
AudioQueue Output fails playing audio almost immediately?
 
 
Q