I am working with core audio using an AUFilePlayer to load a few mp3s into a mixer unit, everything plays great however I am unable to pause the music or rewind the music back to the start. I tried Starting and stopping the AudioGraph, but once the playback is stopped, I can't get it to restart. I also tried using AudioUnitSetProperty to set the file playback to 0
i.e something along these lines:
ScheduledAudioFileRegion rgn;
memset (&rgn.mTimeStamp, 0, sizeof(rgn.mTimeStamp));
rgn.mTimeStamp.mFlags = kAudioTimeStampSampleTimeValid;
rgn.mTimeStamp.mSampleTime = 0;
rgn.mCompletionProc = NULL;
rgn.mCompletionProcUserData = NULL;
rgn.mAudioFile = inputFile;
rgn.mLoopCount = 1;
rgn.mStartFrame = 0;
rgn.mFramesToPlay = nPackets * inputFormat.mFramesPerPacket;
AudioUnitSetProperty(fileUnit, kAudioUnitProperty_ScheduledFileRegion,
kAudioUnitScope_Global, 0,&rgn, sizeof(rgn));
Any suggestions?
In case anyone else is dealing with a similar issue, which cost me several hours searching on google, here's what I've discovered on how to specifically retrieve and set the playhead.
To get the playhead from an AUFilePlayer unit:
AudioTimeStamp timestamp;
UInt32 size = sizeof(timestamp);
err = AudioUnitGetProperty(unit, kAudioUnitProperty_CurrentPlayTime, kAudioUnitScope_Global, 0, ×tamp, &size);
The timestamp.mSampleTime
is the current playhead for that file. Cast mSampleTime
to a float or a double and divide by the file's sample rate to convert to seconds.
For restarting the AUFilePlayer's playhead, I had a more complex scenario where multiple AUFilePlayers pass through a mixer and can be scheduled at different times, multiple times, and with varying loop counts. This is a real-world scenario, and getting them all to restart at the correct time took a little bit of code.
There are four scenarios for each AUFilePlayer and it's schedule:
The playhead is at the beginning, so can be scheduled normally.
The playhead is past the item's duration, and doesn't need to be scheduled at all.
The playhead is before the item has started, so the start time can be moved up.
The playhead is in the middle of playing an item, so the region playing within the file needs to be adjusted, and remaining loops need to be scheduled separately (so they play in full).
Here is some code which demonstrates this (some external structures are from my own code and not Core Audio, but the mechanism should be clear):
// Add each region
for(int iItem = 0; iItem < schedule.items.count; iItem++) {
AEFileScheduleItem *scheduleItem = [schedule.items objectAtIndex:iItem];
// Setup the region
ScheduledAudioFileRegion region;
[file setRegion:®ion schedule:scheduleItem];
// Compute where we are at in it
float playheadTime = schedule.playhead / file.sampleRate;
float endOfItem = scheduleItem.startTime + (file.duration*(1+scheduleItem.loopCount));
// There are four scenarios:
// 1. The playhead is -1
// In this case, we're all done
if(schedule.playhead == -1) {
}
// 2. The playhead is past the item start time and duration*loopCount
// In this case, just ignore it and move on
else if(playheadTime > endOfItem) {
continue;
}
// 3. The playhead is less than or equal to the start time
// In this case, simply subtract the playhead from the start time
else if(playheadTime <= scheduleItem.startTime) {
region.mTimeStamp.mSampleTime -= schedule.playhead;
}
// 4. The playhead is in the middle of the file duration*loopCount
// In this case, set the start time to zero, adjust the loopCount
// startFrame and framesToPlay
else {
// First remove the start time
region.mStartFrame = 0;
double offsetTime = playheadTime - scheduleItem.startTime;
// Next, take out any expired loops
int loopsExpired = floor(offsetTime/file.duration);
int fullLoops = region.mLoopCount - loopsExpired;
region.mLoopCount = 0;
offsetTime -= (loopsExpired * file.duration);
// Then, adjust this segment of a loop accordingly
region.mStartFrame = offsetTime * file.sampleRate;
region.mFramesToPlay = region.mFramesToPlay - region.mStartFrame;
// Finally, schedule any remaining loops separately
if(fullLoops > 0) {
ScheduledAudioFileRegion loops;
[file setRegion:&loops schedule:scheduleItem];
loops.mStartFrame = region.mFramesToPlay;
loops.mLoopCount = fullLoops-1;
if(![super.errors check:AudioUnitSetProperty(unit, kAudioUnitProperty_ScheduledFileRegion, kAudioUnitScope_Global, 0, ®ion, sizeof(region))
location:@"AudioUnitSetProperty(ScheduledFileRegion)"])
return false;
}
}
// Set the region
if(![super.errors check:AudioUnitSetProperty(unit, kAudioUnitProperty_ScheduledFileRegion, kAudioUnitScope_Global, 0, ®ion, sizeof(region))
location:@"AudioUnitSetProperty(ScheduledFileRegion)"])
return false;
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With