• 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
Re: ScheduledAudioFileRegion.mCompletionProc
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: ScheduledAudioFileRegion.mCompletionProc


  • Subject: Re: ScheduledAudioFileRegion.mCompletionProc
  • From: Tim Kemp <email@hidden>
  • Date: Wed, 12 Dec 2012 13:53:45 -0500

Hi Andy,

I had a bit of trouble getting this to work using callbacks. (I use the
audio file player). I want to know when the audio reaches EOF not when the
last region has been loaded. I had trouble with the render callbacks.

I actually got it working fine with a render notify, not a render callback. I'm putting up a lengthy blog post about it with a companion project, but until I do that here's how I achieved it:

- Create a PlaybackInfo struct which holds:
  - the ScheduledAudioFileRegion
  - a handle to whatever selector you want called when playback stops (or the class itself)
  - a variable to hold the number of samples played
  - the file ref
- Add a render notify to the player unit. Pass in the struct instance as the refCon.
- In the render notify, increment the struct's samplesPlayed variable by the number of incoming frames. Then check to see if it's greater than or equal to the length of the region (region.mFramesToPlay) and if so, call the selector.

I don't know if I'm sample-accurate or not, but I'm accurate to the point where I can't hear any obvious lag. Certainly it'll be more on-time than using an NSTimer, which is subject to all kinds of disclaimers about timing.

I'll have sample project and the explanatory post up over the weekend, but for now here's some snippets:

typedef struct PlaybackInfo {
    bool isPlaying;
    long framesPlayed;
    ScheduledAudioFileRegion region;
    ExtAudioFileRef fileRef;
    __unsafe_unretained TKCoreAudioBridge *cab; // The class to call into to stop play
} PlaybackInfo;

I won't show the code which sets up the file to be played, because it's long. It's the usual AudioFilePlayer stuff.

The startPlayback call looks like this. You need to set the render notify when playback starts, and not before:

- (void) startPlayback
{
    _curPlayback.framesPlayed = 0;
    // Apply the region to the file player AU
    checkError(AudioUnitSetProperty(_playerUnit,
                                    kAudioUnitProperty_ScheduledFileRegion,
                                    kAudioUnitScope_Global,
                                    0,
                                    &_curPlayback.region,
                                    sizeof(_curPlayback.region)),
               "AudioUnitSetProperty failed: setting the player AU's region property", false);

    

    // Prime the player
    UInt32 defaultVal = 0;
checkError(AudioUnitSetProperty(_playerUnit,
                                    kAudioUnitProperty_ScheduledFilePrime,
kAudioUnitScope_Global,
                                    0,
                                    &defaultVal,
                                    sizeof(defaultVal)),
  "AudioUnitSetProperty failed: priming the player", false);

    

    // Set up the start time
    AudioTimeStamp startTime;
    memset(&startTime, 0, sizeof(startTime));
    startTime.mFlags = kAudioTimeStampSampleTimeValid;
    startTime.mSampleTime = -1;
    checkError(AudioUnitSetProperty(_playerUnit,
                                    kAudioUnitProperty_ScheduleStartTimeStamp,
                                    kAudioUnitScope_Global,
                                    0,
                                    &startTime,
                                    sizeof(startTime)),
               "AudioUnitSetProperty failed: setting start time", false);
    // Add render notify to playback unit
    checkError(AudioUnitAddRenderNotify(_playerUnit,
                                        &playRenderNotify,
                                        &_curPlayback),
               "AudioUnitAddRenderNotify failed: adding playback notify to player", false);
    _curPlayback.isPlaying = YES;
}

The callback:

OSStatus playRenderNotify(void                        *inRefCon,
                          AudioUnitRenderActionFlags  *ioActionFlags,
                          const AudioTimeStamp        *inTimeStamp,
                          UInt32                      inBusNumber,
                          UInt32                      inNumberFrames,
                          AudioBufferList             *ioData)
{
    if (*ioActionFlags & kAudioUnitRenderAction_PostRender) {
        PlaybackInfo *info = (PlaybackInfo *)inRefCon;
        info->framesPlayed += inNumberFrames;
        if (info->framesPlayed >= info->region.mFramesToPlay) {
            [info->cab stopPlayback];
            [info->cab startPlayback]; // I want to loop, so I start again immediately
        }
    }

    

    return noErr;
}

And stopPlayback:

- (void) stopPlayback
{
    checkError(AudioUnitReset(_playerUnit,
                              kAudioUnitScope_Global,
                              0),
               "AudioUnitReset failed: stopping AudioFilePlayer playback", false);
    // Remove render notify from playback unit
    checkError(AudioUnitRemoveRenderNotify(_playerUnit,
                                           &playRenderNotify,
                                           &_curPlayback),
               "AudioUnitAddRenderNotify failed: adding playback notify to player", false);
    _curPlayback.isPlaying = NO;
}

On 12 Dec 2012, at 13:04, Andy Davidson wrote:

Hi Tim

On 12/11/12 7:01 PM, "Tim Kemp" <email@hidden> wrote:

To keep track when a region has finished playing, you need to count the
frames played yourself and compare to what has been loaded.

What's the best way to do that? Would a render notify on the input of the
mixer that the file player unit is connected to work?


I had a bit of trouble getting this to work using callbacks. (I use the
audio file player). I want to know when the audio reaches EOF not when the
last region has been loaded. I had trouble with the render callbacks.
Sometime I wound up with system time, not the time relative to my time
line. Sorry I can not be more specific. I debugged this code a long time
ago.

I found it easier to calculate the playback time in sec and use a NSTimer.

Andy
 _______________________________________________
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: ScheduledAudioFileRegion.mCompletionProc
      • From: Orestis Markou <email@hidden>
References: 
 >Re: ScheduledAudioFileRegion.mCompletionProc (From: Andy Davidson <email@hidden>)

  • Prev by Date: Re: ScheduledAudioFileRegion.mCompletionProc
  • Next by Date: Re: ScheduledAudioFileRegion.mCompletionProc
  • Previous by thread: Re: ScheduledAudioFileRegion.mCompletionProc
  • Next by thread: Re: ScheduledAudioFileRegion.mCompletionProc
  • Index(es):
    • Date
    • Thread