Question

I have an iOS/Objective-C program that uses a single audio unit to play a generated signal when a button is pressed. I'd like to add functionality such that:

a) When the button is first pressed, a signal is generated in some kind of numeric array.

b) The audio then begins, and the render callback accesses (and plays) that generated signal.

Given my current code, I feel like these additions will just be a few lines, but I'm having trouble with the syntax, which variable types to use, how to track the current sample, and so on. I've included the related code as it is now:


The button press:

- (IBAction)startPressed:(id)sender {
        [self setupAudioPlayer];
        [self createSignal];
        [self playAudio];
}

A line from setupAudioPlayer:

input.inputProcRefCon=&mySignal; // mySignal is an instance var

The audio creation:

-(void)createSignal{
    int beepLength=0.020*Fs; // Fs is sampling frequency
    float beepFrequency=440; // Hz

    // Declare some kind of numeric array "mySignal", which is an instance var.
    mySignal=...?

    // Generate audio signal (pure tone)
    for (int i=1; i<=beepLength; i++) {
        float t=i/Fs;
        mySignal[i]=sinf(2*M_PI*beepFrequency*t);
    }
}

The render callback:

OSStatus RenderTone(
                    void *inRefCon,
                    AudioUnitRenderActionFlags  *ioActionFlags,
                    const AudioTimeStamp        *inTimeStamp,
                    UInt32                      inBusNumber,
                    UInt32                      inNumberFrames,
                    AudioBufferList             *ioData)

{
    const int channel1 = 0;
    Float32 *buffer = (Float32 *)ioData->mBuffers[channel1].mData;

    // This is where things get hazy
    Float32 *mySignal=(Float32 *)inRefCon;
    for (UInt32 frame = 0; frame < inNumberFrames; frame++)
    {
            buffer[frame]=mySignal[?]; 
    }

    return noErr;
}

So, to summarize my questions: How should mySignal be defined? How do I access this instance variable from RenderTone (my 'hazy' code above is just a guess)? How can I track the current sample in RenderTone? Is there anything else missing/wonky in this approach?

Thanks for reading and for any help, really appreciated!

(I have seen sample code that passes a reference to the view controller's instance into the render callback, and then accesses the instance variables that way. However, perhaps mistakenly, I read elsewhere that this wasn't good form as it may involve too much computational overhead for a callback with such strict timing requirements.)

Was it helpful?

Solution

Since you're generating the frames from an algebraic function, why don't you simply follow Matt Gallagher's example? In brief: just move the function inside the render callback and transfer the parameters through the vc instance.

Generally speaking your choices are limited for passing data to a callback that has a pre-defined form. I'm probably the last person to counsel on good form in Objective C, but one of the few options is to use globals.

You could pass mySignal array (or else the frequency) as a global. Not the most 'elegant' object-oriented solution, but one that will work and avoid all the O.O. frou-frou overhead. Seems only appropriate to use a C-based solution, since the render callback is at base a C function.

As to "tracking," not quite sure what you mean, but in my own work with generating tones, I've initialized a remainingCycles global with the tone-length (in frame cycles = length in seconds * Fs or sampleRate whatever you want to call it) and decrementing each pass through the frame loop; when the number hits zero, you end the tone. (Of course, you could use an instance variable instead of a global.)

Maybe this violates the Canons of Object-Oriented Coding, but at the end of the day, you just need to get the job done.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top