1. 混合音頻, 首先就是音樂軌道剛開始音量就很大, 并且在組合資源時又突然停止, 用戶會覺得很震耳, 如果聲音剛開始是漸漸增大, 結束的時候聲音漸漸減小, 會帶來更好的體驗. 另外一個問題就是畫外音軌道的處理. 音樂軌道的聲音覆蓋了畫外音, 解決這種音頻軌道沖突一種名為閃避處理的技術. 在畫外音持續期間將音樂聲調低, 畫外音完成后恢復到之前的音量. 可以使用AVAudioMix來解決這兩個問題.
2. 自動調節音量, 在音頻混合的問題上, 最核心的處理就是調整組合音頻軌道的音量, 當一個組合源播放或導出時, 默認行為是以最大音量或正常音量播放的音頻軌道. 通常比較習慣使用分貝來描述音量, AVFoundation使用浮點型數值, 范圍0.0(靜音) ~ 1.0(最大音量). 音頻軌道默認音量為1.0, 不過可以使用AVMutableAudioMixInputParameters實例可以修改這個值. 這個對象允許在一個指定時間點或給定的時間范圍自動調節音量.
3. AVMutableAudioMixInputParameters提供了兩個方法來實現音量調節, setVolume:atTime: 在指定時間點立即調節音量, 音量在音頻軌道持續時間內會保持不變, 直到有另一個音量調節出現. setVolumeRampFromStartVolume:toEndVolume:timeRange: 允許在一個給定時間范圍內平滑地將音量從一個值調節到另外一個值.
AVCompositionTrack *track = //audio track in composition
CMTime twoSeconds = CMTimeMake(2,1);
CMTime fourSeconds = CMTimeMake(4,1);
CMTime sevenSeconds = CMTimeMake(7,1);
AVMutableAudioMixInputParameters *parameters = [AVMutableAudioMixInputParameters audioMixInputParametersWithTrack:track];
[parameters setVolume:0.5 atTime:kCMTimeZero];
CMTimeRange range = CMTimeRangeFromTimeToTime(twoSeconds, fourSecond);
[parameters setVolumeRampFromStartVolume:0.5f toEndVolume:0.8f timeRange:range];
[parameters setVolume:0.3 atTime:sevenSeconds];
AVMutableAudioMix *audioMix = [AVMutableAudioMix audioMix];
audioMix.inputParameters = @[parameters]; ? ?//音頻混合可以設置為AVPlayerItem或AVAssetExportSession的audioMix屬性進行播放或導出.
4. THCopositionBuilder, 具有一個簡單的接口可以接受THTimeline, THTimeline對象包含了應用程序時間區域的狀態并提供了創建組合對象所需的數據.
@property (nonatomic, strong) THTimeline *timeline;
@property (nonatomic, strong) AVMutableComposition *composition;
@implementation THAudioMixCompositionBuilder
- (id)initWithTimeline:(THTimeline *)timeline {
? ? self = [super init];
? ? if (self) {
? ? ? ? _timeline = timeline;
????}
? ? return self;
}
- (id <THComposition>)buildComposition {
? ? self.composition = [AVMutableComposition composition];
? ? [self addCompositionTrackOfType:AVMediaTypeVideo withMediaItems:self.timeline.videos];
? ? [self addCompositionTrackOfType:AVMediaTypeAudio withMediaItems:self.timeline.voiceOvers];
? ? AVMutableCompositionTrack *musicTrack = [self addCompositionTrackOfType: AVMediaTypeAudio withMediaItems:self.timeline.musicItems];
? ? AVAudioMix *audioMix = [self buildAudioMixWithTrack:musicTrack];
? ? return [THAudioMixComposition compositionWithComposition:self.composition audioMix:audioMix];
}
- (AVMutableCompositionTrack *)addCompositionTrackOfType:(NSString *)type withMediaItems:(NSArray *)mediaItems {
? ? if (!THIsEmpty(mediaItems)) {
? ? ? ? CMPersistentTrackID trackID = kCMPersistentTrackID_Invalid;
? ? ? ? AVMutableCompositionTrack *compositionTrack = [self.composition adMutableTrackWithMediaType:type preferredTrackID:trackID];
? ? ? ? CMTime cursorTime = kCMTimeZero; ? ?// set insert cursor to 0
? ? ? ? for (THMediaItem *item in mediaItems) {
? ? ? ? ? ? if (CMTIME_COMPARE_INLINE(item.startTimeInTimeline != kCMTimeInvalid)) {
? ? ? ? ? ? ? ? cursorTime = item.startTimeInTimeline;
????????????}
? ? ? ? ? ? AVAssetTrack *assetTrack = [[item.asset tracksWithMediaType:type] firstObject];
? ? ? ? ? ? [compositionTrack insertTimeRange:item.timeRange ofTrack:assetTrack atTime:cursorTime error:nil];
? ? ? ? ? ? cursorTime = CMTimeAdd(cursorTime, item.timeRange.duration);
????????}
????? ? return compositionTrack;
????}
? ? return nil;
}
@end
- (AVAudioMix *)buildAudioMixWithTrack:(AVMutableCompositionTrack *)track {
? ? THAudioItem *item = [self.timeline.musicItems firstObject];
? ? if (item) {
? ? ? ? AVMutableAudioMix *audioMix = [AVMutableAudioMix audioMix];
? ? ? ? AVMutableAudioMixInputParameters *parameters = [AVMutableAudioMixInputParameters audioMixInputParametersWithTrack:track]; ? ? ? ?
? ? ? ? for (THVolumeAutomation *automation in item.volumeAutomation) {
? ? ? ? ? ? [parameters setVolumeRampFromStartVolume:automation.startVolume toEndVolume:automation.endVolume timeRange:automation.timeRange];
????????}
? ? ? ? audioMix.inputParameters = @[parameters];
? ? ? ? return audioMix;
????}
? ? return nil;
}