OSXでエミュレータ向けサウンドルーチンを作る

OSX+XCode+swift環境でエミュレータ向けサウンドルーチンを作った話です。
音を鳴らすサンプルは比較的簡単に見つかると思いますが、多くは静的なWAVファイル・MP3ファイルを鳴らすというものでエミュレータのようにリアルタイムで波形を生成する用途には向かないものでした。この辺の情報が少なかったのでメモ。
注意:古いAPIを使っています。警告が出ますがとりあえず動くようです。
swiftと書いてますがこのソース自体はCです。
コンパイルした環境:xcode11.3.1

//
//  audio.c


#include "audio.h"
#include <CoreAudio/AudioHardware.h>

unsigned long    deviceBufferSize;
AudioDeviceID    device;
Boolean        initialized;    // successful init?
Boolean        soundPlaying;    // playing now?
AudioStreamBasicDescription    deviceFormat;    // info about the default device
float amp;
float pan;
PSG *myPSG;

int sTestCount=0;


// コールバック関数です 外部から直接この関数を呼び出すことはありません

OSStatus appIOProc (AudioDeviceID  inDevice, const AudioTimeStamp*  inNow, const AudioBufferList*   inInputData,
                    const AudioTimeStamp*  inInputTime, AudioBufferList*  outOutputData, const AudioTimeStamp* inOutputTime,
                    void* defptr)
{
    int i;
    
    // load instance vars into registers
    
    
    int numSamples = deviceBufferSize / deviceFormat.mBytesPerFrame;
    
    // assume floats for now....
    float *out = (float*)outOutputData->mBuffers[0].mData;
    float wave;
    float pzl=1.0*amp;//左ボリューム
    float pzr=1.0*amp;//右ボリューム
    
    for (i=0; i<numSamples; ++i) {
        
    //サウンド生成のコア部分 約440Hzの矩形波を生成

        wave+=(sTestCount<55)?5000:0;
        sTestCount++;
        if (sTestCount>109){sTestCount=0;}
        wave = wave/32768.0;
        
        // write output
        *out++ = wave * pzl;        // left channel
        *out++ = wave * pzr;        // right channel
    }
    
    return kAudioHardwareNoError;
}

// 初期設定です。ViewのviewDidLoad()などから呼び出すものです
// 大昔のネットのサンプルからのコピペです

void audioSetup(void)
{
    OSStatus                err = kAudioHardwareNoError;
    UInt32                count;
    device = kAudioDeviceUnknown;
    
    initialized = false;
    
    // get the default output device for the HAL
    count = sizeof(device);        // it is required to pass the size of the data to be returned
    err = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultOutputDevice,  &count, (void *) &device);
    if (err != kAudioHardwareNoError) {
        fprintf(stderr, "get kAudioHardwarePropertyDefaultOutputDevice error %d\n", err);
        return;
    }
    
    // get the buffersize that the default device uses for IO
    count = sizeof(deviceBufferSize);    // it is required to pass the size of the data to be returned
    err = AudioDeviceGetProperty(device, 0, false, kAudioDevicePropertyBufferSize, &count, &deviceBufferSize);
    if (err != kAudioHardwareNoError) {
        fprintf(stderr, "get kAudioDevicePropertyBufferSize error %d\n", err);
        return;
    }
    fprintf(stderr, "deviceBufferSize = %ld\n", deviceBufferSize);
    
    // get a description of the data format used by the default device
    count = sizeof(deviceFormat);    // it is required to pass the size of the data to be returned
    err = AudioDeviceGetProperty(device, 0, false, kAudioDevicePropertyStreamFormat, &count, &deviceFormat);
    if (err != kAudioHardwareNoError) {
        fprintf(stderr, "get kAudioDevicePropertyStreamFormat error %d\n", err);
        return;
    }
    if (deviceFormat.mFormatID != kAudioFormatLinearPCM) {
        fprintf(stderr, "mFormatID !=  kAudioFormatLinearPCM\n");
        return;
    }
    if (!(deviceFormat.mFormatFlags & kLinearPCMFormatFlagIsFloat)) {
        fprintf(stderr, "Sorry, currently only works with float format....\n");
        return;
    }
    
    initialized = true;
    amp=1.0;
    pan=1.0;

    err = AudioDeviceAddIOProc(device, appIOProc, nil);    // setup our device with an IO proc
     if (err != kAudioHardwareNoError) {
         fprintf(stderr, "get AudioDeviceAddIOProc error %d\n", err);
         return;
     }

     err = AudioDeviceStart(device, appIOProc);                // start playing sound through the device
     if (err != kAudioHardwareNoError) {
         fprintf(stderr, "get AudioDeviceStart error %d\n", err);
         return;
     }

    
     soundPlaying = true;                        // set the playing status global to true

}