0

I'm writing a little c based synthesizer. I'm having a problem when I try to enqueue buffers with the function AudioQueueEnqueueBuffer. In different executions i'm having different errors, like malloc-error of the buffer or BAD_ISTRUCTION or BAD_ACCESS(code=EXC_I386_...). Sometimes the callback encode successfully enqueue buffers, but the program crashes when is trying to start the AudioQueue or when is copying data into the buffer. Here there is the code, I hope you can help me.

Xcode print to me also this strange warning when my code go into the callback: "warning: could not load any Objective-C class information. This will significantly reduce the quality of type information available.", but I'don't now why...

synthesis.h

#include <stdio.h>
#include <stdlib.h>
#include <AudioToolBox/AudioQueue.h>
#include <CoreAudio/CoreAudioTypes.h>
#include <math.h>
#include <stdbool.h>
#include <MacTypes.h>
#include <string.h>
#include <CoreFoundation/CoreFoundation.h>

#define PI 3.141592
#define DBG(func, ex)   fprintf(stderr, "Error in %s. n. %d\n", func, ex); exit(ex);

static const UInt8 nOfBuffer = 3;

struct wave {
    Float64 samplingRate;
    Float64 phase;
    UInt32  frequency;
    SInt16  *samples;
    UInt16  seconds;
};
typedef struct wave Wave;

struct stream {
    Wave                        sine;
    AudioStreamBasicDescription asbd;
    AudioQueueRef               audioQueue;
    AudioQueueBufferRef         audioBuffer[nOfBuffer];
    UInt32                      bufferSizeByte;
    UInt32                      counter;
    UInt32                      lastPos;
    Boolean                     isRunning;
    UInt32                      nOfPacketToRead;
};
typedef struct stream Stream;

void init(
          Stream *sine
          );

static void fillBuffer(
                void                *userData,
                AudioQueueRef       aQ,
                AudioQueueBufferRef inBuf
                );

void generateSine(
                  Stream *sine
                  );

main.c

#include "synthesis.h"

int main() {
    Stream *sine = (Stream *)malloc(sizeof(Stream));
    init(sine);
    generateSine(sine);
    return 0;
}

synthesis.c

#include "synthesis.h"
#include <limits.h>

static void fillBuffer(void *userData, AudioQueueRef aQ, AudioQueueBufferRef buf) {
    /* 
     callback for fill audioqueue's buffers
     */

    Stream *sine = (Stream *)userData;
    if(!sine->isRunning) return;

    OSStatus err = noErr;
    UInt32 dif = 0;
    UInt32 i = 0;
    SInt16 *data = (SInt16 *)buf->mAudioData;
    UInt32 lenght = buf->mAudioDataBytesCapacity / sizeof(SInt16);

    /* fill the buffer with the samples creates in generateSine */
    while (sine->counter - sine->lastPos < lenght && sine->counter < (sine->sine.samplingRate *
           sine->sine.seconds)) {
        data[i] = sine->sine.samples[sine->counter];
        i++;
        sine->counter++;
    }
    dif = sine->counter - sine->lastPos;
    buf->mAudioDataByteSize = dif * sizeof(SInt16);
    if(dif > 0) {
        err = AudioQueueEnqueueBuffer(aQ, buf, 0, NULL);
        if(err != noErr) {
            DBG("aQenqueuebuffer", err);
        }
        sine->lastPos = sine->counter;
    } else {
        sine->isRunning = false;
        err = AudioQueueStop(aQ, false);
        if(err != noErr) {
            DBG("aQstop", err);
        }
    }
}

void init(Stream *sine) {
    /*
     initialize all var and set the ASBD for an integer 16 bit linear PCM
     */

    OSStatus err = noErr;
    sine->sine.frequency = 440;
    sine->sine.phase = 0.0;
    sine->sine.samplingRate = 96000;
    sine->counter = 0;
    sine->lastPos = 0;
    sine->bufferSizeByte = 0x4000;
    sine->sine.seconds = 2;
    sine->sine.samples = (SInt16 *)malloc(sizeof(SInt16) * sine->sine.seconds * sine->asbd.mSampleRate);
    sine->asbd.mBytesPerPacket = 2;
    sine->asbd.mFramesPerPacket = 1;
    sine->asbd.mBytesPerFrame = 2;
    sine->asbd.mChannelsPerFrame = 1;
    sine->asbd.mBitsPerChannel = 16;
    sine->asbd.mSampleRate = 96000;
    sine->asbd.mFormatID = 'lpcm';
    sine->asbd.mReserved = 0;
    sine->asbd.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger | kLinearPCMFormatFlagIsPacked;
    err = AudioQueueNewOutput(&sine->asbd, fillBuffer, sine, CFRunLoopGetCurrent(), kCFRunLoopCommonModes, 0,
                              &sine->audioQueue);
    if(err != noErr) {
        DBG("aQnewOutput", err);
    }
    for(int i = 0; i < nOfBuffer; i++) {
        err = AudioQueueAllocateBuffer(sine->audioQueue, sine->bufferSizeByte, &sine->audioBuffer[i]);
        if(err != noErr) {
            DBG("aQallocatebuffer", err);
        }
    }
}

void generateSine(Stream *sine) {
    OSStatus err = noErr;
    UInt32 lenght = sine->sine.samplingRate * sine->sine.seconds;

    for(int i = 0; i < lenght; i++) {
        sine->sine.samples[i] = sin(2 * PI * sine->sine.frequency * i + sine->sine.phase) * SHRT_MAX;
    }
    sine->isRunning = true;
    for(int i = 0; i < nOfBuffer; i++) {
        fillBuffer((void *)sine, sine->audioQueue, sine->audioBuffer[i]);
    }
    err = AudioQueueStart(sine->audioQueue, NULL);
    if(err != noErr) {
        DBG("aQstart", err);
    }

    do {
        CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.25, false);
    }while(sine->isRunning);

    AudioQueueDispose(sine->audioQueue, false);
    free(sine->sine.samples);
    free(sine);
}
pino
  • 25
  • 8
  • Note: don't cast the result of `malloc` & friends or `void *` in general. – too honest for this site Mar 28 '17 at 16:47
  • But if i try to fill the buf->mAudioData Xcode tell to me that i have to cast it... – pino Mar 28 '17 at 16:49
  • You then don't use a C compiler, but most likely compile as C++. This is a different language! So either you change the tag or use a C compiler. – too honest for this site Mar 28 '17 at 16:52
  • I'm going to try! But if it's a compiler problem why when i followed the Apple example code "https://developer.apple.com/library/content/documentation/MusicAudio/Conceptual/AudioQueueProgrammingGuide/AQPlayback/PlayingAudio.html#//apple_ref/doc/uid/TP40005343-CH3-SW1" i've used the C compiler and he worked well? – pino Mar 28 '17 at 16:55
  • Apple uses clang, which will not warn for C code, that for sure! So either you change your project settings/file extension or you change the tag. The latter way you also should use C++ facilities, not `malloc` etc. – too honest for this site Mar 28 '17 at 16:58
  • I've tried with the C++ compiler, substituting malloc with new, but the bug is still here.. However thanks for your help, I really appreciate it! – pino Mar 28 '17 at 17:03
  • can you create a small, runnable project with the above code & link to it here? – Rhythmic Fistman Mar 28 '17 at 20:26
  • here there is: https://github.com/leoizzi/synth-audioqueuedemo – pino Mar 28 '17 at 20:54

0 Answers0