他の誰かが同様の問題に取り組んでいて、グーグルで検索するのに数時間かかる場合に備えて、再生ヘッドを具体的に取得して設定する方法について私が発見したものは次のとおりです。
AUFilePlayerユニットから再生ヘッドを取得するには:
AudioTimeStamp timestamp;
UInt32 size = sizeof(timestamp);
err = AudioUnitGetProperty(unit, kAudioUnitProperty_CurrentPlayTime, kAudioUnitScope_Global, 0, ×tamp, &size);
これtimestamp.mSampleTime
は、そのファイルの現在の再生ヘッドです。floatまたはdoubleにキャストmSampleTime
し、ファイルのサンプルレートで割って、秒に変換します。
AUFilePlayerの再生ヘッドを再起動するために、複数のAUFilePlayerがミキサーを通過し、さまざまな時間、複数回、さまざまなループカウントでスケジュールできる、より複雑なシナリオがありました。これは実際のシナリオであり、すべてを正しい時間に再起動するには、少しのコードが必要でした。
各AUFilePlayerには4つのシナリオがあり、そのスケジュールは次のとおりです。
再生ヘッドは最初にあるので、通常どおりにスケジュールできます。
再生ヘッドはアイテムの期間を過ぎており、スケジュールする必要はまったくありません。
再生ヘッドはアイテムが開始する前にあるため、開始時間を上に移動できます。
再生ヘッドはアイテムの再生中にあるため、ファイル内で再生する領域を調整する必要があり、残りのループは個別にスケジュールする必要があります(完全に再生されるように)。
これを示すいくつかのコードがあります(いくつかの外部構造は私自身のコードからのものであり、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;
}