Core Audio Queues Cracking

I get a cracking at the beginning and end of a core audio output queue. The code should simply generate an inaudible tone.

i created a simplified version on github:

https://github.com/MrMatthias/CoreAudioCrackle


the output looks like this:

http://i63.tinypic.com/160yexw.jpg (Frequency is 10kHz in this image instead of 20kHz )


Source:

import "ViewController.h"
#import <stdlib.h>
#import <AudioToolbox/AudioToolbox.h>
#import <Accelerate/Accelerate.h>
#import <objc/runtime.h>
#define BUFFERS 3
#define DURATION 0.5
typedef struct UserData {
    AudioQueueRef               outputQueue;
    UInt32                      outputSamplePosition;
    AudioQueueBufferRef         outputBuffers[BUFFERS];
    AudioStreamBasicDescription outputDesc;
    /
} UserData;
@interface ViewController () {
    UserData userData;
}
@end
@implementation ViewController
- (IBAction)togglePlay:(NSButton*)sender {
    if(sender.tag == 0) { /
        [self startOutputQueue];
        sender.tag = 1;
        sender.title = @"Stop";
    } else { /
        [self stopOutputQueue];
        sender.tag = 0;
        sender.title = @"Start";
    }
}
- (void)viewDidLoad {
    [super viewDidLoad];
    [self setupOutputQueue];
    /
}
-(void)viewWillDisappear {
    [self tearDown];
}
#pragma mark - Output Queue
-(void) startOutputQueue {
    if(userData.outputQueue != NULL) {
        if(!checkError(AudioQueuePrime(userData.outputQueue, 0, NULL), "AudioQueuePrime")) {
            NSLog(@"Error priming QutputQueue");
        }
     
        if(!checkError(AudioQueueStart(userData.outputQueue, NULL), "AudioQueueStart Output")) {
            NSLog(@"Error starting OutputQueue");
        }
    }
}
-(void) setupOutputQueue {
    memset(&userData.outputDesc, 0, sizeof(userData.outputDesc));
    userData.outputDesc.mFormatID = kAudioFormatLinearPCM;
    userData.outputDesc.mFramesPerPacket = 1;
    userData.outputDesc.mFormatFlags = kAudioFormatFlagIsBigEndian | kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked;
    userData.outputDesc.mChannelsPerFrame = 1;
    userData.outputDesc.mSampleRate = 44100;
    userData.outputDesc.mBitsPerChannel = 16;
    userData.outputDesc.mBytesPerFrame = userData.outputDesc.mBytesPerPacket = 2;
    userData.outputSamplePosition = 0;
    if (userData.outputQueue == NULL) {
        if(!checkError(AudioQueueNewOutput(&userData.outputDesc, outputCallback, &userData, CFRunLoopGetCurrent(), kCFRunLoopCommonModes, 0, &userData.outputQueue), "AudioQueueNewOutput")) {
            return;
        }
        UInt32 bufferSize = userData.outputDesc.mBytesPerFrame * userData.outputDesc.mSampleRate * DURATION;
        for (int i=0; i<3; i++) {
            if(!checkError(AudioQueueAllocateBuffer(userData.outputQueue, bufferSize, &userData.outputBuffers[i]), "AudioQueueAllocateBuffer")) {
                return;
            }
            outputCallback(&userData, userData.outputQueue, userData.outputBuffers[i]);
        }
    }
}
-(void)stopOutputQueue {
    if(userData.outputQueue != NULL) {
        checkError(AudioQueueStop(userData.outputQueue, TRUE), "AudioQueueStop");
    }
}
-(void)tearDown {
    [self stopOutputQueue];
    [self deleteOutputQueue];
    [self freeOutputBuffers];
}
-(void) deleteOutputQueue {
    if(userData.outputQueue != NULL) {
        [self freeOutputBuffers];
        checkError(AudioQueueDispose(userData.outputQueue, TRUE), "AudioQueueDispose");
        userData.outputQueue = NULL;
    }
}
-(void) freeOutputBuffers {
    for (int i=0; i<BUFFERS; ++i) {
        checkError(AudioQueueFreeBuffer(userData.outputQueue, userData.outputBuffers[i]), "AudioQueueFreeBuffer");
    }
}
static void outputCallback(void *inUserData, AudioQueueRef inAQ, AudioQueueBufferRef inBuffer) {
    UserData *userData = (UserData*) inUserData;

    UInt32 sampleCount = DURATION * userData->outputDesc.mSampleRate;
    double f1 = userData->outputDesc.mSampleRate / 20000;
    for (int i=0; i<sampleCount; i++) {
        SInt16 sample = CFSwapInt16HostToBig((SInt16)(SHRT_MAX * ( sin((userData->outputSamplePosition + i) * 2.0f * M_PI / f1) )));
        ((SInt16*)inBuffer->mAudioData)[i] = sample;
    }
    userData->outputSamplePosition += sampleCount;
    inBuffer->mAudioDataByteSize = sampleCount * 2;

    AudioQueueEnqueueBuffer(userData->outputQueue, inBuffer, 0, NULL);

}
#pragma mark utility
static BOOL checkError(OSStatus error, const char* location) {
    if (error == noErr) {
        return YES;
    }
    /
    UInt32 swap = CFSwapInt32HostToBig(error);
    char *pSwap =(char*) &swap;
    if (isprint(pSwap[0]) && isprint(pSwap[1]) && isprint(pSwap[2]) && isprint(pSwap[3])) {
        char errorString[7];
        errorString[0] = errorString[5] = '\'';
        sprintf((errorString+1), "%s", pSwap);
        errorString[6] = '\0';
        printf("%s at %s", errorString, location);
    }
    /
    CFErrorRef cferror = CFErrorCreate(kCFAllocatorDefault, kCFErrorDomainOSStatus, error, NULL);
    NSLog(@"Error at %s: %@", location, cferror);
    return NO;

}
@end

Your tone burst appears to go to full volume in less than a tenth of a millisecond, and also end with a sharp transition from full volume to silence. Those sharp transitions to/from high volume levels can create click sounds (key clicks or wide spectrum transient noise).


One way to get rid of these type of key click artifacts is to ramp to full volume over at least 5 to 50 milliseconds in your first audio buffer and ramp down to silence or zero volume for a similar length of time at the end of your last audio buffer. Use a longer ramp for bigger loud speakers, but even a few milliseconds of ramping helps diminish clicking. A linear ramp will work, but a raised cosine ramp may be even better in terms of lowering transient spectral spatter.

Core Audio Queues Cracking
 
 
Q