我正在使用AUFilePlayer将核心音频加载到混音器单元中,所有内容都很棒,但我无法暂停音乐或将音乐重新回放到开头。我尝试启动和停止AudioGraph,但一旦播放停止,我就无法重启。我也尝试使用AudioUnitSetProperty将文件播放设置为0
即这些内容:
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));
有什么建议吗?
答案 0 :(得分:6)
如果其他人正在处理类似的问题,这需要花费我几个小时在谷歌搜索,这就是我发现的如何专门检索和设置播放头。
从AUFilePlayer单元获取播放头:
AudioTimeStamp timestamp;
UInt32 size = sizeof(timestamp);
err = AudioUnitGetProperty(unit, kAudioUnitProperty_CurrentPlayTime, kAudioUnitScope_Global, 0, ×tamp, &size);
timestamp.mSampleTime
是该文件的当前播放头。将mSampleTime
转换为浮点数或双精度数除以文件的采样率以转换为秒数。
为了重新启动AUFilePlayer的播放头,我有一个更复杂的场景,其中多个AUFilePlayers通过一个混音器,可以在不同的时间,多次,以及不同的循环计数进行调度。这是一个真实的场景,让它们在正确的时间重新启动需要一些代码。
每个AUFilePlayer有四种情况,它的时间表是:
播放头位于开头,因此可以正常安排。
播放头超出了项目的持续时间,根本不需要安排。
播放头在项目开始之前,因此可以提升开始时间。
播放头处于播放项目的中间位置,因此需要调整文件中播放的区域,并且需要单独安排剩余的循环(因此它们完全播放)。
< / LI> 醇>这里有一些代码可以证明这一点(一些外部结构来自我自己的代码,而不是Core Audio,但机制应该是明确的):
// 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;
}
答案 1 :(得分:3)
我明白了。万一你遇到同样的问题,这是我的解决方案:
启动时,我用一组文件播放器音频单元初始化AUGraph。我将文件播放器音频单元阵列中每个轨道的播放头设置为零。
在“暂停”时,首先我停止AuGraph。然后我遍历文件播放器音频单元阵列并捕获当前播放头位置。每次按下暂停按钮,我都会确保将新的当前播放头位置添加到旧的播放头位置以获得其真实位置。
当用户点击播放时,我重新初始化AuGraph,就像我第一次启动应用程序一样,只有当用户点击“暂停”而不是将播放头设置为我存储的数字时告诉它在文件的开头播放。
如果用户点击停止,我将存储的播放头位置设置为零,然后停止AuGraph。