• Open Menu Close Menu
  • Apple
  • Shopping Bag
  • Apple
  • Mac
  • iPad
  • iPhone
  • Watch
  • TV
  • Music
  • Support
  • Search apple.com
  • Shopping Bag

Lists

Open Menu Close Menu
  • Terms and Conditions
  • Lists hosted on this site
  • Email the Postmaster
  • Tips for posting to public mailing lists
How to fill the ioData parameter in the Audio Unit rendering call back?
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

How to fill the ioData parameter in the Audio Unit rendering call back?


  • Subject: How to fill the ioData parameter in the Audio Unit rendering call back?
  • From: Xu Ting <email@hidden>
  • Date: Thu, 12 Mar 2009 21:00:56 +0900

Hi, list,

I am working on an iPhone app, in which I use the CARingBuffer to hold
the data fragment
which are read from the audio file and mix them by using an Audio Unit
named Multichannel Mixer.

I had correctly setup the Multichannel Mixer Unit, RemoteIO Unit and
CARingBuffer. When I
call the AUGraphStart(myGraph), it correctly called the callback
function I setup.
But I am confused about the ioData parameter which is passed in to
hold the data that need to be
rendered, because now, my app plays these 2 audio files just like an
old cassette tape player
as if it is out of battery(Playing speed is OK, on the sound sounds
like it). I guess the problem
lies on the ioData parameter, so my question is does my guessing
wrong? If not, how to set the
ioData parameter correctly?

My purpose is play 2 audio files simultaneously, and let one file play
in the left earphone,
and the other in the right one.
I had successfully done this by read in the whole audio files data at
once when I tried to use
the sample code from the CAPlayThru, because iPhone can not read too
much data, so I
have to use the ring buffer and read the data when they are needed.

Here is my current callback function code, and I learned this from
CAPlayThru example.
*I added some of my understanding in the comments including the 2 most
hardest part for me
 understand.

[My callback function]

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

            // in order get access to my CARingBuffer data
        MultichannelMixer *THIS = (MultichannelMixer*)inRefCon;

        float *inDataL, *inDataR;
        AudioBufferList *bufferListL, *bufferListR;
        bufferListL = CAAudioBufferList::Create(1);
        bufferListR = CAAudioBufferList::Create(1);

        //pre-malloc buffers for AudioBufferLists
        bufferListL->mBuffers[0].mNumberChannels = 2;
        bufferListL->mBuffers[0].mDataByteSize = THIS->bufferedFrames;
        bufferListL->mBuffers[0].mData = malloc(THIS->bytesOfBufferedFrames);

        bufferListR->mBuffers[0].mNumberChannels = 2;
        bufferListR->mBuffers[0].mDataByteSize = THIS->bufferedFrames;
        bufferListR->mBuffers[0].mData = malloc(THIS->bytesOfBufferedFrames);

        OSStatus err = noErr;


        // Here is where I am confusing about. If I do not read and set the
        // data one bus by one bus, the Fetch method will show error
and read nothing.
        // So, I should read and set the data for that bus once a
time, is my understanding wrong?

        if (inBusNumber == 0){
            err = THIS->LRingBuffer->Fetch(bufferListL,
inNumberFrames, SInt64(inTimeStamp->mSampleTime -
THIS->SampleTimeUnit), false);
            checkErr(err);
            inDataL = (float *)bufferListL->mBuffers[0].mData;
        }else {
            err = THIS->RRingBuffer->Fetch(bufferListR,
inNumberFrames, SInt64(inTimeStamp->mSampleTime -
THIS->SampleTimeUnit), false);
            checkErr(err);
            inDataR = (float *)bufferListR->mBuffers[0].mData;
        }

            // The ioData parameter does have 2 buffers, because I set
the input bus number to 2.

        float *outA = (float*)ioData->mBuffers[0].mData;
        float *outB = (float*)ioData->mBuffers[1].mData;

            // So, if I got the #1 bus's data, I set them to
outA(Assuming outA is the left channel).

        if (!inDataL) {
        } else {
            for (UInt32 i=0; i<inNumberFrames; ++i)
            {
                outA[i] = inDataL[i];
            }
        }

            // And here is the right one.
        if (!inDataR) {
        } else {
            for (UInt32 i=0; i<inNumberFrames; ++i)
            {
                outB[i] = inDataR[i];
            }
        }

            // After finished using the AudioBufferList structure, destroy it.
        CAAudioBufferList::Destroy(bufferListL);
        CAAudioBufferList::Destroy(bufferListR);

            // Read the next necessary data.
        [THIS readNext:inNumberFrames forBusNumber:inBusNumber];

        return noErr;
    }



[Callback function in CAPlayThru sample code]

    OSStatus renderInput(void *inRefCon,
        AudioUnitRenderActionFlags *ioActionFlags,
        const AudioTimeStamp *inTimeStamp,
        UInt32 inBusNumber,
        UInt32 inNumberFrames,
        AudioBufferList *ioData)
    {
        SynthData& d = *(SynthData*)inRefCon; // get access to Sinewave's data
        UInt32 bufSamples = d.bufs[inBusNumber].numFrames << 1;
        float *in = d.bufs[inBusNumber].data;

        float *outA = (float*)ioData->mBuffers[0].mData;
        float *outB = (float*)ioData->mBuffers[1].mData;
        if (!in) {
            for (UInt32 i=0; i<inNumberFrames; ++i)
            {
                outA[i] = 0.f;
                outB[i] = 0.f;
            }
        } else {
            // Here is the most difficult part for me to understand.
            // Q1: Why does it fill the outA and outB with the same
bus's data and actually 2 times the inNumberFrames required?
            // Q2: What does ioData mean in this callback? Or what
supposed to be fed to ioData?
            UInt32 phase = d.bufs[inBusNumber].phase;
            for (UInt32 i=0; i<inNumberFrames; ++i)
            {
                outA[i] = in[phase++];
                outB[i] = in[phase++];
                if (phase >= bufSamples) phase = 0;
            }
            d.bufs[inBusNumber].phase = phase;
        }
        return noErr;
    }


Thank you in advance.

Tonny Xu
--
Life is like a box of chocolates, u never know what u'r gonna get.
 _______________________________________________
Do not post admin requests to the list. They will be ignored.
Coreaudio-api mailing list      (email@hidden)
Help/Unsubscribe/Update your Subscription:

This email sent to email@hidden

  • Follow-Ups:
    • Re: How to fill the ioData parameter in the Audio Unit rendering call back?
      • From: james mccartney <email@hidden>
  • Prev by Date: MusicSequence MusicPlayer API questions
  • Next by Date: Re: iPhone AudioQueueOfflineRender() woes
  • Previous by thread: Re: MusicSequence MusicPlayer API questions
  • Next by thread: Re: How to fill the ioData parameter in the Audio Unit rendering call back?
  • Index(es):
    • Date
    • Thread