2

I have a skeleton audio app which uses kAudioUnitSubType_HALOutput to play audio via a AURenderCallback. I'm generating a simple pure tone just to test things out, but the tone changes pitch noticeably from time to time; sometimes drifting up or down, and sometimes changing rapidly. It can be up to a couple of tones out at ~500Hz. Here's the callback:

static OSStatus outputCallback(void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags,
                               const AudioTimeStamp *inTimeStamp, UInt32 inOutputBusNumber,
                               UInt32 inNumberFrames, AudioBufferList *ioData) {

    static const float frequency = 1000;
    static const float rate = 48000;
    static float phase = 0;

    SInt16 *buffer = (SInt16 *)ioData->mBuffers[0].mData;

    for (int s = 0; s < inNumberFrames; s++) {
        buffer[s] = (SInt16)(sinf(phase) * INT16_MAX);
        phase += 2.0 * M_PI * frequency / rate;
    }

    return noErr;

}

I understand that audio devices drift over time (especially cheap ones like the built-in IO), but this is a lot of drift — it's unusable for music. Any ideas?

Recording http://files.danhalliday.com/stackoverflow/audio.png

Dan Halliday
  • 725
  • 6
  • 22
  • have you taken a recording looking into it - especially if it is depending on buffers? In my experience CoreAudio doesn't drift at all. – Volker Apr 28 '15 at 20:41
  • I've added it above. Three distinct tones in the first minute; none match the desired 1kHz I set. – Dan Halliday Apr 28 '15 at 20:51
  • and the loop in your callback does produce the wanted results - best tested by filling a wave with it? no samplerate differences or similar "simple" errors? – Volker Apr 28 '15 at 20:54
  • That's right, the code seems to work perfectly other than the drifting. I've tried setting the `kAudioUnitSubType_HALOutput ` to 44.1kHz to match my hardware (and tried taking out the code that sets stream format ie. leaving it at its default) but the problem still persists. – Dan Halliday Apr 28 '15 at 20:57

1 Answers1

3

You're never resetting phase, so its value will increase indefinitely. Since it's stored in a floating-point type, the precision of the stored value will be degraded as the value increases. This is probably the cause of the frequency variations you're describing.

Adding the following lines to the body of the for() loop should significantly mitigate the problem:

if (phase > 2.0 * M_PI)
    phase -= 2.0 * M_PI;

Changing the type of phase from float to double will also help significantly.